From: Ayush Tiwari Date: Fri, 11 Sep 2026 11:30:00 +0530 Subject: [PATCH v1] Fix EXCLUDED virtual generated columns using tableoid Commit 783425175 arranged for the EXCLUDED target list to remain unexpanded while expanding references to virtual generated columns in ON CONFLICT clauses. If a generation expression uses tableoid, this leaves a tableoid Var in the expanded clause. Since the EXCLUDED target list has no matching entry, set_plan_refs() fails with "variable not found in subplan target lists". Add a tableoid Var to the EXCLUDED target list. This is the only system column allowed in generation expressions, and the candidate insertion slot supplies its value. Test both the reported non-conflicting insert and an insert that takes the conflict-update path. Reported-by: Alexander Lakhin Discussion: https://postgr.es/m/18fbeb33-9631-4f6e-9280-2c787aef254e@gmail.com Backpatch-through: 18 --- src/backend/parser/analyze.c | 11 +++++++++++ src/test/regress/expected/generated_virtual.out | 10 ++++++++++ src/test/regress/sql/generated_virtual.sql | 5 +++++ 3 files changed, 26 insertions(+) diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 14202e5cae6bae976b32938728dc5dee79ae3256..7af7dc0bb3d6401ceb79c0239640d7e7059b777c 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -1674,6 +1674,17 @@ BuildOnConflictExcludedTargetlist(Relation targetrel, result = lappend(result, te); } + /* + * Add an entry for tableoid, the only system column that can appear in a + * generation expression, to support references to virtual generated + * columns that use it. + */ + var = makeVar(exclRelIndex, TableOidAttributeNumber, + OIDOID, -1, InvalidOid, 0); + te = makeTargetEntry((Expr *) var, TableOidAttributeNumber, + pstrdup("tableoid"), true); + result = lappend(result, te); + /* * Add a whole-row-Var entry to support references to "EXCLUDED.*". Like * the other entries in the EXCLUDED tlist, its resno must match the Var's diff --git a/src/test/regress/expected/generated_virtual.out b/src/test/regress/expected/generated_virtual.out index 6ee029796f1784f7330a7e8636091d94c24383ee..71868c12550effe455cb048330204c03c2ebbbe5 100644 --- a/src/test/regress/expected/generated_virtual.out +++ b/src/test/regress/expected/generated_virtual.out @@ -589,6 +589,16 @@ SELECT * FROM gtest_tableoid; 2 | t | gtest_tableoid (2 rows) +INSERT INTO gtest_tableoid VALUES (3) + ON CONFLICT (a) DO UPDATE SET a = excluded.a WHERE excluded.b; +INSERT INTO gtest_tableoid VALUES (3) + ON CONFLICT (a) DO UPDATE SET a = excluded.a WHERE excluded.b + RETURNING a, b, c; + a | b | c +---+---+---------------- + 3 | t | gtest_tableoid +(1 row) + -- drop column behavior CREATE TABLE gtest10 (a int PRIMARY KEY, b int, c int GENERATED ALWAYS AS (b * 2) VIRTUAL); ALTER TABLE gtest10 DROP COLUMN b; -- fails diff --git a/src/test/regress/sql/generated_virtual.sql b/src/test/regress/sql/generated_virtual.sql index e4ea63bb3a1f561a235b9f1ccf5acda766747eb9..daa0d4faf78c11fc9ed1d9d24ef7d57d6d929e2d 100644 --- a/src/test/regress/sql/generated_virtual.sql +++ b/src/test/regress/sql/generated_virtual.sql @@ -279,6 +279,11 @@ INSERT INTO gtest_tableoid VALUES (1), (2); ALTER TABLE gtest_tableoid ADD COLUMN c regclass GENERATED ALWAYS AS (tableoid) VIRTUAL; SELECT * FROM gtest_tableoid; +INSERT INTO gtest_tableoid VALUES (3) + ON CONFLICT (a) DO UPDATE SET a = excluded.a WHERE excluded.b; +INSERT INTO gtest_tableoid VALUES (3) + ON CONFLICT (a) DO UPDATE SET a = excluded.a WHERE excluded.b + RETURNING a, b, c; -- drop column behavior CREATE TABLE gtest10 (a int PRIMARY KEY, b int, c int GENERATED ALWAYS AS (b * 2) VIRTUAL); -- 2.43.0