diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index c00b372b84..02cd1beef7 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -32,6 +32,10 @@ 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 @@ -161,7 +165,8 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo) * descriptor of the tuples. */ if (myState->sendDescrip) - SendRowDescriptionMessage(typeinfo, + SendRowDescriptionMessage(&myState->buf, + typeinfo, FetchPortalTargetList(portal), portal->formats); @@ -189,16 +194,109 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo) * send zeroes for the format codes in that case. */ void -SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats) +SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo, + List *targetlist, int16 *formats) { int natts = typeinfo->natts; int proto = PG_PROTOCOL_MAJOR(FrontendProtocol); + + /* tuple descriptor message type */ + pq_beginmessage_reuse(buf, 'T'); + /* # of attrs in tuples */ + pq_sendint16(buf, natts); + + if (proto >= 3) + SendRowDescriptionCols_3(buf, typeinfo, targetlist, formats); + else + SendRowDescriptionCols_2(buf, typeinfo, targetlist, formats); + + pq_endmessage_reuse(buf); +} + +/* + * 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 */ + /* + * Preallocate 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(Oid) /* resorigtbl */ + + sizeof(AttrNumber) /* resorigcol */ + + sizeof(Oid) /* atttypid */ + + sizeof(int16) /* attlen */ + + sizeof(int32) /* attypmod */ + + sizeof(int16) /* format */ + ) * natts); + + for (i = 0; i < natts; ++i) + { + Form_pg_attribute att = TupleDescAttr(typeinfo, i); + Oid atttypid = att->atttypid; + int32 atttypmod = att->atttypmod; + Oid resorigtbl; + AttrNumber 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_writestring(buf, NameStr(att->attname)); + pq_writeint32(buf, resorigtbl); + pq_writeint16(buf, resorigcol); + pq_writeint32(buf, atttypid); + pq_writeint16(buf, att->attlen); + pq_writeint32(buf, atttypmod); + pq_writeint16(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) { @@ -206,44 +304,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_sendint32(buf, atttypid); + pq_sendint16(buf, att->attlen); + pq_sendint32(buf, atttypmod); + /* format info only appears in protocol 3.0 and up */ } - pq_endmessage(&buf); } /* diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index edea6f177b..338ce81331 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -165,6 +165,10 @@ static bool RecoveryConflictPending = false; static bool RecoveryConflictRetryable = true; static ProcSignalReason RecoveryConflictReason; +/* reused buffer to pass to SendRowDescriptionMessage() */ +static MemoryContext row_description_context = NULL; +static StringInfoData row_description_buf; + /* ---------------------------------------------------------------- * decls for routines only used in this file * ---------------------------------------------------------------- @@ -2315,7 +2319,6 @@ static void exec_describe_statement_message(const char *stmt_name) { CachedPlanSource *psrc; - StringInfoData buf; int i; /* @@ -2371,16 +2374,17 @@ exec_describe_statement_message(const char *stmt_name) /* * First describe the parameters... */ - pq_beginmessage(&buf, 't'); /* parameter description message type */ - pq_sendint(&buf, psrc->num_params, 2); + pq_beginmessage_reuse(&row_description_buf, 't'); /* parameter description + * message type */ + pq_sendint(&row_description_buf, psrc->num_params, 2); for (i = 0; i < psrc->num_params; i++) { Oid ptype = psrc->param_types[i]; - pq_sendint(&buf, (int) ptype, 4); + pq_sendint(&row_description_buf, (int) ptype, 4); } - pq_endmessage(&buf); + pq_endmessage_reuse(&row_description_buf); /* * Next send RowDescription or NoData to describe the result... @@ -2392,7 +2396,10 @@ exec_describe_statement_message(const char *stmt_name) /* Get the plan's primary targetlist */ tlist = CachedPlanGetTargetList(psrc, NULL); - SendRowDescriptionMessage(psrc->resultDesc, tlist, NULL); + SendRowDescriptionMessage(&row_description_buf, + psrc->resultDesc, + tlist, + NULL); } else pq_putemptymessage('n'); /* NoData */ @@ -2444,7 +2451,8 @@ exec_describe_portal_message(const char *portal_name) return; /* can't actually do anything... */ if (portal->tupDesc) - SendRowDescriptionMessage(portal->tupDesc, + SendRowDescriptionMessage(&row_description_buf, + portal->tupDesc, FetchPortalTargetList(portal), portal->formats); else @@ -3830,6 +3838,19 @@ PostgresMain(int argc, char *argv[], "MessageContext", ALLOCSET_DEFAULT_SIZES); + /* + * Create memory context and buffer used for RowDescription messages. As + * SendRowDescriptionMessage(), via exec_describe_statement_message(), is + * frequently executed for ever single statement, we don't want to + * allocate a separate buffer every time. + */ + row_description_context = AllocSetContextCreate(TopMemoryContext, + "RowDescriptionContext", + ALLOCSET_DEFAULT_SIZES); + MemoryContextSwitchTo(row_description_context); + initStringInfo(&row_description_buf); + MemoryContextSwitchTo(TopMemoryContext); + /* * Remember stand-alone backend startup time */ diff --git a/src/include/access/printtup.h b/src/include/access/printtup.h index 641715e416..1b5a003a99 100644 --- a/src/include/access/printtup.h +++ b/src/include/access/printtup.h @@ -20,8 +20,8 @@ extern DestReceiver *printtup_create_DR(CommandDest dest); extern void SetRemoteDestReceiverParams(DestReceiver *self, Portal portal); -extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, - int16 *formats); +extern void SendRowDescriptionMessage(StringInfo buf, + TupleDesc typeinfo, List *targetlist, int16 *formats); extern void debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo);