Commit cd902b33 authored by Tom Lane's avatar Tom Lane

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
parent f8df836a
<!-- $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>
......
<!-- $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>.
......
<!--
$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>
......
<!--
$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,
......
This diff is collapsed.
......@@ -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,
......
This diff is collapsed.
......@@ -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, "
"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 "
"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;
......
This diff is collapsed.
......@@ -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;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment