Cache datatype-output-function lookup info across calls of concat().
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 19 Sep 2017 19:09:34 +0000 (15:09 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 19 Sep 2017 19:09:38 +0000 (15:09 -0400)
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

index ebfb823fb8cd9d6a593f5faee72d279421d4bc83..260efd519aac733b58d8ecf43bac1605895869da 100644 (file)
@@ -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));
        }
    }