Allow CHECK constraints to be declared ONLY

This makes them enforceable only on the parent table, not on children
tables.  This is useful in various situations, per discussion involving
people bitten by the restrictive behavior introduced in 8.4.

Message-Id:
8762mp93iw.fsf@comcast.net
CAFaPBrSMMpubkGf4zcRL_YL-AERUbYF_-ZNNYfb3CVwwEqc9TQ@mail.gmail.com

Authors: Nikhil Sontakke, Alex Hunsaker
Reviewed by Robert Haas and myself
This commit is contained in:
Alvaro Herrera 2011-12-05 15:10:18 -03:00
parent 9220362493
commit 61d81bd28d
19 changed files with 212 additions and 79 deletions

View File

@ -2036,6 +2036,16 @@
</entry>
</row>
<row>
<entry><structfield>conisonly</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>
This constraint is defined locally for the relation. It is a
non-inheritable constraint.
</entry>
</row>
<row>
<entry><structfield>conkey</structfield></entry>
<entry><type>int2[]</type></entry>

View File

@ -983,6 +983,14 @@ ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
</programlisting>
</para>
<para>
To add a check constraint only to a table and not to its children:
<programlisting>
ALTER TABLE ONLY distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
</programlisting>
(The check constraint will not be inherited by future children, either.)
</para>
<para>
To remove a check constraint from a table and all its children:
<programlisting>

View File

