aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/tablecmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r--src/backend/commands/tablecmds.c51
1 files changed, 47 insertions, 4 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 54575fcd287..4fc54bd6eba 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -373,7 +373,7 @@ static void RangeVarCallbackForTruncate(const RangeVar *relation,
static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
bool is_partition, List **supconstr,
List **supnotnulls);
-static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
+static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
@@ -973,6 +973,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
cooked->name = NULL;
cooked->attnum = attnum;
cooked->expr = colDef->cooked_default;
+ cooked->is_enforced = true;
cooked->skip_validation = false;
cooked->is_local = true; /* not used for defaults */
cooked->inhcount = 0; /* ditto */
@@ -2890,7 +2891,8 @@ MergeAttributes(List *columns, const List *supers, char relpersistence,
name,
RelationGetRelationName(relation))));
- constraints = MergeCheckConstraint(constraints, name, expr);
+ constraints = MergeCheckConstraint(constraints, name, expr,
+ check[i].ccenforced);
}
}
@@ -3104,7 +3106,7 @@ MergeAttributes(List *columns, const List *supers, char relpersistence,
* the list.
*/
static List *
-MergeCheckConstraint(List *constraints, const char *name, Node *expr)
+MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
{
ListCell *lc;
CookedConstraint *newcon;
@@ -3127,6 +3129,17 @@ MergeCheckConstraint(List *constraints, const char *name, Node *expr)
ereport(ERROR,
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many inheritance parents"));
+
+ /*
+ * When enforceability differs, the merged constraint should be
+ * marked as ENFORCED because one of the parents is ENFORCED.
+ */
+ if (!ccon->is_enforced && is_enforced)
+ {
+ ccon->is_enforced = true;
+ ccon->skip_validation = false;
+ }
+
return constraints;
}
@@ -3145,6 +3158,8 @@ MergeCheckConstraint(List *constraints, const char *name, Node *expr)
newcon->name = pstrdup(name);
newcon->expr = expr;
newcon->inhcount = 1;
+ newcon->is_enforced = is_enforced;
+ newcon->skip_validation = !is_enforced;
return lappend(constraints, newcon);
}
@@ -10428,6 +10443,7 @@ addFkConstraint(addFkConstraintSides fkside,
CONSTRAINT_FOREIGN,
fkconstraint->deferrable,
fkconstraint->initdeferred,
+ true, /* Is Enforced */
fkconstraint->initially_valid,
parentConstr,
RelationGetRelid(rel),
@@ -12014,6 +12030,11 @@ ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
constrName, RelationGetRelationName(rel))));
+ if (!con->conenforced)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot validate NOT ENFORCED constraint")));
+
if (!con->convalidated)
{
AlteredTableInfo *tab;
@@ -16259,6 +16280,9 @@ decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
* The test we apply is to see whether they reverse-compile to the same
* source string. This insulates us from issues like whether attributes
* have the same physical column numbers in parent and child relations.
+ *
+ * Note that we ignore enforceability as there are cases where constraints
+ * with differing enforceability are allowed.
*/
static bool
constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
@@ -16528,13 +16552,25 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
* If the child constraint is "not valid" then cannot merge with a
* valid parent constraint
*/
- if (parent_con->convalidated && !child_con->convalidated)
+ if (parent_con->convalidated && child_con->conenforced &&
+ !child_con->convalidated)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
NameStr(child_con->conname), RelationGetRelationName(child_rel))));
/*
+ * A non-enforced child constraint cannot be merged with an
+ * enforced parent constraint. However, the reverse is allowed,
+ * where the child constraint is enforced.
+ */
+ if (parent_con->conenforced && !child_con->conenforced)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
+ NameStr(child_con->conname), RelationGetRelationName(child_rel))));
+
+ /*
* OK, bump the child constraint's inheritance count. (If we fail
* later on, this change will just roll back.)
*/
@@ -18885,6 +18921,12 @@ ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *p
if (!constr->check[i].ccvalid)
continue;
+ /*
+ * NOT ENFORCED constraints are always marked as invalid, which should
+ * have been ignored.
+ */
+ Assert(constr->check[i].ccenforced);
+
cexpr = stringToNode(constr->check[i].ccbin);
/*
@@ -20195,6 +20237,7 @@ DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
n->is_no_inherit = false;
n->raw_expr = NULL;
n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
+ n->is_enforced = true;
n->initially_valid = true;
n->skip_validation = true;
/* It's a re-add, since it nominally already exists */