Procedure additions to ParseFuncOrColumn are inadequate

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Procedure additions to ParseFuncOrColumn are inadequate
Дата
Msg-id 14497.1529089235@sss.pgh.pa.us
обсуждение исходный текст
Список pgsql-hackers
I noticed that ParseFuncOrColumn isn't terribly thorough about rejecting
non-procedure results when proc_call is true.  Since the caller is just
assuming that it gets back what it expects, that leads to core dumps
or worse:

regression=# call int4(42);
server closed the connection unexpectedly
        This probably means the server terminated abnormally

Also, with the existing ad-hoc approach to this, not all the ereports
are alike; the one for an aggregate lacks the HINT the others provide.

Also, in the cases where it does successfully reject should-be-procedure
or should-not-be-procedure, it reports ERRCODE_UNDEFINED_FUNCTION, which
seems quite inappropriate from here: I'd expect ERRCODE_WRONG_OBJECT_TYPE.

There's also a pre-existing oversight that when we get a result of
FUNCDETAIL_COERCION, we should reject that if there's any aggregate
decoration, but we fail to.

The attached cleans all this up; any objections?

            regards, tom lane

diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index ea5d521..21ddd5b 100644
*** a/src/backend/parser/parse_func.c
--- b/src/backend/parser/parse_func.c
*************** static Node *ParseComplexProjection(Pars
*** 68,73 ****
--- 68,76 ----
   *    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,
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 204,210 ****
       * 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 &&
          !agg_distinct && over == NULL && !func_variadic && argnames == NIL &&
          list_length(funcname) == 1)
      {
--- 207,214 ----
       * 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 && !proc_call &&
!         agg_order == NIL && agg_filter == NULL && !agg_star &&
          !agg_distinct && over == NULL && !func_variadic && argnames == NIL &&
          list_length(funcname) == 1)
      {
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 253,273 ****

      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)
      {
          /*
!          * Normal function found; was there anything indicating it must be an
!          * aggregate?
           */
          if (agg_star)
              ereport(ERROR,
--- 257,298 ----

      cancel_parser_errposition_callback(&pcbstate);

!     /*
!      * 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)
      {
          /*
!          * In these cases, complain if there was anything indicating it must
!          * be an aggregate or window function.
           */
          if (agg_star)
              ereport(ERROR,
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 306,331 ****
                       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)));
      }
      else if (fdresult == FUNCDETAIL_AGGREGATE)
      {
--- 331,344 ----
                       errmsg("OVER specified, but %s is not a window function nor an aggregate function",
                              NameListToString(funcname)),
                       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)
      {
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 336,350 ****
          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);
--- 349,354 ----
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 510,515 ****
--- 514,529 ----
                              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
      {
          /*
diff --git a/src/test/regress/expected/create_procedure.out b/src/test/regress/expected/create_procedure.out
index 67d6717..3049597 100644
*** a/src/test/regress/expected/create_procedure.out
--- b/src/test/regress/expected/create_procedure.out
*************** CALL sum(1);  -- error: not a procedure
*** 126,131 ****
--- 126,132 ----
  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...

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Andrew Gierth
Дата:
Сообщение: Re: BUG #15237: I got "ERROR: source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression"
Следующее
От: Alvaro Herrera
Дата:
Сообщение: Re: pgsql: Store 2PC GID in commit/abort WAL recs for logicaldecoding