Improve the error message for an inappropriate column definition list.
authorTom Lane <[email protected]>
Tue, 22 Sep 2020 14:49:11 +0000 (10:49 -0400)
committerTom Lane <[email protected]>
Tue, 22 Sep 2020 14:49:11 +0000 (10:49 -0400)
The existing message about "a column definition list is only allowed for
functions returning "record"" could be given in some cases where it was
fairly confusing; in particular, a function with multiple OUT parameters
*does* return record according to pg_proc.  Break it down into a couple
more cases to deliver a more on-point complaint.  Per complaint from
Bruce Momjian.

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

src/backend/parser/parse_relation.c
src/test/regress/expected/rangefuncs.out
src/test/regress/sql/rangefuncs.sql

index b875a506463f813def8fc1227fb80731b675bb3f..a56bd86181a70e5136244e9a76d3f2499bcd40d8 100644 (file)
@@ -1737,16 +1737,46 @@ addRangeTableEntryForFunction(ParseState *pstate,
 
        /*
         * A coldeflist is required if the function returns RECORD and hasn't
-        * got a predetermined record type, and is prohibited otherwise.
+        * got a predetermined record type, and is prohibited otherwise.  This
+        * can be a bit confusing, so we expend some effort on delivering a
+        * relevant error message.
         */
        if (coldeflist != NIL)
        {
-           if (functypclass != TYPEFUNC_RECORD)
-               ereport(ERROR,
-                       (errcode(ERRCODE_SYNTAX_ERROR),
-                        errmsg("a column definition list is only allowed for functions returning \"record\""),
-                        parser_errposition(pstate,
-                                           exprLocation((Node *) coldeflist))));
+           switch (functypclass)
+           {
+               case TYPEFUNC_RECORD:
+                   /* ok */
+                   break;
+               case TYPEFUNC_COMPOSITE:
+               case TYPEFUNC_COMPOSITE_DOMAIN:
+
+                   /*
+                    * If the function's raw result type is RECORD, we must
+                    * have resolved it using its OUT parameters.  Otherwise,
+                    * it must have a named composite type.
+                    */
+                   if (exprType(funcexpr) == RECORDOID)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("a column definition list is redundant for a function with OUT parameters"),
+                                parser_errposition(pstate,
+                                                   exprLocation((Node *) coldeflist))));
+                   else
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("a column definition list is redundant for a function returning a named composite type"),
+                                parser_errposition(pstate,
+                                                   exprLocation((Node *) coldeflist))));
+                   break;
+               default:
+                   ereport(ERROR,
+                           (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("a column definition list is only allowed for functions returning \"record\""),
+                            parser_errposition(pstate,
+                                               exprLocation((Node *) coldeflist))));
+                   break;
+           }
        }
        else
        {
index 7eced2845203785a6648546a87dc531b1581d1ea..e618aec2ebca82c8b399718c64ff79c55e77b842 100644 (file)
@@ -2109,6 +2109,19 @@ select * from testrngfunc();
  7.136178 | 7.14
 (1 row)
 
+-- Check a couple of error cases while we're here
+select * from testrngfunc() as t(f1 int8,f2 int8);  -- fail, composite result
+ERROR:  a column definition list is redundant for a function returning a named composite type
+LINE 1: select * from testrngfunc() as t(f1 int8,f2 int8);
+                                         ^
+select * from pg_get_keywords() as t(f1 int8,f2 int8);  -- fail, OUT params
+ERROR:  a column definition list is redundant for a function with OUT parameters
+LINE 1: select * from pg_get_keywords() as t(f1 int8,f2 int8);
+                                             ^
+select * from sin(3) as t(f1 int8,f2 int8);  -- fail, scalar result type
+ERROR:  a column definition list is only allowed for functions returning "record"
+LINE 1: select * from sin(3) as t(f1 int8,f2 int8);
+                                  ^
 drop type rngfunc_type cascade;
 NOTICE:  drop cascades to function testrngfunc()
 --
index ae3119a959eb87321e8484f1915cef1016162dce..5f41cb2d8d0b10195da2d1c6966df76b748f495f 100644 (file)
@@ -629,6 +629,11 @@ explain (verbose, costs off)
 select * from testrngfunc();
 select * from testrngfunc();
 
+-- Check a couple of error cases while we're here
+select * from testrngfunc() as t(f1 int8,f2 int8);  -- fail, composite result
+select * from pg_get_keywords() as t(f1 int8,f2 int8);  -- fail, OUT params
+select * from sin(3) as t(f1 int8,f2 int8);  -- fail, scalar result type
+
 drop type rngfunc_type cascade;
 
 --