Hi all, PFA, patch which implements non-inheritable "ONLY" constraints. This has been achieved by introducing a new column "conisonly" in pg_constraint catalog. Specification of 'ONLY' in the ALTER TABLE ADD CONSTRAINT CHECK command is used to set this new column to true. Constraints which have this column set to true cannot be inherited by present and future children ever.
The psql and pg_dump binaries have been modified to account for such persistent non-inheritable check constraints. This patch also has documentation changes along with relevant changes to the test cases. The regression runs pass fine with this patch applied. Comments and further feedback, if any, appreciated. Regards, Nikhils
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 @@ </row> <row> + <entry><structfield>conisonly</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry> + This constraint is defined locally for the relation. It is a + non-inheritable constraint. + </entry> + </row> + + <row> <entry><structfield>conkey</structfield></entry> <entry><type>int2[]</type></entry> <entry><literal><link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.attnum</></entry> 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); </para> <para> + To add a check constraint only to a table and not to its children: +<programlisting> +ALTER TABLE ONLY distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5); +</programlisting> + (The check constraint will not be inherited by future children too.) + </para> + + <para> To remove a check constraint from a table and all its children: <programlisting> 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);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers