From ed22fb8b0091deea23747310fa7609079a96cf82 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 19 Sep 2017 15:09:34 -0400 Subject: [PATCH] Cache datatype-output-function lookup info across calls of concat(). Testing indicates this can save a third to a half of the runtime of the function. Pavel Stehule, reviewed by Alexander Kuzmenkov Discussion: https://postgr.es/m/CAFj8pRAT62pRgjoHbgTfJUc2uLmeQ4saUj+yVJAEZUiMwNCmdg@mail.gmail.com --- src/backend/utils/adt/varlena.c | 54 +++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index ebfb823fb8..260efd519a 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -4733,11 +4733,48 @@ string_agg_finalfn(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } +/* + * Prepare cache with fmgr info for the output functions of the datatypes of + * the arguments of a concat-like function, beginning with argument "argidx". + * (Arguments before that will have corresponding slots in the resulting + * FmgrInfo array, but we don't fill those slots.) + */ +static FmgrInfo * +build_concat_foutcache(FunctionCallInfo fcinfo, int argidx) +{ + FmgrInfo *foutcache; + int i; + + /* We keep the info in fn_mcxt so it survives across calls */ + foutcache = (FmgrInfo *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + PG_NARGS() * sizeof(FmgrInfo)); + + for (i = argidx; i < PG_NARGS(); i++) + { + Oid valtype; + Oid typOutput; + bool typIsVarlena; + + valtype = get_fn_expr_argtype(fcinfo->flinfo, i); + if (!OidIsValid(valtype)) + elog(ERROR, "could not determine data type of concat() input"); + + getTypeOutputInfo(valtype, &typOutput, &typIsVarlena); + fmgr_info_cxt(typOutput, &foutcache[i], fcinfo->flinfo->fn_mcxt); + } + + fcinfo->flinfo->fn_extra = foutcache; + + return foutcache; +} + /* * Implementation of both concat() and concat_ws(). * * sepstr is the separator string to place between values. - * argidx identifies the first argument to concatenate (counting from zero). + * argidx identifies the first argument to concatenate (counting from zero); + * note that this must be constant across any one series of calls. + * * Returns NULL if result should be NULL, else text value. */ static text * @@ -4746,6 +4783,7 @@ concat_internal(const char *sepstr, int argidx, { text *result; StringInfoData str; + FmgrInfo *foutcache; bool first_arg = true; int i; @@ -4787,14 +4825,16 @@ concat_internal(const char *sepstr, int argidx, /* Normal case without explicit VARIADIC marker */ initStringInfo(&str); + /* Get output function info, building it if first time through */ + foutcache = (FmgrInfo *) fcinfo->flinfo->fn_extra; + if (foutcache == NULL) + foutcache = build_concat_foutcache(fcinfo, argidx); + for (i = argidx; i < PG_NARGS(); i++) { if (!PG_ARGISNULL(i)) { Datum value = PG_GETARG_DATUM(i); - Oid valtype; - Oid typOutput; - bool typIsVarlena; /* add separator if appropriate */ if (first_arg) @@ -4803,12 +4843,8 @@ concat_internal(const char *sepstr, int argidx, appendStringInfoString(&str, sepstr); /* call the appropriate type output function, append the result */ - valtype = get_fn_expr_argtype(fcinfo->flinfo, i); - if (!OidIsValid(valtype)) - elog(ERROR, "could not determine data type of concat() input"); - getTypeOutputInfo(valtype, &typOutput, &typIsVarlena); appendStringInfoString(&str, - OidOutputFunctionCall(typOutput, value)); + OutputFunctionCall(&foutcache[i], value)); } }