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
--