]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix some minor error-checking oversights in ParseFuncOrColumn().
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 16 Jun 2018 18:10:17 +0000 (14:10 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 16 Jun 2018 18:11:14 +0000 (14:11 -0400)
Recent additions to ParseFuncOrColumn to make it reject non-procedure
functions in CALL were neither adequate nor documented.  Reorganize
the code to ensure uniform results for all the cases that should be
rejected.  Also, use ERRCODE_WRONG_OBJECT_TYPE for this case as well
as the converse case of a procedure in a non-CALL context.  The
original coding used ERRCODE_UNDEFINED_FUNCTION which seems wrong,
and is certainly inconsistent with the adjacent wrong-kind-of-routine
errors.

This reorganization also causes the checks for aggregate decoration with
a non-aggregate function to be made in the FUNCDETAIL_COERCION case;
that they were not is a long-standing oversight.

Discussion: https://postgr.es/m/14497.1529089235@sss.pgh.pa.us

src/backend/parser/parse_func.c
src/test/regress/expected/create_procedure.out

index ea5d5212b4c86f9fcaca8afd27032ed0cf3704e7..21ddd5b7e01737fc8bb24ef455ef2ea0505cb0a6 100644 (file)
@@ -68,6 +68,9 @@ static Node *ParseComplexProjection(ParseState *pstate, const char *funcname,
  *     last_srf should be a copy of pstate->p_last_srf from just before we
  *     started transforming fargs.  If the caller knows that fargs couldn't
  *     contain any SRF calls, last_srf can just be pstate->p_last_srf.
+ *
+ *     proc_call is true if we are considering a CALL statement, so that the
+ *     name must resolve to a procedure name, not anything else.
  */
 Node *
 ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
@@ -204,7 +207,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
         * the "function call" could be a projection.  We also check that there
         * wasn't any aggregate or variadic decoration, nor an argument name.
         */
-       if (nargs == 1 && agg_order == NIL && agg_filter == NULL && !agg_star &&
+       if (nargs == 1 && !proc_call &&
+               agg_order == NIL && agg_filter == NULL && !agg_star &&
                !agg_distinct && over == NULL && !func_variadic && argnames == NIL &&
                list_length(funcname) == 1)
        {
@@ -253,21 +257,42 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 
        cancel_parser_errposition_callback(&pcbstate);
 
-       if (fdresult == FUNCDETAIL_COERCION)
-       {
-               /*
-                * We interpreted it as a type coercion. coerce_type can handle these
-                * cases, so why duplicate code...
-                */
-               return coerce_type(pstate, linitial(fargs),
-                                                  actual_arg_types[0], rettype, -1,
-                                                  COERCION_EXPLICIT, COERCE_EXPLICIT_CALL, location);
-       }
-       else if (fdresult == FUNCDETAIL_NORMAL || fdresult == FUNCDETAIL_PROCEDURE)
+       /*
+        * Check for various wrong-kind-of-routine cases.
+        */
+
+       /* If this is a CALL, reject things that aren't procedures */
+       if (proc_call &&
+               (fdresult == FUNCDETAIL_NORMAL ||
+                fdresult == FUNCDETAIL_AGGREGATE ||
+                fdresult == FUNCDETAIL_WINDOWFUNC ||
+                fdresult == FUNCDETAIL_COERCION))
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("%s is not a procedure",
+                                               func_signature_string(funcname, nargs,
+                                                                                         argnames,
+                                                                                         actual_arg_types)),
+                                errhint("To call a function, use SELECT."),
+                                parser_errposition(pstate, location)));
+       /* Conversely, if not a CALL, reject procedures */
+       if (fdresult == FUNCDETAIL_PROCEDURE && !proc_call)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("%s is a procedure",
+                                               func_signature_string(funcname, nargs,
+                                                                                         argnames,
+                                                                                         actual_arg_types)),
+                                errhint("To call a procedure, use CALL."),
+                                parser_errposition(pstate, location)));
+
+       if (fdresult == FUNCDETAIL_NORMAL ||
+               fdresult == FUNCDETAIL_PROCEDURE ||
+               fdresult == FUNCDETAIL_COERCION)
        {
                /*
-                * Normal function found; was there anything indicating it must be an
-                * aggregate?
+                * In these cases, complain if there was anything indicating it must
+                * be an aggregate or window function.
                 */
                if (agg_star)
                        ereport(ERROR,
@@ -306,26 +331,14 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                                         errmsg("OVER specified, but %s is not a window function nor an aggregate function",
                                                        NameListToString(funcname)),
                                         parser_errposition(pstate, location)));
+       }
 
-               if (fdresult == FUNCDETAIL_NORMAL && proc_call)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
-                                        errmsg("%s is not a procedure",
-                                                       func_signature_string(funcname, nargs,
-                                                                                                 argnames,
-                                                                                                 actual_arg_types)),
-                                        errhint("To call a function, use SELECT."),
-                                        parser_errposition(pstate, location)));
-
-               if (fdresult == FUNCDETAIL_PROCEDURE && !proc_call)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
-                                        errmsg("%s is a procedure",
-                                                       func_signature_string(funcname, nargs,
-                                                                                                 argnames,
-                                                                                                 actual_arg_types)),
-                                        errhint("To call a procedure, use CALL."),
-                                        parser_errposition(pstate, location)));
+       /*
+        * So far so good, so do some routine-type-specific processing.
+        */
+       if (fdresult == FUNCDETAIL_NORMAL || fdresult == FUNCDETAIL_PROCEDURE)
+       {
+               /* Nothing special to do for these cases. */
        }
        else if (fdresult == FUNCDETAIL_AGGREGATE)
        {
@@ -336,15 +349,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                Form_pg_aggregate classForm;
                int                     catDirectArgs;
 
-               if (proc_call)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
-                                        errmsg("%s is not a procedure",
-                                                       func_signature_string(funcname, nargs,
-                                                                                                 argnames,
-                                                                                                 actual_arg_types)),
-                                        parser_errposition(pstate, location)));
-
                tup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(funcid));
                if (!HeapTupleIsValid(tup)) /* should not happen */
                        elog(ERROR, "cache lookup failed for aggregate %u", funcid);
@@ -510,6 +514,16 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                                                        NameListToString(funcname)),
                                         parser_errposition(pstate, location)));
        }
+       else if (fdresult == FUNCDETAIL_COERCION)
+       {
+               /*
+                * We interpreted it as a type coercion. coerce_type can handle these
+                * cases, so why duplicate code...
+                */
+               return coerce_type(pstate, linitial(fargs),
+                                                  actual_arg_types[0], rettype, -1,
+                                                  COERCION_EXPLICIT, COERCE_EXPLICIT_CALL, location);
+       }
        else
        {
                /*
index 67d671727c7708eb9551c50e1231f3b0eb2d26fa..30495971bc8be540a45f1f781e893930efd75cd9 100644 (file)
@@ -126,6 +126,7 @@ CALL sum(1);  -- error: not a procedure
 ERROR:  sum(integer) is not a procedure
 LINE 1: CALL sum(1);
              ^
+HINT:  To call a function, use SELECT.
 CREATE PROCEDURE ptestx() LANGUAGE SQL WINDOW AS $$ INSERT INTO cp_test VALUES (1, 'a') $$;
 ERROR:  invalid attribute in procedure definition
 LINE 1: CREATE PROCEDURE ptestx() LANGUAGE SQL WINDOW AS $$ INSERT I...