diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index c3039c8167..3af65accea 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -907,7 +907,8 @@ ALTER TABLE [ IF EXISTS ] name and TABLESPACE actions never recurse to descendant tables; that is, they always act as though ONLY were specified. Adding a constraint can recurse only for 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 NO INHERIT. @@ -1013,7 +1014,7 @@ ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5); To add a check constraint only to a table and not to its children: -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); (The check constraint will not be inherited by future children, either.) diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index bb93214210..4da6eea758 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -47,7 +47,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI [ CONSTRAINT constraint_name ] { NOT NULL | NULL | - CHECK ( expression ) | + CHECK [ NO INHERIT ] ( expression ) | DEFAULT default_expr | UNIQUE index_parameters | PRIMARY KEY index_parameters | @@ -58,7 +58,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI and table_constraint is: [ CONSTRAINT constraint_name ] -{ CHECK ( expression ) | +{ CHECK [ NO INHERIT ] ( expression ) | UNIQUE ( column_name [, ... ] ) index_parameters | PRIMARY KEY ( column_name [, ... ] ) index_parameters | EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] | @@ -299,7 +299,8 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI contain identically-named 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 CHECK + be merged into one copy. A constraint marked NO INHERIT in a + parent will not be considered. Notice that an unnamed CHECK constraint in the new table will never be merged, since a unique name will always be chosen for it. @@ -415,7 +416,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI - CHECK ( expression ) + CHECK [ NO INHERIT ] ( expression ) The 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. + + + A constraint marked with NO INHERIT will not propagate to + children tables. + diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 8bd5a9296e..d029a25c11 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -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, diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 3fd0e60457..998379c8af 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -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. diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index bf174b6a46..dca5d09ee6 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -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); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index a4a36045df..3f0945b57d 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -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\"", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index a98d1b884e..1218d033d1 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -459,7 +459,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, NULL, true, /* islocal */ 0, /* inhcount */ - false); /* isonly */ + false); /* isnoinherit */ } /* diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index fc396c5d30..0db60b161b 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index b0460970d5..9d588feac2 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index dbed70b21d..e690194b74 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -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; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index ae1658a4ff..a289d4bd14 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -420,7 +420,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type character %type extract_arg %type opt_charset -%type opt_varying opt_timezone +%type opt_varying opt_timezone opt_no_inherit %type Iconst SignedIconst %type 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; } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 64ba8ec891..3beed37dd2 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -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; } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 9cadb3f21a..7f0e20ec17 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -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)); diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index db56910691..58fa92935b 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -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); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index fba69532ab..5d6125c425 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -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; diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 844a2912bb..ffaaf4049f 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -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); } diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index 0df47a03e2..953e146d5e 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -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 */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index a43491503a..88383f1e77 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201204141 +#define CATALOG_VERSION_NO 201204201 #endif diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index 2055382096..c0deab73ae 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -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); diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index 22d65951bd..3a77124b00 100644 --- a/src/include/catalog/pg_constraint.h +++ b/src/include/catalog/pg_constraint.h @@ -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 diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index aaa950db26..13b95e11aa 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -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 */ diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index eba0493089..890a51fd9e 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -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; diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 92a64c8dba..6613fea84a 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -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.) diff --git a/src/test/regress/input/constraints.source b/src/test/regress/input/constraints.source index b84d51e9e5..37d06b0127 100644 --- a/src/test/regress/input/constraints.source +++ b/src/test/regress/input/constraints.source @@ -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 -- diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source index 3e02e8dbd4..f37c9b1369 100644 --- a/src/test/regress/output/constraints.source +++ b/src/test/regress/output/constraints.source @@ -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; diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index 50c58d23e1..5c03123b4e 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -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; diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql index 43f910f24a..6e6921aa9c 100644 --- a/src/test/regress/sql/inherit.sql +++ b/src/test/regress/sql/inherit.sql @@ -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