From 4b369d2aa88f0fed53e304932d39defc95b9bbfb Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Wed, 13 Sep 2017 18:41:50 -0700
Subject: [PATCH 3/8] Improve performance of SendRowDescriptionMessage.

Using the new pqformat functions yields performance for statements
with a noticeable number of rows. The function itself is more than
twice as fast.
---
 src/backend/access/common/printtup.c | 156 ++++++++++++++++++++++++++---------
 1 file changed, 118 insertions(+), 38 deletions(-)

diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index 20d20e623e..894f89b63c 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -32,6 +32,8 @@ static bool printtup_internal_20(TupleTableSlot *slot, DestReceiver *self);
 static void printtup_shutdown(DestReceiver *self);
 static void printtup_destroy(DestReceiver *self);
 
+static void SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats);
+static void SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats);
 
 /* ----------------------------------------------------------------
  *		printtup / debugtup support
@@ -189,12 +191,118 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
 {
 	int			natts = typeinfo->natts;
 	int			proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
+	static StringInfo rowDescriptionBuf = NULL;
+
+	/*
+	 * As this routine is executed for every single query, it can be a
+	 * pq_sendstring_prebottleneck. To avoid unnecessary allocator overhead
+	 * reuse a single buffer. That means we'll never shrink below the largest
+	 * row-description sent, but that seems acceptable given the limited size.
+	 */
+	if (unlikely(!rowDescriptionBuf))
+	{
+		MemoryContext oldContext = MemoryContextSwitchTo(TopMemoryContext);
+		rowDescriptionBuf = makeStringInfo();
+		MemoryContextSwitchTo(oldContext);
+	}
+
+	/* tuple descriptor message type */
+	pq_beginmessage_pre(rowDescriptionBuf, 'T');
+	/* # of attrs in tuples */
+	pq_sendint(rowDescriptionBuf, natts, 2);
+
+	if (proto >= 3)
+		SendRowDescriptionCols_3(rowDescriptionBuf, typeinfo, targetlist,
+								 formats);
+	else
+		SendRowDescriptionCols_2(rowDescriptionBuf, typeinfo, targetlist,
+								 formats);
+
+	pq_endmessage_keep(rowDescriptionBuf);
+}
+
+/*
+ * Send description for each column when using v3+ protocol
+ */
+static void
+SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
+{
+	int			natts = typeinfo->natts;
 	int			i;
-	StringInfoData buf;
 	ListCell   *tlist_item = list_head(targetlist);
 
-	pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */
-	pq_sendint(&buf, natts, 2); /* # of attrs in tuples */
+	/*
+	 * Prealloc memory for the entire message to be sent. That allows to use
+	 * the significantly faster inline pqformat.h functions and to avoid
+	 * reallocations. Have to overestimate the size of the column-names, to
+	 * account for character set overhead.
+	 */
+	enlargeStringInfo(buf, (NAMEDATALEN * MAX_CONVERSION_GROWTH /* attname */
+							+ sizeof(int32) /* attlen */
+							+ sizeof(int32) /* resorigtbl */
+							+ sizeof(int16) /* resorigcol */
+							+ sizeof(Oid) /* atttypid */
+							+ sizeof(int16) /* attlen */
+							+ sizeof(int32) /* attypmod */
+						  ) * natts);
+
+	for (i = 0; i < natts; ++i)
+	{
+		Form_pg_attribute att = TupleDescAttr(typeinfo, i);
+		Oid			atttypid = att->atttypid;
+		int32		atttypmod = att->atttypmod;
+		int32		resorigtbl;
+		int32		resorigcol;
+		int16		format;
+
+		/*
+		 * If column is a domain, send the base type and typmod
+		 * instead. Lookup before sending any ints, for efficiency.
+		 */
+		atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
+
+		/* Do we have a non-resjunk tlist item? */
+		while (tlist_item &&
+			   ((TargetEntry *) lfirst(tlist_item))->resjunk)
+			tlist_item = lnext(tlist_item);
+		if (tlist_item)
+		{
+			TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
+
+			resorigtbl = tle->resorigtbl;
+			resorigcol = tle->resorigcol;
+			tlist_item = lnext(tlist_item);
+		}
+		else
+		{
+			/* No info available, so send zeroes */
+			resorigtbl = 0;
+			resorigcol = 0;
+		}
+
+		if (formats)
+			format = formats[i];
+		else
+			format = 0;
+
+		pq_sendstring_pre(buf, NameStr(att->attname));
+		pq_sendint32_pre(buf, resorigtbl);
+		pq_sendint16_pre(buf, resorigcol);
+		pq_sendint32_pre(buf, atttypid);
+		pq_sendint16_pre(buf, att->attlen);
+		pq_sendint32_pre(buf, atttypmod);
+		pq_sendint16_pre(buf, format);
+	}
+}
+
+/*
+ * Send description for each column when using v2 protocol
+ */
+static void
+SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
+{
+	int			natts = typeinfo->natts;
+	int			i;
 
 	for (i = 0; i < natts; ++i)
 	{
@@ -202,44 +310,16 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
 		Oid			atttypid = att->atttypid;
 		int32		atttypmod = att->atttypmod;
 
-		pq_sendstring(&buf, NameStr(att->attname));
-		/* column ID info appears in protocol 3.0 and up */
-		if (proto >= 3)
-		{
-			/* Do we have a non-resjunk tlist item? */
-			while (tlist_item &&
-				   ((TargetEntry *) lfirst(tlist_item))->resjunk)
-				tlist_item = lnext(tlist_item);
-			if (tlist_item)
-			{
-				TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
-
-				pq_sendint(&buf, tle->resorigtbl, 4);
-				pq_sendint(&buf, tle->resorigcol, 2);
-				tlist_item = lnext(tlist_item);
-			}
-			else
-			{
-				/* No info available, so send zeroes */
-				pq_sendint(&buf, 0, 4);
-				pq_sendint(&buf, 0, 2);
-			}
-		}
 		/* If column is a domain, send the base type and typmod instead */
 		atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
-		pq_sendint(&buf, (int) atttypid, sizeof(atttypid));
-		pq_sendint(&buf, att->attlen, sizeof(att->attlen));
-		pq_sendint(&buf, atttypmod, sizeof(atttypmod));
-		/* format info appears in protocol 3.0 and up */
-		if (proto >= 3)
-		{
-			if (formats)
-				pq_sendint(&buf, formats[i], 2);
-			else
-				pq_sendint(&buf, 0, 2);
-		}
+
+		pq_sendstring(buf, NameStr(att->attname));
+		/* column ID only info appears in protocol 3.0 and up */
+		pq_sendint(buf, (int) atttypid, sizeof(atttypid));
+		pq_sendint(buf, att->attlen, sizeof(att->attlen));
+		pq_sendint(buf, atttypmod, sizeof(atttypmod));
+		/* format info only appears in protocol 3.0 and up */
 	}
-	pq_endmessage(&buf);
 }
 
 /*
-- 
2.14.1.536.g6867272d5b.dirty

