1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-18 17:42:25 +03:00

Change the rules for inherited CHECK constraints to be essentially the same

as those for inherited columns; that is, it's no longer allowed for a child
table to not have a check constraint matching one that exists on a parent.
This satisfies the principle of least surprise (rows selected from the parent
will always appear to meet its check constraints) and eliminates some
longstanding bogosity in pg_dump, which formerly had to guess about whether
check constraints were really inherited or not.

The implementation involves adding conislocal and coninhcount columns to
pg_constraint (paralleling attislocal and attinhcount in pg_attribute)
and refactoring various ALTER TABLE actions to be more like those for
columns.

Alex Hunsaker, Nikhil Sontakke, Tom Lane
This commit is contained in:
Tom Lane
2008-05-09 23:32:05 +00:00
parent f8df836ae3
commit cd902b331d
25 changed files with 1391 additions and 575 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.122 2008/01/01 19:45:46 momjian Exp $
* $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.123 2008/05/09 23:32:04 tgl Exp $
*
* NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be
@ -505,20 +505,18 @@ BuildDescForRelation(List *schema)
AttrNumber attnum;
ListCell *l;
TupleDesc desc;
AttrDefault *attrdef = NULL;
TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
bool has_not_null;
char *attname;
Oid atttypid;
int32 atttypmod;
int attdim;
int ndef = 0;
/*
* allocate a new tuple descriptor
*/
natts = list_length(schema);
desc = CreateTemplateTupleDesc(natts, false);
constr->has_not_null = false;
has_not_null = false;
attnum = 0;
@ -547,52 +545,25 @@ BuildDescForRelation(List *schema)
atttypid, atttypmod, attdim);
/* Fill in additional stuff not handled by TupleDescInitEntry */
if (entry->is_not_null)
constr->has_not_null = true;
desc->attrs[attnum - 1]->attnotnull = entry->is_not_null;
/*
* Note we copy only pre-cooked default expressions. Digestion of raw
* ones is someone else's problem.
*/
if (entry->cooked_default != NULL)
{
if (attrdef == NULL)
attrdef = (AttrDefault *) palloc(natts * sizeof(AttrDefault));
attrdef[ndef].adnum = attnum;
attrdef[ndef].adbin = pstrdup(entry->cooked_default);
ndef++;
desc->attrs[attnum - 1]->atthasdef = true;
}
has_not_null |= entry->is_not_null;
desc->attrs[attnum - 1]->attislocal = entry->is_local;
desc->attrs[attnum - 1]->attinhcount = entry->inhcount;
}
if (constr->has_not_null || ndef > 0)
if (has_not_null)
{
desc->constr = constr;
TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
if (ndef > 0) /* DEFAULTs */
{
if (ndef < natts)
constr->defval = (AttrDefault *)
repalloc(attrdef, ndef * sizeof(AttrDefault));
else
constr->defval = attrdef;
constr->num_defval = ndef;
}
else
{
constr->defval = NULL;
constr->num_defval = 0;
}
constr->has_not_null = true;
constr->defval = NULL;
constr->num_defval = 0;
constr->check = NULL;
constr->num_check = 0;
desc->constr = constr;
}
else
{
pfree(constr);
desc->constr = NULL;
}

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.91 2008/01/01 19:45:48 momjian Exp $
* $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.92 2008/05/09 23:32:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -206,6 +206,7 @@ Boot_CreateStmt:
$6,
BOOTSTRAP_SUPERUSERID,
tupdesc,
NIL,
RELKIND_RELATION,
$3,
true,

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.332 2008/03/27 03:57:33 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.333 2008/05/09 23:32:04 tgl Exp $
*
*
* INTERFACE ROUTINES
@ -77,9 +77,15 @@ static Oid AddNewRelationType(const char *typeName,
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 StoreRelCheck(Relation rel, char *ccname, Node *expr,
bool is_local, int inhcount);
static void StoreConstraints(Relation rel, List *cooked_constraints);
static bool MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
bool allow_merge, bool is_local);
static void SetRelationNumChecks(Relation rel, int numchecks);
static Node *cookConstraint(ParseState *pstate,
Node *raw_constraint,
char *relname);
static List *insert_ordered_unique_oid(List *list, Oid datum);
@ -788,6 +794,7 @@ heap_create_with_catalog(const char *relname,
Oid relid,
Oid ownerid,
TupleDesc tupdesc,
List *cooked_constraints,
char relkind,
bool shared_relation,
bool oidislocal,
@ -1004,13 +1011,13 @@ heap_create_with_catalog(const char *relname,
}
/*
* store constraints and defaults passed in the tupdesc, if any.
* Store any supplied constraints and defaults.
*
* NB: this may do a CommandCounterIncrement and rebuild the relcache
* entry, so the relation must be valid and self-consistent at this point.
* In particular, there are not yet constraints and defaults anywhere.
*/
StoreConstraints(new_rel_desc, tupdesc);
StoreConstraints(new_rel_desc, cooked_constraints);
/*
* If there's a special on-commit action, remember it
@ -1426,12 +1433,11 @@ heap_drop_with_catalog(Oid relid)
/*
* Store a default expression for column attnum of relation rel.
* The expression must be presented as a nodeToString() string.
*/
void
StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin)
StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr)
{
Node *expr;
char *adbin;
char *adsrc;
Relation adrel;
HeapTuple tuple;
@ -1445,12 +1451,12 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin)
defobject;
/*
* Need to construct source equivalent of given node-string.
* Flatten expression to string form for storage.
*/
expr = stringToNode(adbin);
adbin = nodeToString(expr);
/*
* deparse it
* Also deparse it to form the mostly-obsolete adsrc field.
*/
adsrc = deparse_expression(expr,
deparse_context_for(RelationGetRelationName(rel),
@ -1482,6 +1488,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin)
pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1]));
pfree(DatumGetPointer(values[Anum_pg_attrdef_adsrc - 1]));
heap_freetuple(tuple);
pfree(adbin);
pfree(adsrc);
/*
@ -1525,27 +1532,27 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin)
/*
* Store a check-constraint expression for the given relation.
* The expression must be presented as a nodeToString() string.
*
* Caller is responsible for updating the count of constraints
* in the pg_class entry for the relation.
*/
static void
StoreRelCheck(Relation rel, char *ccname, char *ccbin)
StoreRelCheck(Relation rel, char *ccname, Node *expr,
bool is_local, int inhcount)
{
Node *expr;
char *ccbin;
char *ccsrc;
List *varList;
int keycount;
int16 *attNos;
/*
* Convert condition to an expression tree.
* Flatten expression to string form for storage.
*/
expr = stringToNode(ccbin);
ccbin = nodeToString(expr);
/*
* deparse it
* Also deparse it to form the mostly-obsolete consrc field.
*/
ccsrc = deparse_expression(expr,
deparse_context_for(RelationGetRelationName(rel),
@ -1553,7 +1560,7 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
false, false);
/*
* Find columns of rel that are used in ccbin
* Find columns of rel that are used in expr
*
* NB: pull_var_clause is okay here only because we don't allow subselects
* in check constraints; it would fail to examine the contents of
@ -1608,26 +1615,29 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
InvalidOid, /* no associated index */
expr, /* Tree form check constraint */
ccbin, /* Binary form check constraint */
ccsrc); /* Source form check constraint */
ccsrc, /* Source form check constraint */
is_local, /* conislocal */
inhcount); /* coninhcount */
pfree(ccbin);
pfree(ccsrc);
}
/*
* Store defaults and constraints passed in via the tuple constraint struct.
* Store defaults and constraints (passed as a list of CookedConstraint).
*
* NOTE: only pre-cooked expressions will be passed this way, which is to
* say expressions inherited from an existing relation. Newly parsed
* expressions can be added later, by direct calls to StoreAttrDefault
* and StoreRelCheck (see AddRelationRawConstraints()).
* and StoreRelCheck (see AddRelationNewConstraints()).
*/
static void
StoreConstraints(Relation rel, TupleDesc tupdesc)
StoreConstraints(Relation rel, List *cooked_constraints)
{
TupleConstr *constr = tupdesc->constr;
int i;
int numchecks = 0;
ListCell *lc;
if (!constr)
if (!cooked_constraints)
return; /* nothing to do */
/*
@ -1637,33 +1647,46 @@ StoreConstraints(Relation rel, TupleDesc tupdesc)
*/
CommandCounterIncrement();
for (i = 0; i < constr->num_defval; i++)
StoreAttrDefault(rel, constr->defval[i].adnum,
constr->defval[i].adbin);
foreach(lc, cooked_constraints)
{
CookedConstraint *con = (CookedConstraint *) lfirst(lc);
for (i = 0; i < constr->num_check; i++)
StoreRelCheck(rel, constr->check[i].ccname,
constr->check[i].ccbin);
switch (con->contype)
{
case CONSTR_DEFAULT:
StoreAttrDefault(rel, con->attnum, con->expr);
break;
case CONSTR_CHECK:
StoreRelCheck(rel, con->name, con->expr,
con->is_local, con->inhcount);
numchecks++;
break;
default:
elog(ERROR, "unrecognized constraint type: %d",
(int) con->contype);
}
}
if (constr->num_check > 0)
SetRelationNumChecks(rel, constr->num_check);
if (numchecks > 0)
SetRelationNumChecks(rel, numchecks);
}
/*
* AddRelationRawConstraints
* AddRelationNewConstraints
*
* Add raw (not-yet-transformed) column default expressions and/or constraint
* check expressions to an existing relation. This is defined to do both
* for efficiency in DefineRelation, but of course you can do just one or
* the other by passing empty lists.
* Add new column default expressions and/or constraint check expressions
* to an existing relation. This is defined to do both for efficiency in
* DefineRelation, but of course you can do just one or the other by passing
* empty lists.
*
* rel: relation to be modified
* rawColDefaults: list of RawColumnDefault structures
* rawConstraints: list of Constraint nodes
* newColDefaults: list of RawColumnDefault structures
* newConstraints: list of Constraint nodes
* allow_merge: TRUE if check constraints may be merged with existing ones
* is_local: TRUE if definition is local, FALSE if it's inherited
*
* All entries in rawColDefaults will be processed. Entries in rawConstraints
* will be processed only if they are CONSTR_CHECK type and contain a "raw"
* expression.
* All entries in newColDefaults will be processed. Entries in newConstraints
* will be processed only if they are CONSTR_CHECK type.
*
* Returns a list of CookedConstraint nodes that shows the cooked form of
* the default and constraint expressions added to the relation.
@ -1674,9 +1697,11 @@ StoreConstraints(Relation rel, TupleDesc tupdesc)
* tuples visible.
*/
List *
AddRelationRawConstraints(Relation rel,
List *rawColDefaults,
List *rawConstraints)
AddRelationNewConstraints(Relation rel,
List *newColDefaults,
List *newConstraints,
bool allow_merge,
bool is_local)
{
List *cookedConstraints = NIL;
TupleDesc tupleDesc;
@ -1715,7 +1740,7 @@ AddRelationRawConstraints(Relation rel,
/*
* Process column default expressions.
*/
foreach(cell, rawColDefaults)
foreach(cell, newColDefaults)
{
RawColumnDefault *colDef = (RawColumnDefault *) lfirst(cell);
Form_pg_attribute atp = rel->rd_att->attrs[colDef->attnum - 1];
@ -1739,13 +1764,15 @@ AddRelationRawConstraints(Relation rel,
(IsA(expr, Const) &&((Const *) expr)->constisnull))
continue;
StoreAttrDefault(rel, colDef->attnum, nodeToString(expr));
StoreAttrDefault(rel, colDef->attnum, expr);
cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
cooked->contype = CONSTR_DEFAULT;
cooked->name = NULL;
cooked->attnum = colDef->attnum;
cooked->expr = expr;
cooked->is_local = is_local;
cooked->inhcount = is_local ? 0 : 1;
cookedConstraints = lappend(cookedConstraints, cooked);
}
@ -1754,45 +1781,35 @@ AddRelationRawConstraints(Relation rel,
*/
numchecks = numoldchecks;
checknames = NIL;
foreach(cell, rawConstraints)
foreach(cell, newConstraints)
{
Constraint *cdef = (Constraint *) lfirst(cell);
char *ccname;
if (cdef->contype != CONSTR_CHECK || cdef->raw_expr == NULL)
if (cdef->contype != CONSTR_CHECK)
continue;
Assert(cdef->cooked_expr == NULL);
/*
* Transform raw parsetree to executable expression.
*/
expr = transformExpr(pstate, cdef->raw_expr);
if (cdef->raw_expr != NULL)
{
Assert(cdef->cooked_expr == NULL);
/*
* Make sure it yields a boolean result.
*/
expr = coerce_to_boolean(pstate, expr, "CHECK");
/*
* Transform raw parsetree to executable expression, and verify
* it's valid as a CHECK constraint.
*/
expr = cookConstraint(pstate, cdef->raw_expr,
RelationGetRelationName(rel));
}
else
{
Assert(cdef->cooked_expr != NULL);
/*
* Make sure no outside relations are referred to.
*/
if (list_length(pstate->p_rtable) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("only table \"%s\" can be referenced in check constraint",
RelationGetRelationName(rel))));
/*
* No subplans or aggregates, either...
*/
if (pstate->p_hasSubLinks)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot use subquery in check constraint")));
if (pstate->p_hasAggs)
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in check constraint")));
/*
* Here, we assume the parser will only pass us valid CHECK
* expressions, so we do no particular checking.
*/
expr = stringToNode(cdef->cooked_expr);
}
/*
* Check name uniqueness, or generate a name if none was given.
@ -1802,15 +1819,6 @@ AddRelationRawConstraints(Relation rel,
ListCell *cell2;
ccname = cdef->name;
/* Check against pre-existing constraints */
if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel),
ccname))
ereport(ERROR,
(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)
@ -1821,6 +1829,19 @@ AddRelationRawConstraints(Relation rel,
errmsg("check constraint \"%s\" already exists",
ccname)));
}
/* save name for future checks */
checknames = lappend(checknames, ccname);
/*
* Check against pre-existing constraints. If we are allowed
* to merge with an existing constraint, there's no more to
* do here. (We omit the duplicate constraint from the result,
* which is what ATAddCheckConstraint wants.)
*/
if (MergeWithExistingConstraint(rel, ccname, expr,
allow_merge, is_local))
continue;
}
else
{
@ -1855,15 +1876,15 @@ AddRelationRawConstraints(Relation rel,
"check",
RelationGetNamespace(rel),
checknames);
}
/* save name for future checks */
checknames = lappend(checknames, ccname);
/* save name for future checks */
checknames = lappend(checknames, ccname);
}
/*
* OK, store it.
*/
StoreRelCheck(rel, ccname, nodeToString(expr));
StoreRelCheck(rel, ccname, expr, is_local, is_local ? 0 : 1);
numchecks++;
@ -1872,6 +1893,8 @@ AddRelationRawConstraints(Relation rel,
cooked->name = ccname;
cooked->attnum = 0;
cooked->expr = expr;
cooked->is_local = is_local;
cooked->inhcount = is_local ? 0 : 1;
cookedConstraints = lappend(cookedConstraints, cooked);
}
@ -1887,6 +1910,90 @@ AddRelationRawConstraints(Relation rel,
return cookedConstraints;
}
/*
* Check for a pre-existing check constraint that conflicts with a proposed
* new one, and either adjust its conislocal/coninhcount settings or throw
* error as needed.
*
* Returns TRUE if merged (constraint is a duplicate), or FALSE if it's
* got a so-far-unique name, or throws error if conflict.
*/
static bool
MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
bool allow_merge, bool is_local)
{
bool found;
Relation conDesc;
SysScanDesc conscan;
ScanKeyData skey[2];
HeapTuple tup;
/* Search for a pg_constraint entry with same name and relation */
conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
found = false;
ScanKeyInit(&skey[0],
Anum_pg_constraint_conname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(ccname));
ScanKeyInit(&skey[1],
Anum_pg_constraint_connamespace,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetNamespace(rel)));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
SnapshotNow, 2, skey);
while (HeapTupleIsValid(tup = systable_getnext(conscan)))
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
if (con->conrelid == RelationGetRelid(rel))
{
/* Found it. Conflicts if not identical check constraint */
if (con->contype == CONSTRAINT_CHECK)
{
Datum val;
bool isnull;
val = fastgetattr(tup,
Anum_pg_constraint_conbin,
conDesc->rd_att, &isnull);
if (isnull)
elog(ERROR, "null conbin for rel %s",
RelationGetRelationName(rel));
if (equal(expr, stringToNode(TextDatumGetCString(val))))
found = true;
}
if (!found || !allow_merge)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("constraint \"%s\" for relation \"%s\" already exists",
ccname, RelationGetRelationName(rel))));
/* OK to update the tuple */
ereport(NOTICE,
(errmsg("merging constraint \"%s\" with inherited definition",
ccname)));
tup = heap_copytuple(tup);
con = (Form_pg_constraint) GETSTRUCT(tup);
if (is_local)
con->conislocal = true;
else
con->coninhcount++;
simple_heap_update(conDesc, &tup->t_self, tup);
CatalogUpdateIndexes(conDesc, tup);
break;
}
}
systable_endscan(conscan);
heap_close(conDesc, RowExclusiveLock);
return found;
}
/*
* Update the count of constraints in the relation's pg_class tuple.
*
@ -2015,63 +2122,52 @@ cookDefault(ParseState *pstate,
return expr;
}
/*
* Removes all constraints on a relation that match the given name.
* Take a raw CHECK constraint expression and convert it to a cooked format
* ready for storage.
*
* It is the responsibility of the calling function to acquire a suitable
* lock on the relation.
*
* Returns: The number of constraints removed.
* Parse state must be set up to recognize any vars that might appear
* in the expression.
*/
int
RemoveRelConstraints(Relation rel, const char *constrName,
DropBehavior behavior)
static Node *
cookConstraint(ParseState *pstate,
Node *raw_constraint,
char *relname)
{
int ndeleted = 0;
Relation conrel;
SysScanDesc conscan;
ScanKeyData key[1];
HeapTuple contup;
/* Grab an appropriate lock on the pg_constraint relation */
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
/* Use the index to scan only constraints of the target relation */
ScanKeyInit(&key[0],
Anum_pg_constraint_conrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true,
SnapshotNow, 1, key);
Node *expr;
/*
* Scan over the result set, removing any matching entries.
* Transform raw parsetree to executable expression.
*/
while ((contup = systable_getnext(conscan)) != NULL)
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
expr = transformExpr(pstate, raw_constraint);
if (strcmp(NameStr(con->conname), constrName) == 0)
{
ObjectAddress conobj;
/*
* Make sure it yields a boolean result.
*/
expr = coerce_to_boolean(pstate, expr, "CHECK");
conobj.classId = ConstraintRelationId;
conobj.objectId = HeapTupleGetOid(contup);
conobj.objectSubId = 0;
/*
* Make sure no outside relations are referred to.
*/
if (list_length(pstate->p_rtable) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("only table \"%s\" can be referenced in check constraint",
relname)));
performDeletion(&conobj, behavior);
/*
* No subplans or aggregates, either...
*/
if (pstate->p_hasSubLinks)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot use subquery in check constraint")));
if (pstate->p_hasAggs)
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in check constraint")));
ndeleted++;
}
}
/* Clean up after the scan */
systable_endscan(conscan);
heap_close(conrel, RowExclusiveLock);
return ndeleted;
return expr;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.296 2008/03/26 21:10:37 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.297 2008/05/09 23:32:04 tgl Exp $
*
*
* INTERFACE ROUTINES
@ -716,7 +716,9 @@ index_create(Oid heapRelationId,
InvalidOid, /* no associated index */
NULL, /* no check constraint */
NULL,
NULL);
NULL,
true, /* islocal */
0); /* inhcount */
referenced.classId = ConstraintRelationId;
referenced.objectId = conOid;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.40 2008/03/26 21:10:37 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.41 2008/05/09 23:32:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -60,7 +60,9 @@ CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Node *conExpr,
const char *conBin,
const char *conSrc)
const char *conSrc,
bool conIsLocal,
int conInhCount)
{
Relation conDesc;
Oid conOid;
@ -145,6 +147,8 @@ CreateConstraintEntry(const char *constraintName,
values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
if (conkeyArray)
values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.9 2008/01/01 19:45:48 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.10 2008/05/09 23:32:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -193,6 +193,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid)
toastOid,
rel->rd_rel->relowner,
tupdesc,
NIL,
RELKIND_TOASTVALUE,
shared_relation,
true,

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.173 2008/04/13 19:18:14 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.174 2008/05/09 23:32:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -639,9 +639,12 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
/*
* Need to make a copy of the tuple descriptor, since
* heap_create_with_catalog modifies it.
* heap_create_with_catalog modifies it. Note that the NewHeap will
* not receive any of the defaults or constraints associated with the
* OldHeap; we don't need 'em, and there's no reason to spend cycles
* inserting them into the catalogs only to delete them.
*/
tupdesc = CreateTupleDescCopyConstr(OldHeapDesc);
tupdesc = CreateTupleDescCopy(OldHeapDesc);
/*
* Use options of the old heap for new heap.
@ -662,6 +665,7 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
InvalidOid,
OldHeap->rd_rel->relowner,
tupdesc,
NIL,
OldHeap->rd_rel->relkind,
OldHeap->rd_rel->relisshared,
true,

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.117 2008/03/27 03:57:33 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.118 2008/05/09 23:32:04 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -2206,7 +2206,9 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
InvalidOid,
expr, /* Tree form check constraint */
ccbin, /* Binary form check constraint */
ccsrc); /* Source form check constraint */
ccsrc, /* Source form check constraint */
true, /* is local */
0); /* inhcount */
/*
* Return the compiled constraint expression so the calling routine can

View File

@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.306 2008/04/21 03:49:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.307 2008/05/09 23:32:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -2626,7 +2626,7 @@ OpenIntoRel(QueryDesc *queryDesc)
false);
(void) heap_reloptions(RELKIND_RELATION, reloptions, true);
/* have to copy the actual tupdesc to get rid of any constraints */
/* Copy the tupdesc because heap_create_with_catalog modifies it */
tupdesc = CreateTupleDescCopy(queryDesc->tupDesc);
/* Now we can actually create the new relation */
@ -2636,6 +2636,7 @@ OpenIntoRel(QueryDesc *queryDesc)
InvalidOid,
GetUserId(),
tupdesc,
NIL,
RELKIND_RELATION,
false,
true,

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.103 2008/03/27 03:57:33 tgl Exp $
* $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.104 2008/05/09 23:32:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -62,8 +62,7 @@ static DumpableObject **oprinfoindex;
static void flagInhTables(TableInfo *tbinfo, int numTables,
InhInfo *inhinfo, int numInherits);
static void flagInhAttrs(TableInfo *tbinfo, int numTables,
InhInfo *inhinfo, int numInherits);
static void flagInhAttrs(TableInfo *tblinfo, int numTables);
static DumpableObject **buildIndexArray(void *objArray, int numObjs,
Size objSize);
static int DOCatalogIdCompare(const void *p1, const void *p2);
@ -191,7 +190,7 @@ getSchemaData(int *numTablesPtr)
if (g_verbose)
write_msg(NULL, "flagging inherited columns in subtables\n");
flagInhAttrs(tblinfo, numTables, inhinfo, numInherits);
flagInhAttrs(tblinfo, numTables);
if (g_verbose)
write_msg(NULL, "reading indexes\n");
@ -257,8 +256,7 @@ flagInhTables(TableInfo *tblinfo, int numTables,
* modifies tblinfo
*/
static void
flagInhAttrs(TableInfo *tblinfo, int numTables,
InhInfo *inhinfo, int numInherits)
flagInhAttrs(TableInfo *tblinfo, int numTables)
{
int i,
j,
@ -414,43 +412,6 @@ flagInhAttrs(TableInfo *tblinfo, int numTables,
tbinfo->inhAttrs[j] = false;
}
}
/*
* Check for inherited CHECK constraints. We assume a constraint is
* inherited if its name matches the name of any constraint in the
* parent. Originally this code tried to compare the expression
* texts, but that can fail if the parent and child tables are in
* 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;
}
}
}
}

View File

@ -12,7 +12,7 @@
* by PostgreSQL
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.489 2008/05/03 23:32:32 adunstan Exp $
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.490 2008/05/09 23:32:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -120,6 +120,7 @@ static void expand_table_name_patterns(SimpleStringList *patterns,
SimpleOidList *oids);
static NamespaceInfo *findNamespace(Oid nsoid, Oid objoid);
static void dumpTableData(Archive *fout, TableDataInfo *tdinfo);
static void guessConstraintInheritance(TableInfo *tblinfo, int numTables);
static void dumpComment(Archive *fout, const char *target,
const char *namespace, const char *owner,
CatalogId catalogId, int subid, DumpId dumpId);
@ -645,6 +646,9 @@ main(int argc, char **argv)
*/
tblinfo = getSchemaData(&numTables);
if (g_fout->remoteVersion < 80400)
guessConstraintInheritance(tblinfo, numTables);
if (!schemaOnly)
getTableData(tblinfo, numTables, oids);
@ -1383,6 +1387,81 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids)
}
/*
* guessConstraintInheritance:
* In pre-8.4 databases, we can't tell for certain which constraints
* are inherited. We assume a CHECK constraint is inherited if its name
* matches the name of any constraint in the parent. Originally this code
* tried to compare the expression texts, but that can fail for various
* reasons --- for example, if the parent and child tables are in different
* schemas, reverse-listing of function calls may produce different text
* (schema-qualified or not) depending on search path.
*
* In 8.4 and up we can rely on the conislocal field to decide which
* constraints must be dumped; much safer.
*
* This function assumes all conislocal flags were initialized to TRUE.
* It clears the flag on anything that seems to be inherited.
*/
static void
guessConstraintInheritance(TableInfo *tblinfo, int numTables)
{
int i,
j,
k;
for (i = 0; i < numTables; i++)
{
TableInfo *tbinfo = &(tblinfo[i]);
int numParents;
TableInfo **parents;
TableInfo *parent;
/* Sequences and views never have parents */
if (tbinfo->relkind == RELKIND_SEQUENCE ||
tbinfo->relkind == RELKIND_VIEW)
continue;
/* Don't bother computing anything for non-target tables, either */
if (!tbinfo->dobj.dump)
continue;
numParents = tbinfo->numParents;
parents = tbinfo->parents;
if (numParents == 0)
continue; /* nothing to see here, move along */
/* scan for inherited CHECK constraints */
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->conislocal = false;
break;
}
}
if (!constr->conislocal)
break;
}
}
}
}
/*
* dumpDatabase:
* dump the database definition
@ -3522,7 +3601,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
constrinfo[j].contype = contype;
constrinfo[j].condef = NULL;
constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
constrinfo[j].coninherited = false;
constrinfo[j].conislocal = true;
constrinfo[j].separate = true;
indxinfo[j].indexconstraint = constrinfo[j].dobj.dumpId;
@ -3623,7 +3702,7 @@ getConstraints(TableInfo tblinfo[], int numTables)
constrinfo[j].contype = 'f';
constrinfo[j].condef = strdup(PQgetvalue(res, j, i_condef));
constrinfo[j].conindex = 0;
constrinfo[j].coninherited = false;
constrinfo[j].conislocal = true;
constrinfo[j].separate = true;
}
@ -3706,7 +3785,7 @@ getDomainConstraints(TypeInfo *tinfo)
constrinfo[i].contype = 'c';
constrinfo[i].condef = strdup(PQgetvalue(res, i, i_consrc));
constrinfo[i].conindex = 0;
constrinfo[i].coninherited = false;
constrinfo[i].conislocal = true;
constrinfo[i].separate = false;
/*
@ -4586,10 +4665,22 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo->dobj.name);
resetPQExpBuffer(q);
if (g_fout->remoteVersion >= 70400)
if (g_fout->remoteVersion >= 80400)
{
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc "
"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, "
"true as conislocal "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
@ -4600,7 +4691,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
{
/* no pg_get_constraintdef, must use consrc */
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
"'CHECK (' || consrc || ')' AS consrc "
"'CHECK (' || consrc || ')' AS consrc, "
"true as conislocal "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
@ -4612,7 +4704,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
/* 7.2 did not have OIDs in pg_relcheck */
appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, "
"rcname AS conname, "
"'CHECK (' || rcsrc || ')' AS consrc "
"'CHECK (' || rcsrc || ')' AS consrc, "
"true as conislocal "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
@ -4622,7 +4715,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
{
appendPQExpBuffer(q, "SELECT tableoid, oid, "
"rcname AS conname, "
"'CHECK (' || rcsrc || ')' AS consrc "
"'CHECK (' || rcsrc || ')' AS consrc, "
"true as conislocal "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
@ -4634,7 +4728,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
appendPQExpBuffer(q, "SELECT "
"(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
"oid, rcname AS conname, "
"'CHECK (' || rcsrc || ')' AS consrc "
"'CHECK (' || rcsrc || ')' AS consrc, "
"true as conislocal "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
@ -4668,7 +4763,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
constrs[j].contype = 'c';
constrs[j].condef = strdup(PQgetvalue(res, j, 3));
constrs[j].conindex = 0;
constrs[j].coninherited = false;
constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't');
constrs[j].separate = false;
constrs[j].dobj.dump = tbinfo->dobj.dump;
@ -4684,8 +4779,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
/*
* 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.
* later (in pre-8.4 databases). We also detect later if the
* constraint must be split out from the table definition.
*/
}
PQclear(res);
@ -8840,7 +8935,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
{
ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
if (constr->coninherited || constr->separate)
if (constr->separate || !constr->conislocal)
continue;
if (actual_atts > 0)
@ -8955,7 +9050,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
{
ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
if (constr->coninherited || constr->separate)
if (constr->separate || !constr->conislocal)
continue;
dumpTableConstraintComment(fout, constr);

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.139 2008/01/01 19:45:55 momjian Exp $
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.140 2008/05/09 23:32:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -353,7 +353,7 @@ typedef struct _constraintInfo
char contype;
char *condef; /* definition, if CHECK or FOREIGN KEY */
DumpId conindex; /* identifies associated index if any */
bool coninherited; /* TRUE if appears to be inherited */
bool conislocal; /* TRUE if constraint has local definition */
bool separate; /* TRUE if must dump as separate item */
} ConstraintInfo;

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.457 2008/05/08 08:58:59 mha Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.458 2008/05/09 23:32:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200805081
#define CATALOG_VERSION_NO 200805091
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.87 2008/01/01 19:45:56 momjian Exp $
* $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.88 2008/05/09 23:32:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -29,6 +29,8 @@ typedef struct CookedConstraint
char *name; /* name, or NULL if none */
AttrNumber attnum; /* which attr (only for DEFAULT) */
Node *expr; /* transformed default or check expr */
bool is_local; /* constraint has local (non-inherited) def */
int inhcount; /* number of times constraint is inherited */
} CookedConstraint;
extern Relation heap_create(const char *relname,
@ -46,6 +48,7 @@ extern Oid heap_create_with_catalog(const char *relname,
Oid relid,
Oid ownerid,
TupleDesc tupdesc,
List *cooked_constraints,
char relkind,
bool shared_relation,
bool oidislocal,
@ -67,11 +70,13 @@ extern void InsertPgClassTuple(Relation pg_class_desc,
Oid new_rel_oid,
Datum reloptions);
extern List *AddRelationRawConstraints(Relation rel,
List *rawColDefaults,
List *rawConstraints);
extern List *AddRelationNewConstraints(Relation rel,
List *newColDefaults,
List *newConstraints,
bool allow_merge,
bool is_local);
extern void StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin);
extern void StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr);
extern Node *cookDefault(ParseState *pstate,
Node *raw_default,
@ -79,9 +84,6 @@ extern Node *cookDefault(ParseState *pstate,
int32 atttypmod,
char *attname);
extern int RemoveRelConstraints(Relation rel, const char *constrName,
DropBehavior behavior);
extern void DeleteRelationTuple(Oid relid);
extern void DeleteAttributeTuples(Oid relid);
extern void RemoveAttributeById(Oid relid, AttrNumber attnum);

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.28 2008/03/27 03:57:34 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.29 2008/05/09 23:32:04 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -71,6 +71,12 @@ CATALOG(pg_constraint,2606)
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.
*/
@ -125,7 +131,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
#define Natts_pg_constraint 18
#define Natts_pg_constraint 20
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@ -137,13 +143,15 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#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
#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 */
@ -192,7 +200,9 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Node *conExpr,
const char *conBin,
const char *conSrc);
const char *conSrc,
bool conIsLocal,
int conInhCount);
extern void RemoveConstraintById(Oid conId);
extern void RenameConstraintById(Oid conId, const char *newname);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.364 2008/04/29 20:44:49 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.365 2008/05/09 23:32:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -892,11 +892,11 @@ typedef enum AlterTableType
AT_AddIndex, /* add index */
AT_ReAddIndex, /* internal to commands/tablecmds.c */
AT_AddConstraint, /* add constraint */
AT_AddConstraintRecurse, /* internal to commands/tablecmds.c */
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_DropConstraintRecurse, /* internal to commands/tablecmds.c */
AT_AlterColumnType, /* alter column type */
AT_ChangeOwner, /* change owner */
AT_ClusterOn, /* CLUSTER ON */

