Improve the error message for an inappropriate column definition list.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 22 Sep 2020 14:49:11 +0000 (10:49 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
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;
 
 --