]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix usage of "tableoid" in GENERATED expressions.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 21 May 2021 19:02:07 +0000 (15:02 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 21 May 2021 19:02:07 +0000 (15:02 -0400)
We consider this supported (though I've got my doubts that it's a
good idea, because tableoid is not immutable).  However, several
code paths failed to fill the field in soon enough, causing such
a GENERATED expression to see zero or the wrong value.  This
occurred when ALTER TABLE adds a new GENERATED column to a table
with existing rows, and during regular INSERT or UPDATE on a
foreign table with GENERATED columns.

Noted during investigation of a report from Vitaly Ustinov.
Back-patch to v12 where GENERATED came in.

Discussion: https://postgr.es/m/CAM_DEiWR2DPT6U4xb-Ehigozzd3n3G37ZB1+867zbsEVtYoJww@mail.gmail.com

src/backend/commands/tablecmds.c
src/backend/executor/nodeModifyTable.c
src/test/regress/expected/generated.out
src/test/regress/sql/generated.sql

index 84e6e9dbc5810e93f281d09fd64ccdac3c82e320..70337c7c07205b5fc58506eacca7bbcd8853b01f 100644 (file)
@@ -5373,6 +5373,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
                                foreach(lc, dropped_attrs)
                                        newslot->tts_isnull[lfirst_int(lc)] = true;
 
+                               /*
+                                * Constraints and GENERATED expressions might reference the
+                                * tableoid column, so fill tts_tableOid with the desired
+                                * value.  (We must do this each time, because it gets
+                                * overwritten with newrel's OID during storing.)
+                                */
+                               newslot->tts_tableOid = RelationGetRelid(oldrel);
+
                                /*
                                 * Process supplied expressions to replace selected columns.
                                 *
@@ -5416,11 +5424,6 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
                                                                           &newslot->tts_isnull[ex->attnum - 1]);
                                }
 
-                               /*
-                                * Constraints might reference the tableoid column, so
-                                * initialize t_tableOid before evaluating them.
-                                */
-                               newslot->tts_tableOid = RelationGetRelid(oldrel);
                                insertslot = newslot;
                        }
                        else
