Recast "ONLY" column CHECK constraints as NO INHERIT

The original syntax wasn't universally loved, and it didn't allow its
usage in CREATE TABLE, only ALTER TABLE.  It now works everywhere, and
it also allows using ALTER TABLE ONLY to add an uninherited CHECK
constraint, per discussion.

The pg_constraint column has accordingly been renamed connoinherit.

This commit partly reverts some of the changes in
61d81bd28d, particularly some pg_dump and
psql bits, because now pg_get_constraintdef includes the necessary NO
INHERIT within the constraint definition.

Author: Nikhil Sontakke
Some tweaks by me
This commit is contained in:
Alvaro Herrera 2012-04-20 23:46:20 -03:00
parent 1f03630011
commit 09ff76fcdb
27 changed files with 197 additions and 133 deletions

View File

@ -907,7 +907,8 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
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,
and is required to do so for such constraints.
and is required to do so for such constraints, except those that are
explicitely marked <literal>NO INHERIT</>.
</para>
<para>
@ -1013,7 +1014,7 @@ ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
<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);
ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK NO INHERIT (char_length(zipcode) = 5);
</programlisting>
(The check constraint will not be inherited by future children, either.)
</para>

View File

@ -47,7 +47,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
{ NOT NULL |
NULL |
CHECK ( <replaceable class="PARAMETER">expression</replaceable> ) |
CHECK [ NO INHERIT ] ( <replaceable class="PARAMETER">expression</replaceable> ) |
DEFAULT <replaceable>default_expr</replaceable> |
UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
@ -58,7 +58,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
<phrase>and <replaceable class="PARAMETER">table_constraint</replaceable> is:</phrase>
[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
{ CHECK ( <replaceable class="PARAMETER">expression</replaceable> ) |
{ CHECK [ NO INHERIT ] ( <replaceable class="PARAMETER">expression</replaceable> ) |
UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
@ -299,7 +299,8 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
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</>
be merged into one copy. A constraint marked <literal>NO INHERIT</> in a
parent will not be considered. 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>
@ -415,7 +416,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
</varlistentry>
<varlistentry>
<term><literal>CHECK ( <replaceable class="PARAMETER">expression</replaceable> )</literal></term>
<term><literal>CHECK [ NO INHERIT ] ( <replaceable class="PARAMETER">expression</replaceable> )</literal></term>
<listitem>
<para>
The <literal>CHECK</> clause specifies an expression producing a
@ -434,6 +435,11 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
subqueries nor refer to variables other than columns of the
current row.
</para>
<para>
A constraint marked with <literal>NO INHERIT</> will not propagate to
children tables.
</para>
</listitem>
</varlistentry>

View File

@ -92,10 +92,12 @@ 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_only);
bool is_validated, bool is_local, int inhcount,
bool is_no_inherit);
static void StoreConstraints(Relation rel, List *cooked_constraints);
static bool MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
bool allow_merge, bool is_local, bool is_only);
bool allow_merge, bool is_local,
bool is_no_inherit);
static void SetRelationNumChecks(Relation rel, int numchecks);
static Node *cookConstraint(ParseState *pstate,
Node *raw_constraint,
@ -1868,7 +1870,8 @@ 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_only)
bool is_validated, bool is_local, int inhcount,
bool is_no_inherit)
{
char *ccbin;
char *ccsrc;
@ -1952,7 +1955,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
ccsrc, /* Source form of check constraint */
is_local, /* conislocal */
inhcount, /* coninhcount */
is_only); /* conisonly */
is_no_inherit); /* connoinherit */
pfree(ccbin);
pfree(ccsrc);
@ -1993,7 +1996,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_only);
con->is_local, con->inhcount, con->is_no_inherit);
numchecks++;
break;
default:
@ -2036,8 +2039,7 @@ AddRelationNewConstraints(Relation rel,
List *newColDefaults,
List *newConstraints,
bool allow_merge,
bool is_local,
bool is_only)
bool is_local)
{
List *cookedConstraints = NIL;
TupleDesc tupleDesc;
@ -2110,7 +2112,7 @@ AddRelationNewConstraints(Relation rel,
cooked->skip_validation = false;
cooked->is_local = is_local;
cooked->inhcount = is_local ? 0 : 1;
cooked->is_only = is_only;
cooked->is_no_inherit = false;
cookedConstraints = lappend(cookedConstraints, cooked);
}
@ -2178,7 +2180,8 @@ AddRelationNewConstraints(Relation rel,
* what ATAddCheckConstraint wants.)
*/
if (MergeWithExistingConstraint(rel, ccname, expr,
allow_merge, is_local, is_only))
allow_merge, is_local,
cdef->is_no_inherit))
continue;
}
else
@ -2225,7 +2228,7 @@ AddRelationNewConstraints(Relation rel,
* OK, store it.
*/
StoreRelCheck(rel, ccname, expr, !cdef->skip_validation, is_local,
is_local ? 0 : 1, is_only);
is_local ? 0 : 1, cdef->is_no_inherit);
numchecks++;
@ -2237,7 +2240,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;
cooked->is_no_inherit = cdef->is_no_inherit;
cookedConstraints = lappend(cookedConstraints, cooked);
}
@ -2266,7 +2269,7 @@ AddRelationNewConstraints(Relation rel,
static bool
MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
bool allow_merge, bool is_local,
bool is_only)
bool is_no_inherit)
{
bool found;
Relation conDesc;
@ -2322,8 +2325,8 @@ MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
tup = heap_copytuple(tup);
con = (Form_pg_constraint) GETSTRUCT(tup);
/* If the constraint is "only" then cannot merge */
if (con->conisonly)
/* If the constraint is "no inherit" then cannot merge */
if (con->connoinherit)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("constraint \"%s\" conflicts with non-inherited constraint on relation \"%s\"",
@ -2333,10 +2336,10 @@ MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
con->conislocal = true;
else
con->coninhcount++;
if (is_only)
if (is_no_inherit)
{
Assert(is_local);
con->conisonly = true;
con->connoinherit = true;
}
/* OK to update the tuple */
ereport(NOTICE,

View File

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

View File

@ -67,7 +67,7 @@ CreateConstraintEntry(const char *constraintName,
const char *conSrc,
bool conIsLocal,
int conInhCount,
bool conIsOnly)
bool conNoInherit)
{
Relation conDesc;
Oid conOid;
@ -170,7 +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);
values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
if (conkeyArray)
values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);

View File

@ -601,7 +601,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;
cooked->is_no_inherit = false;
cookedDefaults = lappend(cookedDefaults, cooked);
descriptor->attrs[attnum - 1]->atthasdef = true;
}
@ -661,7 +661,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
*/
if (rawDefaults || stmt->constraints)
AddRelationNewConstraints(rel, rawDefaults, stmt->constraints,
true, true, false);
true, true);
/*
* Clean up. We keep lock on new relation (although it shouldn't be
@ -1655,7 +1655,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
Node *expr;
/* ignore if the constraint is non-inheritable */
if (check[i].cconly)
if (check[i].ccnoinherit)
continue;
/* adjust varattnos of ccbin here */
@ -1676,7 +1676,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
cooked->skip_validation = false;
cooked->is_local = false;
cooked->inhcount = 1;
cooked->is_only = false;
cooked->is_no_inherit = false;
constraints = lappend(constraints, cooked);
}
}
@ -2399,7 +2399,7 @@ rename_constraint_internal(Oid myrelid,
constraintOid);
con = (Form_pg_constraint) GETSTRUCT(tuple);
if (myrelid && con->contype == CONSTRAINT_CHECK && !con->conisonly)
if (myrelid && con->contype == CONSTRAINT_CHECK && !con->connoinherit)
{
if (recurse)
{
@ -4573,7 +4573,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, false);
AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, false, true);
/* Make the additional catalog changes visible */
CommandCounterIncrement();
@ -5015,7 +5015,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, false);
AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, false, true);
}
}
@ -5680,16 +5680,11 @@ 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, /* allow_merge */
!recursing, /* is_local */
!recurse && !recursing); /* is_only */
recursing, /* allow_merge */
!recursing); /* is_local */
/* Add each to-be-validated constraint to Phase 3's queue */
foreach(lcon, newcons)
@ -5730,9 +5725,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
return;
/*
* Adding an ONLY constraint? No need to find our children
* Adding a NO INHERIT constraint? No need to find our children
*/
if (!recurse && !recursing)
if (constr->is_no_inherit)
return;
/*
@ -5742,6 +5737,16 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
*/
children = find_inheritance_children(RelationGetRelid(rel), lockmode);
/*
* Check if ONLY was specified with ALTER TABLE. If so, allow the
* contraint creation only if there are no children currently. Error out
* otherwise.
*/
if (!recurse && children != NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("constraint must be added to child tables too")));
foreach(child, children)
{
Oid childrelid = lfirst_oid(child);
@ -6127,7 +6132,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
NULL,
true, /* islocal */
0, /* inhcount */
false); /* isonly */
false); /* isnoinherit */
/*
* Create the triggers that will enforce the constraint.
@ -6998,8 +7003,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
ScanKeyData key;
HeapTuple tuple;
bool found = false;
bool is_check_constraint = false;
bool is_only_constraint = false;
bool is_no_inherit_constraint = false;
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
@ -7033,15 +7037,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
constrName, RelationGetRelationName(rel))));
/* 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;
}
is_no_inherit_constraint = con->connoinherit;
/*
* Perform the actual constraint deletion
@ -7084,7 +7080,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 && !is_only_constraint)
if (!is_no_inherit_constraint)
children = find_inheritance_children(RelationGetRelid(rel), lockmode);
else
children = NIL;
@ -9250,8 +9246,8 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
if (parent_con->contype != CONSTRAINT_CHECK)
continue;
/* if the parent's constraint is marked ONLY, it's not inherited */
if (parent_con->conisonly)
/* if the parent's constraint is marked NO INHERIT, it's not inherited */
if (parent_con->connoinherit)
continue;
/* Search for a child constraint matching this one */
@ -9281,8 +9277,8 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
RelationGetRelationName(child_rel),
NameStr(parent_con->conname))));
/* If the constraint is "only" then cannot merge */
if (child_con->conisonly)
/* If the constraint is "no inherit" then cannot merge */
if (child_con->connoinherit)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",

