From 84d7270e821175041839021c806d9b66fd50f3b8 Mon Sep 17 00:00:00 2001
From: AyoubKAZ <kazarayoub2004@gmail.com>
Date: Mon, 7 Sep 2026 01:45:43 +0200
Subject: [PATCH] COPY TO: avoid prefix re-scan and strlen in
 CopyAttributeOutCSV

CopyAttributeOutCSV makes a preliminary scan over each attribute to
determine if quoting is needed.

On the unquoted path, the preliminary scan has already walked the entire
string until '\0'.  By hoisting tptr to the outer scope, we know the
exact string length (tptr - ptr) and can pass it directly to
CopySendData, avoiding a redundant strlen() pass.

On the quoted path, the scan stopped at the first quoting character at
tptr, but the subsequent escape loop previously restarted from the
beginning of the string, rescanning the clean prefix byte-by-byte in
scalar mode.  In the standard RFC 4180 CSV configuration (escapec ==
quotec), any escapec in [ptr, tptr) would have matched quotec and
stopped the preliminary scan right there.  Thus, the prefix [ptr, tptr)
is guaranteed to contain no escapec characters and can be emitted in one
bulk CopySendData call, beginning the escape loop only at tptr.

The preliminary scan loop itself is left untouched, introducing zero
additional instructions or branches.
---
 src/backend/commands/copyto.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c
index 5850608a3fb..b3e1804bf5c 100644
--- a/src/backend/commands/copyto.c
+++ b/src/backend/commands/copyto.c
@@ -1598,6 +1598,7 @@ CopyAttributeOutCSV(CopyToState cstate, const char *string,
 {
 	const char *ptr;
 	const char *start;
+	const char *tptr;
 	char		c;
 	char		delimc = cstate->opts.delim[0];
 	char		quotec = cstate->opts.quote[0];
@@ -1613,6 +1614,8 @@ CopyAttributeOutCSV(CopyToState cstate, const char *string,
 	else
 		ptr = string;
 
+	tptr = ptr;
+
 	/*
 	 * Make a preliminary pass to discover if it needs quoting
 	 */
@@ -1629,8 +1632,6 @@ CopyAttributeOutCSV(CopyToState cstate, const char *string,
 			use_quote = true;
 		else
 		{
-			const char *tptr = ptr;
-
 			while ((c = *tptr) != '\0')
 			{
 				if (c == delimc || c == quotec || c == '\n' || c == '\r')
@@ -1650,6 +1651,18 @@ CopyAttributeOutCSV(CopyToState cstate, const char *string,
 	{
 		CopySendChar(cstate, quotec);
 
+		/*
+		 * When escapec == quotec, any escapec in [ptr, tptr) would have
+		 * matched quotec and triggered use_quote. Thus the scanned prefix
+		 * contains no escapec and can be emitted in bulk, skipping a
+		 * redundant re-scan in the escape loop.
+		 */
+		if (tptr > ptr && escapec == quotec)
+		{
+			CopySendData(cstate, ptr, tptr - ptr);
+			ptr = tptr;
+		}
+
 		/*
 		 * We adopt the same optimization strategy as in CopyAttributeOutText
 		 */
@@ -1674,7 +1687,7 @@ CopyAttributeOutCSV(CopyToState cstate, const char *string,
 	else
 	{
 		/* If it doesn't need quoting, we can just dump it as-is */
-		CopySendString(cstate, ptr);
+		CopySendData(cstate, ptr, tptr - ptr);
 	}
 }
 
-- 
2.34.1

