1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-29 10:41:53 +03:00

Avoid trying to lock OLD/NEW in a rule with FOR UPDATE.

transformLockingClause neglected to exclude the pseudo-RTEs for
OLD/NEW when processing a rule's query.  This led to odd errors
or even crashes later on.  This bug is very ancient, but it's
not terribly surprising that nobody noticed, since the use-case
for SELECT FOR UPDATE in a non-view rule is somewhere between
thin and non-existent.  Still, crashing is not OK.

Per bug #17151 from Zhiyong Wu.  Thanks to Masahiko Sawada
for analysis of the problem.

Discussion: https://postgr.es/m/17151-c03a3e6e4ec9aadb@postgresql.org
This commit is contained in:
Tom Lane
2021-08-19 12:12:35 -04:00
parent ecd4dd9f1d
commit 7fa367d96b
4 changed files with 60 additions and 6 deletions

View File

@ -2753,13 +2753,22 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
if (lockedRels == NIL)
{
/* all regular tables used in query */
/*
* Lock all regular tables used in query and its subqueries. We
* examine inFromCl to exclude auto-added RTEs, particularly NEW/OLD
* in rules. This is a bit of an abuse of a mostly-obsolete flag, but
* it's convenient. We can't rely on the namespace mechanism that has
* largely replaced inFromCl, since for example we need to lock
* base-relation RTEs even if they are masked by upper joins.
*/
i = 0;
foreach(rt, qry->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
++i;
if (!rte->inFromCl)
continue;
switch (rte->rtekind)
{
case RTE_RELATION:
@ -2789,7 +2798,11 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
}
else
{
/* just the named tables */
/*
* Lock just the named tables. As above, we allow locking any base
* relation regardless of alias-visibility rules, so we need to
* examine inFromCl to exclude OLD/NEW.
*/
foreach(l, lockedRels)
{
RangeVar *thisrel = (RangeVar *) lfirst(l);
@ -2810,6 +2823,8 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
++i;
if (!rte->inFromCl)
continue;
if (strcmp(rte->eref->aliasname, thisrel->relname) == 0)
{
switch (rte->rtekind)