View File

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

View File

@ -2340,6 +2340,7 @@ _copyConstraint(const Constraint *from)
COPY_SCALAR_FIELD(deferrable);
COPY_SCALAR_FIELD(initdeferred);
COPY_LOCATION_FIELD(location);
COPY_SCALAR_FIELD(is_no_inherit);
COPY_NODE_FIELD(raw_expr);
COPY_STRING_FIELD(cooked_expr);
COPY_NODE_FIELD(keys);

View File

@ -2198,6 +2198,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_SCALAR_FIELD(deferrable);
COMPARE_SCALAR_FIELD(initdeferred);
COMPARE_LOCATION_FIELD(location);
COMPARE_SCALAR_FIELD(is_no_inherit);
COMPARE_NODE_FIELD(raw_expr);
COMPARE_STRING_FIELD(cooked_expr);
COMPARE_NODE_FIELD(keys);

View File

@ -2607,6 +2607,7 @@ _outConstraint(StringInfo str, const Constraint *node)
case CONSTR_CHECK:
appendStringInfo(str, "CHECK");
WRITE_BOOL_FIELD(is_no_inherit);
WRITE_NODE_FIELD(raw_expr);
WRITE_STRING_FIELD(cooked_expr);
break;

View File

@ -420,7 +420,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
%type <str> character
%type <str> extract_arg
%type <str> opt_charset
%type <boolean> opt_varying opt_timezone
%type <boolean> opt_varying opt_timezone opt_no_inherit
%type <ival> Iconst SignedIconst
%type <str> Sconst comment_text notify_payload
@ -2685,12 +2685,13 @@ ColConstraintElem:
n->indexspace = $4;
$$ = (Node *)n;
}
| CHECK '(' a_expr ')'
| CHECK opt_no_inherit '(' a_expr ')'
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_CHECK;
n->location = @1;
n->raw_expr = $3;
n->is_no_inherit = $2;
n->raw_expr = $4;
n->cooked_expr = NULL;
$$ = (Node *)n;
}
@ -2810,14 +2811,15 @@ TableConstraint:
;
ConstraintElem:
CHECK '(' a_expr ')' ConstraintAttributeSpec
CHECK opt_no_inherit '(' a_expr ')' ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_CHECK;
n->location = @1;
n->raw_expr = $3;
n->is_no_inherit = $2;
n->raw_expr = $4;
n->cooked_expr = NULL;
processCASbits($5, @5, "CHECK",
processCASbits($6, @6, "CHECK",
NULL, NULL, &n->skip_validation,
yyscanner);
n->initially_valid = !n->skip_validation;
@ -2920,6 +2922,10 @@ ConstraintElem:
}
;
opt_no_inherit: NO INHERIT { $$ = TRUE; }
| /* EMPTY */ { $$ = FALSE; }
;
opt_column_list:
'(' columnList ')' { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }

