diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 3553c3b..47d466c 100644 *** a/src/backend/catalog/heap.c --- /bsrc/backend/catalog/heap.c *************** static Oid AddNewRelationType(const char *** 77,83 **** char new_rel_kind, Oid new_array_type); static void RelationRemoveInheritance(Oid relid); ! static void StoreRelCheck(Relation rel, char *ccname, char *ccbin); static void StoreConstraints(Relation rel, TupleDesc tupdesc); static void SetRelationNumChecks(Relation rel, int numchecks); static List *insert_ordered_unique_oid(List *list, Oid datum); --- 77,83 ---- char new_rel_kind, Oid new_array_type); static void RelationRemoveInheritance(Oid relid); ! static void StoreRelCheck(Relation rel, char *ccname, char *ccbin, const bool is_local, const int inhcount); static void StoreConstraints(Relation rel, TupleDesc tupdesc); static void SetRelationNumChecks(Relation rel, int numchecks); static List *insert_ordered_unique_oid(List *list, Oid datum); *************** StoreAttrDefault(Relation rel, AttrNumbe *** 1531,1537 **** * in the pg_class entry for the relation. */ static void ! StoreRelCheck(Relation rel, char *ccname, char *ccbin) { Node *expr; char *ccsrc; --- 1531,1537 ---- * in the pg_class entry for the relation. */ static void ! StoreRelCheck(Relation rel, char *ccname, char *ccbin, const bool is_local, const int inhcount) { Node *expr; char *ccsrc; *************** StoreRelCheck(Relation rel, char *ccname *** 1608,1614 **** InvalidOid, /* no associated index */ expr, /* Tree form check constraint */ ccbin, /* Binary form check constraint */ ! ccsrc); /* Source form check constraint */ pfree(ccsrc); } --- 1608,1616 ---- InvalidOid, /* no associated index */ expr, /* Tree form check constraint */ ccbin, /* Binary form check constraint */ ! ccsrc, /* Source form check constraint */ ! is_local, /* islocal */ ! inhcount); /* coninhcount */ pfree(ccsrc); } *************** StoreConstraints(Relation rel, TupleDesc *** 1643,1649 **** for (i = 0; i < constr->num_check; i++) StoreRelCheck(rel, constr->check[i].ccname, ! constr->check[i].ccbin); if (constr->num_check > 0) SetRelationNumChecks(rel, constr->num_check); --- 1645,1653 ---- for (i = 0; i < constr->num_check; i++) StoreRelCheck(rel, constr->check[i].ccname, ! constr->check[i].ccbin, ! constr->check[i].is_local, ! constr->check[i].inhcount); if (constr->num_check > 0) SetRelationNumChecks(rel, constr->num_check); *************** AddRelationRawConstraints(Relation rel, *** 1811,1816 **** --- 1815,1821 ---- (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("constraint \"%s\" for relation \"%s\" already exists", ccname, RelationGetRelationName(rel)))); + /* Check against other new constraints */ /* Needed because we don't do CommandCounterIncrement in loop */ foreach(cell2, checknames) *************** AddRelationRawConstraints(Relation rel, *** 1863,1869 **** /* * OK, store it. */ ! StoreRelCheck(rel, ccname, nodeToString(expr)); numchecks++; --- 1868,1874 ---- /* * OK, store it. */ ! StoreRelCheck(rel, ccname, nodeToString(expr), cdef->is_local, cdef->inhcount); numchecks++; diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index f07d9ae..6db3084 100644 *** a/src/backend/catalog/index.c --- /bsrc/backend/catalog/index.c *************** index_create(Oid heapRelationId, *** 716,722 **** InvalidOid, /* no associated index */ NULL, /* no check constraint */ NULL, ! NULL); referenced.classId = ConstraintRelationId; referenced.objectId = conOid; --- 716,724 ---- InvalidOid, /* no associated index */ NULL, /* no check constraint */ NULL, ! NULL, ! true, /* islocal */ ! 0); /* inhcount */ referenced.classId = ConstraintRelationId; referenced.objectId = conOid; diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index d9b20fd..eafc143 100644 *** a/src/backend/catalog/pg_constraint.c --- /bsrc/backend/catalog/pg_constraint.c *************** CreateConstraintEntry(const char *constr *** 60,66 **** Oid indexRelId, Node *conExpr, const char *conBin, ! const char *conSrc) { Relation conDesc; Oid conOid; --- 60,68 ---- Oid indexRelId, Node *conExpr, const char *conBin, ! const char *conSrc, ! const bool conIsLocal, ! const int conInhCount) { Relation conDesc; Oid conOid; *************** CreateConstraintEntry(const char *constr *** 171,176 **** --- 173,181 ---- else nulls[Anum_pg_constraint_conffeqop - 1] = 'n'; + values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal); + values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount); + /* * initialize the binary form of the check constraint. */ diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 55c50f9..790b35a 100644 *** a/src/backend/commands/tablecmds.c --- /bsrc/backend/commands/tablecmds.c *************** static void truncate_check_rel(Relation *** 170,175 **** --- 170,177 ---- static List *MergeAttributes(List *schema, List *supers, bool istemp, List **supOids, List **supconstr, int *supOidCount); static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel); + static void MergeInheritedConstraints(Relation rel, List *old_constraints, + List *new_constriants); static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel); static void add_nonduplicate_constraint(Constraint *cdef, ConstrCheck *check, int *ncheck); *************** static void ATExecDropColumn(Relation re *** 232,245 **** bool recurse, bool recursing); static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild); static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint); static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, FkConstraint *fkconstraint); - static void ATPrepDropConstraint(List **wqueue, Relation rel, - bool recurse, AlterTableCmd *cmd); static void ATExecDropConstraint(Relation rel, const char *constrName, ! DropBehavior behavior, bool quiet); static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, --- 234,248 ---- bool recurse, bool recursing); static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild); + static void ATPrepAddConstraint(List **wqueue, Relation rel, bool recurse, + AlterTableCmd *cmd); static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint); static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, FkConstraint *fkconstraint); static void ATExecDropConstraint(Relation rel, const char *constrName, ! DropBehavior behavior, ! bool recurse, bool recursing); static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, *************** static void ATExecEnableDisableRule(Rela *** 263,269 **** static void ATExecAddInherit(Relation rel, RangeVar *parent); static void ATExecDropInherit(Relation rel, RangeVar *parent); static void copy_relation_data(Relation rel, SMgrRelation dst); ! /* ---------------------------------------------------------------- * DefineRelation --- 266,276 ---- static void ATExecAddInherit(Relation rel, RangeVar *parent); static void ATExecDropInherit(Relation rel, RangeVar *parent); static void copy_relation_data(Relation rel, SMgrRelation dst); ! static bool ConstraintIsEquiv(const HeapTuple a, const HeapTuple b, ! const TupleDesc tupleDesc); ! static char *decompile_conbin(HeapTuple contup, TupleDesc tupdesc); ! static void change_opfuncid(Node *node, Oid opfuncid); ! static bool change_opfuncid_walker(Node *node, void *opfuncid); /* ---------------------------------------------------------------- * DefineRelation *************** DefineRelation(CreateStmt *stmt, char re *** 488,493 **** --- 495,505 ---- } } + if (old_constraints && stmt->constraints) + { + MergeInheritedConstraints(rel, old_constraints, stmt->constraints); + } + /* * Parse and add the defaults/constraints, if any. */ *************** MergeAttributes(List *schema, List *supe *** 1053,1058 **** --- 1065,1072 ---- Constraint *cdef = makeNode(Constraint); Node *expr; + cdef->is_local = false; + cdef->inhcount = check[i].inhcount + 1; cdef->contype = CONSTR_CHECK; cdef->name = pstrdup(check[i].ccname); cdef->raw_expr = NULL; *************** MergeAttributes(List *schema, List *supe *** 1175,1180 **** --- 1189,1280 ---- return schema; } + /* + * Go through constraints in new constraints + * if we have a matching constraint in old_constraints and they have a + * matching definition, then remove the constraint from new_constraints + */ + static void + MergeInheritedConstraints(Relation rel, + List *old_constraints, + List *new_constraints) + { + ListCell *cur_item; + ListCell *prev_item; + ListCell *listptr; + + prev_item = NULL; + cur_item = list_head(new_constraints); + + while (cur_item != NULL) + { + bool get_next = true; + Constraint *cdef = (Constraint *) lfirst(cur_item); + + if (cdef->contype == CONSTR_CHECK && + cdef->cooked_expr == NULL && + cdef->name && + cdef->raw_expr) + { + foreach(listptr, old_constraints) + { + Constraint *cdef1 = (Constraint *) lfirst(listptr); + + if (cdef1->contype == CONSTR_CHECK && + cdef1->name && + strcmp(cdef->name, cdef1->name) == 0) + { + ParseState *pstate; + RangeTblEntry *rte; + Node *expr; + char *cooked; + + pstate = make_parsestate(NULL); + rte = addRangeTableEntryForRelation(pstate, + rel, + NULL, + false, + true); + addRTEtoQuery(pstate, rte, true, true, true); + + expr = transformExpr(pstate, cdef->raw_expr); + expr = coerce_to_boolean(pstate, expr, "CHECK"); + change_opfuncid(expr, 0); + + cooked = nodeToString(expr); + + if (strcmp(cooked, cdef1->cooked_expr) == 0) + { + ereport(NOTICE, + (errmsg("merging constraint \"%s\" with inherited definition", + cdef->name))); + + list_delete_cell(new_constraints, cur_item, prev_item); + pfree(cdef); + get_next = false; + if(prev_item) + cur_item = lnext(prev_item); + else + cur_item = list_head(new_constraints); + }else + { + ereport(ERROR, + (errmsg("constraint \"%s\" does not match inherited definition", + cdef->name))); + } + + break; + } + } + } + + if(get_next) + { + prev_item = cur_item; + cur_item = lnext(prev_item); + } + } + } /* * In multiple-inheritance situations, it's possible to inherit *************** add_nonduplicate_constraint(Constraint * *** 1208,1213 **** --- 1308,1315 ---- /* No match on name, so add it to array */ check[*ncheck].ccname = cdef->name; check[*ncheck].ccbin = pstrdup(cdef->cooked_expr); + check[*ncheck].inhcount = cdef->inhcount; + check[*ncheck].is_local = cdef->is_local; (*ncheck)++; } *************** varattnos_map_schema(TupleDesc old, List *** 1306,1311 **** --- 1408,1445 ---- return attmap; } + /* + * change an opfuncid of a Node + * + * Note that the passed node tree is modified in-place! + */ + static void + change_opfuncid(Node *node, Oid opfuncid) + { + Oid *o = palloc(sizeof(Oid)); + *o = opfuncid; + (void) change_opfuncid_walker(node, (void*)o); + pfree(o); + } + + static bool + change_opfuncid_walker(Node *node, void *opfuncid) + { + if (node == NULL) + return false; + + if (IsA(node, OpExpr)) + { + Oid *o = (Oid*)opfuncid; + OpExpr *op = (OpExpr *) node; + + op->opfuncid = *o; + return false; + } + return expression_tree_walker(node, change_opfuncid_walker, + opfuncid); + + } /* * StoreCatalogInheritance *************** ATPrepCmd(List **wqueue, Relation rel, A *** 2038,2064 **** break; case AT_AddConstraint: /* ADD CONSTRAINT */ ATSimplePermissions(rel, false); ! ! /* ! * Currently we recurse only for CHECK constraints, never for ! * foreign-key constraints. UNIQUE/PKEY constraints won't be seen ! * here. ! */ ! if (IsA(cmd->def, Constraint)) ! ATSimpleRecursion(wqueue, rel, cmd, recurse); ! /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; break; case AT_DropConstraint: /* DROP CONSTRAINT */ ATSimplePermissions(rel, false); ! /* Performs own recursion */ ! ATPrepDropConstraint(wqueue, rel, recurse, cmd); ! pass = AT_PASS_DROP; ! break; ! case AT_DropConstraintQuietly: /* DROP CONSTRAINT for child */ ! ATSimplePermissions(rel, false); ! ATSimpleRecursion(wqueue, rel, cmd, recurse); ! /* No command-specific prep needed */ pass = AT_PASS_DROP; break; case AT_AlterColumnType: /* ALTER COLUMN TYPE */ --- 2172,2187 ---- break; case AT_AddConstraint: /* ADD CONSTRAINT */ ATSimplePermissions(rel, false); ! ATPrepAddConstraint(wqueue, rel, recurse, cmd); pass = AT_PASS_ADD_CONSTR; break; case AT_DropConstraint: /* DROP CONSTRAINT */ ATSimplePermissions(rel, false); ! /* we follow AT_DropColumns example for better or for worse so ! * Recursion occurs during execution phase ! * No command-specific prep needed except saving recurse flag */ ! if (recurse) ! cmd->subtype = AT_DropConstraintRecurse; pass = AT_PASS_DROP; break; case AT_AlterColumnType: /* ALTER COLUMN TYPE */ *************** ATExecCmd(AlteredTableInfo *tab, Relatio *** 2247,2256 **** ATExecAddConstraint(tab, rel, cmd->def); break; case AT_DropConstraint: /* DROP CONSTRAINT */ ! ATExecDropConstraint(rel, cmd->name, cmd->behavior, false); break; ! case AT_DropConstraintQuietly: /* DROP CONSTRAINT for child */ ! ATExecDropConstraint(rel, cmd->name, cmd->behavior, true); break; case AT_AlterColumnType: /* ALTER COLUMN TYPE */ ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def); --- 2370,2379 ---- ATExecAddConstraint(tab, rel, cmd->def); break; case AT_DropConstraint: /* DROP CONSTRAINT */ ! ATExecDropConstraint(rel, cmd->name, cmd->behavior, false, false); break; ! case AT_DropConstraintRecurse: /* DROP CONSTRAINT for child */ ! ATExecDropConstraint(rel, cmd->name, cmd->behavior, true, false); break; case AT_AlterColumnType: /* ALTER COLUMN TYPE */ ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def); *************** ATExecAddIndex(AlteredTableInfo *tab, Re *** 3925,3930 **** --- 4048,4102 ---- * ALTER TABLE ADD CONSTRAINT */ static void + ATPrepAddConstraint(List **wqueue, Relation rel, bool recurse, + AlterTableCmd *cmd) + { + /* + * Currently we recurse only for CHECK constraints, never for + * foreign-key constraints. UNIQUE/PKEY constraints won't be seen + * here. + */ + if (!IsA(cmd->def, Constraint)) + { + return; + } + + /* + * Recurse to add the column to child classes, if requested. + * + * We must recurse one level at a time, so that multiply-inheriting + * children are visited the right number of times and end up with the + * right coninhcount. + */ + if (recurse) + { + AlterTableCmd *childCmd = copyObject(cmd); + Constraint *constr = (Constraint *) childCmd->def; + + /* child should see column as singly inherited */ + constr->inhcount = 1; + constr->is_local = false; + + ATOneLevelRecursion(wqueue, rel, childCmd); + } + else + { + /* + * If we are told not to recurse, there had better not be any child + * tables; else the addition would put them out of step. + */ + if (find_inheritance_children(RelationGetRelid(rel)) != NIL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("constraint must be added to child tables too"))); + } + } + + + /* + * ALTER TABLE ADD CONSTRAINT + */ + static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint) { switch (nodeTag(newConstraint)) *************** ATAddForeignKeyConstraint(AlteredTableIn *** 4284,4290 **** indexOid, NULL, /* no check constraint */ NULL, ! NULL); /* * Create the triggers that will enforce the constraint. --- 4456,4464 ---- indexOid, NULL, /* no check constraint */ NULL, ! NULL, ! true, /* islocal */ ! 0); /* inhcount */ /* * Create the triggers that will enforce the constraint. *************** createForeignKeyTriggers(Relation rel, F *** 4804,4847 **** * ALTER TABLE DROP CONSTRAINT */ static void ! ATPrepDropConstraint(List **wqueue, Relation rel, ! bool recurse, AlterTableCmd *cmd) { /* ! * We don't want errors or noise from child tables, so we have to pass ! * down a modified command. */ ! if (recurse) { ! AlterTableCmd *childCmd = copyObject(cmd); ! childCmd->subtype = AT_DropConstraintQuietly; ! ATSimpleRecursion(wqueue, rel, childCmd, recurse); ! } ! } ! static void ! ATExecDropConstraint(Relation rel, const char *constrName, ! DropBehavior behavior, bool quiet) ! { ! int deleted; ! deleted = RemoveRelConstraints(rel, constrName, behavior); ! if (!quiet) ! { ! /* If zero constraints deleted, complain */ ! if (deleted == 0) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_OBJECT), ! errmsg("constraint \"%s\" does not exist", ! constrName))); ! /* Otherwise if more than one constraint deleted, notify */ ! else if (deleted > 1) ! ereport(NOTICE, ! (errmsg("multiple constraints named \"%s\" were dropped", ! constrName))); } } /* --- 4978,5096 ---- * ALTER TABLE DROP CONSTRAINT */ static void ! ATExecDropConstraint(Relation rel, const char *constrName, ! DropBehavior behavior, ! bool recurse, bool recursing) { + HeapTuple tuple; + List *children; + Relation conrel; + TupleDesc tupdesc; + Oid relid; + Form_pg_constraint con; + + relid = RelationGetRelid(rel); + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + tupdesc = RelationGetDescr(conrel); + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) + ATSimplePermissions(rel, false); + + tuple = SearchSysCache(CONSTRNAME, + ObjectIdGetDatum(relid), + CStringGetDatum(constrName), + 0, 0); + + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("constraint \"%s\" of relation \"%s\" does not exist", + constrName, RelationGetRelationName(rel)))); + + con = (Form_pg_constraint) GETSTRUCT(tuple); + + /* Don't drop inherited constraints */ + if (con->coninhcount > 0 && !recursing) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"", + constrName, + RelationGetRelationName(rel)))); + + ReleaseSysCache(tuple); + /* ! * Propagate to children as appropriate. Unlike most other ALTER ! * routines, we have to do this one level of recursion at a time; we can't ! * use find_all_inheritors to do it in one pass. */ ! children = find_inheritance_children(RelationGetRelid(rel)); ! ! if (children) { ! ListCell *child; ! foreach(child, children) ! { ! Oid childrelid = lfirst_oid(child); ! Relation childrel; ! Form_pg_constraint childcon; ! childrel = heap_open(childrelid, AccessExclusiveLock); ! CheckTableNotInUse(childrel, "ALTER TABLE"); ! tuple = SearchSysCacheCopy(CONSTRNAME, ! ObjectIdGetDatum(childrelid), ! CStringGetDatum(constrName), ! 0, 0); ! if (!HeapTupleIsValid(tuple)) ! elog(ERROR, "cache lookup failed for constraint \"%s\" of relation %u", ! constrName, childrelid); ! ! childcon = (Form_pg_constraint) GETSTRUCT(tuple); ! ! if (childcon->coninhcount <= 0) ! elog(ERROR, "relation %u has non-inherited constraint \"%s\"", ! childrelid, constrName); ! if (recurse) ! { ! if (childcon->coninhcount == 1 && !childcon->conislocal) ! { ! ATExecDropConstraint(childrel, constrName, behavior, true, true); ! } ! else ! { ! childcon->coninhcount--; ! ! simple_heap_update(conrel, &tuple->t_self, tuple); ! CatalogUpdateIndexes(conrel, tuple); ! } ! } ! else ! { ! /* ! * If we were told to drop ONLY in this table (no recursion), ! * we need to mark the inheritors' attribute as locally ! * defined rather than inherited. ! */ ! ! childcon->coninhcount--; ! childcon->conislocal = true; ! ! simple_heap_update(conrel, &tuple->t_self, tuple); ! CatalogUpdateIndexes(conrel, tuple); ! } ! ! heap_freetuple(tuple); ! heap_close(childrel, AccessExclusiveLock); ! } } + + RemoveRelConstraints(rel, constrName, behavior); + + heap_close(conrel, RowExclusiveLock); } /* *************** decompile_conbin(HeapTuple contup, Tuple *** 6249,6254 **** --- 6498,6523 ---- } /* + * See if two constraints are "functionally" equivalent, but not necessarily equal + */ + static bool + ConstraintIsEquiv(const HeapTuple a, const HeapTuple b, const TupleDesc tupleDesc) + { + const Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a); + const Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b); + + if (acon->condeferrable != bcon->condeferrable || + acon->condeferred != bcon->condeferred || + strcmp(decompile_conbin(a, tupleDesc), + decompile_conbin(b, tupleDesc)) != 0) + { + return 0; + }else + { + return 1; + } + } + /* * Check columns in child table match up with columns in parent, and increment * their attinhcount. * *************** MergeAttributesIntoExisting(Relation chi *** 6342,6350 **** * make it possible to ensure no records are mistakenly inserted into the * master in partitioned tables rather than the appropriate child. * - * XXX This is O(N^2) which may be an issue with tables with hundreds of - * constraints. As long as tables have more like 10 constraints it shouldn't be - * a problem though. Even 100 constraints ought not be the end of the world. */ static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel) --- 6611,6616 ---- *************** MergeConstraintsIntoExisting(Relation ch *** 6354,6439 **** SysScanDesc scan; ScanKeyData key; HeapTuple constraintTuple; - ListCell *elem; - List *constraints; ! /* First gather up the child's constraint definitions */ ! catalogRelation = heap_open(ConstraintRelationId, AccessShareLock); tupleDesc = RelationGetDescr(catalogRelation); ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(RelationGetRelid(child_rel))); scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true, SnapshotNow, 1, &key); - constraints = NIL; - while (HeapTupleIsValid(constraintTuple = systable_getnext(scan))) - { - Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple); - - if (con->contype != CONSTRAINT_CHECK) - continue; - - constraints = lappend(constraints, heap_copytuple(constraintTuple)); - } - - systable_endscan(scan); - - /* Then scan through the parent's constraints looking for matches */ - ScanKeyInit(&key, - Anum_pg_constraint_conrelid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(RelationGetRelid(parent_rel))); - scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true, - SnapshotNow, 1, &key); - while (HeapTupleIsValid(constraintTuple = systable_getnext(scan))) { Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple); - bool found = false; - Form_pg_constraint child_con = NULL; - HeapTuple child_contuple = NULL; if (parent_con->contype != CONSTRAINT_CHECK) continue; ! foreach(elem, constraints) ! { ! child_contuple = (HeapTuple) lfirst(elem); ! child_con = (Form_pg_constraint) GETSTRUCT(child_contuple); ! if (strcmp(NameStr(parent_con->conname), ! NameStr(child_con->conname)) == 0) ! { ! found = true; ! break; ! } ! } ! if (!found) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("child table is missing constraint \"%s\"", NameStr(parent_con->conname)))); ! if (parent_con->condeferrable != child_con->condeferrable || ! parent_con->condeferred != child_con->condeferred || ! strcmp(decompile_conbin(constraintTuple, tupleDesc), ! decompile_conbin(child_contuple, tupleDesc)) != 0) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("constraint definition for check constraint \"%s\" does not match", NameStr(parent_con->conname)))); ! /* ! * TODO: add conislocal,coninhcount to constraints. This is where we ! * would have to bump them just like attributes ! */ } systable_endscan(scan); ! heap_close(catalogRelation, AccessShareLock); } /* --- 6620,6673 ---- SysScanDesc scan; ScanKeyData key; HeapTuple constraintTuple; ! catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock); tupleDesc = RelationGetDescr(catalogRelation); ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(RelationGetRelid(parent_rel))); scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true, SnapshotNow, 1, &key); while (HeapTupleIsValid(constraintTuple = systable_getnext(scan))) { + HeapTuple childTuple; + Form_pg_constraint child_con; Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple); if (parent_con->contype != CONSTRAINT_CHECK) continue; ! childTuple = SearchSysCacheCopy(CONSTRNAME, ! ObjectIdGetDatum(RelationGetRelid(child_rel)), ! CStringGetDatum(NameStr(parent_con->conname)), ! 0, 0); ! if (!HeapTupleIsValid(childTuple)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("child table is missing constraint \"%s\"", NameStr(parent_con->conname)))); ! child_con = (Form_pg_constraint) GETSTRUCT(childTuple); ! ! if (!ConstraintIsEquiv(constraintTuple, childTuple, tupleDesc)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("constraint definition for check constraint \"%s\" does not match", NameStr(parent_con->conname)))); ! /* increment coninhcount */ ! child_con->coninhcount++; ! simple_heap_update(catalogRelation, &childTuple->t_self, childTuple); ! CatalogUpdateIndexes(catalogRelation, childTuple); ! heap_freetuple(childTuple); } systable_endscan(scan); ! heap_close(catalogRelation, RowExclusiveLock); } /* *************** ATExecDropInherit(Relation rel, RangeVar *** 6458,6465 **** --- 6692,6701 ---- ScanKeyData key[3]; HeapTuple inheritsTuple, attributeTuple, + constraintTuple, depTuple; bool found = false; + TupleDesc tupdesc; /* * AccessShareLock on the parent is probably enough, seeing that DROP *************** ATExecDropInherit(Relation rel, RangeVar *** 6497,6503 **** break; } } - systable_endscan(scan); heap_close(catalogRelation, RowExclusiveLock); --- 6733,6738 ---- *************** ATExecDropInherit(Relation rel, RangeVar *** 6547,6552 **** --- 6782,6822 ---- systable_endscan(scan); heap_close(catalogRelation, RowExclusiveLock); + catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock); + ScanKeyInit(&key[0], + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationGetRelid(rel))); + scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, + true, SnapshotNow, 1, key); + + tupdesc = RelationGetDescr(catalogRelation); + + while (HeapTupleIsValid(constraintTuple = systable_getnext(scan))) + { + Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple); + + if (con->contype != CONSTRAINT_CHECK) + continue; + + if (con->coninhcount > 0) + { + HeapTuple copyTuple = heap_copytuple(constraintTuple); + Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple); + + copy_con->coninhcount--; + if (copy_con->coninhcount == 0) + copy_con->conislocal = true; + + simple_heap_update(catalogRelation, ©Tuple->t_self, copyTuple); + CatalogUpdateIndexes(catalogRelation, copyTuple); + heap_freetuple(copyTuple); + } + } + + systable_endscan(scan); + heap_close(catalogRelation, RowExclusiveLock); + /* * Drop the dependency * *************** AtEOSubXact_on_commit_actions(bool isCom *** 7050,7052 **** --- 7320,7324 ---- } } } + + diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 188fe43..123209d 100644 *** a/src/backend/commands/typecmds.c --- /bsrc/backend/commands/typecmds.c *************** domainAddConstraint(Oid domainOid, Oid d *** 2206,2212 **** InvalidOid, expr, /* Tree form check constraint */ ccbin, /* Binary form check constraint */ ! ccsrc); /* Source form check constraint */ /* * Return the compiled constraint expression so the calling routine can --- 2206,2214 ---- InvalidOid, expr, /* Tree form check constraint */ ccbin, /* Binary form check constraint */ ! ccsrc, /* Source form check constraint */ ! true, /* is local */ ! 0); /* inhcount */ /* * Return the compiled constraint expression so the calling routine can diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 77bba77..c7fc509 100644 *** a/src/backend/nodes/copyfuncs.c --- /bsrc/backend/nodes/copyfuncs.c *************** _copyConstraint(Constraint *from) *** 1814,1819 **** --- 1814,1821 ---- COPY_NODE_FIELD(keys); COPY_NODE_FIELD(options); COPY_STRING_FIELD(indexspace); + COPY_SCALAR_FIELD(inhcount); + COPY_SCALAR_FIELD(is_local); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 7645e45..fe4ca20 100644 *** a/src/backend/nodes/equalfuncs.c --- /bsrc/backend/nodes/equalfuncs.c *************** _equalConstraint(Constraint *a, Constrai *** 1839,1844 **** --- 1839,1846 ---- COMPARE_NODE_FIELD(keys); COMPARE_NODE_FIELD(options); COMPARE_STRING_FIELD(indexspace); + COMPARE_SCALAR_FIELD(inhcount); + COMPARE_SCALAR_FIELD(is_local); return true; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 72ee2ff..450186c 100644 *** a/src/backend/nodes/outfuncs.c --- /bsrc/backend/nodes/outfuncs.c *************** _outConstraint(StringInfo str, Constrain *** 2033,2038 **** --- 2033,2041 ---- appendStringInfo(str, ""); break; } + + WRITE_INT_FIELD(inhcount); + WRITE_BOOL_FIELD(is_local); } static void diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index efd50f6..a5f6932 100644 *** a/src/backend/parser/gram.y --- /bsrc/backend/parser/gram.y *************** ColConstraintElem: *** 2068,2073 **** --- 2068,2075 ---- n->cooked_expr = NULL; n->keys = NULL; n->indexspace = NULL; + n->is_local = true; + n->inhcount = 0; $$ = (Node *)n; } | DEFAULT b_expr *************** ConstraintElem: *** 2208,2213 **** --- 2210,2217 ---- n->raw_expr = $3; n->cooked_expr = NULL; n->indexspace = NULL; + n->inhcount = 0; + n->is_local = true; $$ = (Node *)n; } | UNIQUE '(' columnList ')' opt_definition OptConsTableSpace diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index f2087e4..f105d39 100644 *** a/src/backend/parser/parse_utilcmd.c --- /bsrc/backend/parser/parse_utilcmd.c *************** transformColumnDefinition(ParseState *ps *** 409,414 **** --- 409,417 ---- { constraint = lfirst(clist); + constraint->is_local = true; + constraint->inhcount = 0; + /* * If this column constraint is a FOREIGN KEY constraint, then we fill * in the current attribute's name and throw it into the list of FK *************** transformInhRelation(ParseState *pstate, *** 685,690 **** --- 688,695 ---- n->cooked_expr = nodeToString(ccbin_node); n->indexspace = NULL; cxt->ckconstraints = lappend(cxt->ckconstraints, (Node *) n); + n->is_local = true; + n->inhcount = 0; } } diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 3f3c67d..250d468 100644 *** a/src/backend/utils/cache/catcache.c --- /bsrc/backend/utils/cache/catcache.c *************** CatalogCacheInitializeCache(CatCache *ca *** 962,968 **** else { if (cache->cc_key[i] != ObjectIdAttributeNumber) ! elog(FATAL, "only sys attr supported in caches is OID"); keytype = OIDOID; } --- 962,968 ---- else { if (cache->cc_key[i] != ObjectIdAttributeNumber) ! elog(FATAL, "only sys attr supported in caches is OID for rel: %s", cache->cc_relname); keytype = OIDOID; } diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index 7ff0e8a..789c6a4 100644 *** a/src/backend/utils/cache/syscache.c --- /bsrc/backend/utils/cache/syscache.c *************** static const struct cachedesc cacheinfo[ *** 305,310 **** --- 305,322 ---- }, 128 }, + {ConstraintRelationId, // CONSTRNAME + ConstraintRelidNameIndexId, + Anum_pg_constraint_conrelid, + 2, + { + Anum_pg_constraint_conrelid, + Anum_pg_constraint_conname, + 0, + 0 + }, + 256, + }, {ConstraintRelationId, /* CONSTROID */ ConstraintOidIndexId, 0, diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index c5fb69a..aa683be 100644 *** a/src/include/access/tupdesc.h --- /bsrc/include/access/tupdesc.h *************** typedef struct attrDefault *** 27,34 **** typedef struct constrCheck { ! char *ccname; ! char *ccbin; /* nodeToString representation of expr */ } ConstrCheck; /* This structure contains constraints of a tuple */ --- 27,36 ---- typedef struct constrCheck { ! char *ccname; ! char *ccbin; /* nodeToString representation of expr */ ! bool is_local; ! int inhcount; } ConstrCheck; /* This structure contains constraints of a tuple */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index b9f34f2..9b17a73 100644 *** a/src/include/catalog/catversion.h --- /bsrc/include/catalog/catversion.h *************** *** 53,58 **** */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 200803222 #endif --- 53,58 ---- */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 200803290 #endif diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 7dc5215..d962930 100644 *** a/src/include/catalog/indexing.h --- /bsrc/include/catalog/indexing.h *************** DECLARE_INDEX(pg_constraint_contypid_ind *** 121,126 **** --- 121,128 ---- #define ConstraintTypidIndexId 2666 DECLARE_UNIQUE_INDEX(pg_constraint_oid_index, 2667, on pg_constraint using btree(oid oid_ops)); #define ConstraintOidIndexId 2667 + DECLARE_UNIQUE_INDEX(pg_constraint_relid_coname_index, 2700, on pg_constraint using btree(conrelid oid_ops, conname name_ops)); + #define ConstraintRelidNameIndexId 2700 DECLARE_UNIQUE_INDEX(pg_conversion_default_index, 2668, on pg_conversion using btree(connamespace oid_ops, conforencoding int4_ops, contoencoding int4_ops, oid oid_ops)); #define ConversionDefaultIndexId 2668 diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index 501899d..9e4a7c9 100644 *** a/src/include/catalog/pg_constraint.h --- /bsrc/include/catalog/pg_constraint.h *************** CATALOG(pg_constraint,2606) *** 71,76 **** --- 71,82 ---- char confdeltype; /* foreign key's ON DELETE action */ char confmatchtype; /* foreign key's match type */ + /* Has a local definition (hence, do not drop when coninhcount is 0) */ + bool conislocal; + + /* Number of times inherited from direct parent relation(s) */ + int4 coninhcount; + /* * VARIABLE LENGTH FIELDS start here. These fields may be NULL, too. */ *************** typedef FormData_pg_constraint *Form_pg_ *** 125,150 **** * compiler constants for pg_constraint * ---------------- */ ! #define Natts_pg_constraint 18 ! #define Anum_pg_constraint_conname 1 ! #define Anum_pg_constraint_connamespace 2 ! #define Anum_pg_constraint_contype 3 ! #define Anum_pg_constraint_condeferrable 4 ! #define Anum_pg_constraint_condeferred 5 ! #define Anum_pg_constraint_conrelid 6 ! #define Anum_pg_constraint_contypid 7 ! #define Anum_pg_constraint_confrelid 8 ! #define Anum_pg_constraint_confupdtype 9 ! #define Anum_pg_constraint_confdeltype 10 ! #define Anum_pg_constraint_confmatchtype 11 ! #define Anum_pg_constraint_conkey 12 ! #define Anum_pg_constraint_confkey 13 ! #define Anum_pg_constraint_conpfeqop 14 ! #define Anum_pg_constraint_conppeqop 15 ! #define Anum_pg_constraint_conffeqop 16 ! #define Anum_pg_constraint_conbin 17 ! #define Anum_pg_constraint_consrc 18 ! /* Valid values for contype */ #define CONSTRAINT_CHECK 'c' --- 131,157 ---- * compiler constants for pg_constraint * ---------------- */ ! #define Natts_pg_constraint 20 ! #define Anum_pg_constraint_conname 1 ! #define Anum_pg_constraint_connamespace 2 ! #define Anum_pg_constraint_contype 3 ! #define Anum_pg_constraint_condeferrable 4 ! #define Anum_pg_constraint_condeferred 5 ! #define Anum_pg_constraint_conrelid 6 ! #define Anum_pg_constraint_contypid 7 ! #define Anum_pg_constraint_confrelid 8 ! #define Anum_pg_constraint_confupdtype 9 ! #define Anum_pg_constraint_confdeltype 10 ! #define Anum_pg_constraint_confmatchtype 11 ! #define Anum_pg_constraint_conislocal 12 ! #define Anum_pg_constraint_coninhcount 13 ! #define Anum_pg_constraint_conkey 14 ! #define Anum_pg_constraint_confkey 15 ! #define Anum_pg_constraint_conpfeqop 16 ! #define Anum_pg_constraint_conppeqop 17 ! #define Anum_pg_constraint_conffeqop 18 ! #define Anum_pg_constraint_conbin 19 ! #define Anum_pg_constraint_consrc 20 /* Valid values for contype */ #define CONSTRAINT_CHECK 'c' *************** extern Oid CreateConstraintEntry(const c *** 192,198 **** Oid indexRelId, Node *conExpr, const char *conBin, ! const char *conSrc); extern void RemoveConstraintById(Oid conId); extern void RenameConstraintById(Oid conId, const char *newname); --- 199,207 ---- Oid indexRelId, Node *conExpr, const char *conBin, ! const char *conSrc, ! const bool conIsLocal, ! const int conInhCout); extern void RemoveConstraintById(Oid conId); extern void RenameConstraintById(Oid conId, const char *newname); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index f786f18..e5c1d65 100644 *** a/src/include/nodes/parsenodes.h --- /bsrc/include/nodes/parsenodes.h *************** typedef enum AlterTableType *** 902,908 **** AT_ProcessedConstraint, /* pre-processed add constraint (local in * parser/parse_utilcmd.c) */ AT_DropConstraint, /* drop constraint */ ! AT_DropConstraintQuietly, /* drop constraint, no error/warning (local in * commands/tablecmds.c) */ AT_AlterColumnType, /* alter column type */ AT_ChangeOwner, /* change owner */ --- 902,908 ---- AT_ProcessedConstraint, /* pre-processed add constraint (local in * parser/parse_utilcmd.c) */ AT_DropConstraint, /* drop constraint */ ! AT_DropConstraintRecurse, /* drop constraint recursion (local in * commands/tablecmds.c) */ AT_AlterColumnType, /* alter column type */ AT_ChangeOwner, /* change owner */ *************** typedef struct Constraint *** 1165,1170 **** --- 1165,1172 ---- List *options; /* options from WITH clause */ char *indexspace; /* index tablespace for PKEY/UNIQUE * constraints; NULL for default */ + bool is_local; + int inhcount; } Constraint; /* ---------- diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h index 76248de..b99da72 100644 *** a/src/include/utils/syscache.h --- /bsrc/include/utils/syscache.h *************** *** 28,81 **** * Keep them in alphabetical order. */ ! #define AGGFNOID 0 ! #define AMNAME 1 ! #define AMOID 2 ! #define AMOPOPID 3 ! #define AMOPSTRATEGY 4 ! #define AMPROCNUM 5 ! #define ATTNAME 6 ! #define ATTNUM 7 ! #define AUTHMEMMEMROLE 8 ! #define AUTHMEMROLEMEM 9 ! #define AUTHNAME 10 ! #define AUTHOID 11 ! #define CASTSOURCETARGET 12 ! #define CLAAMNAMENSP 13 ! #define CLAOID 14 ! #define CONDEFAULT 15 ! #define CONNAMENSP 16 ! #define CONSTROID 17 ! #define CONVOID 18 ! #define DATABASEOID 19 ! #define ENUMOID 20 ! #define ENUMTYPOIDNAME 21 ! #define INDEXRELID 22 ! #define LANGNAME 23 ! #define LANGOID 24 ! #define NAMESPACENAME 25 ! #define NAMESPACEOID 26 ! #define OPERNAMENSP 27 ! #define OPEROID 28 ! #define OPFAMILYAMNAMENSP 29 ! #define OPFAMILYOID 30 ! #define PROCNAMEARGSNSP 31 ! #define PROCOID 32 ! #define RELNAMENSP 33 ! #define RELOID 34 ! #define RULERELNAME 35 ! #define STATRELATT 36 ! #define TSCONFIGMAP 37 ! #define TSCONFIGNAMENSP 38 ! #define TSCONFIGOID 39 ! #define TSDICTNAMENSP 40 ! #define TSDICTOID 41 ! #define TSPARSERNAMENSP 42 ! #define TSPARSEROID 43 ! #define TSTEMPLATENAMENSP 44 ! #define TSTEMPLATEOID 45 ! #define TYPENAMENSP 46 ! #define TYPEOID 47 extern void InitCatalogCache(void); extern void InitCatalogCachePhase2(void); --- 28,84 ---- * Keep them in alphabetical order. */ ! enum SysCacheIdent { ! AGGFNOID = 0, ! AMNAME, ! AMOID, ! AMOPOPID, ! AMOPSTRATEGY, ! AMPROCNUM, ! ATTNAME, ! ATTNUM, ! AUTHMEMMEMROLE, ! AUTHMEMROLEMEM, ! AUTHNAME, ! AUTHOID, ! CASTSOURCETARGET, ! CLAAMNAMENSP, ! CLAOID, ! CONDEFAULT, ! CONNAMENSP, ! CONSTRNAME, ! CONSTROID, ! CONVOID, ! DATABASEOID, ! ENUMOID, ! ENUMTYPOIDNAME, ! INDEXRELID, ! LANGNAME, ! LANGOID, ! NAMESPACENAME, ! NAMESPACEOID, ! OPERNAMENSP, ! OPEROID, ! OPFAMILYAMNAMENSP, ! OPFAMILYOID, ! PROCNAMEARGSNSP, ! PROCOID, ! RELNAMENSP, ! RELOID, ! RULERELNAME, ! STATRELATT, ! TSCONFIGMAP, ! TSCONFIGNAMENSP, ! TSCONFIGOID, ! TSDICTNAMENSP, ! TSDICTOID, ! TSPARSERNAMENSP, ! TSPARSEROID, ! TSTEMPLATENAMENSP, ! TSTEMPLATEOID, ! TYPENAMENSP, ! TYPEOID, ! }; extern void InitCatalogCache(void); extern void InitCatalogCachePhase2(void); diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index cba489d..58ee537 100644 *** a/src/test/regress/expected/alter_table.out --- /bsrc/test/regress/expected/alter_table.out *************** create table atacc1 (test int); *** 383,391 **** create table atacc2 (test2 int); create table atacc3 (test3 int) inherits (atacc1, atacc2); alter table only atacc2 add constraint foo check (test2>0); -- fail and then succeed on atacc2 insert into atacc2 (test2) values (-3); - ERROR: new row for relation "atacc2" violates check constraint "foo" insert into atacc2 (test2) values (3); -- both succeed on atacc3 insert into atacc3 (test2) values (-3); --- 383,391 ---- create table atacc2 (test2 int); create table atacc3 (test3 int) inherits (atacc1, atacc2); alter table only atacc2 add constraint foo check (test2>0); + ERROR: constraint must be added to child tables too -- fail and then succeed on atacc2 insert into atacc2 (test2) values (-3); insert into atacc2 (test2) values (3); -- both succeed on atacc3 insert into atacc3 (test2) values (-3); diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index f81776f..b2c684b 100644 *** a/src/test/regress/expected/inherit.out --- /bsrc/test/regress/expected/inherit.out *************** drop function p2text(p2); *** 692,694 **** --- 692,770 ---- drop table c1; drop table p2; drop table p1; + CREATE TABLE ac (aa TEXT); + alter table ac add constraint ac_check check (aa is not null); + CREATE TABLE bc (bb TEXT) INHERITS (ac); + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + conname | contype | conislocal | coninhcount | consrc + ----------+---------+------------+-------------+------------------ + ac_check | c | t | 0 | (aa IS NOT NULL) + ac_check | c | f | 1 | (aa IS NOT NULL) + (2 rows) + + alter table bc drop constraint ac_check; + ERROR: cannot drop inherited constraint "ac_check" of relation "bc" + alter table ac drop constraint ac_check; + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + conname | contype | conislocal | coninhcount | consrc + ---------+---------+------------+-------------+-------- + (0 rows) + + alter table ac add constraint ac_check check (aa is not null); + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + conname | contype | conislocal | coninhcount | consrc + ----------+---------+------------+-------------+------------------ + ac_check | c | t | 0 | (aa IS NOT NULL) + ac_check | c | f | 1 | (aa IS NOT NULL) + (2 rows) + + insert into ac (aa) values (NULL); + ERROR: new row for relation "ac" violates check constraint "ac_check" + insert into bc (aa) values (NULL); + ERROR: new row for relation "bc" violates check constraint "ac_check" + alter table bc drop constraint ac_check; + ERROR: cannot drop inherited constraint "ac_check" of relation "bc" + alter table ac drop constraint ac_check; + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + conname | contype | conislocal | coninhcount | consrc + ---------+---------+------------+-------------+-------- + (0 rows) + + alter table ac add constraint ac_check check (aa is not null); + alter table bc no inherit ac; + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + conname | contype | conislocal | coninhcount | consrc + ----------+---------+------------+-------------+------------------ + ac_check | c | t | 0 | (aa IS NOT NULL) + ac_check | c | t | 0 | (aa IS NOT NULL) + (2 rows) + + alter table bc drop constraint ac_check; + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + conname | contype | conislocal | coninhcount | consrc + ----------+---------+------------+-------------+------------------ + ac_check | c | t | 0 | (aa IS NOT NULL) + (1 row) + + alter table ac drop constraint ac_check; + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + conname | contype | conislocal | coninhcount | consrc + ---------+---------+------------+-------------+-------- + (0 rows) + + drop table bc; + drop table ac; + create table ac (a int constraint check_a check (a <> 0)); + create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac); + NOTICE: merging column "a" with inherited definition + NOTICE: merging constraint "check_a" with inherited definition + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + conname | contype | conislocal | coninhcount | consrc + ---------+---------+------------+-------------+---------- + check_a | c | t | 0 | (a <> 0) + check_b | c | t | 0 | (b <> 0) + check_a | c | f | 1 | (a <> 0) + (3 rows) + + drop table bc; + drop table ac; diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql index b0499a6..9c17849 100644 *** a/src/test/regress/sql/inherit.sql --- /bsrc/test/regress/sql/inherit.sql *************** drop function p2text(p2); *** 196,198 **** --- 196,236 ---- drop table c1; drop table p2; drop table p1; + + CREATE TABLE ac (aa TEXT); + alter table ac add constraint ac_check check (aa is not null); + CREATE TABLE bc (bb TEXT) INHERITS (ac); + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + + alter table bc drop constraint ac_check; + alter table ac drop constraint ac_check; + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + + alter table ac add constraint ac_check check (aa is not null); + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + + insert into ac (aa) values (NULL); + insert into bc (aa) values (NULL); + + alter table bc drop constraint ac_check; + alter table ac drop constraint ac_check; + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + + alter table ac add constraint ac_check check (aa is not null); + alter table bc no inherit ac; + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + alter table bc drop constraint ac_check; + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + alter table ac drop constraint ac_check; + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + + drop table bc; + drop table ac; + + create table ac (a int constraint check_a check (a <> 0)); + create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac); + select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname; + + + drop table bc; + drop table ac;