Fix parsing of qualified relation names in RETURNING.
authorDean Rasheed <dean.a.rasheed@gmail.com>
Fri, 17 Jan 2025 10:35:07 +0000 (10:35 +0000)
committerDean Rasheed <dean.a.rasheed@gmail.com>
Fri, 17 Jan 2025 10:35:07 +0000 (10:35 +0000)
Given a qualified refname, refnameNamespaceItem() will search for a
matching namespace item by relation OID, rather than by name. Commit
80feb727c8 broke this by adding additional namespace items for OLD and
NEW in the RETURNING list, which have the same relation OID, causing
ambiguity. Fix this by ignoring these in the search, which is correct
since they don't match the qualified relation name, and so there is no
real ambiguity.

Reported by Richard Guo.

Discussion: https://postgr.es/m/CAMbWs49MBjWYWDROJ8MZ%3DY%2B4UgRQa10wzik1tWrD5yto9eoGXg%40mail.gmail.com

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

index 679bf640c623c182258f1708e187f23ac7e3d23f..101fba34b187603060e5750c3bfc746160a9e75e 100644 (file)
@@ -125,7 +125,10 @@ static bool isQueryUsingTempRelation_walker(Node *node, void *context);
  * that (a) has no alias and (b) is for the same relation identified by
  * schemaname.refname.  In this case we convert schemaname.refname to a
  * relation OID and search by relid, rather than by alias name.  This is
- * peculiar, but it's what SQL says to do.
+ * peculiar, but it's what SQL says to do.  While processing a query's
+ * RETURNING list, there may be additional namespace items for OLD and NEW,
+ * with the same relation OID as the target namespace item.  These are
+ * ignored in the search, since they don't match by schemaname.refname.
  */
 ParseNamespaceItem *
 refnameNamespaceItem(ParseState *pstate,
@@ -255,6 +258,9 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
        /* If not inside LATERAL, ignore lateral-only items */
        if (nsitem->p_lateral_only && !pstate->p_lateral_active)
            continue;
+       /* Ignore OLD/NEW namespace items that can appear in RETURNING */
+       if (nsitem->p_returning_type != VAR_RETURNING_DEFAULT)
+           continue;
 
        /* yes, the test for alias == NULL should be there... */
        if (rte->rtekind == RTE_RELATION &&
index 6bd36a811fcff2d70d7e24aba23d75c600ea420d..d1394c67833eb3d26ae26e3e37d439fc4d6a8bd6 100644 (file)
@@ -766,6 +766,16 @@ DELETE FROM zerocol
 (1 row)
 
 DROP TABLE zerocol;
+-- Test schema-qualified table name in RETURNING list
+CREATE TABLE public.tt(a int, b int);
+INSERT INTO public.tt VALUES (1, 10);
+UPDATE public.tt SET b = b * 2 RETURNING a, b, old.b, new.b, tt.b, public.tt.b;
+ a | b  | b  | b  | b  | b  
+---+----+----+----+----+----
+ 1 | 20 | 10 | 20 | 20 | 20
+(1 row)
+
+DROP TABLE public.tt;
 -- Test cross-partition updates and attribute mapping
 CREATE TABLE foo_parted (a int, b float8, c text) PARTITION BY LIST (a);
 CREATE TABLE foo_part_s1 PARTITION OF foo_parted FOR VALUES IN (1);
index b7258a3d97eda016c4c827930cb49cdb331f5c81..54caf56244c0fc0f1f81028a0a5fa460c38b16ab 100644 (file)
@@ -312,6 +312,12 @@ DELETE FROM zerocol
             new.tableoid::regclass, new.ctid, ctid, *;
 DROP TABLE zerocol;
 
+-- Test schema-qualified table name in RETURNING list
+CREATE TABLE public.tt(a int, b int);
+INSERT INTO public.tt VALUES (1, 10);
+UPDATE public.tt SET b = b * 2 RETURNING a, b, old.b, new.b, tt.b, public.tt.b;
+DROP TABLE public.tt;
+
 -- Test cross-partition updates and attribute mapping
 CREATE TABLE foo_parted (a int, b float8, c text) PARTITION BY LIST (a);
 CREATE TABLE foo_part_s1 PARTITION OF foo_parted FOR VALUES IN (1);