@ -92,10 +92,10 @@ static Oid AddNewRelationType(const char *typeName,
Oid new_array_type);
static void RelationRemoveInheritance(Oid relid);
static void StoreRelCheck(Relation rel, char *ccname, Node *expr,
bool is_validated, bool is_local, int inhcount);
bool is_validated, bool is_local, int inhcount, bool is_only);
static void StoreConstraints(Relation rel, List *cooked_constraints);
static bool MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
bool allow_merge, bool is_local);
bool allow_merge, bool is_local, bool is_only);
static void SetRelationNumChecks(Relation rel, int numchecks);
static Node *cookConstraint(ParseState *pstate,
Node *raw_constraint,
@ -1859,7 +1859,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr)
*/
static void
StoreRelCheck(Relation rel, char *ccname, Node *expr,
bool is_validated, bool is_local, int inhcount)
bool is_validated, bool is_local, int inhcount, bool is_only)
{
char *ccbin;
char *ccsrc;
@ -1942,7 +1942,8 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
ccbin, /* Binary form of check constraint */
ccsrc, /* Source form of check constraint */
is_local, /* conislocal */
inhcount); /* coninhcount */
inhcount, /* coninhcount */
is_only); /* conisonly */
pfree(ccbin);
pfree(ccsrc);
@ -1983,7 +1984,7 @@ StoreConstraints(Relation rel, List *cooked_constraints)
break;
case CONSTR_CHECK:
StoreRelCheck(rel, con->name, con->expr, !con->skip_validation,
con->is_local, con->inhcount);
con->is_local, con->inhcount, con->is_only);
numchecks++;
break;
default:
@ -2026,7 +2027,8 @@ AddRelationNewConstraints(Relation rel,
List *newColDefaults,
List *newConstraints,
bool allow_merge,
bool is_local)
bool is_local,
bool is_only)
{
List *cookedConstraints = NIL;
TupleDesc tupleDesc;
@ -2099,6 +2101,7 @@ AddRelationNewConstraints(Relation rel,
cooked->skip_validation = false;
cooked->is_local = is_local;
cooked->inhcount = is_local ? 0 : 1;
cooked->is_only = is_only;
cookedConstraints = lappend(cookedConstraints, cooked);
}
@ -2166,7 +2169,7 @@ AddRelationNewConstraints(Relation rel,
* what ATAddCheckConstraint wants.)
*/
if (MergeWithExistingConstraint(rel, ccname, expr,
allow_merge, is_local))
allow_merge, is_local, is_only))
continue;
}
else
@ -2213,7 +2216,7 @@ AddRelationNewConstraints(Relation rel,
* OK, store it.
*/
StoreRelCheck(rel, ccname, expr, !cdef->skip_validation, is_local,
is_local ? 0 : 1);
is_local ? 0 : 1, is_only);
numchecks++;
@ -2225,6 +2228,7 @@ AddRelationNewConstraints(Relation rel,
cooked->skip_validation = cdef->skip_validation;
cooked->is_local = is_local;
cooked->inhcount = is_local ? 0 : 1;
cooked->is_only = is_only;
cookedConstraints = lappend(cookedConstraints, cooked);
}
@ -2250,7 +2254,8 @@ AddRelationNewConstraints(Relation rel,
*/
static bool
MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
bool allow_merge, bool is_local)
bool allow_merge, bool is_local,
bool is_only)
{
bool found;
Relation conDesc;
@ -2312,6 +2317,11 @@ MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
con->conislocal = true;
else
con->coninhcount++;
if (is_only)
{
Assert(is_local);
con->conisonly = true;
}
simple_heap_update(conDesc, &tup->t_self, tup);
CatalogUpdateIndexes(conDesc, tup);
break;

View File

@ -1155,7 +1155,8 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
true, /* islocal */
0); /* inhcount */
0, /* inhcount */
false); /* isonly */
/*
* Register the index as internally dependent on the constraint.

View File

@ -66,7 +66,8 @@ CreateConstraintEntry(const char *constraintName,
const char *conBin,
const char *conSrc,
bool conIsLocal,
int conInhCount)
int conInhCount,
bool conIsOnly)
{
Relation conDesc;
Oid conOid;
@ -169,6 +170,7 @@ CreateConstraintEntry(const char *constraintName,
values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
values[Anum_pg_constraint_conisonly - 1] = BoolGetDatum(conIsOnly);
if (conkeyArray)
values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);

View File

@ -579,6 +579,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
cooked->skip_validation = false;
cooked->is_local = true; /* not used for defaults */
cooked->inhcount = 0; /* ditto */
cooked->is_only = false;
cookedDefaults = lappend(cookedDefaults, cooked);
descriptor->attrs[attnum - 1]->atthasdef = true;
}
@ -638,7 +639,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
*/
if (rawDefaults || stmt->constraints)
AddRelationNewConstraints(rel, rawDefaults, stmt->constraints,
true, true);
true, true, false);
/*
* Clean up. We keep lock on new relation (although it shouldn't be
@ -1599,6 +1600,10 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
char *name = check[i].ccname;
Node *expr;
/* ignore if the constraint is non-inheritable */
if (check[i].cconly)
continue;
/* adjust varattnos of ccbin here */
expr = stringToNode(check[i].ccbin);
change_varattnos_of_a_node(expr, newattno);
@ -1617,6 +1622,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
cooked->skip_validation = false;
cooked->is_local = false;
cooked->inhcount = 1;
cooked->is_only = false;
constraints = lappend(constraints, cooked);
}
}
@ -4501,7 +4507,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
* This function is intended for CREATE TABLE, so it processes a
* _list_ of defaults, but we just do one.
*/
AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, false, true);
AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, false, true, false);
/* Make the additional catalog changes visible */
CommandCounterIncrement();
@ -4898,7 +4904,7 @@ ATExecColumnDefault(Relation rel, const char *colName,
* This function is intended for CREATE TABLE, so it processes a
* _list_ of defaults, but we just do one.
*/
AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, false, true);
AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, false, true, false);
}
}
@ -5562,10 +5568,16 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
* omitted from the returned list, which is what we want: we do not need
* to do any validation work. That can only happen at child tables,
* though, since we disallow merging at the top level.
*
* Note: we set is_only based on the recurse flag which is false when
* interpretInhOption() of our statement returns false all the way up
* in AlterTable and gets passed all the way down to here.
*/
newcons = AddRelationNewConstraints(rel, NIL,
list_make1(copyObject(constr)),
recursing, !recursing);
recursing, /* allow_merge */
!recursing, /* is_local */
!recurse && !recursing); /* is_only */
/* Add each to-be-validated constraint to Phase 3's queue */
foreach(lcon, newcons)
@ -5605,6 +5617,12 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
if (newcons == NIL)
return;
/*
* Adding an ONLY constraint? No need to find our children
*/
if (!recurse && !recursing)
return;
/*
* Propagate to children as appropriate. Unlike most other ALTER
* routines, we have to do this one level of recursion at a time; we can't
@ -5612,15 +5630,6 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
*/
children = find_inheritance_children(RelationGetRelid(rel), lockmode);
/*
* If we are told not to recurse, there had better not be any child
* tables; else the addition would put them out of step.
*/
if (children && !recurse)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("constraint must be added to child tables too")));
foreach(child, children)
{
Oid childrelid = lfirst_oid(child);
@ -5914,7 +5923,8 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
NULL,
NULL,
true, /* islocal */
0); /* inhcount */
0, /* inhcount */
false); /* isonly */
/*
* Create the triggers that will enforce the constraint.
@ -6755,6 +6765,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
HeapTuple tuple;
bool found = false;
bool is_check_constraint = false;
bool is_only_constraint = false;
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
@ -6791,6 +6802,12 @@ ATExecDropConstraint(Relation rel, const char *constrName,
/* Right now only CHECK constraints can be inherited */
if (con->contype == CONSTRAINT_CHECK)
is_check_constraint = true;
if (con->conisonly)
{
Assert(is_check_constraint);
is_only_constraint = true;
}
/*
* Perform the actual constraint deletion
@ -6802,6 +6819,9 @@ ATExecDropConstraint(Relation rel, const char *constrName,
performDeletion(&conobj, behavior);
found = true;
/* constraint found and dropped -- no need to keep looping */
break;
}
systable_endscan(scan);
@ -6830,7 +6850,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
* routines, we have to do this one level of recursion at a time; we can't
* use find_all_inheritors to do it in one pass.
*/
if (is_check_constraint)
if (is_check_constraint && !is_only_constraint)
children = find_inheritance_children(RelationGetRelid(rel), lockmode);
else
children = NIL;