View File

@ -378,19 +378,21 @@ drop table atacc2 cascade;
NOTICE: drop cascades to table atacc3
NOTICE: drop cascades to constraint foo on table atacc3
drop table atacc1;
-- let's try only to add only to the parent
-- adding only to a parent is disallowed as of 8.4
create table atacc1 (test int);
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);
create table atacc2 (test2 int) inherits (atacc1);
-- fail:
alter table only atacc1 add constraint foo check (test>0);
ERROR: constraint must be added to child tables too
-- ok:
alter table only atacc2 add constraint foo check (test>0);
-- check constraint not there on parent
insert into atacc1 (test) values (-3);
insert into atacc1 (test) values (3);
-- check constraint is there on child
insert into atacc2 (test) 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);
insert into atacc3 (test2) values (3);
drop table atacc3;
insert into atacc2 (test) values (3);
drop table atacc2;
drop table atacc1;
-- test unique constraint adding
@ -1230,7 +1232,7 @@ alter table p1 add column f2 text;
NOTICE: merging definition of column "f2" for child "c1"
insert into p1 values (1,2,'abc');
insert into c1 values(11,'xyz',33,0); -- should fail
ERROR: new row for relation "c1" violates check constraint "c1_a1_check"
ERROR: new row for relation "c1" violates check constraint "p1_a1_check"
insert into c1 values(11,'xyz',33,22);
select * from p1;
f1 | a1 | f2
@ -1249,7 +1251,7 @@ select * from p1;
drop table p1 cascade;
NOTICE: drop cascades to table c1
NOTICE: drop cascades to constraint c1_a1_check on table c1
NOTICE: drop cascades to constraint p1_a1_check on table c1
-- test that operations with a dropped column do not try to reference
-- its datatype
create domain mytype as text;

