Don't allow system columns in CHECK constraints, except tableoid.
authorRobert Haas
Mon, 23 Sep 2013 17:31:22 +0000 (13:31 -0400)
committerRobert Haas
Mon, 23 Sep 2013 17:31:22 +0000 (13:31 -0400)
Previously, arbitray system columns could be mentioned in table
constraints, but they were not correctly checked at runtime, because
the values weren't actually set correctly in the tuple.  Since it
seems easy enough to initialize the table OID properly, do that,
and continue allowing that column, but disallow the rest unless and
until someone figures out a way to make them work properly.

No back-patch, because this doesn't seem important enough to take the
risk of destabilizing the back branches.  In fact, this will pose a
dump-and-reload hazard for those upgrading from previous versions:
constraints that were accepted before but were not correctly enforced
will now either be enforced correctly or not accepted at all.  Either
could result in restore failures, but in practice I think very few
users will notice the difference, since the use case is pretty
marginal anyway and few users will be relying on features that have
not historically worked.

Amit Kapila, reviewed by Rushabh Lathia, with doc changes by me.

doc/src/sgml/ref/create_table.sgml
src/backend/commands/copy.c
src/backend/commands/tablecmds.c
src/backend/executor/nodeModifyTable.c
src/backend/parser/parse_relation.c
src/test/regress/input/constraints.source
src/test/regress/output/constraints.source

index 26eca6731c2ecd1d3eb1eebed59163c2de569707..a422edd2313dfcf51db3fa53d272227dec5f844c 100644 (file)
@@ -430,7 +430,8 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
      
       Currently, CHECK expressions cannot contain
       subqueries nor refer to variables other than columns of the
-      current row.
+      current row.  The system column tableoid
+      may be referenced, but not any other system column.
      
 
      
index 31819cce1d8660411f8a44e8d6e3ae6af8bc8d15..6b20144a48c27cb6557a8166fd6e79715def8b3a 100644 (file)
@@ -2217,6 +2217,12 @@ CopyFrom(CopyState cstate)
        if (loaded_oid != InvalidOid)
            HeapTupleSetOid(tuple, loaded_oid);
 
+       /*
+        * Constraints might reference the tableoid column, so initialize
+        * t_tableOid before evaluating them.
+        */
+       tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+
        /* Triggers and stuff need to be invoked in query context. */
        MemoryContextSwitchTo(oldcontext);
 
index adc74dd7e40228d63538137c4f97fd580910ce00..8839f986b4b15d4c81d87b8ef44a35be4c8abbce 100644 (file)
@@ -3857,6 +3857,12 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
                /* Preserve OID, if any */
                if (newTupDesc->tdhasoid)
                    HeapTupleSetOid(tuple, tupOid);
+
+               /*
+                * Constraints might reference the tableoid column, so initialize
+                * t_tableOid before evaluating them.
+                */
+               tuple->t_tableOid = RelationGetRelid(oldrel);
            }
 
            /* Now check any constraints on the possibly-changed tuple */