index 2a01449f25a3f25e9e5b19ee0295e53b9d75897d..c5d1c924322f6916e4c848ed33c19b4a801acee3 100644 (file)
@@ -437,6 +437,12 @@ ExecInsert(ModifyTableState *mtstate,
        }
        else if (resultRelInfo->ri_FdwRoutine)
        {
+               /*
+                * GENERATED expressions might reference the tableoid column, so
+                * (re-)initialize tts_tableOid before evaluating them.
+                */
+               slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+
                /*
                 * Compute stored generated columns
                 */
@@ -458,7 +464,7 @@ ExecInsert(ModifyTableState *mtstate,
                /*
                 * AFTER ROW Triggers or RETURNING expressions might reference the
                 * tableoid column, so (re-)initialize tts_tableOid before evaluating
-                * them.
+                * them.  (This covers the case where the FDW replaced the slot.)
                 */
                slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
        }
@@ -467,8 +473,8 @@ ExecInsert(ModifyTableState *mtstate,
                WCOKind         wco_kind;
 
                /*
-                * Constraints might reference the tableoid column, so (re-)initialize
-                * tts_tableOid before evaluating them.
+                * Constraints and GENERATED expressions might reference the tableoid
+                * column, so (re-)initialize tts_tableOid before evaluating them.
                 */
                slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
 
@@ -1172,6 +1178,12 @@ ExecUpdate(ModifyTableState *mtstate,
        }
        else if (resultRelInfo->ri_FdwRoutine)
        {
+               /*
+                * GENERATED expressions might reference the tableoid column, so
+                * (re-)initialize tts_tableOid before evaluating them.
+                */
+               slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+
                /*
                 * Compute stored generated columns
                 */
@@ -1193,7 +1205,7 @@ ExecUpdate(ModifyTableState *mtstate,
                /*
                 * AFTER ROW Triggers or RETURNING expressions might reference the
                 * tableoid column, so (re-)initialize tts_tableOid before evaluating
-                * them.
+                * them.  (This covers the case where the FDW replaced the slot.)
                 */
                slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
        }
@@ -1204,8 +1216,8 @@ ExecUpdate(ModifyTableState *mtstate,
                bool            update_indexes;
 
                /*
-                * Constraints might reference the tableoid column, so (re-)initialize
-                * tts_tableOid before evaluating them.
+                * Constraints and GENERATED expressions might reference the tableoid
+                * column, so (re-)initialize tts_tableOid before evaluating them.
                 */
                slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
 
index ea58de36670cba8e5385a5d75eb1362f2a309d4d..82a9e73ebc139031b311b05f26301b4576eae56b 100644 (file)
@@ -64,6 +64,7 @@ ERROR:  both identity and generation expression specified for column "b" of tabl
 LINE 1: ...t PRIMARY KEY, b int GENERATED ALWAYS AS identity GENERATED ...
                                                              ^
 -- reference to system column not allowed in generated column
+-- (except tableoid, which we test below)
 CREATE TABLE gtest_err_6a (a int PRIMARY KEY, b bool GENERATED ALWAYS AS (xmin <> 37) STORED);
 ERROR:  cannot use system column "xmin" in column generation expression
 LINE 1: ...a (a int PRIMARY KEY, b bool GENERATED ALWAYS AS (xmin <> 37...
@@ -414,14 +415,16 @@ DROP TYPE double_int;
 -- using tableoid is allowed
 CREATE TABLE gtest_tableoid (
   a int PRIMARY KEY,
-  b bool GENERATED ALWAYS AS (tableoid <> 0) STORED
+  b bool GENERATED ALWAYS AS (tableoid = 'gtest_tableoid'::regclass) STORED
 );
 INSERT INTO gtest_tableoid VALUES (1), (2);
+ALTER TABLE gtest_tableoid ADD COLUMN
+  c regclass GENERATED ALWAYS AS (tableoid) STORED;
 SELECT * FROM gtest_tableoid;
- a | b 
----+---
- 1 | t
- 2 | t
+ a | b |       c        
+---+---+----------------
+ 1 | t | gtest_tableoid
+ 2 | t | gtest_tableoid
 (2 rows)
 
 -- drop column behavior
index 5400510edca373cf02543a29e622b193e736f5ca..208f1d91faa3f02d42d177db8df4eab82445c782 100644 (file)
@@ -29,6 +29,7 @@ CREATE TABLE gtest_err_5a (a int PRIMARY KEY, b int DEFAULT 5 GENERATED ALWAYS A
 CREATE TABLE gtest_err_5b (a int PRIMARY KEY, b int GENERATED ALWAYS AS identity GENERATED ALWAYS AS (a * 2) STORED);
 
 -- reference to system column not allowed in generated column
+-- (except tableoid, which we test below)
 CREATE TABLE gtest_err_6a (a int PRIMARY KEY, b bool GENERATED ALWAYS AS (xmin <> 37) STORED);
 
 -- various prohibited constructs
@@ -201,9 +202,11 @@ DROP TYPE double_int;
 -- using tableoid is allowed
 CREATE TABLE gtest_tableoid (
   a int PRIMARY KEY,
-  b bool GENERATED ALWAYS AS (tableoid <> 0) STORED
+  b bool GENERATED ALWAYS AS (tableoid = 'gtest_tableoid'::regclass) STORED
 );
 INSERT INTO gtest_tableoid VALUES (1), (2);
+ALTER TABLE gtest_tableoid ADD COLUMN
+  c regclass GENERATED ALWAYS AS (tableoid) STORED;
 SELECT * FROM gtest_tableoid;
 
 -- drop column behavior