View File

@ -692,3 +692,220 @@ drop function p2text(p2);
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 pc.relname, 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 1,2;
relname | conname | contype | conislocal | coninhcount | consrc
---------+----------+---------+------------+-------------+------------------
ac | ac_check | c | t | 0 | (aa IS NOT NULL)
bc | 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; -- fail, disallowed
ERROR: cannot drop inherited constraint "ac_check" of relation "bc"
alter table ac drop constraint ac_check;
select pc.relname, 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 1,2;
relname | conname | contype | conislocal | coninhcount | consrc
---------+---------+---------+------------+-------------+--------
(0 rows)
-- try the unnamed-constraint case
alter table ac add check (aa is not null);
select pc.relname, 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 1,2;
relname | conname | contype | conislocal | coninhcount | consrc
---------+-------------+---------+------------+-------------+------------------
ac | ac_aa_check | c | t | 0 | (aa IS NOT NULL)
bc | ac_aa_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_aa_check"
insert into bc (aa) values (NULL);
ERROR: new row for relation "bc" violates check constraint "ac_aa_check"
alter table bc drop constraint ac_aa_check; -- fail, disallowed
ERROR: cannot drop inherited constraint "ac_aa_check" of relation "bc"
alter table ac drop constraint ac_aa_check;
select pc.relname, 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 1,2;
relname | conname | contype | conislocal | coninhcount | consrc
---------+---------+---------+------------+-------------+--------
(0 rows)
alter table ac add constraint ac_check check (aa is not null);
alter table bc no inherit ac;
select pc.relname, 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 1,2;
relname | conname | contype | conislocal | coninhcount | consrc
---------+----------+---------+------------+-------------+------------------
ac | ac_check | c | t | 0 | (aa IS NOT NULL)
bc | ac_check | c | t | 0 | (aa IS NOT NULL)
(2 rows)
alter table bc drop constraint ac_check;
select pc.relname, 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 1,2;
relname | conname | contype | conislocal | coninhcount | consrc
---------+----------+---------+------------+-------------+------------------
ac | ac_check | c | t | 0 | (aa IS NOT NULL)
(1 row)
alter table ac drop constraint ac_check;
select pc.relname, 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 1,2;
relname | conname | contype | conislocal | coninhcount | consrc
---------+---------+---------+------------+-------------+--------
(0 rows)
drop table bc;
drop table ac;
create table ac (a int constraint check_a check (a <> 0));
create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
NOTICE: merging column "a" with inherited definition
NOTICE: merging constraint "check_a" with inherited definition
select pc.relname, 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 1,2;
relname | conname | contype | conislocal | coninhcount | consrc
---------+---------+---------+------------+-------------+----------
ac | check_a | c | t | 0 | (a <> 0)
bc | check_a | c | t | 1 | (a <> 0)
bc | check_b | c | t | 0 | (b <> 0)
(3 rows)
drop table bc;
drop table ac;
create table ac (a int constraint check_a check (a <> 0));
create table bc (b int constraint check_b check (b <> 0));
create table cc (c int constraint check_c check (c <> 0)) inherits (ac, bc);
select pc.relname, 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', 'cc') order by 1,2;
relname | conname | contype | conislocal | coninhcount | consrc
---------+---------+---------+------------+-------------+----------
ac | check_a | c | t | 0 | (a <> 0)
bc | check_b | c | t | 0 | (b <> 0)
cc | check_a | c | f | 1 | (a <> 0)
cc | check_b | c | f | 1 | (b <> 0)
cc | check_c | c | t | 0 | (c <> 0)
(5 rows)
alter table cc no inherit bc;
select pc.relname, 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', 'cc') order by 1,2;
relname | conname | contype | conislocal | coninhcount | consrc
---------+---------+---------+------------+-------------+----------
ac | check_a | c | t | 0 | (a <> 0)
bc | check_b | c | t | 0 | (b <> 0)
cc | check_a | c | f | 1 | (a <> 0)
cc | check_b | c | t | 0 | (b <> 0)
cc | check_c | c | t | 0 | (c <> 0)
(5 rows)
drop table cc;
drop table bc;
drop table ac;
create table p1(f1 int);
create table p2(f2 int);
create table c1(f3 int) inherits(p1,p2);
insert into c1 values(1,-1,2);
alter table p2 add constraint cc check (f2>0); -- fail
ERROR: check constraint "cc" is violated by some row
alter table p2 add check (f2>0); -- check it without a name, too
ERROR: check constraint "p2_f2_check" is violated by some row
delete from c1;
insert into c1 values(1,1,2);
alter table p2 add check (f2>0);
insert into c1 values(1,-1,2); -- fail
ERROR: new row for relation "c1" violates check constraint "p2_f2_check"
create table c2(f3 int) inherits(p1,p2);
\d c2
Table "public.c2"
Column | Type | Modifiers
--------+---------+-----------
f1 | integer |
f2 | integer |
f3 | integer |
Check constraints:
"p2_f2_check" CHECK (f2 > 0)
Inherits: p1,
p2
create table c3 (f4 int) inherits(c1,c2);
NOTICE: merging multiple inherited definitions of column "f1"
NOTICE: merging multiple inherited definitions of column "f2"
NOTICE: merging multiple inherited definitions of column "f3"
\d c3
Table "public.c3"
Column | Type | Modifiers
--------+---------+-----------
f1 | integer |
f2 | integer |
f3 | integer |
f4 | integer |
Check constraints:
"p2_f2_check" CHECK (f2 > 0)
Inherits: c1,
c2
drop table p1 cascade;
NOTICE: drop cascades to table c2
NOTICE: drop cascades to table c3
NOTICE: drop cascades to constraint p2_f2_check on table c3
NOTICE: drop cascades to constraint p2_f2_check on table c2
NOTICE: drop cascades to table c1
NOTICE: drop cascades to constraint p2_f2_check on table c1
drop table p2 cascade;
create table pp1 (f1 int);
create table cc1 (f2 text, f3 int) inherits (pp1);
alter table pp1 add column a1 int check (a1 > 0);
\d cc1
Table "public.cc1"
Column | Type | Modifiers
--------+---------+-----------
f1 | integer |
f2 | text |
f3 | integer |
a1 | integer |
Check constraints:
"pp1_a1_check" CHECK (a1 > 0)
Inherits: pp1
create table cc2(f4 float) inherits(pp1,cc1);
NOTICE: merging multiple inherited definitions of column "f1"
NOTICE: merging multiple inherited definitions of column "a1"
\d cc2
Table "public.cc2"
Column | Type | Modifiers
--------+------------------+-----------
f1 | integer |
a1 | integer |
f2 | text |
f3 | integer |
f4 | double precision |
Check constraints:
"pp1_a1_check" CHECK (a1 > 0)
Inherits: pp1,
cc1
alter table pp1 add column a2 int check (a2 > 0);
NOTICE: merging definition of column "a2" for child "cc2"
NOTICE: merging constraint "pp1_a2_check" with inherited definition
\d cc2
Table "public.cc2"
Column | Type | Modifiers
--------+------------------+-----------
f1 | integer |
a1 | integer |
f2 | text |
f3 | integer |
f4 | double precision |
a2 | integer |
Check constraints:
"pp1_a1_check" CHECK (a1 > 0)
"pp1_a2_check" CHECK (a2 > 0)
Inherits: pp1,
cc1
drop table pp1 cascade;
NOTICE: drop cascades to table cc2
NOTICE: drop cascades to constraint pp1_a1_check on table cc2
NOTICE: drop cascades to constraint pp1_a2_check on table cc2
NOTICE: drop cascades to table cc1
NOTICE: drop cascades to constraint pp1_a1_check on table cc1
NOTICE: drop cascades to constraint pp1_a2_check on table cc1

