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);