diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index 26eca6731c..a422edd231 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -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. diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 31819cce1d..6b20144a48 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -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); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index adc74dd7e4..8839f986b4 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -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 */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 15f5dccb82..189df713ea 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -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 * diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 39922d32c5..5f469135cb 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -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 */ diff --git a/src/test/regress/input/constraints.source b/src/test/regress/input/constraints.source index 2a63037888..16d38f6d1e 100644 --- a/src/test/regress/input/constraints.source +++ b/src/test/regress/input/constraints.source @@ -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 -- diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source index 18a5dd8ab1..2ffd263dd3 100644 --- a/src/test/regress/output/constraints.source +++ b/src/test/regress/output/constraints.source @@ -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 --