View File

@ -389,19 +389,20 @@ select test2 from atacc2;
drop table atacc2 cascade;
drop table atacc1;
-- let's try only to add only to the parent
-- adding only to a parent is disallowed as of 8.4
create table atacc1 (test int);
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);
insert into atacc2 (test2) values (3);
-- both succeed on atacc3
insert into atacc3 (test2) values (-3);
insert into atacc3 (test2) values (3);
drop table atacc3;
create table atacc2 (test2 int) inherits (atacc1);
-- fail:
alter table only atacc1 add constraint foo check (test>0);
-- ok:
alter table only atacc2 add constraint foo check (test>0);
-- check constraint not there on parent
insert into atacc1 (test) values (-3);
insert into atacc1 (test) values (3);
-- check constraint is there on child
insert into atacc2 (test) values (-3);
insert into atacc2 (test) values (3);
drop table atacc2;
drop table atacc1;

View File

@ -196,3 +196,83 @@ drop function p2text(p2);
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 pc.relname, 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 1,2;
insert into ac (aa) values (NULL);
insert into bc (aa) values (NULL);
alter table bc drop constraint ac_check; -- fail, disallowed
alter table ac drop constraint ac_check;
select pc.relname, 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 1,2;
-- try the unnamed-constraint case
alter table ac add check (aa is not null);
select pc.relname, 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 1,2;
insert into ac (aa) values (NULL);
insert into bc (aa) values (NULL);
alter table bc drop constraint ac_aa_check; -- fail, disallowed
alter table ac drop constraint ac_aa_check;
select pc.relname, 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 1,2;
alter table ac add constraint ac_check check (aa is not null);
alter table bc no inherit ac;
select pc.relname, 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 1,2;
alter table bc drop constraint ac_check;
select pc.relname, 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 1,2;
alter table ac drop constraint ac_check;
select pc.relname, 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 1,2;
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 pc.relname, 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 1,2;
drop table bc;
drop table ac;
create table ac (a int constraint check_a check (a <> 0));
create table bc (b int constraint check_b check (b <> 0));
create table cc (c int constraint check_c check (c <> 0)) inherits (ac, bc);
select pc.relname, 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', 'cc') order by 1,2;
alter table cc no inherit bc;
select pc.relname, 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', 'cc') order by 1,2;
drop table cc;
drop table bc;
drop table ac;
create table p1(f1 int);
create table p2(f2 int);
create table c1(f3 int) inherits(p1,p2);
insert into c1 values(1,-1,2);
alter table p2 add constraint cc check (f2>0); -- fail
alter table p2 add check (f2>0); -- check it without a name, too
delete from c1;
insert into c1 values(1,1,2);
alter table p2 add check (f2>0);
insert into c1 values(1,-1,2); -- fail
create table c2(f3 int) inherits(p1,p2);
\d c2
create table c3 (f4 int) inherits(c1,c2);
\d c3
drop table p1 cascade;
drop table p2 cascade;
create table pp1 (f1 int);
create table cc1 (f2 text, f3 int) inherits (pp1);
alter table pp1 add column a1 int check (a1 > 0);
\d cc1
create table cc2(f4 float) inherits(pp1,cc1);
\d cc2
alter table pp1 add column a2 int check (a2 > 0);
\d cc2
drop table pp1 cascade;