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:49 +0000 (18:49 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 2 Apr 2007 18:49:49 +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 9f3a85dbf85871e57d1298052911179b7344f3b9..ec6ede2a5443a6584f1f3e39f5551797119d3f6d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.91.4.3 2007/02/02 00:03:44 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.91.4.4 2007/04/02 18:49:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -848,9 +848,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
@@ -863,7 +863,6 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList,
                    JunkFilter **junkFilter)
 {
    Query      *parse;
-   int         cmd;
    List       *tlist;
    ListCell   *tlistitem;
    int         tlistlen;
@@ -892,46 +891,45 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, 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);
 
    typerelid = typeidTypeRelid(rettype);
 
-   if (fn_typtype == 'b' || fn_typtype == 'd')
+   if (fn_typtype == 'b' || fn_typtype == 'd' ||
+       rettype == VOIDOID)
    {
        /* Shouldn't have a typerelid */
        Assert(typerelid == InvalidOid);
 
        /*
-        * 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.)
         */