index 15f5dccb82a6ea10cc0fe44c62b0988e6bc36b97..189df713eab7f9da54e97d57dc24676889c15be3 100644 (file)
@@ -246,6 +246,12 @@ ExecInsert(TupleTableSlot *slot,
    }
    else
    {
+       /*
+        * Constraints might reference the tableoid column, so initialize
+        * t_tableOid before evaluating them.
+        */
+       tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+
        /*
         * Check the constraints of the tuple
         */
@@ -653,6 +659,12 @@ ExecUpdate(ItemPointer tupleid,
    {
        LockTupleMode lockmode;
 
+       /*
+        * Constraints might reference the tableoid column, so initialize
+        * t_tableOid before evaluating them.
+        */
+       tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+
        /*
         * Check the constraints of the tuple
         *
index 39922d32c5c89e6f5283c1c6c437bffc0642b465..5f469135cbfe0d066953165b45c37fca0e593483 100644 (file)
@@ -551,6 +551,16 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname,
    {
        /* quick check to see if name could be a system column */
        attnum = specialAttNum(colname);
+
+       /* In constraint check, no system column is allowed except tableOid */
+       if (pstate->p_expr_kind == EXPR_KIND_CHECK_CONSTRAINT &&
+           attnum < InvalidAttrNumber && attnum !=  TableOidAttributeNumber)
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+                    errmsg("system column \"%s\" reference in check constraint is invalid",
+                       colname),
+                    parser_errposition(pstate, location)));
+
        if (attnum != InvalidAttrNumber)
        {
            /* now check to see if column actually is defined */
index 2a630378882cea5d68cb8d25bc6f671a184122c3..16d38f6d1e754f608929b0bf59ea7bf1751b81ef 100644 (file)
@@ -126,6 +126,28 @@ INSERT INTO INSERT_TBL VALUES (null, null, null);
 
 SELECT '' AS nine, * FROM INSERT_TBL;
 
+--
+-- Check constraints on system columns
+--
+
+CREATE TABLE SYS_COL_CHECK_TBL (city text, state text, is_capital bool,
+                  altitude int,
+                  CHECK (NOT (is_capital AND tableoid::regclass::text = 'sys_col_check_tbl')));
+
+INSERT INTO SYS_COL_CHECK_TBL VALUES ('Seattle', 'Washington', false, 100);
+INSERT INTO SYS_COL_CHECK_TBL VALUES ('Olympia', 'Washington', true, 100);
+
+SELECT *, tableoid::regclass::text FROM SYS_COL_CHECK_TBL;
+
+DROP TABLE SYS_COL_CHECK_TBL;
+
+--
+-- Check constraints on system columns other then TableOid should return error
+--
+CREATE TABLE SYS_COL_CHECK_TBL (city text, state text, is_capital bool,
+                  altitude int,
+                 CHECK (NOT (is_capital AND ctid::text = 'sys_col_check_tbl')));
+
 --
 -- Check inheritance of defaults and constraints
 --
index 18a5dd8ab19d508cf564f604cc2173096fdcd885..2ffd263dd352d8fd2a87f4d10a20d8205c00e543 100644 (file)
@@ -204,6 +204,30 @@ SELECT '' AS nine, * FROM INSERT_TBL;
       |   |               |   
 (7 rows)
 
+--
+-- Check constraints on system columns
+--
+CREATE TABLE SYS_COL_CHECK_TBL (city text, state text, is_capital bool,
+                  altitude int,
+                  CHECK (NOT (is_capital AND tableoid::regclass::text = 'sys_col_check_tbl')));
+INSERT INTO SYS_COL_CHECK_TBL VALUES ('Seattle', 'Washington', false, 100);
+INSERT INTO SYS_COL_CHECK_TBL VALUES ('Olympia', 'Washington', true, 100);
+ERROR:  new row for relation "sys_col_check_tbl" violates check constraint "sys_col_check_tbl_check"
+DETAIL:  Failing row contains (Olympia, Washington, t, 100).
+SELECT *, tableoid::regclass::text FROM SYS_COL_CHECK_TBL;
+  city   |   state    | is_capital | altitude |     tableoid      
+---------+------------+------------+----------+-------------------
+ Seattle | Washington | f          |      100 | sys_col_check_tbl
+(1 row)
+
+DROP TABLE SYS_COL_CHECK_TBL;
+--
+-- Check constraints on system columns other then TableOid should return error
+--
+CREATE TABLE SYS_COL_CHECK_TBL (city text, state text, is_capital bool,
+                  altitude int,
+                 CHECK (NOT (is_capital AND ctid::text = 'sys_col_check_tbl')));
+ERROR:  system column "ctid" reference in check constraint is invalid
 --
 -- Check inheritance of defaults and constraints
 --