1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-03 20:02:46 +03:00

Improve error report for PL/pgSQL reserved word used as a field name.

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
This commit is contained in:
Tom Lane
2025-06-30 17:06:39 -04:00
parent 999f172ded
commit 0836683a89
3 changed files with 50 additions and 7 deletions

View File

@ -79,3 +79,25 @@ begin
end $$; end $$;
NOTICE: execute = 10 NOTICE: execute = 10
NOTICE: r.strict = 1 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

View File

@ -1211,17 +1211,22 @@ resolve_column_ref(ParseState *pstate, PLpgSQL_expr *expr,
} }
/* /*
* We should not get here, because a RECFIELD datum should * Ideally we'd never get here, because a RECFIELD datum
* have been built at parse time for every possible qualified * should have been built at parse time for every qualified
* reference to fields of this record. But if we do, handle * reference to a field of this record that appears in the
* it like field-not-found: throw error or return NULL. * 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) if (error_if_no_field)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("record \"%s\" has no field \"%s\"", errmsg("field name \"%s\" is a reserved key word",
(nnames_field == 1) ? name1 : name2,
colname), colname),
errhint("Use double quotes to quote it."),
parser_errposition(pstate, cref->location))); parser_errposition(pstate, cref->location)));
} }
break; break;

View File

@ -50,3 +50,19 @@ begin
select 1 as strict into r; select 1 as strict into r;
raise notice 'r.strict = %', r.strict; raise notice 'r.strict = %', r.strict;
end $$; 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 $$;