]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix check_sql_fn_retval to allow the case where a SQL function declared to
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 2 Apr 2007 18:49:41 +0000 (18:49 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 2 Apr 2007 18:49:41 +0000 (18:49 +0000)
return void ends with a SELECT, if that SELECT has a single result that is
also of type void.  Without this, it's hard to write a void function that
calls another void function.  Per gripe from Peter.

Back-patch as far as 8.0.

src/backend/executor/functions.c

index d004bab79690bdf6eaa71a47e42f5133edd2b841..84398e8a9602f1f55edfffdda216079dab231baf 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.98.2.3 2007/02/02 00:03:30 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.98.2.4 2007/04/02 18:49:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -841,9 +841,9 @@ ShutdownSQLFunction(Datum arg)
  * as rettype.  (This means we can't check the type during function definition
  * of a polymorphic function.)
  *
- * The return value is true if the function returns the entire tuple result
- * of its final SELECT, and false otherwise.  Note that because we allow
- * "SELECT rowtype_expression", this may be false even when the declared
+ * This function returns true if the sql function returns the entire tuple
+ * result of its final SELECT, and false otherwise.  Note that because we
+ * allow "SELECT rowtype_expression", this may be false even when the declared
  * function return type is a rowtype.
  *
  * If junkFilter isn't NULL, then *junkFilter is set to a JunkFilter defined
@@ -856,7 +856,6 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
                                        JunkFilter **junkFilter)
 {
        Query      *parse;
-       int                     cmd;
        List       *tlist;
        ListCell   *tlistitem;
        int                     tlistlen;
@@ -881,42 +880,42 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
        /* find the final query */
        parse = (Query *) lfirst(list_tail(queryTreeList));
 
-       cmd = parse->commandType;
-       tlist = parse->targetList;
-
        /*
-        * The last query must be a SELECT if and only if return type isn't VOID.
+        * If the last query isn't a SELECT, the return type must be VOID.
         */
-       if (rettype == VOIDOID)
+       if (!(parse->commandType == CMD_SELECT && parse->into == NULL))
        {
-               if (cmd == CMD_SELECT)
+               if (rettype != VOIDOID)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                         errmsg("return type mismatch in function declared to return %s",
                                        format_type_be(rettype)),
-                        errdetail("Function's final statement must not be a SELECT.")));
+                        errdetail("Function's final statement must be a SELECT.")));
                return false;
        }
 
-       /* by here, the function is declared to return some type */
-       if (cmd != CMD_SELECT)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
-                        errmsg("return type mismatch in function declared to return %s",
-                                       format_type_be(rettype)),
-                                errdetail("Function's final statement must be a SELECT.")));
+       /*
+        * OK, it's a SELECT, so it must return something matching the declared
+        * type.  (We used to insist that the declared type not be VOID in this
+        * case, but that makes it hard to write a void function that exits
+        * after calling another void function.  Instead, we insist that the
+        * SELECT return void ... so void is treated as if it were a scalar type
+        * below.)
+        */
 
        /*
         * Count the non-junk entries in the result targetlist.
         */
+       tlist = parse->targetList;
        tlistlen = ExecCleanTargetListLength(tlist);
 
        fn_typtype = get_typtype(rettype);
 
-       if (fn_typtype == 'b' || fn_typtype == 'd')
+       if (fn_typtype == 'b' || fn_typtype == 'd' ||
+               rettype == VOIDOID)
        {
                /*
-                * For base-type returns, the target list should have exactly one
+                * For scalar-type returns, the target list should have exactly one
                 * entry, and its type should agree with what the user declared. (As
                 * of Postgres 7.2, we accept binary-compatible types too.)
                 */