diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index a15ac6c..8c56652 100644
*** a/doc/src/sgml/catalogs.sgml
--- /bdoc/src/sgml/catalogs.sgml
***************
*** 1908,1913 ****
--- 1908,1927 ----
+ conislocal
+ bool
+
+ This constraint is defined locally in the relation. Note that a constraint can be locally defined and inherited simultaneously
+
+
+
+ coninhcount
+ int4
+
+ The number of direct ancestors this constraint has. A constraint with a nonzero number of ancestors cannot be dropped nor renamed
+
+
+
conkey
int2[]
pg_attribute.attnum>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 3553c3b..783186c 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, /* conislocal */
! 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,
*** 1863,1869 ****
/*
* OK, store it.
*/
! StoreRelCheck(rel, ccname, nodeToString(expr));
numchecks++;
--- 1867,1873 ----
/*
* 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 91b64ac..50a72c3 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,503 ----
}
}
+ if (old_constraints && stmt->constraints)
+ MergeInheritedConstraints(rel, old_constraints, stmt->constraints);
+
/*
* Parse and add the defaults/constraints, if any.
*/
*************** MergeAttributes(List *schema, List *supe
*** 1059,1064 ****
--- 1069,1076 ----
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
*** 1181,1186 ****
--- 1193,1294 ----
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);
+
+ /*
+ * We only merge CHECK constraints right now
+ */
+ 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");
+
+ /*
+ * We need to fixup the opfuncid because it
+ * got cleared in cdef1->cooked_expr
+ */
+ 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 *
*** 1214,1219 ****
--- 1322,1329 ----
/* 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
*** 1312,1317 ****
--- 1422,1459 ----
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
*** 2044,2070 ****
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 */
--- 2186,2200 ----
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);
! /* 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
*** 2253,2262 ****
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);
--- 2383,2392 ----
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);
*************** ATExecAddColumn(AlteredTableInfo *tab, R
*** 3273,3279 ****
* _list_ of defaults, but we just do one.
*/
AddRelationRawConstraints(rel, list_make1(rawEnt), NIL);
-
/* Make the additional catalog changes visible */
CommandCounterIncrement();
}
--- 3403,3408 ----
*************** ATExecAddIndex(AlteredTableInfo *tab, Re
*** 3936,3941 ****
--- 4065,4119 ----
* 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
*** 4295,4301 ****
indexOid,
NULL, /* no check constraint */
NULL,
! NULL);
/*
* Create the triggers that will enforce the constraint.
--- 4473,4481 ----
indexOid,
NULL, /* no check constraint */
NULL,
! NULL,
! true, /* islocal */
! 0); /* inhcount */
/*
* Create the triggers that will enforce the constraint.
*************** createForeignKeyTriggers(Relation rel, F
*** 4815,4858 ****
* 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)));
}
}
/*
--- 4995,5109 ----
* ALTER TABLE DROP CONSTRAINT
*/
static void
! ATExecDropConstraint(Relation rel, const char *constrName,
! DropBehavior behavior,
! bool recurse, bool recursing)
{
+ HeapTuple tuple;
+ List *children;
+ Relation conrel;
+ Form_pg_constraint con;
+
+ conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
+
+ /* At top level, permission check was done in ATPrepCmd, else do it */
+ if (recursing)
+ ATSimplePermissions(rel, false);
+
+ tuple = SearchSysCache(CONSTRNAME,
+ ObjectIdGetDatum(RelationGetRelid(rel)),
+ 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
*** 6260,6265 ****
--- 6511,6533 ----
}
/*
+ * 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
*** 6353,6361 ****
* 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)
--- 6621,6626 ----
*************** MergeConstraintsIntoExisting(Relation ch
*** 6365,6450 ****
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);
}
/*
--- 6630,6683 ----
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
*** 6469,6476 ****
--- 6702,6711 ----
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
*** 6508,6514 ****
break;
}
}
-
systable_endscan(scan);
heap_close(catalogRelation, RowExclusiveLock);
--- 6743,6748 ----
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6558,6563 ****
--- 6792,6832 ----
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 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 aa38052..005886e 100644
*** a/src/backend/nodes/copyfuncs.c
--- /bsrc/backend/nodes/copyfuncs.c
*************** _copyConstraint(Constraint *from)
*** 1808,1813 ****
--- 1808,1815 ----
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 dc85594..e232a37 100644
*** a/src/backend/nodes/equalfuncs.c
--- /bsrc/backend/nodes/equalfuncs.c
*************** _equalConstraint(Constraint *a, Constrai
*** 1837,1842 ****
--- 1837,1844 ----
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 d06f035..382f6b6 100644
*** a/src/backend/nodes/outfuncs.c
--- /bsrc/backend/nodes/outfuncs.c
*************** _outConstraint(StringInfo str, Constrain
*** 2028,2033 ****
--- 2028,2036 ----
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 6ca09b8..1575dbe 100644
*** a/src/backend/parser/gram.y
--- /bsrc/backend/parser/gram.y
*************** ColConstraintElem:
*** 2069,2074 ****
--- 2069,2076 ----
n->cooked_expr = NULL;
n->keys = NULL;
n->indexspace = NULL;
+ n->is_local = true;
+ n->inhcount = 0;
$$ = (Node *)n;
}
| DEFAULT b_expr
*************** ConstraintElem:
*** 2209,2214 ****
--- 2211,2218 ----
n->raw_expr = $3;
n->cooked_expr = NULL;
n->indexspace = NULL;
+ n->is_local = true;
+ n->inhcount = 0;
$$ = (Node *)n;
}
| UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index db1bcd8..d0e4d41 100644
*** a/src/backend/parser/parse_utilcmd.c
--- /bsrc/backend/parser/parse_utilcmd.c
*************** transformInhRelation(ParseState *pstate,
*** 688,693 ****
--- 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..8f1af5f 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 relation: %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/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index b1dfd02..3129bfb 100644
*** a/src/bin/pg_dump/common.c
--- /bsrc/bin/pg_dump/common.c
*************** static DumpableObject **oprinfoindex;
*** 62,69 ****
static void flagInhTables(TableInfo *tbinfo, int numTables,
InhInfo *inhinfo, int numInherits);
! static void flagInhAttrs(TableInfo *tbinfo, int numTables,
! InhInfo *inhinfo, int numInherits);
static DumpableObject **buildIndexArray(void *objArray, int numObjs,
Size objSize);
static int DOCatalogIdCompare(const void *p1, const void *p2);
--- 62,70 ----
static void flagInhTables(TableInfo *tbinfo, int numTables,
InhInfo *inhinfo, int numInherits);
! static void flagInhAttrs(TableInfo *tblinfo, int numTables,
! InhInfo *inhinfo, int numInherits,
! int remoteVersion);
static DumpableObject **buildIndexArray(void *objArray, int numObjs,
Size objSize);
static int DOCatalogIdCompare(const void *p1, const void *p2);
*************** static int strInArray(const char *patter
*** 77,83 ****
* Collect information about all potentially dumpable objects
*/
TableInfo *
! getSchemaData(int *numTablesPtr)
{
NamespaceInfo *nsinfo;
AggInfo *agginfo;
--- 78,84 ----
* Collect information about all potentially dumpable objects
*/
TableInfo *
! getSchemaData(int *numTablesPtr, int remoteVersion)
{
NamespaceInfo *nsinfo;
AggInfo *agginfo;
*************** getSchemaData(int *numTablesPtr)
*** 191,197 ****
if (g_verbose)
write_msg(NULL, "flagging inherited columns in subtables\n");
! flagInhAttrs(tblinfo, numTables, inhinfo, numInherits);
if (g_verbose)
write_msg(NULL, "reading indexes\n");
--- 192,198 ----
if (g_verbose)
write_msg(NULL, "flagging inherited columns in subtables\n");
! flagInhAttrs(tblinfo, numTables, inhinfo, numInherits, remoteVersion);
if (g_verbose)
write_msg(NULL, "reading indexes\n");
*************** flagInhTables(TableInfo *tblinfo, int nu
*** 258,264 ****
*/
static void
flagInhAttrs(TableInfo *tblinfo, int numTables,
! InhInfo *inhinfo, int numInherits)
{
int i,
j,
--- 259,266 ----
*/
static void
flagInhAttrs(TableInfo *tblinfo, int numTables,
! InhInfo *inhinfo, int numInherits,
! int remoteVersion)
{
int i,
j,
*************** flagInhAttrs(TableInfo *tblinfo, int num
*** 423,454 ****
* different schemas, because reverse-listing of function calls may
* produce different text (schema-qualified or not) depending on
* search path. We really need a more bulletproof way of detecting
! * inherited constraints --- pg_constraint should record this
* explicitly!
*/
! for (j = 0; j < tbinfo->ncheck; j++)
{
! ConstraintInfo *constr;
!
! constr = &(tbinfo->checkexprs[j]);
!
! for (k = 0; k < numParents; k++)
{
! int l;
! parent = parents[k];
! for (l = 0; l < parent->ncheck; l++)
{
! ConstraintInfo *pconstr = &(parent->checkexprs[l]);
! if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
{
! constr->coninherited = true;
! break;
}
}
- if (constr->coninherited)
- break;
}
}
}
--- 425,460 ----
* different schemas, because reverse-listing of function calls may
* produce different text (schema-qualified or not) depending on
* search path. We really need a more bulletproof way of detecting
! * inherited constraints
! * The above is pre-80300 behaviour, now pg_constraint records this
* explicitly!
*/
! if (remoteVersion <= 80300)
{
! for (j = 0; j < tbinfo->ncheck; j++)
{
! ConstraintInfo *constr;
! constr = &(tbinfo->checkexprs[j]);
!
! for (k = 0; k < numParents; k++)
{
! int l;
! parent = parents[k];
! for (l = 0; l < parent->ncheck; l++)
{
! ConstraintInfo *pconstr = &(parent->checkexprs[l]);
!
! if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
! {
! constr->coninherited = true;
! break;
! }
}
+ if (constr->coninherited)
+ break;
}
}
}
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 36ba457..4c6096e 100644
*** a/src/bin/pg_dump/pg_dump.c
--- /bsrc/bin/pg_dump/pg_dump.c
*************** main(int argc, char **argv)
*** 637,643 ****
* Now scan the database and create DumpableObject structs for all the
* objects we intend to dump.
*/
! tblinfo = getSchemaData(&numTables);
if (!schemaOnly)
getTableData(tblinfo, numTables, oids);
--- 637,643 ----
* Now scan the database and create DumpableObject structs for all the
* objects we intend to dump.
*/
! tblinfo = getSchemaData(&numTables, g_fout->remoteVersion);
if (!schemaOnly)
getTableData(tblinfo, numTables, oids);
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4580,4589 ****
tbinfo->dobj.name);
resetPQExpBuffer(q);
! if (g_fout->remoteVersion >= 70400)
{
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! "pg_catalog.pg_get_constraintdef(oid) AS consrc "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
--- 4580,4602 ----
tbinfo->dobj.name);
resetPQExpBuffer(q);
! if (g_fout->remoteVersion > 80300)
{
+ /* 8.3 did not have conislocal attribute in pg_constraint */
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
! "conislocal "
! "FROM pg_catalog.pg_constraint "
! "WHERE conrelid = '%u'::pg_catalog.oid "
! " AND contype = 'c' "
! "ORDER BY conname",
! tbinfo->dobj.catId.oid);
! }
! else if (g_fout->remoteVersion >= 70400)
! {
! appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
! "false as conislocal "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4594,4600 ****
{
/* no pg_get_constraintdef, must use consrc */
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! "'CHECK (' || consrc || ')' AS consrc "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
--- 4607,4614 ----
{
/* no pg_get_constraintdef, must use consrc */
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! "'CHECK (' || consrc || ')' AS consrc, "
! "false as conislocal "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4606,4612 ****
/* 7.2 did not have OIDs in pg_relcheck */
appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, "
"rcname AS conname, "
! "'CHECK (' || rcsrc || ')' AS consrc "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
--- 4620,4627 ----
/* 7.2 did not have OIDs in pg_relcheck */
appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, "
"rcname AS conname, "
! "'CHECK (' || rcsrc || ')' AS consrc, "
! "false as conislocal "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4616,4622 ****
{
appendPQExpBuffer(q, "SELECT tableoid, oid, "
"rcname AS conname, "
! "'CHECK (' || rcsrc || ')' AS consrc "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
--- 4631,4638 ----
{
appendPQExpBuffer(q, "SELECT tableoid, oid, "
"rcname AS conname, "
! "'CHECK (' || rcsrc || ')' AS consrc, "
! "false as conislocal "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4628,4634 ****
appendPQExpBuffer(q, "SELECT "
"(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
"oid, rcname AS conname, "
! "'CHECK (' || rcsrc || ')' AS consrc "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
--- 4644,4651 ----
appendPQExpBuffer(q, "SELECT "
"(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
"oid, rcname AS conname, "
! "'CHECK (' || rcsrc || ')' AS consrc, "
! " false as conislocal"
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4662,4669 ****
constrs[j].contype = 'c';
constrs[j].condef = strdup(PQgetvalue(res, j, 3));
constrs[j].conindex = 0;
- constrs[j].coninherited = false;
constrs[j].separate = false;
constrs[j].dobj.dump = tbinfo->dobj.dump;
--- 4679,4687 ----
constrs[j].contype = 'c';
constrs[j].condef = strdup(PQgetvalue(res, j, 3));
constrs[j].conindex = 0;
constrs[j].separate = false;
+ /* depending on conislocal value, set coninherited */
+ constrs[j].coninherited = (PQgetvalue(res, j, 4)[0] == 't');
constrs[j].dobj.dump = tbinfo->dobj.dump;
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4678,4685 ****
/*
* If the constraint is inherited, this will be detected
! * later. We also detect later if the constraint must be
! * split out from the table definition.
*/
}
PQclear(res);
--- 4696,4703 ----
/*
* If the constraint is inherited, this will be detected
! * later for pre-80300 case. We also detect later if the constraint
! * must be split out from the table definition.
*/
}
PQclear(res);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index f4cd179..23f76ec 100644
*** a/src/bin/pg_dump/pg_dump.h
--- /bsrc/bin/pg_dump/pg_dump.h
*************** extern char g_opaque_type[10]; /* name f
*** 429,435 ****
* common utility functions
*/
! extern TableInfo *getSchemaData(int *numTablesPtr);
typedef enum _OidOptions
{
--- 429,435 ----
* common utility functions
*/
! extern TableInfo *getSchemaData(int *numTablesPtr, int remoteVersion);
typedef enum _OidOptions
{
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 e787c52..9b17a73 100644
*** a/src/include/catalog/catversion.h
--- /bsrc/include/catalog/catversion.h
***************
*** 53,58 ****
*/
/* yyyymmddN */
! #define CATALOG_VERSION_NO 200804292
#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..64caa94 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 54dfe7c..cd965ff 100644
*** a/src/include/nodes/parsenodes.h
--- /bsrc/include/nodes/parsenodes.h
*************** typedef enum AlterTableType
*** 895,901 ****
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 */
--- 895,901 ----
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
*** 1158,1163 ****
--- 1158,1165 ----
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 51d5afa..25e0e8e 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..f96e2a5 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, 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)
+
+ 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, 1;
+ 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, 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 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, 1;
+ 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, 1;
+ 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, 1;
+ 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, 1;
+ 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, 1;
+ conname | contype | conislocal | coninhcount | consrc
+ ---------+---------+------------+-------------+----------
+ check_a | c | t | 0 | (a <> 0)
+ check_a | c | f | 1 | (a <> 0)
+ check_b | c | t | 0 | (b <> 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..e72143d 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, 1;
+
+ 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, 1;
+
+ 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, 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 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, 1;
+
+ 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, 1;
+ 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, 1;
+ 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, 1;
+
+ 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, 1;
+
+
+ drop table bc;
+ drop table ac;