diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 87a4d73..477b958 100644 *** a/src/backend/catalog/heap.c --- /bsrc/backend/catalog/heap.c *************** static Oid AddNewRelationType(const char *** 74,80 **** 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); --- 74,80 ---- 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 *** 1530,1536 **** * in the pg_class entry for the relation. */ static void ! StoreRelCheck(Relation rel, char *ccname, char *ccbin) { Node *expr; char *ccsrc; --- 1530,1536 ---- * 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 *** 1607,1613 **** InvalidOid, /* no associated index */ expr, /* Tree form check constraint */ ccbin, /* Binary form check constraint */ ! ccsrc); /* Source form check constraint */ pfree(ccsrc); } --- 1607,1615 ---- 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 *** 1642,1648 **** 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); --- 1644,1652 ---- 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, *** 1793,1798 **** --- 1797,1842 ---- (errcode(ERRCODE_GROUPING_ERROR), errmsg("cannot use aggregate function in check constraint"))); + /* are we adding the constraint to a recursion child? */ + /* XXX: this does not really work.... needs to be higher up _or_ we need to filter out dups + * because right now if one does create table ( ... constraint check_b) inherits(junk); + * we increase our inhcount to 2 for check_b (assuming junk has a check_b constraint) + * plus we never check the consrc/conbin and make sure the match */ + if (cdef->name != NULL) + { + HeapTuple tuple; + Form_pg_constraint con; + Relation conrel; + + tuple = SearchSysCacheCopy(CONSTRNAME, + ObjectIdGetDatum(RelationGetRelid(rel)), + CStringGetDatum(cdef->name), + 0, 0); + + /* + * if we had the constraint, increment the inhcount + * otherwise we just continue on adding the constraint + */ + if (HeapTupleIsValid(tuple)) + { + con = (Form_pg_constraint) GETSTRUCT(tuple); + con->coninhcount++; + + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + simple_heap_update(conrel, &tuple->t_self, tuple); + CatalogUpdateIndexes(conrel, tuple); + heap_freetuple(tuple); + heap_close(conrel, RowExclusiveLock); + + ereport(NOTICE, + (errmsg("merging constraint \"%s\" for child \"%s\"", + cdef->name, RelationGetRelationName(rel)))); + + /* all done, go onto the next constraint */ + continue; + } + } + /* * Check name uniqueness, or generate a name if none was given. */ *************** AddRelationRawConstraints(Relation rel, *** 1810,1815 **** --- 1854,1860 ---- (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, *** 1862,1868 **** /* * OK, store it. */ ! StoreRelCheck(rel, ccname, nodeToString(expr)); numchecks++; --- 1907,1913 ---- /* * 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 fd45460..7d97777 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 073047b..b1c4a80 100644 *** a/src/backend/catalog/pg_constraint.c --- /bsrc/backend/catalog/pg_constraint.c *************** *** 28,34 **** #include "utils/lsyscache.h" #include "utils/syscache.h" - /* * CreateConstraintEntry * Create a constraint table entry. --- 28,33 ---- *************** CreateConstraintEntry(const char *constr *** 59,65 **** Oid indexRelId, Node *conExpr, const char *conBin, ! const char *conSrc) { Relation conDesc; Oid conOid; --- 58,66 ---- Oid indexRelId, Node *conExpr, const char *conBin, ! const char *conSrc, ! const bool conIsLocal, ! const int conInhCount) { Relation conDesc; Oid conOid; *************** CreateConstraintEntry(const char *constr *** 170,175 **** --- 171,179 ---- 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. */ *************** AlterConstraintNamespaces(Oid ownerId, O *** 695,697 **** --- 699,744 ---- heap_close(conRel, RowExclusiveLock); } + + /* + * Obtain the source-text form of the constraint expression for a check + * constraint, given its pg_constraint tuple + */ + static char * + decompile_conbin(HeapTuple contup, TupleDesc tupdesc) + { + Form_pg_constraint con; + bool isnull; + Datum attr; + Datum expr; + + con = (Form_pg_constraint) GETSTRUCT(contup); + attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull); + if (isnull) + elog(ERROR, "null conbin for constraint %u", HeapTupleGetOid(contup)); + + expr = DirectFunctionCall2(pg_get_expr, attr, + ObjectIdGetDatum(con->conrelid)); + return DatumGetCString(DirectFunctionCall1(textout, expr)); + } + + /* + * See if tow constraints are "functionally" equivalent, but not necessarily equal + */ + 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; + } + } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 886b792..78925c8 100644 *** a/src/backend/commands/tablecmds.c --- /bsrc/backend/commands/tablecmds.c *************** static void ATExecDropColumn(Relation re *** 229,242 **** 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, --- 229,243 ---- 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 ATExecAddInherit(Relation re *** 261,267 **** static void ATExecDropInherit(Relation rel, RangeVar *parent); static void copy_relation_data(Relation rel, SMgrRelation dst); - /* ---------------------------------------------------------------- * DefineRelation * Creates a new relation. --- 262,267 ---- *************** DefineRelation(CreateStmt *stmt, char re *** 387,392 **** --- 387,393 ---- check = (ConstrCheck *) palloc((list_length(old_constraints) + list_length(stmt->constraints)) * sizeof(ConstrCheck)); + /* deal with constraints from MergeAttributes */ foreach(listptr, old_constraints) { *************** DefineRelation(CreateStmt *stmt, char re *** 400,405 **** --- 401,407 ---- * parse_utilcmd.c might have passed some precooked constraints too, * due to LIKE tab INCLUDING CONSTRAINTS */ + foreach(listptr, stmt->constraints) { Constraint *cdef = (Constraint *) lfirst(listptr); *************** DefineRelation(CreateStmt *stmt, char re *** 407,421 **** if (cdef->contype == CONSTR_CHECK && cdef->cooked_expr != NULL) add_nonduplicate_constraint(cdef, check, &ncheck); } /* if we found any, insert 'em into the descriptor */ if (ncheck > 0) { if (descriptor->constr == NULL) { ! descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr)); ! descriptor->constr->defval = NULL; ! descriptor->constr->num_defval = 0; ! descriptor->constr->has_not_null = false; } descriptor->constr->num_check = ncheck; descriptor->constr->check = check; --- 409,424 ---- if (cdef->contype == CONSTR_CHECK && cdef->cooked_expr != NULL) add_nonduplicate_constraint(cdef, check, &ncheck); } + /* if we found any, insert 'em into the descriptor */ if (ncheck > 0) { if (descriptor->constr == NULL) { ! descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr)); ! descriptor->constr->defval = NULL; ! descriptor->constr->num_defval = 0; ! descriptor->constr->has_not_null = false; } descriptor->constr->num_check = ncheck; descriptor->constr->check = check; *************** MergeAttributes(List *schema, List *supe *** 991,996 **** --- 994,1001 ---- 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; *************** static void *** 1126,1141 **** add_nonduplicate_constraint(Constraint *cdef, ConstrCheck *check, int *ncheck) { int i; - /* Should only see precooked constraints here */ Assert(cdef->contype == CONSTR_CHECK); Assert(cdef->name != NULL); Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL); for (i = 0; i < *ncheck; i++) { if (strcmp(check[i].ccname, cdef->name) != 0) continue; if (strcmp(check[i].ccbin, cdef->cooked_expr) == 0) return; /* duplicate constraint, so ignore it */ ereport(ERROR, --- 1131,1148 ---- add_nonduplicate_constraint(Constraint *cdef, ConstrCheck *check, int *ncheck) { int i; /* Should only see precooked constraints here */ Assert(cdef->contype == CONSTR_CHECK); Assert(cdef->name != NULL); + + Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL); for (i = 0; i < *ncheck; i++) { if (strcmp(check[i].ccname, cdef->name) != 0) continue; + if (strcmp(check[i].ccbin, cdef->cooked_expr) == 0) return; /* duplicate constraint, so ignore it */ ereport(ERROR, *************** add_nonduplicate_constraint(Constraint * *** 1146,1151 **** --- 1153,1160 ---- /* 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)++; } *************** ATPrepCmd(List **wqueue, Relation rel, A *** 1949,1975 **** 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 */ --- 1958,1973 ---- 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 *** 2158,2167 **** 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); --- 2156,2165 ---- 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 *** 3836,3841 **** --- 3834,3888 ---- * 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)) *************** ATExecAddConstraint(AlteredTableInfo *ta *** 3872,3877 **** --- 3919,3926 ---- newcon = (NewConstraint *) palloc0(sizeof(NewConstraint)); newcon->name = ccon->name; newcon->contype = ccon->contype; + + /* ExecQual wants implicit-AND format */ newcon->qual = (Node *) make_ands_implicit((Expr *) ccon->expr); *************** ATAddForeignKeyConstraint(AlteredTableIn *** 4195,4201 **** indexOid, NULL, /* no check constraint */ NULL, ! NULL); /* * Create the triggers that will enforce the constraint. --- 4244,4252 ---- indexOid, NULL, /* no check constraint */ NULL, ! NULL, ! true, /* islocal */ ! 0); /* inhcount */ /* * Create the triggers that will enforce the constraint. *************** createForeignKeyTriggers(Relation rel, F *** 4715,4758 **** * 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))); } } /* --- 4766,4883 ---- * 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 = RelationGetRelid(rel); + Form_pg_constraint con; + + 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); } /* *************** ATExecAddInherit(Relation child_rel, Ran *** 6138,6165 **** } /* - * Obtain the source-text form of the constraint expression for a check - * constraint, given its pg_constraint tuple - */ - static char * - decompile_conbin(HeapTuple contup, TupleDesc tupdesc) - { - Form_pg_constraint con; - bool isnull; - Datum attr; - Datum expr; - - con = (Form_pg_constraint) GETSTRUCT(contup); - attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull); - if (isnull) - elog(ERROR, "null conbin for constraint %u", HeapTupleGetOid(contup)); - - expr = DirectFunctionCall2(pg_get_expr, attr, - ObjectIdGetDatum(con->conrelid)); - return DatumGetCString(DirectFunctionCall1(textout, expr)); - } - - /* * Check columns in child table match up with columns in parent, and increment * their attinhcount. * --- 6263,6268 ---- *************** MergeAttributesIntoExisting(Relation chi *** 6253,6261 **** * 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) --- 6356,6361 ---- *************** MergeConstraintsIntoExisting(Relation ch *** 6265,6350 **** 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); } /* --- 6365,6418 ---- 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 *** 6369,6376 **** --- 6437,6446 ---- 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 *** 6408,6414 **** break; } } - systable_endscan(scan); heap_close(catalogRelation, RowExclusiveLock); --- 6478,6483 ---- *************** ATExecDropInherit(Relation rel, RangeVar *** 6458,6463 **** --- 6527,6567 ---- 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 * diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index a1394d8..14c65be 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 f7c1fd0..3026cf6 100644 *** a/src/backend/nodes/copyfuncs.c --- /bsrc/backend/nodes/copyfuncs.c *************** _copyConstraint(Constraint *from) *** 1805,1810 **** --- 1805,1812 ---- 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 3ff9691..9b64310 100644 *** a/src/backend/nodes/equalfuncs.c --- /bsrc/backend/nodes/equalfuncs.c *************** _equalConstraint(Constraint *a, Constrai *** 1832,1837 **** --- 1832,1839 ---- 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 2f5b1a9..c479b67 100644 *** a/src/backend/nodes/outfuncs.c --- /bsrc/backend/nodes/outfuncs.c *************** _outConstraint(StringInfo str, Constrain *** 2026,2031 **** --- 2026,2034 ---- 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 0205ef5..a502cb4 100644 *** a/src/backend/parser/gram.y --- /bsrc/backend/parser/gram.y *************** ColConstraintElem: *** 2067,2072 **** --- 2067,2074 ---- n->cooked_expr = NULL; n->keys = NULL; n->indexspace = NULL; + n->is_local = true; + n->inhcount = 0; $$ = (Node *)n; } | DEFAULT b_expr *************** ConstraintElem: *** 2207,2212 **** --- 2209,2216 ---- 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 eec1e30..06bd3f8 100644 *** a/src/backend/parser/parse_utilcmd.c --- /bsrc/backend/parser/parse_utilcmd.c *************** transformColumnDefinition(ParseState *ps *** 398,403 **** --- 398,406 ---- { 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, *** 674,679 **** --- 677,684 ---- 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 8cbf494..8436871 100644 *** a/src/backend/utils/cache/catcache.c --- /bsrc/backend/utils/cache/catcache.c *************** CatalogCacheInitializeCache(CatCache *ca *** 961,967 **** else { if (cache->cc_key[i] != ObjectIdAttributeNumber) ! elog(FATAL, "only sys attr supported in caches is OID"); keytype = OIDOID; } --- 961,967 ---- 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 7e5550a..846a48d 100644 *** a/src/include/catalog/catversion.h --- /bsrc/include/catalog/catversion.h *************** *** 53,58 **** */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 200711281 #endif --- 53,58 ---- */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 20080305 #endif diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index f28afc8..06b1c86 100644 *** a/src/include/catalog/heap.h --- /bsrc/include/catalog/heap.h *************** extern void InsertPgClassTuple(Relation *** 67,72 **** --- 67,73 ---- Oid new_rel_oid, Datum reloptions); + extern List *AddRelationRawConstraints(Relation rel, List *rawColDefaults, List *rawConstraints); 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 ed71aa2..1ca0d6d 100644 *** a/src/include/catalog/pg_constraint.h --- /bsrc/include/catalog/pg_constraint.h *************** *** 19,24 **** --- 19,26 ---- #ifndef PG_CONSTRAINT_H #define PG_CONSTRAINT_H + #include "access/htup.h" + #include "access/tupdesc.h" #include "nodes/pg_list.h" /* ---------------- *************** CATALOG(pg_constraint,2606) *** 77,82 **** --- 79,90 ---- 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_ *** 131,156 **** * 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' --- 139,168 ---- * compiler constants for pg_constraint * ---------------- */ ! enum { ! Anum_pg_constraint_conname = 1, ! Anum_pg_constraint_connamespace, ! Anum_pg_constraint_contype, ! Anum_pg_constraint_condeferrable, ! Anum_pg_constraint_condeferred, ! Anum_pg_constraint_conrelid, ! Anum_pg_constraint_contypid, ! Anum_pg_constraint_confrelid, ! Anum_pg_constraint_confupdtype, ! Anum_pg_constraint_confdeltype, ! Anum_pg_constraint_confmatchtype, ! Anum_pg_constraint_conislocal, ! Anum_pg_constraint_coninhcount, ! Anum_pg_constraint_conkey, ! Anum_pg_constraint_confkey, ! Anum_pg_constraint_conpfeqop, ! Anum_pg_constraint_conppeqop, ! Anum_pg_constraint_conffeqop, ! Anum_pg_constraint_conbin, ! Anum_pg_constraint_consrc, ! Anum_pg_constraint_END ! }; ! #define Natts_pg_constraint Anum_pg_constraint_END-1 /* Valid values for contype */ #define CONSTRAINT_CHECK 'c' *************** extern Oid CreateConstraintEntry(const c *** 198,204 **** Oid indexRelId, Node *conExpr, const char *conBin, ! const char *conSrc); extern void RemoveConstraintById(Oid conId); extern void RenameConstraintById(Oid conId, const char *newname); --- 210,218 ---- 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); *************** extern char *ChooseConstraintName(const *** 212,215 **** --- 226,232 ---- extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, Oid newNspId, bool isType); + /* See if two constraints are "functionally" equivalent, but not necessarily equal */ + bool ConstraintIsEquiv(const HeapTuple a, const HeapTuple b, const TupleDesc tupdesc); + #endif /* PG_CONSTRAINT_H */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 7b2716d..4f1240e 100644 *** a/src/include/nodes/parsenodes.h --- /bsrc/include/nodes/parsenodes.h *************** typedef enum AlterTableType *** 894,900 **** 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 */ --- 894,900 ---- 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 *** 1157,1162 **** --- 1157,1164 ---- 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..52faae2 100644 *** a/src/test/regress/expected/inherit.out --- /bsrc/test/regress/expected/inherit.out *************** drop function p2text(p2); *** 692,694 **** --- 692,784 ---- drop table c1; drop table p2; drop table p1; + CREATE TABLE ac (aa TEXT); + insert into ac (aa) values ('junk'); + alter table ac add constraint ac_check check (aa is not null); + select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in (select ac.tableoid from ac); + conname | contype | conislocal | coninhcount | consrc + ----------+---------+------------+-------------+------------------ + ac_check | c | t | 0 | (aa IS NOT NULL) + (1 row) + + CREATE TABLE bc (bb TEXT) INHERITS (ac); + insert into bc (aa) values ('junk'); + select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc limit 1)); + 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 conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc)); + conname | contype | conislocal | coninhcount | consrc + ---------+---------+------------+-------------+-------- + (0 rows) + + alter table ac add constraint ac_check check (aa is not null); + select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc limit 1)); + 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 conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc)); + 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 conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc)); + 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 conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc)); + conname | contype | conislocal | coninhcount | consrc + ----------+---------+------------+-------------+------------------ + ac_check | c | t | 0 | (aa IS NOT NULL) + (1 row) + + alter table ac drop constraint ac_check; + select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc)); + 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 + insert into ac (a) values (1); + insert into bc (a) values (1); + select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc)); + conname | contype | conislocal | coninhcount | consrc + ---------+---------+------------+-------------+---------- + check_b | c | t | 0 | (b <> 0) + check_a | c | t | 0 | (a <> 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..9831532 100644 *** a/src/test/regress/sql/inherit.sql --- /bsrc/test/regress/sql/inherit.sql *************** drop function p2text(p2); *** 196,198 **** --- 196,243 ---- drop table c1; drop table p2; drop table p1; + + CREATE TABLE ac (aa TEXT); + insert into ac (aa) values ('junk'); + alter table ac add constraint ac_check check (aa is not null); + select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in (select ac.tableoid from ac); + CREATE TABLE bc (bb TEXT) INHERITS (ac); + insert into bc (aa) values ('junk'); + select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc limit 1)); + + 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 conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc)); + + alter table ac add constraint ac_check check (aa is not null); + select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc limit 1)); + + 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 conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc)); + + alter table ac add constraint ac_check check (aa is not null); + alter table bc no inherit ac; + select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc)); + alter table bc drop constraint ac_check; + select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc)); + alter table ac drop constraint ac_check; + select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc)); + + 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); + insert into ac (a) values (1); + insert into bc (a) values (1); + select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc)); + + drop table bc; + drop table ac;