From 7b9913519e51a683059193261c8630eb953b3b99 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 2 Jun 2020 17:48:43 -0700
Subject: [PATCH v2 9/9] heavy-wip: Allow string buffer reuse in send
 functions.

Author:
Reviewed-By:
Discussion: https://postgr.es/m/
Backpatch:
---
 src/include/libpq/pqformat.h         | 30 +++++++++++++---------------
 src/backend/access/common/printtup.c |  9 +++++++--
 src/backend/commands/copy.c          |  5 ++++-
 src/backend/utils/adt/int.c          | 10 ++++++----
 src/backend/utils/adt/int8.c         |  9 +++++----
 src/backend/utils/adt/varlena.c      | 10 ++++++----
 src/backend/utils/fmgr/fmgr.c        | 16 ++++++++++++++-
 7 files changed, 57 insertions(+), 32 deletions(-)

diff --git a/src/include/libpq/pqformat.h b/src/include/libpq/pqformat.h
index 6af153b7f77..c8ac781c726 100644
--- a/src/include/libpq/pqformat.h
+++ b/src/include/libpq/pqformat.h
@@ -39,27 +39,25 @@ static inline void
 pq_begintypsend(StringInfo buf)
 {
 	initStringInfo(buf);
-	/* Reserve four bytes for the bytea length word */
-	appendStringInfoSpaces(buf, 4);
+
+	/*
+	 * Reserve four bytes for the bytea length word.  We don't need to fill
+	 * them with anything (pq_endtypsend will do that), and this function is
+	 * enough of a hot spot that it's worth cheating to save some cycles. Note
+	 * in particular that we don't bother to guarantee that the buffer is
+	 * null-terminated.
+	 */
+	Assert(buf->maxlen > 4);
+	buf->len = 4;
 }
 
-/* --------------------------------
- *		pq_begintypsend_ex	- like pq_begintypesend, but with length hint
- *
- * This can be used over pq_begintypesend if the caller can cheaply determine
- * how much data will be sent, reducing the initial size of the
- * StringInfo. The passed in size need not include the overhead of the length
- * word.
- * --------------------------------
- */
 static inline void
-pq_begintypsend_ex(StringInfo buf, int size)
+pq_begintypsend_res(StringInfo buf)
 {
-	initStringInfoEx(buf, size + 4);
-	/* Reserve four bytes for the bytea length word */
-	appendStringInfoSpaces(buf, 4);
-}
+	Assert(buf && buf->data && buf->len == 0);
 
+	buf->len = 4;
+}
 
 /* --------------------------------
  *		pq_endtypsend	- finish constructing a bytea result
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index db23bb99c16..7065dc2110b 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -76,6 +76,7 @@ typedef struct
 	int			nattrs;
 	PrinttupAttrInfo *myinfo;	/* Cached info about each attr */
 	StringInfoData buf;			/* output buffer (*not* in tmpcontext) */
+	StringInfoData fieldbuf;	/*  */
 	MemoryContext tmpcontext;	/* Memory context for per-row workspace */
 } DR_printtup;
 
@@ -148,6 +149,8 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	 */
 	initStringInfo(&myState->buf);
 
