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:
parent
f8df836ae3
commit
cd902b331d
@ -1,4 +1,4 @@
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.165 2008/04/14 17:05:32 tgl Exp $ -->
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.166 2008/05/09 23:32:03 tgl Exp $ -->
|
||||
<!--
|
||||
Documentation of the system catalogs, directed toward PostgreSQL developers
|
||||
-->
|
||||
@ -1907,6 +1907,26 @@
|
||||
<entry>Foreign key match type</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>conislocal</structfield></entry>
|
||||
<entry><type>bool</type></entry>
|
||||
<entry></entry>
|
||||
<entry>
|
||||
This constraint is defined locally in the relation. Note that a
|
||||
constraint can be locally defined and inherited simultaneously
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>coninhcount</structfield></entry>
|
||||
<entry><type>int4</type></entry>
|
||||
<entry></entry>
|
||||
<entry>
|
||||
The number of direct ancestors this constraint has. A constraint with
|
||||
a nonzero number of ancestors cannot be dropped nor renamed
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><structfield>conkey</structfield></entry>
|
||||
<entry><type>int2[]</type></entry>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/ddl.sgml,v 1.81 2008/01/13 17:58:54 tgl Exp $ -->
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/ddl.sgml,v 1.82 2008/05/09 23:32:03 tgl Exp $ -->
|
||||
|
||||
<chapter id="ddl">
|
||||
<title>Data Definition</title>
|
||||
@ -2107,7 +2107,8 @@ VALUES ('New York', NULL, NULL, 'NY');
|
||||
|
||||
<para>
|
||||
A parent table cannot be dropped while any of its children remain. Neither
|
||||
can columns of child tables be dropped or altered if they are inherited
|
||||
can columns or check constraints of child tables be dropped or altered
|
||||
if they are inherited
|
||||
from any parent tables. If you wish to remove a table and all of its
|
||||
descendants, one easy way is to drop the parent table with the
|
||||
<literal>CASCADE</literal> option.
|
||||
@ -2117,7 +2118,7 @@ VALUES ('New York', NULL, NULL, 'NY');
|
||||
<xref linkend="sql-altertable" endterm="sql-altertable-title"> will
|
||||
propagate any changes in column data definitions and check
|
||||
constraints down the inheritance hierarchy. Again, dropping
|
||||
columns or constraints on parent tables is only possible when using
|
||||
columns that are depended on by other tables is only possible when using
|
||||
the <literal>CASCADE</literal> option. <command>ALTER
|
||||
TABLE</command> follows the same rules for duplicate column merging
|
||||
and rejection that apply during <command>CREATE TABLE</command>.
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.98 2007/11/28 15:42:31 petere Exp $
|
||||
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.99 2008/05/09 23:32:03 tgl Exp $
|
||||
PostgreSQL documentation
|
||||
-->
|
||||
|
||||
@ -713,7 +713,8 @@ ALTER TABLE table ALTER COLUMN anycol TYPE anytype;
|
||||
The <literal>TRIGGER</>, <literal>CLUSTER</>, <literal>OWNER</>,
|
||||
and <literal>TABLESPACE</> actions never recurse to descendant tables;
|
||||
that is, they always act as though <literal>ONLY</> were specified.
|
||||
Adding a constraint can recurse only for <literal>CHECK</> constraints.
|
||||
Adding a constraint can recurse only for <literal>CHECK</> constraints,
|
||||
and is required to do so for such constraints.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -804,7 +805,7 @@ ALTER TABLE distributors ALTER COLUMN street DROP NOT NULL;
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To add a check constraint to a table:
|
||||
To add a check constraint to a table and all its children:
|
||||
<programlisting>
|
||||
ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
|
||||
</programlisting>
|
||||
@ -817,6 +818,14 @@ ALTER TABLE distributors DROP CONSTRAINT zipchk;
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To remove a check constraint from a table only:
|
||||
<programlisting>
|
||||
ALTER TABLE ONLY distributors DROP CONSTRAINT zipchk;
|
||||
</programlisting>
|
||||
(The check constraint remains in place for any child tables.)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To add a foreign key constraint to a table:
|
||||
<programlisting>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.109 2007/07/17 05:02:00 neilc Exp $
|
||||
$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.110 2008/05/09 23:32:04 tgl Exp $
|
||||
PostgreSQL documentation
|
||||
-->
|
||||
|
||||
@ -210,16 +210,25 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
|
||||
the new table. If the column name list of the new table
|
||||
contains a column name that is also inherited, the data type must
|
||||
likewise match the inherited column(s), and the column
|
||||
definitions are merged into one. However, inherited and new
|
||||
column declarations of the same name need not specify identical
|
||||
constraints: all constraints provided from any declaration are
|
||||
merged together and all are applied to the new table. If the
|
||||
definitions are merged into one. If the
|
||||
new table explicitly specifies a default value for the column,
|
||||
this default overrides any defaults from inherited declarations
|
||||
of the column. Otherwise, any parents that specify default
|
||||
values for the column must all specify the same default, or an
|
||||
error will be reported.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>CHECK</> constraints are merged in essentially the same way as
|
||||
columns: if multiple parent tables and/or the new table definition
|
||||
contain identically-named <literal>CHECK</> constraints, these
|
||||
constraints must all have the same check expression, or an error will be
|
||||
reported. Constraints having the same name and expression will
|
||||
be merged into one copy. Notice that an unnamed <literal>CHECK</>
|
||||
constraint in the new table will never be merged, since a unique name
|
||||
will always be chosen for it.
|
||||
</para>
|
||||
|
||||
<!--
|
||||
<para>
|
||||
<productname>PostgreSQL</> automatically allows the
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user