Move checking an explicit VARIADIC "any" argument into the parser.
authorAndrew Dunstan <andrew@dunslane.net>
Thu, 18 Jul 2013 15:52:12 +0000 (11:52 -0400)
committerAndrew Dunstan <andrew@dunslane.net>
Thu, 18 Jul 2013 15:52:12 +0000 (11:52 -0400)
This is more efficient and simpler . It does mean that an untyped NULL
can no longer be used in such cases, which should be mentioned in
Release Notes, but doesn't seem a terrible loss. The workaround is to
cast the NULL to some array type.

Pavel Stehule, reviewed by Jeevan Chalke.

src/backend/catalog/pg_aggregate.c
src/backend/parser/parse_func.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/varlena.c
src/include/parser/parse_func.h
src/test/regress/expected/text.out
src/test/regress/sql/text.sql

index e80b60053d5e792e8e5155c84d0635ed3a303a6f..480c17cd3908375cbb70d6dfeb6659ee0da2810c 100644 (file)
@@ -332,6 +332,7 @@ lookup_agg_function(List *fnName,
    Oid         fnOid;
    bool        retset;
    int         nvargs;
+   Oid         vatype;
    Oid        *true_oid_array;
    FuncDetailCode fdresult;
    AclResult   aclresult;
@@ -346,7 +347,8 @@ lookup_agg_function(List *fnName,
     */
    fdresult = func_get_detail(fnName, NIL, NIL,
                               nargs, input_types, false, false,
-                              &fnOid, rettype, &retset, &nvargs,
+                              &fnOid, rettype, &retset,
+                              &nvargs, &vatype,
                               &true_oid_array, NULL);
 
    /* only valid case is a normal function not returning a set */
index e54922f8037b414ee1259d276965889edb44d6b6..1f02c9a57572cca5026309ca028d3520ec0358f4 100644 (file)
@@ -79,6 +79,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
    Node       *retval;
    bool        retset;
    int         nvargs;
+   Oid         vatype;
    FuncDetailCode fdresult;
 
    /*
@@ -214,7 +215,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
    fdresult = func_get_detail(funcname, fargs, argnames, nargs,
                               actual_arg_types,
                               !func_variadic, true,
-                              &funcid, &rettype, &retset, &nvargs,
+                              &funcid, &rettype, &retset,
+                              &nvargs, &vatype,
                               &declared_arg_types, &argdefaults);
    if (fdresult == FUNCDETAIL_COERCION)
    {
@@ -382,6 +384,22 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
        fargs = lappend(fargs, newa);
    }
 
+   /*
+    * When function is called an explicit VARIADIC labeled parameter,
+    * and the declared_arg_type is "any", then sanity check the actual
+    * parameter type now - it must be an array.
+    */
+   if (nargs > 0 && vatype == ANYOID && func_variadic)
+   {
+       Oid     va_arr_typid = actual_arg_types[nargs - 1];
+
+       if (!OidIsValid(get_element_type(va_arr_typid)))
+           ereport(ERROR,
+                   (errcode(ERRCODE_DATATYPE_MISMATCH),
+                    errmsg("VARIADIC argument must be an array"),
+             parser_errposition(pstate, exprLocation((Node *) llast(fargs)))));
+   }
+
    /* build the appropriate output structure */
    if (fdresult == FUNCDETAIL_NORMAL)
    {
@@ -1033,6 +1051,7 @@ func_get_detail(List *funcname,
                Oid *rettype,   /* return value */
                bool *retset,   /* return value */
                int *nvargs,    /* return value */
+               Oid *vatype,    /* return value */
                Oid **true_typeids,     /* return value */
                List **argdefaults)     /* optional return value */
 {
@@ -1251,6 +1270,7 @@ func_get_detail(List *funcname,
        pform = (Form_pg_proc) GETSTRUCT(ftup);
        *rettype = pform->prorettype;
        *retset = pform->proretset;
+       *vatype = pform->provariadic;
        /* fetch default args if caller wants 'em */
        if (argdefaults && best_candidate->ndargs > 0)
        {
index 976bc98e3756727514e090d7415adf875f29dc7a..40b565a11d6ddf40b3cd8e692e625efe1f6dc3a7 100644 (file)
@@ -8584,6 +8584,7 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
    Oid         p_rettype;
    bool        p_retset;
    int         p_nvargs;
+   Oid         p_vatype;
    Oid        *p_true_typeids;
 
    proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
@@ -8634,7 +8635,8 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
                               NIL, argnames, nargs, argtypes,
                               !use_variadic, true,
                               &p_funcid, &p_rettype,
-                              &p_retset, &p_nvargs, &p_true_typeids, NULL);
+                              &p_retset, &p_nvargs, &p_vatype,
+                              &p_true_typeids, NULL);
    if ((p_result == FUNCDETAIL_NORMAL ||
         p_result == FUNCDETAIL_AGGREGATE ||
         p_result == FUNCDETAIL_WINDOWFUNC) &&
index 56349e7e2aa6b45c927d2f03e6674ddacfe1fdf2..4aa344896f9f704b8a1681a8978c0218ee49b510 100644 (file)
@@ -3820,7 +3820,6 @@ concat_internal(const char *sepstr, int argidx,
     */
    if (get_fn_expr_variadic(fcinfo->flinfo))
    {
-       Oid         arr_typid;
        ArrayType  *arr;
 
        /* Should have just the one argument */
@@ -3831,20 +3830,16 @@ concat_internal(const char *sepstr, int argidx,
            return NULL;
 
        /*
-        * Non-null argument had better be an array.  The parser doesn't
-        * enforce this for VARIADIC ANY functions (maybe it should?), so that
-        * check uses ereport not just elog.
+        * Non-null argument had better be an array
+        *
+        * Correct values are ensured by parser check, but this function
+        * can be called directly, bypassing the parser, so we should do
+        * some minimal check too - this form of call requires correctly set
+        * expr argtype in flinfo.
         */
-       arr_typid = get_fn_expr_argtype(fcinfo->flinfo, argidx);
-       if (!OidIsValid(arr_typid))
-           elog(ERROR, "could not determine data type of concat() input");
-
-       if (!OidIsValid(get_element_type(arr_typid)))
-           ereport(ERROR,
-                   (errcode(ERRCODE_DATATYPE_MISMATCH),
-                    errmsg("VARIADIC argument must be an array")));
+       Assert(OidIsValid(get_fn_expr_argtype(fcinfo->flinfo, argidx)));
+       Assert(OidIsValid(get_element_type(get_fn_expr_argtype(fcinfo->flinfo, argidx))));
 
-       /* OK, safe to fetch the array value */
        arr = PG_GETARG_ARRAYTYPE_P(argidx);
 
        /*
@@ -4049,7 +4044,6 @@ text_format(PG_FUNCTION_ARGS)
    /* If argument is marked VARIADIC, expand array into elements */
    if (get_fn_expr_variadic(fcinfo->flinfo))
    {
-       Oid         arr_typid;
        ArrayType  *arr;
        int16       elmlen;
        bool        elmbyval;
@@ -4065,20 +4059,16 @@ text_format(PG_FUNCTION_ARGS)
        else
        {
            /*
-            * Non-null argument had better be an array.  The parser doesn't
-            * enforce this for VARIADIC ANY functions (maybe it should?), so
-            * that check uses ereport not just elog.
+            * Non-null argument had better be an array
+            *
+            * Correct values are ensured by parser check, but this function
+            * can be called directly, bypassing the parser, so we should do
+            * some minimal check too - this form of call requires correctly set
+            * expr argtype in flinfo.
             */
-           arr_typid = get_fn_expr_argtype(fcinfo->flinfo, 1);
-           if (!OidIsValid(arr_typid))
-               elog(ERROR, "could not determine data type of format() input");
-
-           if (!OidIsValid(get_element_type(arr_typid)))
-               ereport(ERROR,
-                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                        errmsg("VARIADIC argument must be an array")));
+           Assert(OidIsValid(get_fn_expr_argtype(fcinfo->flinfo, 1)));
+           Assert(OidIsValid(get_element_type(get_fn_expr_argtype(fcinfo->flinfo, 1))));
 
-           /* OK, safe to fetch the array value */
            arr = PG_GETARG_ARRAYTYPE_P(1);
 
            /* Get info about array element type */
index d63cb95b3479cc7d8b3e273c42285812b9601a5d..d33eef3482c18bfae64d86ca5968bde05b984b46 100644 (file)
@@ -52,8 +52,8 @@ extern FuncDetailCode func_get_detail(List *funcname,
                int nargs, Oid *argtypes,
                bool expand_variadic, bool expand_defaults,
                Oid *funcid, Oid *rettype,
-               bool *retset, int *nvargs, Oid **true_typeids,
-               List **argdefaults);
+               bool *retset, int *nvargs, Oid *vatype,
+               Oid **true_typeids, List **argdefaults);
 
 extern int func_match_argtypes(int nargs,
                    Oid *input_typeids,
index 4b1c62bf53c9f92c0e01ed111fa2710e8d7658a7..1587046d95aec566b7ad6cc6bc9d7c3b5a40f7a7 100644 (file)
@@ -149,13 +149,13 @@ select concat_ws(',', variadic array[1,2,3]);
  1,2,3
 (1 row)
 
-select concat_ws(',', variadic NULL);
+select concat_ws(',', variadic NULL::int[]);
  concat_ws 
 -----------
  
 (1 row)
 
-select concat(variadic NULL) is NULL;
+select concat(variadic NULL::int[]) is NULL;
  ?column? 
 ----------
  t
@@ -170,6 +170,8 @@ select concat(variadic '{}'::int[]) = '';
 --should fail
 select concat_ws(',', variadic 10);
 ERROR:  VARIADIC argument must be an array
+LINE 1: select concat_ws(',', variadic 10);
+                                       ^
 /*
  * format
  */
@@ -315,8 +317,8 @@ select format('%2$s, %1$s', variadic array[1, 2]);
  2, 1
 (1 row)
 
--- variadic argument can be NULL, but should not be referenced
-select format('Hello', variadic NULL);
+-- variadic argument can be array type NULL, but should not be referenced
+select format('Hello', variadic NULL::int[]);
  format 
 --------
  Hello
index c4ed74b39d4424627f6456feeef03a5193ba8999..60c15b54c0f51ebf0ce4b3fd10a231ca507b638f 100644 (file)
@@ -47,8 +47,8 @@ select quote_literal(e'\\');
 -- check variadic labeled argument
 select concat(variadic array[1,2,3]);
 select concat_ws(',', variadic array[1,2,3]);
-select concat_ws(',', variadic NULL);
-select concat(variadic NULL) is NULL;
+select concat_ws(',', variadic NULL::int[]);
+select concat(variadic NULL::int[]) is NULL;
 select concat(variadic '{}'::int[]) = '';
 --should fail
 select concat_ws(',', variadic 10);
@@ -93,8 +93,8 @@ select format('%s, %s', variadic array[true, false]::text[]);
 -- check variadic with positional placeholders
 select format('%2$s, %1$s', variadic array['first', 'second']);
 select format('%2$s, %1$s', variadic array[1, 2]);
--- variadic argument can be NULL, but should not be referenced
-select format('Hello', variadic NULL);
+-- variadic argument can be array type NULL, but should not be referenced
+select format('Hello', variadic NULL::int[]);
 -- variadic argument allows simulating more than FUNC_MAX_ARGS parameters
 select format(string_agg('%s',','), variadic array_agg(i))
 from generate_series(1,200) g(i);