+	initStringInfo(&myState->fieldbuf);
+
 	/*
 	 * Create a temporary memory context that we can reset once per row to
 	 * recover palloc'd memory.  This avoids any problems with leaks inside
@@ -366,7 +369,7 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
 			fmgr_info(thisState->typoutput, &thisState->finfo);
 			InitFunctionCallInfoData(thisState->fcinfo_data.fcinfo,
 									 &thisState->finfo, 1, InvalidOid,
-									 NULL, NULL);
+									 (Node *) &myState->fieldbuf, NULL);
 		}
 		else if (format == 1)
 		{
@@ -376,7 +379,7 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
 			fmgr_info(thisState->typsend, &thisState->finfo);
 			InitFunctionCallInfoData(thisState->fcinfo_data.fcinfo,
 									 &thisState->finfo, 1, InvalidOid,
-									 NULL, NULL);
+									 (Node *) &myState->fieldbuf, NULL);
 		}
 		else
 			ereport(ERROR,
@@ -396,6 +399,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
 	DR_printtup *myState = (DR_printtup *) self;
 	MemoryContext oldcontext;
 	StringInfo	buf = &myState->buf;
+	StringInfo	fieldbuf = &myState->fieldbuf;
 	int			natts = typeinfo->natts;
 	int			i;
 
@@ -472,6 +476,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
 
 			pq_sendint32(buf, outputlen);
 			pq_sendbytes(buf, VARDATA(outputbytes), outputlen);
+			resetStringInfo(fieldbuf);
 		}
 	}
 
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index bec1ce51260..45945c2f67f 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1902,6 +1902,7 @@ BeginCopyTo(ParseState *pstate,
 
 	cstate = BeginCopy(pstate, false, rel, query, queryRelId, attnamelist,
 					   options);
+	initStringInfo(&cstate->attribute_buf);
 	oldcontext = MemoryContextSwitchTo(cstate->copycontext);
 
 	if (pipe)
@@ -2084,7 +2085,7 @@ CopyTo(CopyState cstate)
 		fmgr_info(out_func_oid, &attr->out_finfo);
 		InitFunctionCallInfoData(attr->out_fcinfo.fcinfo, &attr->out_finfo,
 								 1, InvalidOid,
-								 NULL, NULL);
+								 (Node *) &cstate->attribute_buf, NULL);
 	}
 
 	/*
@@ -2201,6 +2202,7 @@ CopyOneRowTo(CopyState cstate, TupleTableSlot *slot)
 	MemoryContext oldcontext;
 	ListCell   *cur;
 	char	   *string;
+	StringInfo	attribute_buf = &cstate->attribute_buf;
 
 	MemoryContextReset(cstate->rowcontext);
 	oldcontext = MemoryContextSwitchTo(cstate->rowcontext);
@@ -2279,6 +2281,7 @@ CopyOneRowTo(CopyState cstate, TupleTableSlot *slot)
 
 				CopySendInt32(cstate, outputlen);
 				CopySendData(cstate, VARDATA(outputbytes), outputlen);
+				resetStringInfo(attribute_buf);
 			}
 		}
 	}
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 84d22fc5b99..811527b0eb0 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -303,11 +303,13 @@ Datum
 int4send(PG_FUNCTION_ARGS)
 {
 	int32		arg1 = PG_GETARG_INT32(0);
-	StringInfoData buf;
+	StringInfo	buf;
 
-	pq_begintypsend_ex(&buf, 4);
-	pq_sendint32(&buf, arg1);
-	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+	buf = (StringInfo ) fcinfo->context;
+
+	pq_begintypsend_res(buf);
+	pq_sendint32(buf, arg1);
+	PG_RETURN_BYTEA_P(pq_endtypsend(buf));
 }
 
 
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index b258778622d..dad0663dc58 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -173,11 +173,12 @@ Datum
 int8send(PG_FUNCTION_ARGS)
 {
 	int64		arg1 = PG_GETARG_INT64(0);
-	StringInfoData buf;
+	StringInfo	buf;
 
-	pq_begintypsend_ex(&buf, 8);
-	pq_sendint64(&buf, arg1);
-	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+	buf = (StringInfo ) fcinfo->context;
+	pq_begintypsend_res(buf);
+	pq_sendint64(buf, arg1);
+	PG_RETURN_BYTEA_P(pq_endtypsend(buf));
 }
 
 
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index f3c1a63455a..29edb03b49a 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -582,11 +582,13 @@ Datum
 textsend(PG_FUNCTION_ARGS)
 {
 	text	   *t = PG_GETARG_TEXT_PP(0);
-	StringInfoData buf;
+	StringInfo	buf = (StringInfo ) fcinfo->context;
 
-	pq_begintypsend(&buf);
-	pq_sendtext(&buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
-	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+	Assert(fcinfo->context);
+
+	pq_begintypsend_res(buf);
+	pq_sendtext(buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
+	PG_RETURN_BYTEA_P(pq_endtypsend(buf));
 }
 
 
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 03c614b234a..13483b09881 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -1637,7 +1637,21 @@ ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
 bytea *
 SendFunctionCall(FmgrInfo *flinfo, Datum val)
 {
-	return DatumGetByteaP(FunctionCall1(flinfo, val));
+	StringInfoData buf;
+	Datum result;
+	LOCAL_FCINFO(fcinfo, 1);
+
+	initStringInfo(&buf);
+
+	InitFunctionCallInfoData(*fcinfo, flinfo, 1, InvalidOid, (Node *) &buf, NULL);
+	fcinfo->args[0].value = val;
+	fcinfo->args[0].isnull = false;
+	result = FunctionCallInvoke(fcinfo);
+
+	if (fcinfo->isnull)
+		elog(ERROR, "function %u returned NULL", flinfo->fn_oid);
+
+	return DatumGetByteaP(result);
 }
 
 /*
-- 
2.25.0.114.g5b0ca878e0

