From c06aa2636a47812ef17d8be7d7e43a8a49e88403 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Sat, 17 Feb 2024 17:38:47 -0800 Subject: [PATCH v3 10/10] heavy-wip: Allow string buffer reuse in send functions Author: Reviewed-By: Discussion: https://postgr.es/m/ Backpatch: --- src/include/libpq/pqformat.h | 19 +++++++++++++++++-- src/backend/access/common/printtup.c | 9 +++++++-- src/backend/commands/copyto.c | 8 +++++++- 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 | 17 ++++++++++++++++- 7 files changed, 64 insertions(+), 18 deletions(-) diff --git a/src/include/libpq/pqformat.h b/src/include/libpq/pqformat.h index bc35bf3f28a..7792691b4cf 100644 --- a/src/include/libpq/pqformat.h +++ b/src/include/libpq/pqformat.h @@ -40,8 +40,16 @@ 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; } /* -------------------------------- @@ -61,6 +69,13 @@ pq_begintypsend_with_size(StringInfo buf, int size) appendStringInfoSpaces(buf, 4); } +static inline void +pq_begintypsend_res(StringInfo buf) +{ + 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 bee3fc26220..d7d6d12f242 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -69,6 +69,7 @@ typedef struct int nattrs; PrinttupAttrInfo *myinfo; /* Cached info about each attr */ StringInfoData buf; /* output buffer (*not* in tmpcontext) */ + StringInfoData fieldbuf; /* FIXME */ MemoryContext tmpcontext; /* Memory context for per-row workspace */ } DR_printtup; @@ -128,6 +129,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 @@ -289,7 +292,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) { @@ -299,7 +302,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, @@ -319,6 +322,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; @@ -398,6 +402,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self) pq_sendint32(buf, outputlen); pq_sendbytes(buf, VARDATA(outputbytes), outputlen); + resetStringInfo(fieldbuf); } } diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c index b5cada5cb75..dd552db4828 100644 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@ -110,6 +110,8 @@ typedef struct CopyToStateData */ MemoryContext copycontext; /* per-copy execution context */ + StringInfoData attribute_buf; + CopyOutAttributeInfo *out_attributes; MemoryContext rowcontext; /* per-row evaluation context */ @@ -782,6 +784,8 @@ DoCopyTo(CopyToState cstate) /* We use fe_msgbuf as a per-row buffer regardless of copy_dest */ cstate->fe_msgbuf = makeStringInfo(); + initStringInfo(&cstate->attribute_buf); + /* Get info about the columns we need to process. */ cstate->out_attributes = (CopyOutAttributeInfo *) palloc(num_phys_attrs * sizeof(CopyOutAttributeInfo)); @@ -810,7 +814,7 @@ DoCopyTo(CopyToState 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); } /* @@ -938,6 +942,7 @@ CopyOneRowTo(CopyToState cstate, TupleTableSlot *slot) MemoryContext oldcontext; ListCell *cur; char *string; + StringInfo attribute_buf = &cstate->attribute_buf; MemoryContextReset(cstate->rowcontext); oldcontext = MemoryContextSwitchTo(cstate->rowcontext); @@ -1021,6 +1026,7 @@ CopyOneRowTo(CopyToState 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 761dc4acce9..2fdf2b247de 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -322,11 +322,13 @@ Datum int4send(PG_FUNCTION_ARGS) { int32 arg1 = PG_GETARG_INT32(0); - StringInfoData buf; + StringInfo buf; - pq_begintypsend_with_size(&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 5423cb2ecde..a171125b110 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -95,11 +95,12 @@ Datum int8send(PG_FUNCTION_ARGS) { int64 arg1 = PG_GETARG_INT64(0); - StringInfoData buf; + StringInfo buf; - pq_begintypsend_with_size(&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 ea696c1dc71..387e368726e 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -619,11 +619,13 @@ Datum textsend(PG_FUNCTION_ARGS) { text *t = PG_GETARG_TEXT_PP(0); - StringInfoData buf; + StringInfo buf = (StringInfo) fcinfo->context; - pq_begintypsend_with_size(&buf, VARSIZE_ANY_EXHDR(t)); - 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 e48a86be54b..5677a9a6bdf 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -1743,7 +1743,22 @@ 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.38.0