View File

@ -1332,7 +1332,10 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
prettyFlags, 0);
/*
* Now emit the constraint definition. There are cases where
* Now emit the constraint definition, adding NO INHERIT if
* necessary.
*
* There are cases where
* the constraint expression will be fully parenthesized and
* we don't need the outer parens ... but there are other
* cases where we do need 'em. Be conservative for now.
@ -1340,7 +1343,9 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
* Note that simply checking for leading '(' and trailing ')'
* would NOT be good enough, consider "(x > 0) AND (y > 0)".
*/
appendStringInfo(&buf, "CHECK (%s)", consrc);
appendStringInfo(&buf, "CHECK %s(%s)",
conForm->connoinherit ? "NO INHERIT " : "",
consrc);
break;
}

View File

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

View File

@ -5960,13 +5960,12 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
if (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).
* convalidated is new in 9.2 (actually, it 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, conisonly "
"conislocal, convalidated "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
@ -5975,10 +5974,10 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
}
else if (fout->remoteVersion >= 80400)
{
/* conislocal is new in 8.4 */
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
"conislocal, true AS convalidated, "
"false as conisonly "
"conislocal, true AS convalidated "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
@ -5989,8 +5988,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
{
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
"true AS conislocal, true AS convalidated, "
"false as conisonly "
"true AS conislocal, true AS convalidated "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
@ -6002,8 +6000,7 @@ getTableAttrs(Archive *fout, 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, "
"false as conisonly "
"true AS conislocal, true AS convalidated "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
@ -6016,8 +6013,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
appendPQExpBuffer(q, "SELECT tableoid, 0 AS oid, "
"rcname AS conname, "
"'CHECK (' || rcsrc || ')' AS consrc, "
"true AS conislocal, true AS convalidated, "
"false as conisonly "
"true AS conislocal, true AS convalidated "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
@ -6028,8 +6024,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
appendPQExpBuffer(q, "SELECT tableoid, oid, "
"rcname AS conname, "
"'CHECK (' || rcsrc || ')' AS consrc, "
"true AS conislocal, true AS convalidated, "
"false as conisonly "
"true AS conislocal, true AS convalidated "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
@ -6042,8 +6037,7 @@ getTableAttrs(Archive *fout, 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, "
"false as conisonly "
"true AS conislocal, true AS convalidated "
"FROM pg_relcheck "
"WHERE rcrelid = '%u'::oid "
"ORDER BY rcname",
@ -6068,7 +6062,6 @@ getTableAttrs(Archive *fout, 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));
@ -6085,14 +6078,12 @@ getTableAttrs(Archive *fout, 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. An ONLY constraint needs to be dumped
* separately too.
* the constraint.
*/
constrs[j].separate = !validated || isonly;
constrs[j].separate = !validated;
constrs[j].dobj.dump = tbinfo->dobj.dump;
@ -13048,9 +13039,9 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
/* Ignore if not to be dumped separately */
if (coninfo->separate)
{
/* 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));
/* not ONLY since we want it to propagate to children */
appendPQExpBuffer(q, "ALTER TABLE %s\n",
fmtId(tbinfo->dobj.name));
appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
fmtId(coninfo->dobj.name),
coninfo->condef);

View File

@ -370,7 +370,6 @@ 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

@ -1786,20 +1786,13 @@ 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, %s, "
"SELECT r.conname, "
"pg_catalog.pg_get_constraintdef(r.oid, true)\n"
"FROM pg_catalog.pg_constraint r\n"
"WHERE r.conrelid = '%s' AND r.contype = 'c'\n"
"ORDER BY 2 DESC, 1;",
is_only, oid);
"WHERE r.conrelid = '%s' AND r.contype = 'c'\n"
"ORDER BY 1;",
oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
@ -1812,10 +1805,9 @@ describeOneTableDetails(const char *schemaname,
for (i = 0; i < tuples; i++)
{
/* untranslated contraint name and def */
printfPQExpBuffer(&buf, " \"%s\"%s%s",
printfPQExpBuffer(&buf, " \"%s\" %s",
PQgetvalue(result, i, 0),
(strcmp(PQgetvalue(result, i, 1), "t") == 0) ? " (ONLY) ":" ",
PQgetvalue(result, i, 2));
PQgetvalue(result, i, 1));
printTableAddFooter(&cont, buf.data);
}

View File

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

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201204141
#define CATALOG_VERSION_NO 201204201
#endif

View File

@ -33,7 +33,8 @@ 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 */
bool is_no_inherit; /* constraint has local def and cannot be
* inherited */
} CookedConstraint;
extern Relation heap_create(const char *relname,
@ -92,8 +93,7 @@ extern List *AddRelationNewConstraints(Relation rel,
List *newColDefaults,
List *newConstraints,
bool allow_merge,
bool is_local,
bool is_only);
bool is_local);
extern void StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr);

View File

@ -89,7 +89,7 @@ CATALOG(pg_constraint,2606)
int4 coninhcount;
/* Has a local definition and cannot be inherited */
bool conisonly;
bool connoinherit;
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/*
@ -166,7 +166,7 @@ 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_conisonly 16
#define Anum_pg_constraint_connoinherit 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
#define Anum_pg_constraint_conpfeqop 19

View File

@ -1528,6 +1528,7 @@ typedef struct Constraint
int location; /* token location, or -1 if unknown */
/* Fields used for constraints with expressions (CHECK and DEFAULT): */
bool is_no_inherit; /* is constraint non-inheritable? */
Node *raw_expr; /* expr, as untransformed parse tree */
char *cooked_expr; /* expr, as nodeToString representation */

View File

@ -235,7 +235,7 @@ Check constraints:
"con1foo" CHECK (a > 0)
Inherits: constraint_rename_test
ALTER TABLE ONLY constraint_rename_test ADD CONSTRAINT con2 CHECK (b > 0);
ALTER TABLE constraint_rename_test ADD CONSTRAINT con2 CHECK NO INHERIT (b > 0);
ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con2 TO con2foo; -- ok
ALTER TABLE constraint_rename_test RENAME CONSTRAINT con2foo TO con2bar; -- ok
\d constraint_rename_test
@ -246,8 +246,8 @@ Table "public.constraint_rename_test"
b | integer |
c | integer |
Check constraints:
"con2bar" (ONLY) CHECK (b > 0)
"con1foo" CHECK (a > 0)
"con2bar" CHECK NO INHERIT (b > 0)
Number of child tables: 1 (Use \d+ to list them.)
\d constraint_rename_test2
@ -275,8 +275,8 @@ Table "public.constraint_rename_test"
Indexes:
"con3foo" PRIMARY KEY, btree (a)
Check constraints:
"con2bar" (ONLY) CHECK (b > 0)
"con1foo" CHECK (a > 0)
"con2bar" CHECK NO INHERIT (b > 0)
Number of child tables: 1 (Use \d+ to list them.)
\d constraint_rename_test2
@ -643,7 +643,7 @@ drop table atacc1;
create table atacc1 (test int);
create table atacc2 (test2 int) inherits (atacc1);
-- ok:
alter table only atacc1 add constraint foo check (test>0);
alter table atacc1 add constraint foo check no inherit (test>0);
-- check constraint is not there on child
insert into atacc2 (test) values (-3);
-- check constraint is there on parent
@ -652,7 +652,7 @@ 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);
alter table atacc2 add constraint foo check no inherit (test>0);
ERROR: check constraint "foo" is violated by some row
drop table atacc2;
drop table atacc1;

View File

@ -598,17 +598,17 @@ 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 p1chk check no inherit (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' order by 1,2;
relname | conname | contype | conislocal | coninhcount | conisonly
---------+---------+---------+------------+-------------+-----------
-- connoinherit should be true for NO INHERIT constraint
select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.connoinherit from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname = 'p1' order by 1,2;
relname | conname | contype | conislocal | coninhcount | connoinherit
---------+---------+---------+------------+-------------+--------------
p1 | p1chk | c | t | 0 | t
p1 | p2chk | c | t | 0 | f
(2 rows)
-- Test that child does not inherit ONLY constraints
-- Test that child does not inherit NO INHERIT constraints
create table c1 () inherits (p1);
\d p1
Table "public.p1"
@ -616,7 +616,7 @@ create table c1 () inherits (p1);
--------+---------+-----------
ff1 | integer |
Check constraints:
"p1chk" (ONLY) CHECK (ff1 > 0)
"p1chk" CHECK NO INHERIT (ff1 > 0)
"p2chk" CHECK (ff1 > 10)
Number of child tables: 1 (Use \d+ to list them.)

View File

@ -143,6 +143,34 @@ SELECT * FROM INSERT_CHILD;
DROP TABLE INSERT_CHILD;
--
-- Check NO INHERIT type of constraints and inheritance
--
CREATE TABLE ATACC1 (TEST INT
CHECK NO INHERIT (TEST > 0));
CREATE TABLE ATACC2 (TEST2 INT) INHERITS (ATACC1);
-- 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);
DROP TABLE ATACC1 CASCADE;
CREATE TABLE ATACC1 (TEST INT, TEST2 INT
CHECK (TEST > 0), CHECK NO INHERIT (TEST2 > 10));
CREATE TABLE ATACC2 () INHERITS (ATACC1);
-- check constraint is there on child
INSERT INTO ATACC2 (TEST) VALUES (-3);
-- check constraint is there on parent
INSERT INTO ATACC1 (TEST) VALUES (-3);
-- check constraint is not there on child
INSERT INTO ATACC2 (TEST2) VALUES (3);
-- check constraint is there on parent
INSERT INTO ATACC1 (TEST2) VALUES (3);
DROP TABLE ATACC1 CASCADE;
--
-- Check constraints on INSERT INTO
--

View File

@ -228,6 +228,39 @@ SELECT * FROM INSERT_CHILD;
DROP TABLE INSERT_CHILD;
--
-- Check NO INHERIT type of constraints and inheritance
--
CREATE TABLE ATACC1 (TEST INT
CHECK NO INHERIT (TEST > 0));
CREATE TABLE ATACC2 (TEST2 INT) INHERITS (ATACC1);
-- 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);
ERROR: new row for relation "atacc1" violates check constraint "atacc1_test_check"
DETAIL: Failing row contains (-3).
DROP TABLE ATACC1 CASCADE;
NOTICE: drop cascades to table atacc2
CREATE TABLE ATACC1 (TEST INT, TEST2 INT
CHECK (TEST > 0), CHECK NO INHERIT (TEST2 > 10));
CREATE TABLE ATACC2 () INHERITS (ATACC1);
-- check constraint is there on child
INSERT INTO ATACC2 (TEST) VALUES (-3);
ERROR: new row for relation "atacc2" violates check constraint "atacc1_test_check"
DETAIL: Failing row contains (-3, null).
-- check constraint is there on parent
INSERT INTO ATACC1 (TEST) VALUES (-3);
ERROR: new row for relation "atacc1" violates check constraint "atacc1_test_check"
DETAIL: Failing row contains (-3, null).
-- check constraint is not there on child
INSERT INTO ATACC2 (TEST2) VALUES (3);
-- check constraint is there on parent
INSERT INTO ATACC1 (TEST2) VALUES (3);
ERROR: new row for relation "atacc1" violates check constraint "atacc1_test2_check"
DETAIL: Failing row contains (null, 3).
DROP TABLE ATACC1 CASCADE;
NOTICE: drop cascades to table atacc2
--
-- Check constraints on INSERT INTO
--
DELETE FROM INSERT_TBL;

View File

@ -218,7 +218,7 @@ ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- fa
ALTER TABLE constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- ok
\d constraint_rename_test
\d constraint_rename_test2
ALTER TABLE ONLY constraint_rename_test ADD CONSTRAINT con2 CHECK (b > 0);
ALTER TABLE constraint_rename_test ADD CONSTRAINT con2 CHECK NO INHERIT (b > 0);
ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con2 TO con2foo; -- ok
ALTER TABLE constraint_rename_test RENAME CONSTRAINT con2foo TO con2bar; -- ok
\d constraint_rename_test
@ -500,14 +500,14 @@ drop table atacc1;
create table atacc1 (test int);
create table atacc2 (test2 int) inherits (atacc1);
-- ok:
alter table only atacc1 add constraint foo check (test>0);
alter table atacc1 add constraint foo check no inherit (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);
-- fail, violating row:
alter table only atacc2 add constraint foo check (test>0);
alter table atacc2 add constraint foo check no inherit (test>0);
drop table atacc2;
drop table atacc1;

View File

@ -140,12 +140,12 @@ 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 p1chk check no inherit (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' order by 1,2;
-- connoinherit should be true for NO INHERIT constraint
select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.connoinherit from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname = 'p1' order by 1,2;
-- Test that child does not inherit ONLY constraints
-- Test that child does not inherit NO INHERIT constraints
create table c1 () inherits (p1);
\d p1
\d c1