View File

@ -449,7 +449,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
true, /* islocal */
0); /* inhcount */
0, /* inhcount */
false); /* isonly */
}
/*

View File

@ -2934,7 +2934,8 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
ccbin, /* Binary form of check constraint */
ccsrc, /* Source form of check constraint */
true, /* is local */
0); /* inhcount */
0, /* inhcount */
false); /* is only */
/*
* Return the compiled constraint expression so the calling routine can

View File

@ -3261,6 +3261,7 @@ CheckConstraintFetch(Relation relation)
RelationGetRelationName(relation));
check[found].ccvalid = conform->convalidated;
check[found].cconly = conform->conisonly;
check[found].ccname = MemoryContextStrdup(CacheMemoryContext,
NameStr(conform->conname));

View File

@ -6019,11 +6019,16 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo->dobj.name);
resetPQExpBuffer(q);
if (g_fout->remoteVersion >= 90100)
if (g_fout->remoteVersion >= 90200)
{
/*
* conisonly and convalidated are new in 9.2 (actually, the latter
* is there in 9.1, but it wasn't ever false for check constraints
* until 9.2).
*/
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
"conislocal, convalidated "
"conislocal, convalidated, conisonly "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
@ -6034,7 +6039,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
{
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
"conislocal, true AS convalidated "
"conislocal, true AS convalidated, "
"false as conisonly "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
@ -6045,7 +6051,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
{
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
"true AS conislocal, true AS convalidated "
"true AS conislocal, true AS convalidated, "
"false as conisonly "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
@ -6057,7 +6064,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
/* no pg_get_constraintdef, must use consrc */
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
"'CHECK (' || consrc || ')' AS consrc, "
"true AS conislocal, true AS convalidated "
"true AS conislocal, true AS convalidated, "
"false as conisonly "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
@ -6070,7 +6078,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
appendPQExpBuffer(q, "SELECT tableoid, 0 AS oid, "
"rcname AS conname, "
"'CHECK (' || rcsrc || ')' AS consrc, "
"true AS conislocal, true AS convalidated "
"true AS conislocal, true AS convalidated, "
"false as conisonly "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
@ -6081,7 +6090,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
appendPQExpBuffer(q, "SELECT tableoid, oid, "
"rcname AS conname, "
"'CHECK (' || rcsrc || ')' AS consrc, "
"true AS conislocal, true AS convalidated "
"true AS conislocal, true AS convalidated, "
"false as conisonly "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
@ -6094,7 +6104,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
"oid, rcname AS conname, "
"'CHECK (' || rcsrc || ')' AS consrc, "
"true AS conislocal, true AS convalidated "
"true AS conislocal, true AS convalidated, "
"false as conisonly "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
@ -6120,6 +6131,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
for (j = 0; j < numConstrs; j++)
{
bool validated = PQgetvalue(res, j, 5)[0] == 't';
bool isonly = PQgetvalue(res, j, 6)[0] == 't';
constrs[j].dobj.objType = DO_CONSTRAINT;
constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
@ -6136,12 +6148,14 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
constrs[j].condeferrable = false;
constrs[j].condeferred = false;
constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't');
constrs[j].conisonly = isonly;
/*
* An unvalidated constraint needs to be dumped separately, so
* that potentially-violating existing data is loaded before
* the constraint.
* the constraint. An ONLY constraint needs to be dumped
* separately too.
*/
constrs[j].separate = !validated;
constrs[j].separate = !validated || isonly;
constrs[j].dobj.dump = tbinfo->dobj.dump;
@ -6149,12 +6163,12 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
* Mark the constraint as needing to appear before the table
* --- this is so that any other dependencies of the
* constraint will be emitted before we try to create the
* table. If the constraint is not validated, it will be
* table. If the constraint is to be dumped separately, it will be
* dumped after data is loaded anyway, so don't do it. (There's
* an automatic dependency in the opposite direction anyway, so
* don't need to add one manually here.)
*/
if (validated)
if (!constrs[j].separate)
addObjectDependency(&tbinfo->dobj,
constrs[j].dobj.dumpId);
@ -13193,9 +13207,9 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
/* Ignore if not to be dumped separately */
if (coninfo->separate)
{
/* not ONLY since we want it to propagate to children */
appendPQExpBuffer(q, "ALTER TABLE %s\n",
fmtId(tbinfo->dobj.name));
/* add ONLY if we do not want it to propagate to children */
appendPQExpBuffer(q, "ALTER TABLE %s %s\n",
coninfo->conisonly ? "ONLY" : "", fmtId(tbinfo->dobj.name));
appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
fmtId(coninfo->dobj.name),
coninfo->condef);

View File

@ -378,6 +378,7 @@ typedef struct _constraintInfo
bool condeferrable; /* TRUE if constraint is DEFERRABLE */
bool condeferred; /* TRUE if constraint is INITIALLY DEFERRED */
bool conislocal; /* TRUE if constraint has local definition */
bool conisonly; /* TRUE if constraint is non-inheritable */
bool separate; /* TRUE if must dump as separate item */
} ConstraintInfo;

View File

@ -1781,12 +1781,20 @@ describeOneTableDetails(const char *schemaname,
/* print table (and column) check constraints */
if (tableinfo.checks)
{
char *is_only;
if (pset.sversion >= 90200)
is_only = "r.conisonly";
else
is_only = "false AS conisonly";
printfPQExpBuffer(&buf,
"SELECT r.conname, "
"SELECT r.conname, %s, "
"pg_catalog.pg_get_constraintdef(r.oid, true)\n"
"FROM pg_catalog.pg_constraint r\n"
"WHERE r.conrelid = '%s' AND r.contype = 'c'\nORDER BY 1;",
oid);
"WHERE r.conrelid = '%s' AND r.contype = 'c'\n"
"ORDER BY 2 DESC, 1;",
is_only, oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
@ -1799,9 +1807,10 @@ describeOneTableDetails(const char *schemaname,
for (i = 0; i < tuples; i++)
{
/* untranslated contraint name and def */
printfPQExpBuffer(&buf, " \"%s\" %s",
printfPQExpBuffer(&buf, " \"%s\"%s%s",
PQgetvalue(result, i, 0),
PQgetvalue(result, i, 1));
(strcmp(PQgetvalue(result, i, 1), "t") == 0) ? " (ONLY) ":" ",
PQgetvalue(result, i, 2));
printTableAddFooter(&cont, buf.data);
}

View File

@ -30,6 +30,7 @@ typedef struct constrCheck
char *ccname;
char *ccbin; /* nodeToString representation of expr */
bool ccvalid;
bool cconly; /* this is a non-inheritable constraint */
} ConstrCheck;
/* This structure contains constraints of a tuple */

View File

@ -33,6 +33,7 @@ typedef struct CookedConstraint
bool skip_validation; /* skip validation? (only for CHECK) */
bool is_local; /* constraint has local (non-inherited) def */
int inhcount; /* number of times constraint is inherited */
bool is_only; /* constraint has local def and cannot be inherited */
} CookedConstraint;
extern Relation heap_create(const char *relname,
@ -91,7 +92,8 @@ extern List *AddRelationNewConstraints(Relation rel,
List *newColDefaults,
List *newConstraints,
bool allow_merge,
bool is_local);
bool is_local,
bool is_only);
extern void StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr);

View File

@ -88,6 +88,9 @@ CATALOG(pg_constraint,2606)
/* Number of times inherited from direct parent relation(s) */
int4 coninhcount;
/* Has a local definition and cannot be inherited */
bool conisonly;
/*
* VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
*/
@ -149,7 +152,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
#define Natts_pg_constraint 23
#define Natts_pg_constraint 24
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@ -165,14 +168,15 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_confmatchtype 13
#define Anum_pg_constraint_conislocal 14
#define Anum_pg_constraint_coninhcount 15
#define Anum_pg_constraint_conkey 16
#define Anum_pg_constraint_confkey 17
#define Anum_pg_constraint_conpfeqop 18
#define Anum_pg_constraint_conppeqop 19
#define Anum_pg_constraint_conffeqop 20
#define Anum_pg_constraint_conexclop 21
#define Anum_pg_constraint_conbin 22
#define Anum_pg_constraint_consrc 23
#define Anum_pg_constraint_conisonly 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
#define Anum_pg_constraint_conpfeqop 19
#define Anum_pg_constraint_conppeqop 20
#define Anum_pg_constraint_conffeqop 21
#define Anum_pg_constraint_conexclop 22
#define Anum_pg_constraint_conbin 23
#define Anum_pg_constraint_consrc 24
/* Valid values for contype */
@ -227,7 +231,8 @@ extern Oid CreateConstraintEntry(const char *constraintName,
const char *conBin,
const char *conSrc,
bool conIsLocal,
int conInhCount);
int conInhCount,
bool conIsOnly);
extern void RemoveConstraintById(Oid conId);
extern void RenameConstraintById(Oid conId, const char *newname);

View File

@ -498,22 +498,21 @@ select test2 from atacc2;
drop table atacc2 cascade;
NOTICE: drop cascades to table atacc3
drop table atacc1;
-- adding only to a parent is disallowed as of 8.4
-- adding only to a parent is allowed as of 9.2
create table atacc1 (test int);
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
alter table only atacc1 add constraint foo check (test>0);
-- check constraint is not there on child
insert into atacc2 (test) values (-3);
ERROR: new row for relation "atacc2" violates check constraint "foo"
DETAIL: Failing row contains (-3, null).
insert into atacc2 (test) values (3);
-- check constraint is there on parent
insert into atacc1 (test) values (-3);
ERROR: new row for relation "atacc1" violates check constraint "foo"
DETAIL: Failing row contains (-3).
insert into atacc1 (test) values (3);
-- fail, violating row:
alter table only atacc2 add constraint foo check (test>0);
ERROR: check constraint "foo" is violated by some row
drop table atacc2;
drop table atacc1;
-- test unique constraint adding

View File

@ -683,6 +683,41 @@ select * from d;
32 | one | two | three
(1 row)
-- Test non-inheritable parent constraints
create table p1(ff1 int);
alter table only p1 add constraint p1chk check (ff1 > 0);
alter table p1 add constraint p2chk check (ff1 > 10);
-- conisonly should be true for ONLY constraint
select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.conisonly from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname = 'p1';
relname | conname | contype | conislocal | coninhcount | conisonly
---------+---------+---------+------------+-------------+-----------
p1 | p1chk | c | t | 0 | t
p1 | p2chk | c | t | 0 | f
(2 rows)
-- Test that child does not inherit ONLY constraints
create table c1 () inherits (p1);
\d p1
Table "public.p1"
Column | Type | Modifiers
--------+---------+-----------
ff1 | integer |
Check constraints:
"p1chk" (ONLY) CHECK (ff1 > 0)
"p2chk" CHECK (ff1 > 10)
Number of child tables: 1 (Use \d+ to list them.)
\d c1
Table "public.c1"
Column | Type | Modifiers
--------+---------+-----------
ff1 | integer |
Check constraints:
"p2chk" CHECK (ff1 > 10)
Inherits: p1
drop table p1 cascade;
NOTICE: drop cascades to table c1
-- Tests for casting between the rowtypes of parent and child
-- tables. See the pgsql-hackers thread beginning Dec. 4/04
create table base (i integer);

View File

@ -450,20 +450,19 @@ select test2 from atacc2;
drop table atacc2 cascade;
drop table atacc1;
-- adding only to a parent is disallowed as of 8.4
-- adding only to a parent is allowed as of 9.2
create table atacc1 (test int);
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
alter table only atacc1 add constraint foo check (test>0);
-- check constraint is not there on child
insert into atacc2 (test) values (-3);
-- check constraint is 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);
-- fail, violating row:
alter table only atacc2 add constraint foo check (test>0);
drop table atacc2;
drop table atacc1;

View File

@ -188,6 +188,20 @@ insert into d values('test','one','two','three');
alter table a alter column aa type integer using bit_length(aa);
select * from d;
-- Test non-inheritable parent constraints
create table p1(ff1 int);
alter table only p1 add constraint p1chk check (ff1 > 0);
alter table p1 add constraint p2chk check (ff1 > 10);
-- conisonly should be true for ONLY constraint
select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.conisonly from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname = 'p1';
-- Test that child does not inherit ONLY constraints
create table c1 () inherits (p1);
\d p1
\d c1
drop table p1 cascade;
-- Tests for casting between the rowtypes of parent and child
-- tables. See the pgsql-hackers thread beginning Dec. 4/04
create table base (i integer);