]> granicus.if.org Git - postgresql/commitdiff
Add a utility function to extract variadic function arguments
authorAndrew Dunstan <andrew@dunslane.net>
Wed, 25 Oct 2017 11:13:11 +0000 (07:13 -0400)
committerAndrew Dunstan <andrew@dunslane.net>
Wed, 25 Oct 2017 11:19:59 +0000 (07:19 -0400)
This is epecially useful in the case or "VARIADIC ANY" functions. The
caller can get the artguments and types regardless of whether or not and
explicit VARIADIC array argument has been used. The function also
provides an option to convert arguments on type "unknown" to to "text".

Michael Paquier and me, reviewed by Tom Lane.

Backpatch to 9.4 in order to support the following json bug fix.

src/backend/utils/fmgr/funcapi.c
src/include/funcapi.h

index 34dfd84074b1b7f39cddf7c7946b49f01485c678..10e2fffd6f673eabf77e3ba0ddde83f37bab3541 100644 (file)
@@ -2,7 +2,7 @@
  *
  * funcapi.c
  *       Utility and convenience functions for fmgr functions that return
- *       sets and/or composite types.
+ *       sets and/or composite types, or deal with VARIADIC inputs.
  *
  * Copyright (c) 2002-2016, PostgreSQL Global Development Group
  *
@@ -1396,3 +1396,116 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
 
        return tupdesc;
 }
+
+/*
+ * extract_variadic_args
+ *
+ * Extract a set of argument values, types and NULL markers for a given
+ * input function which makes use of a VARIADIC input whose argument list
+ * depends on the caller context. When doing a VARIADIC call, the caller
+ * has provided one argument made of an array of values, so deconstruct the
+ * array data before using it for the next processing. If no VARIADIC call
+ * is used, just fill in the status data based on all the arguments given
+ * by the caller.
+ *
+ * This function returns the number of arguments generated, or -1 in the
+ * case of "VARIADIC NULL".
+ */
+int
+extract_variadic_args(FunctionCallInfo fcinfo, int variadic_start,
+                                         bool convert_unknown, Datum **args, Oid **types,
+                                         bool **nulls)
+{
+       bool            variadic = get_fn_expr_variadic(fcinfo->flinfo);
+       Datum      *args_res;
+       bool       *nulls_res;
+       Oid                *types_res;
+       int                     nargs, i;
+
+       *args = NULL;
+       *types = NULL;
+       *nulls = NULL;
+
+       if (variadic)
+       {
+               ArrayType  *array_in;
+               Oid                     element_type;
+               bool            typbyval;
+               char            typalign;
+               int16           typlen;
+
+               Assert(PG_NARGS() == variadic_start + 1);
+
+               if (PG_ARGISNULL(variadic_start))
+                       return -1;
+
+               array_in = PG_GETARG_ARRAYTYPE_P(variadic_start);
+               element_type = ARR_ELEMTYPE(array_in);
+
+               get_typlenbyvalalign(element_type,
+                                                        &typlen, &typbyval, &typalign);
+               deconstruct_array(array_in, element_type, typlen, typbyval,
+                                                 typalign, &args_res, &nulls_res,
+                                                 &nargs);
+
+               /* All the elements of the array have the same type */
+               types_res = (Oid *) palloc0(nargs * sizeof(Oid));
+               for (i = 0; i < nargs; i++)
+                       types_res[i] = element_type;
+       }
+       else
+       {
+               nargs = PG_NARGS() - variadic_start;
+               Assert (nargs > 0);
+               nulls_res = (bool *) palloc0(nargs * sizeof(bool));
+               args_res = (Datum *) palloc0(nargs * sizeof(Datum));
+               types_res = (Oid *) palloc0(nargs * sizeof(Oid));
+
+               for (i = 0; i < nargs; i++)
+               {
+                       nulls_res[i] = PG_ARGISNULL(i + variadic_start);
+                       types_res[i] = get_fn_expr_argtype(fcinfo->flinfo,
+                                                                                          i + variadic_start);
+
+                       /*
+                        * Turn a constant (more or less literal) value that's of unknown
+                        * type into text if required . Unknowns come in as a cstring
+                        * pointer.
+                        * Note: for functions declared as taking type "any", the parser
+                        * will not do any type conversion on unknown-type literals (that
+                        * is, undecorated strings or NULLs).
+                        */
+                       if (convert_unknown &&
+                               types_res[i] == UNKNOWNOID &&
+                               get_fn_expr_arg_stable(fcinfo->flinfo, i + variadic_start))
+                       {
+                               types_res[i] = TEXTOID;
+
+                               if (PG_ARGISNULL(i + variadic_start))
+                                       args_res[i] = (Datum) 0;
+                               else
+                                       args_res[i] =
+                                               CStringGetTextDatum(PG_GETARG_POINTER(i + variadic_start));
+                       }
+                       else
+                       {
+                               /* no conversion needed, just take the datum as given */
+                               args_res[i] = PG_GETARG_DATUM(i + variadic_start);
+                       }
+
+                       if (!OidIsValid(types_res[i]) ||
+                               (convert_unknown && types_res[i] == UNKNOWNOID))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                errmsg("could not determine data type for argument %d",
+                                                               i + 1)));
+               }
+       }
+
+       /* Fill in results */
+       *args = args_res;
+       *nulls = nulls_res;
+       *types = types_res;
+
+       return nargs;
+}
index e73a82427ca3dd0684e7c24621f74ade731e6432..2b39c7ee38fbb82d8fb80e2dba86c8894195ed3c 100644 (file)
@@ -2,6 +2,7 @@
  *
  * funcapi.h
  *       Definitions for functions which return composite type and/or sets
+ *       or work on VARIADIC inputs.
  *
  * This file must be included by all Postgres modules that either define
  * or call FUNCAPI-callable functions or macros.
@@ -315,4 +316,26 @@ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
                PG_RETURN_NULL(); \
        } while (0)
 
+/*----------
+ *     Support to ease writing of functions dealing with VARIADIC inputs
+ *----------
+ *
+ * This function extracts a set of argument values, types and NULL markers
+ * for a given input function. This returns a set of data:
+ * - **values includes the set of Datum values extracted.
+ * - **types the data type OID for each element.
+ * - **nulls tracks if an element is NULL.
+ *
+ * variadic_start indicates the argument number where the VARIADIC argument
+ * starts.
+ * convert_unknown set to true will enforce the conversion of arguments
+ * with unknown data type to text.
+ *
+ * The return result is the number of elements stored, or -1 in the case of
+ * "VARIADIC NULL".
+ */
+extern int extract_variadic_args(FunctionCallInfo fcinfo, int variadic_start,
+                                                                bool convert_unknown, Datum **values,
+                                                                Oid **types, bool **nulls);
+
 #endif   /* FUNCAPI_H */