]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Improve error report for PL/pgSQL reserved word used as a field name.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 30 Jun 2025 21:06:39 +0000 (17:06 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 30 Jun 2025 21:06:39 +0000 (17:06 -0400)
The current code in resolve_column_ref (dating to commits 01f7d2990
and fe24d7816) believes that not finding a RECFIELD datum is a
can't-happen case, in consequence of which I didn't spend a whole lot
of time considering what to do if it did happen.  But it turns out
that it *can* happen if the would-be field name is a fully-reserved
PL/pgSQL keyword.  Change the error message to describe that
situation, and add a test case demonstrating it.

This might need further refinement if anyone can find other ways to
trigger a failure here; but without an example it's not clear what
other error to throw.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Pavel Stehule <pavel.stehule@gmail.com>
Discussion: https://postgr.es/m/2185258.1745617445@sss.pgh.pa.us

src/pl/plpgsql/src/expected/plpgsql_misc.out
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/sql/plpgsql_misc.sql

index 7bb4f432e7dafe81a7a6143f2248ffaa5b89fbd0..ffb377f5f54ff44aa855edf1f214d6eb1325fd18 100644 (file)
@@ -79,3 +79,25 @@ begin
 end $$;
 NOTICE:  execute = 10
 NOTICE:  r.strict = 1
+-- Test handling of a reserved keyword as a record field name.
+do $$ declare r record;
+begin
+  select 1 as x, 2 as foreach into r;
+  raise notice 'r.x = %', r.x;
+  raise notice 'r.foreach = %', r.foreach;  -- fails
+end $$;
+NOTICE:  r.x = 1
+ERROR:  field name "foreach" is a reserved key word
+LINE 1: r.foreach
+        ^
+HINT:  Use double quotes to quote it.
+QUERY:  r.foreach
+CONTEXT:  PL/pgSQL function inline_code_block line 5 at RAISE
+do $$ declare r record;
+begin
+  select 1 as x, 2 as foreach into r;
+  raise notice 'r.x = %', r.x;
+  raise notice 'r."foreach" = %', r."foreach";  -- ok
+end $$;
+NOTICE:  r.x = 1
+NOTICE:  r."foreach" = 2
index b80c59447fb576a12a7a1d3205ded6759b9cf94f..ee961425a5b7e9f59c68c3eb3362ffb4adafba87 100644 (file)
@@ -1211,17 +1211,22 @@ resolve_column_ref(ParseState *pstate, PLpgSQL_expr *expr,
                                }
 
                                /*
-                                * We should not get here, because a RECFIELD datum should
-                                * have been built at parse time for every possible qualified
-                                * reference to fields of this record.  But if we do, handle
-                                * it like field-not-found: throw error or return NULL.
+                                * Ideally we'd never get here, because a RECFIELD datum
+                                * should have been built at parse time for every qualified
+                                * reference to a field of this record that appears in the
+                                * source text.  However, plpgsql_yylex will not build such a
+                                * datum unless the field name lexes as token type IDENT.
+                                * Hence, if the would-be field name is a PL/pgSQL reserved
+                                * word, we lose.  Assume that that's what happened and tell
+                                * the user to quote it, unless the caller prefers we just
+                                * return NULL.
                                 */
                                if (error_if_no_field)
                                        ereport(ERROR,
-                                                       (errcode(ERRCODE_UNDEFINED_COLUMN),
-                                                        errmsg("record \"%s\" has no field \"%s\"",
-                                                                       (nnames_field == 1) ? name1 : name2,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("field name \"%s\" is a reserved key word",
                                                                        colname),
+                                                        errhint("Use double quotes to quote it."),
                                                         parser_errposition(pstate, cref->location)));
                        }
                        break;
index 103a20bf8820c569cc7354aa9240d87a41b79f46..0bc39fcf3257c11ab3bdec27db6d409ddc159491 100644 (file)
@@ -50,3 +50,19 @@ begin
   select 1 as strict into r;
   raise notice 'r.strict = %', r.strict;
 end $$;
+
+-- Test handling of a reserved keyword as a record field name.
+
+do $$ declare r record;
+begin
+  select 1 as x, 2 as foreach into r;
+  raise notice 'r.x = %', r.x;
+  raise notice 'r.foreach = %', r.foreach;  -- fails
+end $$;
+
+do $$ declare r record;
+begin
+  select 1 as x, 2 as foreach into r;
+  raise notice 'r.x = %', r.x;
+  raise notice 'r."foreach" = %', r."foreach";  -- ok
+end $$;