diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 5e5f8a7..683ad67 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1995,6 +1995,16 @@ + conisonly + bool + + + This constraint is defined locally for the relation. It is a + non-inheritable constraint. + + + + conkey int2[] pg_attribute.attnum diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 4c2a4cd..3ee3ec0 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -984,6 +984,14 @@ 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); + + (The check constraint will not be inherited by future children too.) + + + To remove a check constraint from a table and all its children: ALTER TABLE distributors DROP CONSTRAINT zipchk; diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 4399493..1b382b8 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -98,10 +98,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, @@ -1860,7 +1860,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; @@ -1943,7 +1943,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); @@ -1984,7 +1985,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: @@ -2100,6 +2101,7 @@ AddRelationNewConstraints(Relation rel, cooked->skip_validation = false; cooked->is_local = is_local; cooked->inhcount = is_local ? 0 : 1; + cooked->is_only = false; cookedConstraints = lappend(cookedConstraints, cooked); } @@ -2167,7 +2169,7 @@ AddRelationNewConstraints(Relation rel, * what ATAddCheckConstraint wants.) */ if (MergeWithExistingConstraint(rel, ccname, expr, - allow_merge, is_local)) + allow_merge, is_local, cdef->is_only)) continue; } else @@ -2214,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, cdef->is_only); numchecks++; @@ -2226,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 = cdef->is_only; cookedConstraints = lappend(cookedConstraints, cooked); } @@ -2251,7 +2254,7 @@ 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; @@ -2313,6 +2316,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; diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 75b4c14..477cad3 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1160,7 +1160,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. diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 6997994..cfe82ea 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -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); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 82bb756..ac2368f 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -569,6 +569,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; } @@ -1566,6 +1567,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); @@ -1584,6 +1589,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); } } @@ -5433,6 +5439,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, ListCell *lcon; List *children; ListCell *child; + bool skip_children = false; /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) @@ -5499,12 +5506,26 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, /* * If we are told not to recurse, there had better not be any child - * tables; else the addition would put them out of step. + * tables; else the addition would put them out of step. Unless these are + * ONLY type of constraints of course. */ if (children && !recurse) - ereport(ERROR, + { + foreach(lcon, newcons) + { + CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon); + + if (ccon->is_only) + skip_children = true; + else + skip_children = false; + } + + if (!skip_children) + ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("constraint must be added to child tables too"))); + } foreach(child, children) { @@ -5512,6 +5533,13 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Relation childrel; AlteredTableInfo *childtab; + /* + * Skipping the constraint should be good enough for the special case. + * No need to even release the locks on the children immediately.. + */ + if (skip_children) + break; + /* find_inheritance_children already got lock */ childrel = heap_open(childrelid, NoLock); CheckTableNotInUse(childrel, "ALTER TABLE"); @@ -5799,7 +5827,8 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, NULL, NULL, true, /* islocal */ - 0); /* inhcount */ + 0, /* inhcount */ + false); /* isonly */ /* * Create the triggers that will enforce the constraint. diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 4c31f19..d24fb6b 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -451,7 +451,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, NULL, NULL, true, /* islocal */ - 0); /* inhcount */ + 0, /* inhcount */ + false); /* isonly */ } /* diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 7c27f85..a6c1ab3 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -2542,8 +2542,9 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, expr, /* Tree form of check constraint */ ccbin, /* Binary form of check constraint */ ccsrc, /* Source form of check constraint */ - true, /* is local */ - 0); /* inhcount */ + true, /* is local */ + 0, /* inhcount */ + false); /* is only */ /* * Return the compiled constraint expression so the calling routine can diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 7a51456..e04d082 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2328,6 +2328,7 @@ _copyConstraint(Constraint *from) COPY_LOCATION_FIELD(location); COPY_NODE_FIELD(raw_expr); COPY_STRING_FIELD(cooked_expr); + COPY_SCALAR_FIELD(is_only); COPY_NODE_FIELD(keys); COPY_NODE_FIELD(exclusions); COPY_NODE_FIELD(options); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 4052a9a..4e13451 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -2257,6 +2257,7 @@ _equalConstraint(Constraint *a, Constraint *b) COMPARE_LOCATION_FIELD(location); COMPARE_NODE_FIELD(raw_expr); COMPARE_STRING_FIELD(cooked_expr); + COMPARE_SCALAR_FIELD(is_only); COMPARE_NODE_FIELD(keys); COMPARE_NODE_FIELD(exclusions); COMPARE_NODE_FIELD(options); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b5be09a..f0d65b5 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2585,6 +2585,8 @@ _outConstraint(StringInfo str, Constraint *node) break; case CONSTR_CHECK: + if (node->is_only) + appendStringInfo(str, " (ONLY) "); appendStringInfo(str, "CHECK"); WRITE_NODE_FIELD(raw_expr); WRITE_STRING_FIELD(cooked_expr); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index ac094aa..df0af9d 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -1621,6 +1621,24 @@ AlterTableStmt: n->relation = $3; n->cmds = $4; n->relkind = OBJECT_TABLE; + /* Check if ONLY was used in the relation expr */ + if (n->relation->inhOpt == INH_NO) + { + ListCell *lcmd; + foreach(lcmd, n->cmds) + { + AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd); + + /* mark check constraints as non-inheritable */ + if (cmd->subtype == AT_AddConstraint) + { + Constraint *n = (Constraint *)cmd->def; + + if (n->contype == CONSTR_CHECK) + n->is_only = true; + } + } + } $$ = (Node *)n; } | ALTER INDEX qualified_name alter_table_cmds @@ -2625,6 +2643,7 @@ ColConstraintElem: n->location = @1; n->raw_expr = $3; n->cooked_expr = NULL; + n->is_only = false; $$ = (Node *)n; } | DEFAULT b_expr @@ -2758,6 +2777,7 @@ ConstraintElem: n->location = @1; n->raw_expr = $3; n->cooked_expr = NULL; + n->is_only = false; processCASbits($5, @5, "CHECK", NULL, NULL, &n->skip_validation, yyscanner); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 809222b..940f122 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -3251,6 +3251,7 @@ CheckConstraintFetch(Relation relation) RelationGetRelationName(relation)); check[found].ccvalid = conform->convalidated; + check[found].cconly = conform->conisonly; 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 f2ee57c..7f40948 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -5895,11 +5895,22 @@ getTableAttrs(TableInfo *tblinfo, int numTables) tbinfo->dobj.name); resetPQExpBuffer(q); - if (g_fout->remoteVersion >= 80400) + if (g_fout->remoteVersion > 90100) { appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " "pg_catalog.pg_get_constraintdef(oid) AS consrc, " - "conislocal " + "conislocal, conisonly " + "FROM pg_catalog.pg_constraint " + "WHERE conrelid = '%u'::pg_catalog.oid " + " AND contype = 'c' " + "ORDER BY conname", + tbinfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 80400) + { + appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " + "pg_catalog.pg_get_constraintdef(oid) AS consrc, " + "conislocal, false AS conisonly " "FROM pg_catalog.pg_constraint " "WHERE conrelid = '%u'::pg_catalog.oid " " AND contype = 'c' " @@ -5910,7 +5921,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) { appendPQExpBuffer(q, "SELECT tableoid, oid, conname, " "pg_catalog.pg_get_constraintdef(oid) AS consrc, " - "true AS conislocal " + "true AS conislocal, false AS conisonly " "FROM pg_catalog.pg_constraint " "WHERE conrelid = '%u'::pg_catalog.oid " " AND contype = 'c' " @@ -5922,7 +5933,7 @@ 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 conislocal, false AS conisonly " "FROM pg_catalog.pg_constraint " "WHERE conrelid = '%u'::pg_catalog.oid " " AND contype = 'c' " @@ -5935,7 +5946,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) appendPQExpBuffer(q, "SELECT tableoid, 0 AS oid, " "rcname AS conname, " "'CHECK (' || rcsrc || ')' AS consrc, " - "true AS conislocal " + "true AS conislocal, false AS conisonly " "FROM pg_relcheck " "WHERE rcrelid = '%u'::oid " "ORDER BY rcname", @@ -5946,7 +5957,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) appendPQExpBuffer(q, "SELECT tableoid, oid, " "rcname AS conname, " "'CHECK (' || rcsrc || ')' AS consrc, " - "true AS conislocal " + "true AS conislocal, false AS conisonly " "FROM pg_relcheck " "WHERE rcrelid = '%u'::oid " "ORDER BY rcname", @@ -5959,7 +5970,7 @@ 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 conislocal, false AS conisonly " "FROM pg_relcheck " "WHERE rcrelid = '%u'::oid " "ORDER BY rcname", @@ -5999,7 +6010,11 @@ getTableAttrs(TableInfo *tblinfo, int numTables) constrs[j].condeferrable = false; constrs[j].condeferred = false; constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't'); - constrs[j].separate = false; + constrs[j].conisonly = (PQgetvalue(res, j, 5)[0] == 't'); + if (constrs[j].conisonly) + constrs[j].separate = true; + else + constrs[j].separate = false; constrs[j].dobj.dump = tbinfo->dobj.dump; @@ -6008,8 +6023,15 @@ getTableAttrs(TableInfo *tblinfo, int numTables) * --- this is so that any other dependencies of the * constraint will be emitted before we try to create the * table. + * + * But if it is an ONLY object, the constraint has to appear + * after the create table. */ - addObjectDependency(&tbinfo->dobj, + if (constrs[j].conisonly) + addObjectDependency(&constrs[j].dobj, + tbinfo->dobj.dumpId); + else + addObjectDependency(&tbinfo->dobj, constrs[j].dobj.dumpId); /* @@ -12823,9 +12845,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); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index c95614b..ee25311 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -376,6 +376,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; diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index b50c5d6..00c2051 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -1729,12 +1729,18 @@ describeOneTableDetails(const char *schemaname, /* print table (and column) check constraints */ if (tableinfo.checks) { + char *is_only; + if (pset.sversion > 90100) + is_only = "r.conisonly, "; + else + is_only = "false AS r.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); + is_only, oid); result = PSQLexec(buf.data, false); if (!result) goto error_return; @@ -1747,9 +1753,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); } diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index 8b99cb8..d5e1333 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -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 */ diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index aee2d88..d3a588f 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -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, diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index 1566af2..b8fb01d 100644 --- a/src/include/catalog/pg_constraint.h +++ b/src/include/catalog/pg_constraint.h @@ -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. */ @@ -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); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 92e40d3..d3dc246 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1522,6 +1522,7 @@ typedef struct Constraint /* Fields used for constraints with expressions (CHECK and DEFAULT): */ Node *raw_expr; /* expr, as untransformed parse tree */ char *cooked_expr; /* expr, as nodeToString representation */ + bool is_only; /* has local definition, cannot be inherited */ /* Fields used for unique constraints (UNIQUE and PRIMARY KEY): */ List *keys; /* String nodes naming referenced column(s) */ diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 005a88b..d6c1bc1 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -493,16 +493,16 @@ 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: +-- ok: 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 +-- check constraint is there on parent insert into atacc1 (test) values (-3); +ERROR: new row for relation "atacc1" violates check constraint "foo" insert into atacc1 (test) values (3); -- check constraint is there on child insert into atacc2 (test) values (-3); diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index d59ca44..16abada 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -681,6 +681,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); diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index 95e898c..61d63d5 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -450,15 +450,15 @@ 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: +-- ok: 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 +-- check constraint is there on parent insert into atacc1 (test) values (-3); insert into atacc1 (test) values (3); -- check constraint is there on child diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql index 3087a14..9e992ab 100644 --- a/src/test/regress/sql/inherit.sql +++ b/src/test/regress/sql/inherit.sql @@ -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);