From 7b9913519e51a683059193261c8630eb953b3b99 Mon Sep 17 00:00:00 2001 From: Andres Freund 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