]> granicus.if.org Git - postgresql/commitdiff
Change the rules for inherited CHECK constraints to be essentially the same
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 9 May 2008 23:32:05 +0000 (23:32 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 9 May 2008 23:32:05 +0000 (23:32 +0000)
as those for inherited columns; that is, it's no longer allowed for a child
table to not have a check constraint matching one that exists on a parent.
This satisfies the principle of least surprise (rows selected from the parent
will always appear to meet its check constraints) and eliminates some
longstanding bogosity in pg_dump, which formerly had to guess about whether
check constraints were really inherited or not.

The implementation involves adding conislocal and coninhcount columns to
pg_constraint (paralleling attislocal and attinhcount in pg_attribute)
and refactoring various ALTER TABLE actions to be more like those for
columns.

Alex Hunsaker, Nikhil Sontakke, Tom Lane

25 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/ddl.sgml
doc/src/sgml/ref/alter_table.sgml
doc/src/sgml/ref/create_table.sgml
src/backend/access/common/tupdesc.c
src/backend/bootstrap/bootparse.y
src/backend/catalog/heap.c
src/backend/catalog/index.c
src/backend/catalog/pg_constraint.c
src/backend/catalog/toasting.c
src/backend/commands/cluster.c
src/backend/commands/tablecmds.c
src/backend/commands/typecmds.c
src/backend/executor/execMain.c
src/bin/pg_dump/common.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/include/catalog/catversion.h
src/include/catalog/heap.h
src/include/catalog/pg_constraint.h
src/include/nodes/parsenodes.h
src/test/regress/expected/alter_table.out
src/test/regress/expected/inherit.out
src/test/regress/sql/alter_table.sql
src/test/regress/sql/inherit.sql

index f43c46908c0add7622ed37b56fc3f64ecf000e7d..d0b34789cf157ada1bc7f7929122956915503fa5 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.165 2008/04/14 17:05:32 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.166 2008/05/09 23:32:03 tgl Exp $ -->
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
  -->
       <entry>Foreign key match type</entry>
      </row>
 
+     <row>
+      <entry><structfield>conislocal</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry></entry>
+      <entry>
+       This constraint is defined locally in the relation.  Note that a
+       constraint can be locally defined and inherited simultaneously
+      </entry>
+     </row>
+
+     <row>
+      <entry><structfield>coninhcount</structfield></entry>
+      <entry><type>int4</type></entry>
+      <entry></entry>
+      <entry>
+       The number of direct ancestors this constraint has.  A constraint with
+       a nonzero number of ancestors cannot be dropped nor renamed
+      </entry>
+     </row>
+
      <row>
       <entry><structfield>conkey</structfield></entry>
       <entry><type>int2[]</type></entry>
index 98233866300c6e3c50d1d16e310f2c842d7bd03a..183d1e89f5ff7119317176bd86e0ac13be6e9ef5 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/ddl.sgml,v 1.81 2008/01/13 17:58:54 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/ddl.sgml,v 1.82 2008/05/09 23:32:03 tgl Exp $ -->
 
 <chapter id="ddl">
  <title>Data Definition</title>
@@ -2107,7 +2107,8 @@ VALUES ('New York', NULL, NULL, 'NY');
 
   <para>
    A parent table cannot be dropped while any of its children remain. Neither
-   can columns of child tables be dropped or altered if they are inherited
+   can columns or check constraints of child tables be dropped or altered
+   if they are inherited
    from any parent tables. If you wish to remove a table and all of its
    descendants, one easy way is to drop the parent table with the
    <literal>CASCADE</literal> option.
@@ -2117,7 +2118,7 @@ VALUES ('New York', NULL, NULL, 'NY');
    <xref linkend="sql-altertable" endterm="sql-altertable-title"> will
    propagate any changes in column data definitions and check
    constraints down the inheritance hierarchy.  Again, dropping
-   columns or constraints on parent tables is only possible when using
+   columns that are depended on by other tables is only possible when using
    the <literal>CASCADE</literal> option. <command>ALTER
    TABLE</command> follows the same rules for duplicate column merging
    and rejection that apply during <command>CREATE TABLE</command>.
index d6bf184d649e54ee867136941a456b8e86355415..ab929728a7ff5c7647bc214936ecd672a640dd09 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.98 2007/11/28 15:42:31 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.99 2008/05/09 23:32:03 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -713,7 +713,8 @@ ALTER TABLE table ALTER COLUMN anycol TYPE anytype;
     The <literal>TRIGGER</>, <literal>CLUSTER</>, <literal>OWNER</>,
     and <literal>TABLESPACE</> actions never recurse to descendant tables;
     that is, they always act as though <literal>ONLY</> were specified.
-    Adding a constraint can recurse only for <literal>CHECK</> constraints.
+    Adding a constraint can recurse only for <literal>CHECK</> constraints,
+    and is required to do so for such constraints.
    </para>
 
    <para>
@@ -804,7 +805,7 @@ ALTER TABLE distributors ALTER COLUMN street DROP NOT NULL;
   </para>
 
   <para> 
-   To add a check constraint to a table:
+   To add a check constraint to a table and all its children:
 <programlisting>
 ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
 </programlisting>
@@ -817,6 +818,14 @@ ALTER TABLE distributors DROP CONSTRAINT zipchk;
 </programlisting>
   </para>
 
+  <para> 
+   To remove a check constraint from a table only:
+<programlisting>
+ALTER TABLE ONLY distributors DROP CONSTRAINT zipchk;
+</programlisting>
+   (The check constraint remains in place for any child tables.)
+  </para>
+
   <para> 
    To add a foreign key constraint to a table:
 <programlisting>
index 68e8f045e6aaf454f9829dbec75011c34e31f3dd..ef28a8d21529046a6d3251246c6b1505622fcc7c 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.109 2007/07/17 05:02:00 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.110 2008/05/09 23:32:04 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -210,16 +210,25 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
       the new table.  If the column name list of the new table
       contains a column name that is also inherited, the data type must
       likewise match the inherited column(s), and the column
-      definitions are merged into one.  However, inherited and new
-      column declarations of the same name need not specify identical
-      constraints: all constraints provided from any declaration are
-      merged together and all are applied to the new table.  If the
+      definitions are merged into one.  If the
       new table explicitly specifies a default value for the column,
       this default overrides any defaults from inherited declarations
       of the column.  Otherwise, any parents that specify default
       values for the column must all specify the same default, or an
       error will be reported.
      </para>
+
+     <para>
+      <literal>CHECK</> constraints are merged in essentially the same way as
+      columns: if multiple parent tables and/or the new table definition
+      contain identically-named <literal>CHECK</> constraints, these
+      constraints must all have the same check expression, or an error will be
+      reported.  Constraints having the same name and expression will
+      be merged into one copy.  Notice that an unnamed <literal>CHECK</>
+      constraint in the new table will never be merged, since a unique name
+      will always be chosen for it.
+     </para>
+
 <!--
      <para>
       <productname>PostgreSQL</> automatically allows the
index 7f8df9f6829f0bcac73086125c561ca4694edf06..1d5a98c069a70730218927f78ef7ccb59a0dbb92 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.122 2008/01/01 19:45:46 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.123 2008/05/09 23:32:04 tgl Exp $
  *
  * NOTES
  *       some of the executor utility code such as "ExecTypeFromTL" should be
@@ -505,20 +505,18 @@ BuildDescForRelation(List *schema)
        AttrNumber      attnum;
        ListCell   *l;
        TupleDesc       desc;
-       AttrDefault *attrdef = NULL;
-       TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
+       bool            has_not_null;
        char       *attname;
        Oid                     atttypid;
        int32           atttypmod;
        int                     attdim;
-       int                     ndef = 0;
 
        /*
         * allocate a new tuple descriptor
         */
        natts = list_length(schema);
        desc = CreateTemplateTupleDesc(natts, false);
-       constr->has_not_null = false;
+       has_not_null = false;
 
        attnum = 0;
 
@@ -547,52 +545,25 @@ BuildDescForRelation(List *schema)
                                                   atttypid, atttypmod, attdim);
 
                /* Fill in additional stuff not handled by TupleDescInitEntry */
-               if (entry->is_not_null)
-                       constr->has_not_null = true;
                desc->attrs[attnum - 1]->attnotnull = entry->is_not_null;
-
-               /*
-                * Note we copy only pre-cooked default expressions. Digestion of raw
-                * ones is someone else's problem.
-                */
-               if (entry->cooked_default != NULL)
-               {
-                       if (attrdef == NULL)
-                               attrdef = (AttrDefault *) palloc(natts * sizeof(AttrDefault));
-                       attrdef[ndef].adnum = attnum;
-                       attrdef[ndef].adbin = pstrdup(entry->cooked_default);
-                       ndef++;
-                       desc->attrs[attnum - 1]->atthasdef = true;
-               }
-
+               has_not_null |= entry->is_not_null;
                desc->attrs[attnum - 1]->attislocal = entry->is_local;
                desc->attrs[attnum - 1]->attinhcount = entry->inhcount;
        }
 
-       if (constr->has_not_null || ndef > 0)
+       if (has_not_null)
        {
-               desc->constr = constr;
+               TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
 
-               if (ndef > 0)                   /* DEFAULTs */
-               {
-                       if (ndef < natts)
-                               constr->defval = (AttrDefault *)
-                                       repalloc(attrdef, ndef * sizeof(AttrDefault));
-                       else
-                               constr->defval = attrdef;
-                       constr->num_defval = ndef;
-               }
-               else
-               {
-                       constr->defval = NULL;
-                       constr->num_defval = 0;
-               }
+               constr->has_not_null = true;
+               constr->defval = NULL;
+               constr->num_defval = 0;
                constr->check = NULL;
                constr->num_check = 0;
+               desc->constr = constr;
        }
        else
        {
-               pfree(constr);
                desc->constr = NULL;
        }
 
index a6b904f83ebd62dcc56efe5aed138d42d258c16e..da76d76d9d2ccf88dbbb10db043b6c0605a2dcb5 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.91 2008/01/01 19:45:48 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.92 2008/05/09 23:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -206,6 +206,7 @@ Boot_CreateStmt:
                                                                                                          $6,
                                                                                                          BOOTSTRAP_SUPERUSERID,
                                                                                                          tupdesc,
+                                                                                                         NIL,
                                                                                                          RELKIND_RELATION,
                                                                                                          $3,
                                                                                                          true,
index 567280e152914d9bbcaeddead90281928b6756d0..d12e63445a5b373d15da101ec6f3864d8acb89df 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.332 2008/03/27 03:57:33 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.333 2008/05/09 23:32:04 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -77,9 +77,15 @@ static Oid AddNewRelationType(const char *typeName,
                                   char new_rel_kind,
                                   Oid new_array_type);
 static void RelationRemoveInheritance(Oid relid);
-static void StoreRelCheck(Relation rel, char *ccname, char *ccbin);
-static void StoreConstraints(Relation rel, TupleDesc tupdesc);
+static void StoreRelCheck(Relation rel, char *ccname, Node *expr,
+                                                 bool is_local, int inhcount);
+static void StoreConstraints(Relation rel, List *cooked_constraints);
+static bool MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
+                                                                               bool allow_merge, bool is_local);
 static void SetRelationNumChecks(Relation rel, int numchecks);
+static Node *cookConstraint(ParseState *pstate,
+                          Node *raw_constraint,
+                          char *relname);
 static List *insert_ordered_unique_oid(List *list, Oid datum);
 
 
@@ -788,6 +794,7 @@ heap_create_with_catalog(const char *relname,
                                                 Oid relid,
                                                 Oid ownerid,
                                                 TupleDesc tupdesc,
+                                                List *cooked_constraints,
                                                 char relkind,
                                                 bool shared_relation,
                                                 bool oidislocal,
@@ -1004,13 +1011,13 @@ heap_create_with_catalog(const char *relname,
        }
 
        /*
-        * store constraints and defaults passed in the tupdesc, if any.
+        * Store any supplied constraints and defaults.
         *
         * NB: this may do a CommandCounterIncrement and rebuild the relcache
         * entry, so the relation must be valid and self-consistent at this point.
         * In particular, there are not yet constraints and defaults anywhere.
         */
-       StoreConstraints(new_rel_desc, tupdesc);
+       StoreConstraints(new_rel_desc, cooked_constraints);
 
        /*
         * If there's a special on-commit action, remember it
@@ -1426,12 +1433,11 @@ heap_drop_with_catalog(Oid relid)
 
 /*
  * Store a default expression for column attnum of relation rel.
- * The expression must be presented as a nodeToString() string.
  */
 void
-StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin)
+StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr)
 {
-       Node       *expr;
+       char       *adbin;
        char       *adsrc;
        Relation        adrel;
        HeapTuple       tuple;
@@ -1445,12 +1451,12 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin)
                                defobject;
 
        /*
-        * Need to construct source equivalent of given node-string.
+        * Flatten expression to string form for storage.
         */
-       expr = stringToNode(adbin);
+       adbin = nodeToString(expr);
 
        /*
-        * deparse it
+        * Also deparse it to form the mostly-obsolete adsrc field.
         */
        adsrc = deparse_expression(expr,
                                                        deparse_context_for(RelationGetRelationName(rel),
@@ -1482,6 +1488,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin)
        pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1]));
        pfree(DatumGetPointer(values[Anum_pg_attrdef_adsrc - 1]));
        heap_freetuple(tuple);
+       pfree(adbin);
        pfree(adsrc);
 
        /*
@@ -1525,27 +1532,27 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin)
 
 /*
  * Store a check-constraint expression for the given relation.
- * The expression must be presented as a nodeToString() string.
  *
  * Caller is responsible for updating the count of constraints
  * in the pg_class entry for the relation.
  */
 static void
-StoreRelCheck(Relation rel, char *ccname, char *ccbin)
+StoreRelCheck(Relation rel, char *ccname, Node *expr,
+                         bool is_local, int inhcount)
 {
-       Node       *expr;
+       char       *ccbin;
        char       *ccsrc;
        List       *varList;
        int                     keycount;
        int16      *attNos;
 
        /*
-        * Convert condition to an expression tree.
+        * Flatten expression to string form for storage.
         */
-       expr = stringToNode(ccbin);
+       ccbin = nodeToString(expr);
 
        /*
-        * deparse it
+        * Also deparse it to form the mostly-obsolete consrc field.
         */
        ccsrc = deparse_expression(expr,
                                                        deparse_context_for(RelationGetRelationName(rel),
@@ -1553,7 +1560,7 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
                                                           false, false);
 
        /*
-        * Find columns of rel that are used in ccbin
+        * Find columns of rel that are used in expr
         *
         * NB: pull_var_clause is okay here only because we don't allow subselects
         * in check constraints; it would fail to examine the contents of
@@ -1608,26 +1615,29 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
                                                  InvalidOid,   /* no associated index */
                                                  expr, /* Tree form check constraint */
                                                  ccbin,        /* Binary form check constraint */
-                                                 ccsrc);               /* Source form check constraint */
+                                                 ccsrc,        /* Source form check constraint */
+                                                 is_local,     /* conislocal */
+                                                 inhcount); /* coninhcount */
 
+       pfree(ccbin);
        pfree(ccsrc);
 }
 
 /*
- * Store defaults and constraints passed in via the tuple constraint struct.
+ * Store defaults and constraints (passed as a list of CookedConstraint).
  *
  * NOTE: only pre-cooked expressions will be passed this way, which is to
  * say expressions inherited from an existing relation.  Newly parsed
  * expressions can be added later, by direct calls to StoreAttrDefault
- * and StoreRelCheck (see AddRelationRawConstraints()).
+ * and StoreRelCheck (see AddRelationNewConstraints()).
  */
 static void
-StoreConstraints(Relation rel, TupleDesc tupdesc)
+StoreConstraints(Relation rel, List *cooked_constraints)
 {
-       TupleConstr *constr = tupdesc->constr;
-       int                     i;
+       int                     numchecks = 0;
+       ListCell   *lc;
 
-       if (!constr)
+       if (!cooked_constraints)
                return;                                 /* nothing to do */
 
        /*
@@ -1637,33 +1647,46 @@ StoreConstraints(Relation rel, TupleDesc tupdesc)
         */
        CommandCounterIncrement();
 
-       for (i = 0; i < constr->num_defval; i++)
-               StoreAttrDefault(rel, constr->defval[i].adnum,
-                                                constr->defval[i].adbin);
+       foreach(lc, cooked_constraints)
+       {
+               CookedConstraint *con = (CookedConstraint *) lfirst(lc);
 
-       for (i = 0; i < constr->num_check; i++)
-               StoreRelCheck(rel, constr->check[i].ccname,
-                                         constr->check[i].ccbin);
+               switch (con->contype)
+               {
+                       case CONSTR_DEFAULT:
+                               StoreAttrDefault(rel, con->attnum, con->expr);
+                               break;
+                       case CONSTR_CHECK:
+                               StoreRelCheck(rel, con->name, con->expr,
+                                                         con->is_local, con->inhcount);
+                               numchecks++;
+                               break;
+                       default:
+                               elog(ERROR, "unrecognized constraint type: %d",
+                                        (int) con->contype);
+               }
+       }
 
-       if (constr->num_check > 0)
-               SetRelationNumChecks(rel, constr->num_check);
+       if (numchecks > 0)
+               SetRelationNumChecks(rel, numchecks);
 }
 
 /*
- * AddRelationRawConstraints
+ * AddRelationNewConstraints
  *
- * Add raw (not-yet-transformed) column default expressions and/or constraint
- * check expressions to an existing relation.  This is defined to do both
- * for efficiency in DefineRelation, but of course you can do just one or
- * the other by passing empty lists.
+ * Add new column default expressions and/or constraint check expressions
+ * to an existing relation.  This is defined to do both for efficiency in
+ * DefineRelation, but of course you can do just one or the other by passing
+ * empty lists.
  *
  * rel: relation to be modified
- * rawColDefaults: list of RawColumnDefault structures
- * rawConstraints: list of Constraint nodes
+ * newColDefaults: list of RawColumnDefault structures
+ * newConstraints: list of Constraint nodes
+ * allow_merge: TRUE if check constraints may be merged with existing ones
+ * is_local: TRUE if definition is local, FALSE if it's inherited
  *
- * All entries in rawColDefaults will be processed.  Entries in rawConstraints
- * will be processed only if they are CONSTR_CHECK type and contain a "raw"
- * expression.
+ * All entries in newColDefaults will be processed.  Entries in newConstraints
+ * will be processed only if they are CONSTR_CHECK type.
  *
  * Returns a list of CookedConstraint nodes that shows the cooked form of
  * the default and constraint expressions added to the relation.
@@ -1674,9 +1697,11 @@ StoreConstraints(Relation rel, TupleDesc tupdesc)
  * tuples visible.
  */
 List *
-AddRelationRawConstraints(Relation rel,
-                                                 List *rawColDefaults,
-                                                 List *rawConstraints)
+AddRelationNewConstraints(Relation rel,
+                                                 List *newColDefaults,
+                                                 List *newConstraints,
+                                                 bool allow_merge,
+                                                 bool is_local)
 {
        List       *cookedConstraints = NIL;
        TupleDesc       tupleDesc;
@@ -1715,7 +1740,7 @@ AddRelationRawConstraints(Relation rel,
        /*
         * Process column default expressions.
         */
-       foreach(cell, rawColDefaults)
+       foreach(cell, newColDefaults)
        {
                RawColumnDefault *colDef = (RawColumnDefault *) lfirst(cell);
                Form_pg_attribute atp = rel->rd_att->attrs[colDef->attnum - 1];
@@ -1739,13 +1764,15 @@ AddRelationRawConstraints(Relation rel,
                        (IsA(expr, Const) &&((Const *) expr)->constisnull))
                        continue;
 
-               StoreAttrDefault(rel, colDef->attnum, nodeToString(expr));
+               StoreAttrDefault(rel, colDef->attnum, expr);
 
                cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
                cooked->contype = CONSTR_DEFAULT;
                cooked->name = NULL;
                cooked->attnum = colDef->attnum;
                cooked->expr = expr;
+               cooked->is_local = is_local;
+               cooked->inhcount = is_local ? 0 : 1;
                cookedConstraints = lappend(cookedConstraints, cooked);
        }
 
@@ -1754,45 +1781,35 @@ AddRelationRawConstraints(Relation rel,
         */
        numchecks = numoldchecks;
        checknames = NIL;
-       foreach(cell, rawConstraints)
+       foreach(cell, newConstraints)
        {
                Constraint *cdef = (Constraint *) lfirst(cell);
                char       *ccname;
 
-               if (cdef->contype != CONSTR_CHECK || cdef->raw_expr == NULL)
+               if (cdef->contype != CONSTR_CHECK)
                        continue;
-               Assert(cdef->cooked_expr == NULL);
 
-               /*
-                * Transform raw parsetree to executable expression.
-                */
-               expr = transformExpr(pstate, cdef->raw_expr);
-
-               /*
-                * Make sure it yields a boolean result.
-                */
-               expr = coerce_to_boolean(pstate, expr, "CHECK");
+               if (cdef->raw_expr != NULL)
+               {
+                       Assert(cdef->cooked_expr == NULL);
 
-               /*
-                * Make sure no outside relations are referred to.
-                */
-               if (list_length(pstate->p_rtable) != 1)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-                       errmsg("only table \"%s\" can be referenced in check constraint",
-                                  RelationGetRelationName(rel))));
+                       /*
+                        * Transform raw parsetree to executable expression, and verify
+                        * it's valid as a CHECK constraint.
+                        */
+                       expr = cookConstraint(pstate, cdef->raw_expr,
+                                                                 RelationGetRelationName(rel));
+               }
+               else
+               {
+                       Assert(cdef->cooked_expr != NULL);
 
-               /*
-                * No subplans or aggregates, either...
-                */
-               if (pstate->p_hasSubLinks)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("cannot use subquery in check constraint")));
-               if (pstate->p_hasAggs)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_GROUPING_ERROR),
-                          errmsg("cannot use aggregate function in check constraint")));
+                       /*
+                        * Here, we assume the parser will only pass us valid CHECK
+                        * expressions, so we do no particular checking.
+                        */
+                       expr = stringToNode(cdef->cooked_expr);
+               }
 
                /*
                 * Check name uniqueness, or generate a name if none was given.
@@ -1802,15 +1819,6 @@ AddRelationRawConstraints(Relation rel,
                        ListCell   *cell2;
 
                        ccname = cdef->name;
-                       /* Check against pre-existing constraints */
-                       if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
-                                                                        RelationGetRelid(rel),
-                                                                        RelationGetNamespace(rel),
-                                                                        ccname))
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_DUPLICATE_OBJECT),
-                               errmsg("constraint \"%s\" for relation \"%s\" already exists",
-                                          ccname, RelationGetRelationName(rel))));
                        /* Check against other new constraints */
                        /* Needed because we don't do CommandCounterIncrement in loop */
                        foreach(cell2, checknames)
@@ -1821,6 +1829,19 @@ AddRelationRawConstraints(Relation rel,
                                                         errmsg("check constraint \"%s\" already exists",
                                                                        ccname)));
                        }
+
+                       /* save name for future checks */
+                       checknames = lappend(checknames, ccname);
+
+                       /*
+                        * Check against pre-existing constraints.  If we are allowed
+                        * to merge with an existing constraint, there's no more to
+                        * do here.  (We omit the duplicate constraint from the result,
+                        * which is what ATAddCheckConstraint wants.)
+                        */
+                       if (MergeWithExistingConstraint(rel, ccname, expr,
+                                                                                       allow_merge, is_local))
+                               continue;
                }
                else
                {
@@ -1855,15 +1876,15 @@ AddRelationRawConstraints(Relation rel,
                                                                                  "check",
                                                                                  RelationGetNamespace(rel),
                                                                                  checknames);
-               }
 
-               /* save name for future checks */
-               checknames = lappend(checknames, ccname);
+                       /* save name for future checks */
+                       checknames = lappend(checknames, ccname);
+               }
 
                /*
                 * OK, store it.
                 */
-               StoreRelCheck(rel, ccname, nodeToString(expr));
+               StoreRelCheck(rel, ccname, expr, is_local, is_local ? 0 : 1);
 
                numchecks++;
 
@@ -1872,6 +1893,8 @@ AddRelationRawConstraints(Relation rel,
                cooked->name = ccname;
                cooked->attnum = 0;
                cooked->expr = expr;
+               cooked->is_local = is_local;
+               cooked->inhcount = is_local ? 0 : 1;
                cookedConstraints = lappend(cookedConstraints, cooked);
        }
 
@@ -1887,6 +1910,90 @@ AddRelationRawConstraints(Relation rel,
        return cookedConstraints;
 }
 
+/*
+ * Check for a pre-existing check constraint that conflicts with a proposed
+ * new one, and either adjust its conislocal/coninhcount settings or throw
+ * error as needed.
+ *
+ * Returns TRUE if merged (constraint is a duplicate), or FALSE if it's
+ * got a so-far-unique name, or throws error if conflict.
+ */
+static bool
+MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
+                                                       bool allow_merge, bool is_local)
+{
+       bool            found;
+       Relation        conDesc;
+       SysScanDesc conscan;
+       ScanKeyData skey[2];
+       HeapTuple       tup;
+
+       /* Search for a pg_constraint entry with same name and relation */
+       conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
+
+       found = false;
+
+       ScanKeyInit(&skey[0],
+                               Anum_pg_constraint_conname,
+                               BTEqualStrategyNumber, F_NAMEEQ,
+                               CStringGetDatum(ccname));
+
+       ScanKeyInit(&skey[1],
+                               Anum_pg_constraint_connamespace,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(RelationGetNamespace(rel)));
+
+       conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
+                                                                SnapshotNow, 2, skey);
+
+       while (HeapTupleIsValid(tup = systable_getnext(conscan)))
+       {
+               Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
+
+               if (con->conrelid == RelationGetRelid(rel))
+               {
+                       /* Found it.  Conflicts if not identical check constraint */
+                       if (con->contype == CONSTRAINT_CHECK)
+                       {
+                               Datum   val;
+                               bool    isnull;
+
+                               val = fastgetattr(tup,
+                                                                 Anum_pg_constraint_conbin,
+                                                                 conDesc->rd_att, &isnull);
+                               if (isnull)
+                                       elog(ERROR, "null conbin for rel %s",
+                                                RelationGetRelationName(rel));
+                               if (equal(expr, stringToNode(TextDatumGetCString(val))))
+                                       found = true;
+                       }
+                       if (!found || !allow_merge)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                               errmsg("constraint \"%s\" for relation \"%s\" already exists",
+                                          ccname, RelationGetRelationName(rel))));
+                       /* OK to update the tuple */
+                       ereport(NOTICE,
+                                       (errmsg("merging constraint \"%s\" with inherited definition",
+                                                       ccname)));
+                       tup = heap_copytuple(tup);
+                       con = (Form_pg_constraint) GETSTRUCT(tup);
+                       if (is_local)
+                               con->conislocal = true;
+                       else
+                               con->coninhcount++;
+                       simple_heap_update(conDesc, &tup->t_self, tup);
+                       CatalogUpdateIndexes(conDesc, tup);
+                       break;
+               }
+       }
+
+       systable_endscan(conscan);
+       heap_close(conDesc, RowExclusiveLock);
+
+       return found;
+}
+
 /*
  * Update the count of constraints in the relation's pg_class tuple.
  *
@@ -2015,63 +2122,52 @@ cookDefault(ParseState *pstate,
        return expr;
 }
 
-
 /*
- * Removes all constraints on a relation that match the given name.
- *
- * It is the responsibility of the calling function to acquire a suitable
- * lock on the relation.
+ * Take a raw CHECK constraint expression and convert it to a cooked format
+ * ready for storage.
  *
- * Returns: The number of constraints removed.
+ * Parse state must be set up to recognize any vars that might appear
+ * in the expression.
  */
-int
-RemoveRelConstraints(Relation rel, const char *constrName,
-                                        DropBehavior behavior)
+static Node *
+cookConstraint(ParseState *pstate,
+                          Node *raw_constraint,
+                          char *relname)
 {
-       int                     ndeleted = 0;
-       Relation        conrel;
-       SysScanDesc conscan;
-       ScanKeyData key[1];
-       HeapTuple       contup;
-
-       /* Grab an appropriate lock on the pg_constraint relation */
-       conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
-
-       /* Use the index to scan only constraints of the target relation */
-       ScanKeyInit(&key[0],
-                               Anum_pg_constraint_conrelid,
-                               BTEqualStrategyNumber, F_OIDEQ,
-                               ObjectIdGetDatum(RelationGetRelid(rel)));
-
-       conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true,
-                                                                SnapshotNow, 1, key);
+       Node       *expr;
 
        /*
-        * Scan over the result set, removing any matching entries.
+        * Transform raw parsetree to executable expression.
         */
-       while ((contup = systable_getnext(conscan)) != NULL)
-       {
-               Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
+       expr = transformExpr(pstate, raw_constraint);
 
-               if (strcmp(NameStr(con->conname), constrName) == 0)
-               {
-                       ObjectAddress conobj;
-
-                       conobj.classId = ConstraintRelationId;
-                       conobj.objectId = HeapTupleGetOid(contup);
-                       conobj.objectSubId = 0;
-
-                       performDeletion(&conobj, behavior);
+       /*
+        * Make sure it yields a boolean result.
+        */
+       expr = coerce_to_boolean(pstate, expr, "CHECK");
 
-                       ndeleted++;
-               }
-       }
+       /*
+        * Make sure no outside relations are referred to.
+        */
+       if (list_length(pstate->p_rtable) != 1)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+                                errmsg("only table \"%s\" can be referenced in check constraint",
+                                               relname)));
 
-       /* Clean up after the scan */
-       systable_endscan(conscan);
-       heap_close(conrel, RowExclusiveLock);
+       /*
+        * No subplans or aggregates, either...
+        */
+       if (pstate->p_hasSubLinks)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("cannot use subquery in check constraint")));
+       if (pstate->p_hasAggs)
+               ereport(ERROR,
+                               (errcode(ERRCODE_GROUPING_ERROR),
+                                errmsg("cannot use aggregate function in check constraint")));
 
-       return ndeleted;
+       return expr;
 }
 
 
index 2cb91eb69f3c6e68ddd35a99781347cffafa1284..4a68722fa68f04ddcf953cfa585bddd0b9c890db 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.296 2008/03/26 21:10:37 alvherre Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.297 2008/05/09 23:32:04 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -716,7 +716,9 @@ index_create(Oid heapRelationId,
                                                                                   InvalidOid,  /* no associated index */
                                                                                   NULL,                /* no check constraint */
                                                                                   NULL,
-                                                                                  NULL);
+                                                                                  NULL,
+                                                                                  true, /* islocal */
+                                                                                  0); /* inhcount */
 
                        referenced.classId = ConstraintRelationId;
                        referenced.objectId = conOid;
index 2cc51b01e2b0de683698f0e9eb766c2d486c0c52..bb790e5fc41fae17611a1ce7da9b71f304c36a83 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.40 2008/03/26 21:10:37 alvherre Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.41 2008/05/09 23:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,7 +60,9 @@ CreateConstraintEntry(const char *constraintName,
                                          Oid indexRelId,
                                          Node *conExpr,
                                          const char *conBin,
-                                         const char *conSrc)
+                                         const char *conSrc,
+                                         bool conIsLocal,
+                                         int conInhCount)
 {
        Relation        conDesc;
        Oid                     conOid;
@@ -145,6 +147,8 @@ CreateConstraintEntry(const char *constraintName,
        values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
        values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
        values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
+       values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
+       values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
 
        if (conkeyArray)
                values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
index 3f4d65a038368d1c267ca947141188194284018b..0171d1f73386269fbdc786aff1afd595db8ca825 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.9 2008/01/01 19:45:48 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.10 2008/05/09 23:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -193,6 +193,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid)
                                                                                   toastOid,
                                                                                   rel->rd_rel->relowner,
                                                                                   tupdesc,
+                                                                                  NIL,
                                                                                   RELKIND_TOASTVALUE,
                                                                                   shared_relation,
                                                                                   true,
index 0c83e237fe81677c5eb235e64567a4bc371a4756..196d6baaa41d801aaa9e96c1539c375aaad4a62d 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.173 2008/04/13 19:18:14 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.174 2008/05/09 23:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -639,9 +639,12 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
 
        /*
         * Need to make a copy of the tuple descriptor, since
-        * heap_create_with_catalog modifies it.
+        * heap_create_with_catalog modifies it.  Note that the NewHeap will
+        * not receive any of the defaults or constraints associated with the
+        * OldHeap; we don't need 'em, and there's no reason to spend cycles
+        * inserting them into the catalogs only to delete them.
         */
-       tupdesc = CreateTupleDescCopyConstr(OldHeapDesc);
+       tupdesc = CreateTupleDescCopy(OldHeapDesc);
 
        /*
         * Use options of the old heap for new heap.
@@ -662,6 +665,7 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
                                                                                  InvalidOid,
                                                                                  OldHeap->rd_rel->relowner,
                                                                                  tupdesc,
+                                                                                 NIL,
                                                                                  OldHeap->rd_rel->relkind,
                                                                                  OldHeap->rd_rel->relisshared,
                                                                                  true,
index 9d6939bbc11d1547622b311c2de806c2219b363e..1aaa0bd86fe91b9663a193ff5633fc33646c9d23 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.251 2008/04/24 20:17:50 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.252 2008/05/09 23:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -169,11 +169,10 @@ typedef struct NewColumnValue
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, bool istemp,
                                List **supOids, List **supconstr, int *supOidCount);
-static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
-static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
-static void add_nonduplicate_constraint(Constraint *cdef,
-                                                       ConstrCheck *check, int *ncheck);
+static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
 static bool change_varattnos_walker(Node *node, const AttrNumber *newattno);
+static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
+static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
 static void StoreCatalogInheritance(Oid relationId, List *supers);
 static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
                                                 int16 seqNumber, Relation inhRelation);
@@ -201,7 +200,8 @@ static void ATController(Relation rel, List *cmds, bool recurse);
 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                  bool recurse, bool recursing);
 static void ATRewriteCatalogs(List **wqueue);
-static void ATExecCmd(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd);
+static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
+                                         AlterTableCmd *cmd);
 static void ATRewriteTables(List **wqueue);
 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
@@ -232,14 +232,18 @@ static void ATExecDropColumn(Relation rel, const char *colName,
                                 bool recurse, bool recursing);
 static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
                           IndexStmt *stmt, bool is_rebuild);
-static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel,
-                                       Node *newConstraint);
+static void ATExecAddConstraint(List **wqueue,
+                                                               AlteredTableInfo *tab, Relation rel,
+                                                               Node *newConstraint, bool recurse);
+static void ATAddCheckConstraint(List **wqueue,
+                                                                AlteredTableInfo *tab, Relation rel,
+                                                                Constraint *constr,
+                                                                bool recurse, bool recursing);
 static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
                                                  FkConstraint *fkconstraint);
-static void ATPrepDropConstraint(List **wqueue, Relation rel,
-                                        bool recurse, AlterTableCmd *cmd);
 static void ATExecDropConstraint(Relation rel, const char *constrName,
-                                        DropBehavior behavior, bool quiet);
+                                                                DropBehavior behavior, 
+                                                                bool recurse, bool recursing);
 static void ATPrepAlterColumnType(List **wqueue,
                                          AlteredTableInfo *tab, Relation rel,
                                          bool recurse, bool recursing,
@@ -287,6 +291,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
        bool            localHasOids;
        int                     parentOidCount;
        List       *rawDefaults;
+       List       *cookedDefaults;
        Datum           reloptions;
        ListCell   *listptr;
        AttrNumber      attnum;
@@ -370,67 +375,78 @@ DefineRelation(CreateStmt *stmt, char relkind)
                                                         &inheritOids, &old_constraints, &parentOidCount);
 
        /*
-        * Create a relation descriptor from the relation schema and create the
-        * relation.  Note that in this stage only inherited (pre-cooked) defaults
-        * and constraints will be included into the new relation.
-        * (BuildDescForRelation takes care of the inherited defaults, but we have
-        * to copy inherited constraints here.)
+        * Create a tuple descriptor from the relation schema.  Note that this
+        * deals with column names, types, and NOT NULL constraints, but not
+        * default values or CHECK constraints; we handle those below.
         */
        descriptor = BuildDescForRelation(schema);
 
        localHasOids = interpretOidsOption(stmt->options);
        descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
 
-       if (old_constraints || stmt->constraints)
+       /*
+        * Find columns with default values and prepare for insertion of the
+        * defaults.  Pre-cooked (that is, inherited) defaults go into a list of
+        * CookedConstraint structs that we'll pass to heap_create_with_catalog,
+        * while raw defaults go into a list of RawColumnDefault structs that
+        * will be processed by AddRelationNewConstraints.  (We can't deal with
+        * raw expressions until we can do transformExpr.)
+        *
+        * We can set the atthasdef flags now in the tuple descriptor; this just
+        * saves StoreAttrDefault from having to do an immediate update of the
+        * pg_attribute rows.
+        */
+       rawDefaults = NIL;
+       cookedDefaults = NIL;
+       attnum = 0;
+
+       foreach(listptr, schema)
        {
-               ConstrCheck *check;
-               int                     ncheck = 0;
-
-               /* make array that's certainly big enough */
-               check = (ConstrCheck *)
-                       palloc((list_length(old_constraints) +
-                                       list_length(stmt->constraints)) * sizeof(ConstrCheck));
-               /* deal with constraints from MergeAttributes */
-               foreach(listptr, old_constraints)
-               {
-                       Constraint *cdef = (Constraint *) lfirst(listptr);
+               ColumnDef  *colDef = lfirst(listptr);
 
-                       if (cdef->contype == CONSTR_CHECK)
-                               add_nonduplicate_constraint(cdef, check, &ncheck);
-               }
+               attnum++;
 
-               /*
-                * parse_utilcmd.c might have passed some precooked constraints too,
-                * due to LIKE tab INCLUDING CONSTRAINTS
-                */
-               foreach(listptr, stmt->constraints)
+               if (colDef->raw_default != NULL)
                {
-                       Constraint *cdef = (Constraint *) lfirst(listptr);
+                       RawColumnDefault *rawEnt;
 
-                       if (cdef->contype == CONSTR_CHECK && cdef->cooked_expr != NULL)
-                               add_nonduplicate_constraint(cdef, check, &ncheck);
+                       Assert(colDef->cooked_default == NULL);
+
+                       rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
+                       rawEnt->attnum = attnum;
+                       rawEnt->raw_default = colDef->raw_default;
+                       rawDefaults = lappend(rawDefaults, rawEnt);
+                       descriptor->attrs[attnum - 1]->atthasdef = true;
                }
-               /* if we found any, insert 'em into the descriptor */
-               if (ncheck > 0)
+               else if (colDef->cooked_default != NULL)
                {
-                       if (descriptor->constr == NULL)
-                       {
-                               descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr));
-                               descriptor->constr->defval = NULL;
-                               descriptor->constr->num_defval = 0;
-                               descriptor->constr->has_not_null = false;
-                       }
-                       descriptor->constr->num_check = ncheck;
-                       descriptor->constr->check = check;
+                       CookedConstraint *cooked;
+
+                       cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
+                       cooked->contype = CONSTR_DEFAULT;
+                       cooked->name = NULL;
+                       cooked->attnum = attnum;
+                       cooked->expr = stringToNode(colDef->cooked_default);
+                       cooked->is_local = true;        /* not used for defaults */
+                       cooked->inhcount = 0;           /* ditto */
+                       cookedDefaults = lappend(cookedDefaults, cooked);
+                       descriptor->attrs[attnum - 1]->atthasdef = true;
                }
        }
 
+       /*
+        * Create the relation.  Inherited defaults and constraints are passed
+        * in for immediate handling --- since they don't need parsing, they
+        * can be stored immediately.
+        */
        relationId = heap_create_with_catalog(relname,
                                                                                  namespaceId,
                                                                                  tablespaceId,
                                                                                  InvalidOid,
                                                                                  GetUserId(),
                                                                                  descriptor,
+                                                                                 list_concat(cookedDefaults,
+                                                                                                         old_constraints),
                                                                                  relkind,
                                                                                  false,
                                                                                  localHasOids,
@@ -463,36 +479,10 @@ DefineRelation(CreateStmt *stmt, char relkind)
         * apply the parser's transformExpr routine, but transformExpr doesn't
         * work unless we have a pre-existing relation. So, the transformation has
         * to be postponed to this final step of CREATE TABLE.
-        *
-        * First, scan schema to find new column defaults.
-        */
-       rawDefaults = NIL;
-       attnum = 0;
-
-       foreach(listptr, schema)
-       {
-               ColumnDef  *colDef = lfirst(listptr);
-
-               attnum++;
-
-               if (colDef->raw_default != NULL)
-               {
-                       RawColumnDefault *rawEnt;
-
-                       Assert(colDef->cooked_default == NULL);
-
-                       rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
-                       rawEnt->attnum = attnum;
-                       rawEnt->raw_default = colDef->raw_default;
-                       rawDefaults = lappend(rawDefaults, rawEnt);
-               }
-       }
-
-       /*
-        * Parse and add the defaults/constraints, if any.
         */
        if (rawDefaults || stmt->constraints)
-               AddRelationRawConstraints(rel, rawDefaults, stmt->constraints);
+               AddRelationNewConstraints(rel, rawDefaults, stmt->constraints,
+                                                                 true, true);
 
        /*
         * Clean up.  We keep lock on new relation (although it shouldn't be
@@ -1046,8 +1036,9 @@ MergeAttributes(List *schema, List *supers, bool istemp,
                }
 
                /*
-                * Now copy the constraints of this parent, adjusting attnos using the
-                * completed newattno[] map
+                * Now copy the CHECK constraints of this parent, adjusting attnos
+                * using the completed newattno[] map.  Identically named constraints
+                * are merged if possible, else we throw error.
                 */
                if (constr && constr->num_check > 0)
                {
@@ -1056,17 +1047,28 @@ MergeAttributes(List *schema, List *supers, bool istemp,
 
                        for (i = 0; i < constr->num_check; i++)
                        {
-                               Constraint *cdef = makeNode(Constraint);
+                               char       *name = check[i].ccname;
                                Node       *expr;
 
-                               cdef->contype = CONSTR_CHECK;
-                               cdef->name = pstrdup(check[i].ccname);
-                               cdef->raw_expr = NULL;
                                /* adjust varattnos of ccbin here */
                                expr = stringToNode(check[i].ccbin);
                                change_varattnos_of_a_node(expr, newattno);
-                               cdef->cooked_expr = nodeToString(expr);
-                               constraints = lappend(constraints, cdef);
+
+                               /* check for duplicate */
+                               if (!MergeCheckConstraint(constraints, name, expr))
+                               {
+                                       /* nope, this is a new one */
+                                       CookedConstraint *cooked;
+
+                                       cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
+                                       cooked->contype = CONSTR_CHECK;
+                                       cooked->name = pstrdup(name);
+                                       cooked->attnum = 0;             /* not used for constraints */
+                                       cooked->expr = expr;
+                                       cooked->is_local = false;
+                                       cooked->inhcount = 1;
+                                       constraints = lappend(constraints, cooked);
+                               }
                        }
                }
 
@@ -1183,38 +1185,46 @@ MergeAttributes(List *schema, List *supers, bool istemp,
 
 
 /*
- * In multiple-inheritance situations, it's possible to inherit
- * the same grandparent constraint through multiple parents.
- * Hence, we want to discard inherited constraints that match as to
- * both name and expression.  Otherwise, gripe if there are conflicting
- * names.  Nonconflicting constraints are added to the array check[]
- * of length *ncheck ... caller must ensure there is room!
+ * MergeCheckConstraint
+ *             Try to merge an inherited CHECK constraint with previous ones
+ *
+ * If we inherit identically-named constraints from multiple parents, we must
+ * merge them, or throw an error if they don't have identical definitions.
+ *
+ * constraints is a list of CookedConstraint structs for previous constraints.
+ *
+ * Returns TRUE if merged (constraint is a duplicate), or FALSE if it's
+ * got a so-far-unique name, or throws error if conflict.
  */
-static void
-add_nonduplicate_constraint(Constraint *cdef, ConstrCheck *check, int *ncheck)
+static bool
+MergeCheckConstraint(List *constraints, char *name, Node *expr)
 {
-       int                     i;
-
-       /* Should only see precooked constraints here */
-       Assert(cdef->contype == CONSTR_CHECK);
-       Assert(cdef->name != NULL);
-       Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL);
+       ListCell   *lc;
 
-       for (i = 0; i < *ncheck; i++)
+       foreach(lc, constraints)
        {
-               if (strcmp(check[i].ccname, cdef->name) != 0)
+               CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
+
+               Assert(ccon->contype == CONSTR_CHECK);
+
+               /* Non-matching names never conflict */
+               if (strcmp(ccon->name, name) != 0)
                        continue;
-               if (strcmp(check[i].ccbin, cdef->cooked_expr) == 0)
-                       return;                         /* duplicate constraint, so ignore it */
+
+               if (equal(expr, ccon->expr))
+               {
+                       /* OK to merge */
+                       ccon->inhcount++;
+                       return true;
+               }
+
                ereport(ERROR,
                                (errcode(ERRCODE_DUPLICATE_OBJECT),
                                 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
-                                               cdef->name)));
+                                               name)));
        }
-       /* No match on name, so add it to array */
-       check[*ncheck].ccname = cdef->name;
-       check[*ncheck].ccbin = pstrdup(cdef->cooked_expr);
-       (*ncheck)++;
+
+       return false;
 }
 
 
@@ -1252,7 +1262,7 @@ change_varattnos_walker(Node *node, const AttrNumber *newattno)
                         * currently.
                         */
                        Assert(newattno[var->varattno - 1] > 0);
-                       var->varattno = newattno[var->varattno - 1];
+                       var->varattno = var->varoattno = newattno[var->varattno - 1];
                }
                return false;
        }
@@ -1887,8 +1897,9 @@ CheckTableNotInUse(Relation rel, const char *stmt)
  * expressions that need to be evaluated with respect to the old table
  * schema.
  *
- * ATRewriteCatalogs performs phase 2 for each affected table (note that
- * phases 2 and 3 do no explicit recursion, since phase 1 already did it).
+ * ATRewriteCatalogs performs phase 2 for each affected table.  (Note that
+ * phases 2 and 3 normally do no explicit recursion, since phase 1 already
+ * did it --- although some subcommands have to recurse in phase 2 instead.)
  * Certain subcommands need to be performed before others to avoid
  * unnecessary conflicts; for example, DROP COLUMN should come before
  * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
@@ -2044,27 +2055,18 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                        break;
                case AT_AddConstraint:  /* ADD CONSTRAINT */
                        ATSimplePermissions(rel, false);
-
-                       /*
-                        * Currently we recurse only for CHECK constraints, never for
-                        * foreign-key constraints.  UNIQUE/PKEY constraints won't be seen
-                        * here.
-                        */
-                       if (IsA(cmd->def, Constraint))
-                               ATSimpleRecursion(wqueue, rel, cmd, recurse);
-                       /* No command-specific prep needed */
+                       /* Recursion occurs during execution phase */
+                       /* No command-specific prep needed except saving recurse flag */
+                       if (recurse)
+                               cmd->subtype = AT_AddConstraintRecurse;
                        pass = AT_PASS_ADD_CONSTR;
                        break;
                case AT_DropConstraint: /* DROP CONSTRAINT */
                        ATSimplePermissions(rel, false);
-                       /* Performs own recursion */
-                       ATPrepDropConstraint(wqueue, rel, recurse, cmd);
-                       pass = AT_PASS_DROP;
-                       break;
-               case AT_DropConstraintQuietly:  /* DROP CONSTRAINT for child */
-                       ATSimplePermissions(rel, false);
-                       ATSimpleRecursion(wqueue, rel, cmd, recurse);
-                       /* No command-specific prep needed */
+                       /* Recursion occurs during execution phase */
+                       /* No command-specific prep needed except saving recurse flag */
+                       if (recurse)
+                               cmd->subtype = AT_DropConstraintRecurse;
                        pass = AT_PASS_DROP;
                        break;
                case AT_AlterColumnType:                /* ALTER COLUMN TYPE */
@@ -2181,7 +2183,7 @@ ATRewriteCatalogs(List **wqueue)
                        rel = relation_open(tab->relid, NoLock);
 
                        foreach(lcmd, subcmds)
-                               ATExecCmd(tab, rel, (AlterTableCmd *) lfirst(lcmd));
+                               ATExecCmd(wqueue, tab, rel, (AlterTableCmd *) lfirst(lcmd));
 
                        /*
                         * After the ALTER TYPE pass, do cleanup work (this is not done in
@@ -2215,7 +2217,8 @@ ATRewriteCatalogs(List **wqueue)
  * ATExecCmd: dispatch a subcommand to appropriate execution routine
  */
 static void
-ATExecCmd(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd)
+ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
+                 AlterTableCmd *cmd)
 {
        switch (cmd->subtype)
        {
@@ -2250,13 +2253,16 @@ ATExecCmd(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd)
                        ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true);
                        break;
                case AT_AddConstraint:  /* ADD CONSTRAINT */
-                       ATExecAddConstraint(tab, rel, cmd->def);
+                       ATExecAddConstraint(wqueue, tab, rel, cmd->def, false);
+                       break;
+               case AT_AddConstraintRecurse:   /* ADD CONSTRAINT with recursion */
+                       ATExecAddConstraint(wqueue, tab, rel, cmd->def, true);
                        break;
                case AT_DropConstraint: /* DROP CONSTRAINT */
-                       ATExecDropConstraint(rel, cmd->name, cmd->behavior, false);
+                       ATExecDropConstraint(rel, cmd->name, cmd->behavior, false, false);
                        break;
-               case AT_DropConstraintQuietly:  /* DROP CONSTRAINT for child */
-                       ATExecDropConstraint(rel, cmd->name, cmd->behavior, true);
+               case AT_DropConstraintRecurse:  /* DROP CONSTRAINT with recursion */
+                       ATExecDropConstraint(rel, cmd->name, cmd->behavior, true, false);
                        break;
                case AT_AlterColumnType:                /* ALTER COLUMN TYPE */
                        ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
@@ -3272,7 +3278,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
                 * This function is intended for CREATE TABLE, so it processes a
                 * _list_ of defaults, but we just do one.
                 */
-               AddRelationRawConstraints(rel, list_make1(rawEnt), NIL);
+               AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, false, true);
 
                /* Make the additional catalog changes visible */
                CommandCounterIncrement();
@@ -3297,7 +3303,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
         * the constraints more directly.)
         *
         * Note: we use build_column_default, and not just the cooked default
-        * returned by AddRelationRawConstraints, so that the right thing happens
+        * returned by AddRelationNewConstraints, so that the right thing happens
         * when a datatype's default applies.
         */
        defval = (Expr *) build_column_default(rel, attribute->attnum);
@@ -3552,7 +3558,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.
                 */
-               AddRelationRawConstraints(rel, list_make1(rawEnt), NIL);
+               AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, false, true);
        }
 }
 
@@ -3829,7 +3835,7 @@ ATExecDropColumn(Relation rel, const char *colName,
                        {
                                /*
                                 * If we were told to drop ONLY in this table (no recursion),
-                                * we need to mark the inheritors' attribute as locally
+                                * we need to mark the inheritors' attributes as locally
                                 * defined rather than inherited.
                                 */
                                childatt->attinhcount--;
@@ -3936,7 +3942,8 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
  * ALTER TABLE ADD CONSTRAINT
  */
 static void
-ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint)
+ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
+                                       Node *newConstraint, bool recurse)
 {
        switch (nodeTag(newConstraint))
        {
@@ -3953,34 +3960,9 @@ ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint)
                                switch (constr->contype)
                                {
                                        case CONSTR_CHECK:
-                                               {
-                                                       List       *newcons;
-                                                       ListCell   *lcon;
-
-                                                       /*
-                                                        * Call AddRelationRawConstraints to do the work.
-                                                        * It returns a list of cooked constraints.
-                                                        */
-                                                       newcons = AddRelationRawConstraints(rel, NIL,
-                                                                                                                list_make1(constr));
-                                                       /* Add each constraint to Phase 3's queue */
-                                                       foreach(lcon, newcons)
-                                                       {
-                                                               CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
-                                                               NewConstraint *newcon;
-
-                                                               newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
-                                                               newcon->name = ccon->name;
-                                                               newcon->contype = ccon->contype;
-                                                               /* ExecQual wants implicit-AND format */
-                                                               newcon->qual = (Node *)
-                                                                       make_ands_implicit((Expr *) ccon->expr);
-
-                                                               tab->constraints = lappend(tab->constraints,
-                                                                                                                  newcon);
-                                                       }
-                                                       break;
-                                               }
+                                               ATAddCheckConstraint(wqueue, tab, rel,
+                                                                                        constr, recurse, false);
+                                               break;
                                        default:
                                                elog(ERROR, "unrecognized constraint type: %d",
                                                         (int) constr->contype);
@@ -3992,6 +3974,9 @@ ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint)
                                FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
 
                                /*
+                                * Note that we currently never recurse for FK constraints,
+                                * so the "recurse" flag is silently ignored.
+                                *
                                 * Assign or validate constraint name
                                 */
                                if (fkconstraint->constr_name)
@@ -4024,6 +4009,107 @@ ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint)
        }
 }
 
+/*
+ * Add a check constraint to a single table and its children
+ *
+ * Subroutine for ATExecAddConstraint.
+ *
+ * We must recurse to child tables during execution, rather than using
+ * ALTER TABLE's normal prep-time recursion.  The reason is that all the
+ * constraints *must* be given the same name, else they won't be seen as
+ * related later.  If the user didn't explicitly specify a name, then
+ * AddRelationNewConstraints would normally assign different names to the
+ * child constraints.  To fix that, we must capture the name assigned at
+ * the parent table and pass that down.
+ */
+static void
+ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
+                                        Constraint *constr, bool recurse, bool recursing)
+{
+       List       *newcons;
+       ListCell   *lcon;
+       List       *children;
+       ListCell   *child;
+
+       /* At top level, permission check was done in ATPrepCmd, else do it */
+       if (recursing)
+               ATSimplePermissions(rel, false);
+
+       /*
+        * Call AddRelationNewConstraints to do the work, making sure it works on
+        * a copy of the Constraint so transformExpr can't modify the original.
+        * It returns a list of cooked constraints.
+        *
+        * If the constraint ends up getting merged with a pre-existing one, it's
+        * 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.
+        */
+       newcons = AddRelationNewConstraints(rel, NIL,
+                                                                               list_make1(copyObject(constr)),
+                                                                               recursing, !recursing);
+
+       /* Add each constraint to Phase 3's queue */
+       foreach(lcon, newcons)
+       {
+               CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
+               NewConstraint *newcon;
+
+               newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
+               newcon->name = ccon->name;
+               newcon->contype = ccon->contype;
+               /* ExecQual wants implicit-AND format */
+               newcon->qual = (Node *) make_ands_implicit((Expr *) ccon->expr);
+
+               tab->constraints = lappend(tab->constraints, newcon);
+
+               /* Save the actually assigned name if it was defaulted */
+               if (constr->name == NULL)
+                       constr->name = ccon->name;
+       }
+
+       /* At this point we must have a locked-down name to use */
+       Assert(constr->name != NULL);
+
+       /* Advance command counter in case same table is visited multiple times */
+       CommandCounterIncrement();
+
+       /*
+        * Propagate to children as appropriate.  Unlike most other ALTER
+        * 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.
+        */
+       children = find_inheritance_children(RelationGetRelid(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.
+        */
+       if (children && !recurse)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                errmsg("constraint must be added to child tables too")));
+
+       foreach(child, children)
+       {
+               Oid                     childrelid = lfirst_oid(child);
+               Relation        childrel;
+               AlteredTableInfo *childtab;
+
+               childrel = heap_open(childrelid, AccessExclusiveLock);
+               CheckTableNotInUse(childrel, "ALTER TABLE");
+
+               /* Find or create work queue entry for this table */
+               childtab = ATGetQueueEntry(wqueue, childrel);
+
+               /* Recurse to child */
+               ATAddCheckConstraint(wqueue, childtab, childrel,
+                                                        constr, recurse, true);
+
+               heap_close(childrel, NoLock);
+       }
+}
+
 /*
  * Add a foreign-key constraint to a single table
  *
@@ -4295,7 +4381,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
                                                                          indexOid,
                                                                          NULL,         /* no check constraint */
                                                                          NULL,
-                                                                         NULL);
+                                                                         NULL,
+                                                                         true, /* islocal */
+                                                                         0); /* inhcount */
 
        /*
         * Create the triggers that will enforce the constraint.
@@ -4813,46 +4901,186 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
 
 /*
  * ALTER TABLE DROP CONSTRAINT
+ *
+ * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
  */
 static void
-ATPrepDropConstraint(List **wqueue, Relation rel,
-                                        bool recurse, AlterTableCmd *cmd)
+ATExecDropConstraint(Relation rel, const char *constrName,
+                                        DropBehavior behavior,
+                                        bool recurse, bool recursing)
 {
+       List       *children;
+       ListCell   *child;
+       Relation        conrel;
+       Form_pg_constraint con;
+       SysScanDesc scan;
+       ScanKeyData key;
+       HeapTuple       tuple;
+       bool            found = false;
+       bool            is_check_constraint = false;
+
+       /* At top level, permission check was done in ATPrepCmd, else do it */
+       if (recursing)
+               ATSimplePermissions(rel, false);
+
+       conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
+
        /*
-        * We don't want errors or noise from child tables, so we have to pass
-        * down a modified command.
+        * Find and drop the target constraint
         */
-       if (recurse)
+       ScanKeyInit(&key,
+                               Anum_pg_constraint_conrelid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(RelationGetRelid(rel)));
+       scan = systable_beginscan(conrel, ConstraintRelidIndexId,
+                                                         true, SnapshotNow, 1, &key);
+
+       while (HeapTupleIsValid(tuple = systable_getnext(scan)))
        {
-               AlterTableCmd *childCmd = copyObject(cmd);
+               ObjectAddress conobj;
+
+               con = (Form_pg_constraint) GETSTRUCT(tuple);
+
+               if (strcmp(NameStr(con->conname), constrName) != 0)
+                       continue;
+
+               /* Don't drop inherited constraints */
+               if (con->coninhcount > 0 && !recursing)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                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;
+
+               /*
+                * Perform the actual constraint deletion
+                */
+               conobj.classId = ConstraintRelationId;
+               conobj.objectId = HeapTupleGetOid(tuple);
+               conobj.objectSubId = 0;
+
+               performDeletion(&conobj, behavior);
 
-               childCmd->subtype = AT_DropConstraintQuietly;
-               ATSimpleRecursion(wqueue, rel, childCmd, recurse);
+               found = true;
        }
-}
 
-static void
-ATExecDropConstraint(Relation rel, const char *constrName,
-                                        DropBehavior behavior, bool quiet)
-{
-       int                     deleted;
+       systable_endscan(scan);
 
-       deleted = RemoveRelConstraints(rel, constrName, behavior);
+       if (!found)
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("constraint \"%s\" of relation \"%s\" does not exist",
+                                               constrName, RelationGetRelationName(rel))));
 
-       if (!quiet)
+       /*
+        * Propagate to children as appropriate.  Unlike most other ALTER
+        * 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)
+               children = find_inheritance_children(RelationGetRelid(rel));
+       else
+               children = NIL;
+
+       foreach(child, children)
        {
-               /* If zero constraints deleted, complain */
-               if (deleted == 0)
+               Oid                     childrelid = lfirst_oid(child);
+               Relation        childrel;
+
+               childrel = heap_open(childrelid, AccessExclusiveLock);
+               CheckTableNotInUse(childrel, "ALTER TABLE");
+
+               ScanKeyInit(&key,
+                                       Anum_pg_constraint_conrelid,
+                                       BTEqualStrategyNumber, F_OIDEQ,
+                                       ObjectIdGetDatum(childrelid));
+               scan = systable_beginscan(conrel, ConstraintRelidIndexId,
+                                                                 true, SnapshotNow, 1, &key);
+
+               found = false;
+
+               while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+               {
+                       HeapTuple copy_tuple;
+
+                       con = (Form_pg_constraint) GETSTRUCT(tuple);
+
+                       /* Right now only CHECK constraints can be inherited */
+                       if (con->contype != CONSTRAINT_CHECK)
+                               continue;
+
+                       if (strcmp(NameStr(con->conname), constrName) != 0)
+                               continue;
+
+                       found = true;
+
+                       if (con->coninhcount <= 0)              /* shouldn't happen */
+                               elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
+                                        childrelid, constrName);
+
+                       copy_tuple = heap_copytuple(tuple);
+                       con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
+
+                       if (recurse)
+                       {
+                               /*
+                                * If the child constraint has other definition sources,
+                                * just decrement its inheritance count; if not, recurse
+                                * to delete it.
+                                */
+                               if (con->coninhcount == 1 && !con->conislocal)
+                               {
+                                       /* Time to delete this child constraint, too */
+                                       ATExecDropConstraint(childrel, constrName, behavior,
+                                                                                true, true);
+                               }
+                               else
+                               {
+                                       /* Child constraint must survive my deletion */
+                                       con->coninhcount--;
+                                       simple_heap_update(conrel, &copy_tuple->t_self, copy_tuple);
+                                       CatalogUpdateIndexes(conrel, copy_tuple);
+
+                                       /* Make update visible */
+                                       CommandCounterIncrement();
+                               }
+                       }
+                       else
+                       {
+                               /*
+                                * If we were told to drop ONLY in this table (no
+                                * recursion), we need to mark the inheritors' constraints
+                                * as locally defined rather than inherited.
+                                */
+                               con->coninhcount--;
+                               con->conislocal = true;
+
+                               simple_heap_update(conrel, &copy_tuple->t_self, copy_tuple);
+                               CatalogUpdateIndexes(conrel, copy_tuple);
+
+                               /* Make update visible */
+                               CommandCounterIncrement();
+                       }
+
+                       heap_freetuple(copy_tuple);
+               }
+
+               systable_endscan(scan);
+
+               if (!found)
                        ereport(ERROR,
                                        (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                        errmsg("constraint \"%s\" does not exist",
-                                                       constrName)));
-               /* Otherwise if more than one constraint deleted, notify */
-               else if (deleted > 1)
-                       ereport(NOTICE,
-                                       (errmsg("multiple constraints named \"%s\" were dropped",
-                                                       constrName)));
+                                        errmsg("constraint \"%s\" of relation \"%s\" does not exist",
+                                                       constrName,
+                                                       RelationGetRelationName(childrel))));
+
+               heap_close(childrel, NoLock);
        }
+
+       heap_close(conrel, RowExclusiveLock);
 }
 
 /*
@@ -5314,7 +5542,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                 */
                RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true);
 
-               StoreAttrDefault(rel, attnum, nodeToString(defaultexpr));
+               StoreAttrDefault(rel, attnum, defaultexpr);
        }
 
        /* Cleanup */
@@ -6216,10 +6444,10 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent)
                                                RelationGetRelationName(child_rel),
                                                RelationGetRelationName(parent_rel))));
 
-       /* Match up the columns and bump attinhcount and attislocal */
+       /* Match up the columns and bump attinhcount as needed */
        MergeAttributesIntoExisting(child_rel, parent_rel);
 
-       /* Match up the constraints and make sure they're present in child */
+       /* Match up the constraints and bump coninhcount as needed */
        MergeConstraintsIntoExisting(child_rel, parent_rel);
 
        /*
@@ -6259,6 +6487,28 @@ decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
        return TextDatumGetCString(expr);
 }
 
+/*
+ * Determine whether two check constraints are functionally equivalent
+ *
+ * The test we apply is to see whether they reverse-compile to the same
+ * source string.  This insulates us from issues like whether attributes
+ * have the same physical column numbers in parent and child relations.
+ */
+static bool
+constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
+{
+       Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
+       Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
+
+       if (acon->condeferrable != bcon->condeferrable ||
+               acon->condeferred != bcon->condeferred ||
+               strcmp(decompile_conbin(a, tupleDesc),
+                          decompile_conbin(b, tupleDesc)) != 0)
+               return false;
+       else
+               return true;
+}
+
 /*
  * Check columns in child table match up with columns in parent, and increment
  * their attinhcount.
@@ -6342,7 +6592,8 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
 }
 
 /*
- * Check constraints in child table match up with constraints in parent
+ * Check constraints in child table match up with constraints in parent,
+ * and increment their coninhcount.
  *
  * Called by ATExecAddInherit
  *
@@ -6360,91 +6611,87 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
 static void
 MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
 {
-       Relation        catalogRelation;
-       TupleDesc       tupleDesc;
-       SysScanDesc scan;
-       ScanKeyData key;
-       HeapTuple       constraintTuple;
-       ListCell   *elem;
-       List       *constraints;
+       Relation        catalog_relation;
+       TupleDesc       tuple_desc;
+       SysScanDesc parent_scan;
+       ScanKeyData parent_key;
+       HeapTuple       parent_tuple;
 
-       /* First gather up the child's constraint definitions */
-       catalogRelation = heap_open(ConstraintRelationId, AccessShareLock);
-       tupleDesc = RelationGetDescr(catalogRelation);
+       catalog_relation = heap_open(ConstraintRelationId, RowExclusiveLock);
+       tuple_desc = RelationGetDescr(catalog_relation);
 
-       ScanKeyInit(&key,
+       /* Outer loop scans through the parent's constraint definitions */
+       ScanKeyInit(&parent_key,
                                Anum_pg_constraint_conrelid,
                                BTEqualStrategyNumber, F_OIDEQ,
-                               ObjectIdGetDatum(RelationGetRelid(child_rel)));
-       scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
-                                                         true, SnapshotNow, 1, &key);
+                               ObjectIdGetDatum(RelationGetRelid(parent_rel)));
+       parent_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
+                                                                        true, SnapshotNow, 1, &parent_key);
 
-       constraints = NIL;
-       while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
+       while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
        {
-               Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
+               Form_pg_constraint      parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
+               SysScanDesc                     child_scan;
+               ScanKeyData                     child_key;
+               HeapTuple                       child_tuple;
+               bool                            found = false;
 
-               if (con->contype != CONSTRAINT_CHECK)
+               if (parent_con->contype != CONSTRAINT_CHECK)
                        continue;
 
-               constraints = lappend(constraints, heap_copytuple(constraintTuple));
-       }
+               /* Search for a child constraint matching this one */
+               ScanKeyInit(&child_key,
+                                       Anum_pg_constraint_conrelid,
+                                       BTEqualStrategyNumber, F_OIDEQ,
+                                       ObjectIdGetDatum(RelationGetRelid(child_rel)));
+               child_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
+                                                                               true, SnapshotNow, 1, &child_key);
 
-       systable_endscan(scan);
+               while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
+               {
+                       Form_pg_constraint      child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
+                       HeapTuple child_copy;
 
-       /* Then scan through the parent's constraints looking for matches */
-       ScanKeyInit(&key,
-                               Anum_pg_constraint_conrelid,
-                               BTEqualStrategyNumber, F_OIDEQ,
-                               ObjectIdGetDatum(RelationGetRelid(parent_rel)));
-       scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true,
-                                                         SnapshotNow, 1, &key);
+                       if (child_con->contype != CONSTRAINT_CHECK)
+                               continue;
 
-       while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
-       {
-               Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
-               bool            found = false;
-               Form_pg_constraint child_con = NULL;
-               HeapTuple       child_contuple = NULL;
+                       if (strcmp(NameStr(parent_con->conname),
+                                          NameStr(child_con->conname)) != 0)
+                               continue;
 
-               if (parent_con->contype != CONSTRAINT_CHECK)
-                       continue;
+                       if (!constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
+                                                               RelationGetRelationName(child_rel),
+                                                               NameStr(parent_con->conname))));
 
-               foreach(elem, constraints)
-               {
-                       child_contuple = (HeapTuple) lfirst(elem);
-                       child_con = (Form_pg_constraint) GETSTRUCT(child_contuple);
-                       if (strcmp(NameStr(parent_con->conname),
-                                          NameStr(child_con->conname)) == 0)
-                       {
-                               found = true;
-                               break;
-                       }
+                       /*
+                        * OK, bump the child constraint's inheritance count.  (If we fail
+                        * later on, this change will just roll back.)
+                        */
+                       child_copy = heap_copytuple(child_tuple);
+                       child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
+                       child_con->coninhcount++;
+                       simple_heap_update(catalog_relation, &child_copy->t_self, child_copy);
+                       CatalogUpdateIndexes(catalog_relation, child_copy);
+                       heap_freetuple(child_copy);
+
+                       found = true;
+                       break;
                }
 
+               systable_endscan(child_scan);
+
                if (!found)
                        ereport(ERROR,
                                        (errcode(ERRCODE_DATATYPE_MISMATCH),
                                         errmsg("child table is missing constraint \"%s\"",
                                                        NameStr(parent_con->conname))));
-
-               if (parent_con->condeferrable != child_con->condeferrable ||
-                       parent_con->condeferred != child_con->condeferred ||
-                       strcmp(decompile_conbin(constraintTuple, tupleDesc),
-                                  decompile_conbin(child_contuple, tupleDesc)) != 0)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                        errmsg("constraint definition for check constraint \"%s\" does not match",
-                                                       NameStr(parent_con->conname))));
-
-               /*
-                * TODO: add conislocal,coninhcount to constraints. This is where we
-                * would have to bump them just like attributes
-                */
        }
 
-       systable_endscan(scan);
-       heap_close(catalogRelation, AccessShareLock);
+       systable_endscan(parent_scan);
+       heap_close(catalog_relation, RowExclusiveLock);
 }
 
 /*
@@ -6459,6 +6706,9 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
  * parent then its columns will never be automatically dropped which may
  * surprise. But at least we'll never surprise by dropping columns someone
  * isn't expecting to be dropped which would actually mean data loss.
+ *
+ * coninhcount and conislocal for inherited constraints are adjusted in
+ * exactly the same way.
  */
 static void
 ATExecDropInherit(Relation rel, RangeVar *parent)
@@ -6469,7 +6719,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent)
        ScanKeyData key[3];
        HeapTuple       inheritsTuple,
                                attributeTuple,
+                               constraintTuple,
                                depTuple;
+       List       *connames;
        bool            found = false;
 
        /*
@@ -6558,6 +6810,81 @@ ATExecDropInherit(Relation rel, RangeVar *parent)
        systable_endscan(scan);
        heap_close(catalogRelation, RowExclusiveLock);
 
+       /*
+        * Likewise, find inherited check constraints and disinherit them.
+        * To do this, we first need a list of the names of the parent's check
+        * constraints.  (We cheat a bit by only checking for name matches,
+        * assuming that the expressions will match.)
+        */
+       catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
+       ScanKeyInit(&key[0],
+                               Anum_pg_constraint_conrelid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(RelationGetRelid(parent_rel)));
+       scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
+                                                         true, SnapshotNow, 1, key);
+
+       connames = NIL;
+
+       while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
+       {
+               Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
+
+               if (con->contype == CONSTRAINT_CHECK)
+                       connames = lappend(connames, pstrdup(NameStr(con->conname)));
+       }
+
+       systable_endscan(scan);
+
+       /* Now scan the child's constraints */
+       ScanKeyInit(&key[0],
+                               Anum_pg_constraint_conrelid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(RelationGetRelid(rel)));
+       scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
+                                                         true, SnapshotNow, 1, key);
+
+       while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
+       {
+               Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
+               bool    match;
+               ListCell *lc;
+
+               if (con->contype != CONSTRAINT_CHECK)
+                       continue;
+
+               match = false;
+               foreach (lc, connames)
+               {
+                       if (strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
+                       {
+                               match = true;
+                               break;
+                       }
+               }
+
+               if (match)
+               {
+                       /* Decrement inhcount and possibly set islocal to true */
+                       HeapTuple       copyTuple = heap_copytuple(constraintTuple);
+                       Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
+                       if (copy_con->coninhcount <= 0)         /* shouldn't happen */
+                               elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
+                                        RelationGetRelid(rel), NameStr(copy_con->conname));
+
+                       copy_con->coninhcount--;
+                       if (copy_con->coninhcount == 0)
+                               copy_con->conislocal = true;
+
+                       simple_heap_update(catalogRelation, &copyTuple->t_self, copyTuple);
+                       CatalogUpdateIndexes(catalogRelation, copyTuple);
+                       heap_freetuple(copyTuple);
+               }
+       }
+
+       systable_endscan(scan);
+       heap_close(catalogRelation, RowExclusiveLock);
+
        /*
         * Drop the dependency
         *
index 3f557b078b7f688442af8d1aa60fc0e22d469f70..880788fd22bbad04160f3582cef42922aca7e299 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.117 2008/03/27 03:57:33 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.118 2008/05/09 23:32:04 tgl Exp $
  *
  * DESCRIPTION
  *       The "DefineFoo" routines take the parse tree and pick out the
@@ -2206,7 +2206,9 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
                                                  InvalidOid,
                                                  expr, /* Tree form check constraint */
                                                  ccbin,        /* Binary form check constraint */
-                                                 ccsrc);               /* Source form check constraint */
+                                                 ccsrc,        /* Source form check constraint */
+                                                 true, /* is local */
+                                                 0);   /* inhcount */
 
        /*
         * Return the compiled constraint expression so the calling routine can
index 8b14c04b56d0747358584af9b2c0559170378c44..048542b99b97537136966e5a3c42234cdbcbcc9f 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.306 2008/04/21 03:49:45 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.307 2008/05/09 23:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2626,7 +2626,7 @@ OpenIntoRel(QueryDesc *queryDesc)
                                                                         false);
        (void) heap_reloptions(RELKIND_RELATION, reloptions, true);
 
-       /* have to copy the actual tupdesc to get rid of any constraints */
+       /* Copy the tupdesc because heap_create_with_catalog modifies it */
        tupdesc = CreateTupleDescCopy(queryDesc->tupDesc);
 
        /* Now we can actually create the new relation */
@@ -2636,6 +2636,7 @@ OpenIntoRel(QueryDesc *queryDesc)
                                                                                          InvalidOid,
                                                                                          GetUserId(),
                                                                                          tupdesc,
+                                                                                         NIL,
                                                                                          RELKIND_RELATION,
                                                                                          false,
                                                                                          true,
index 2d41a52e1916e1a6c3388c35bce0628a8744b7ec..8950dee0ca867cfe9fce10b55fab23189e8bbf67 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.103 2008/03/27 03:57:33 tgl Exp $
+ *       $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.104 2008/05/09 23:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -62,8 +62,7 @@ static DumpableObject **oprinfoindex;
 
 static void flagInhTables(TableInfo *tbinfo, int numTables,
                          InhInfo *inhinfo, int numInherits);
-static void flagInhAttrs(TableInfo *tbinfo, int numTables,
-                        InhInfo *inhinfo, int numInherits);
+static void flagInhAttrs(TableInfo *tblinfo, int numTables);
 static DumpableObject **buildIndexArray(void *objArray, int numObjs,
                                Size objSize);
 static int     DOCatalogIdCompare(const void *p1, const void *p2);
@@ -191,7 +190,7 @@ getSchemaData(int *numTablesPtr)
 
        if (g_verbose)
                write_msg(NULL, "flagging inherited columns in subtables\n");
-       flagInhAttrs(tblinfo, numTables, inhinfo, numInherits);
+       flagInhAttrs(tblinfo, numTables);
 
        if (g_verbose)
                write_msg(NULL, "reading indexes\n");
@@ -257,8 +256,7 @@ flagInhTables(TableInfo *tblinfo, int numTables,
  * modifies tblinfo
  */
 static void
-flagInhAttrs(TableInfo *tblinfo, int numTables,
-                        InhInfo *inhinfo, int numInherits)
+flagInhAttrs(TableInfo *tblinfo, int numTables)
 {
        int                     i,
                                j,
@@ -414,43 +412,6 @@ flagInhAttrs(TableInfo *tblinfo, int numTables,
                                        tbinfo->inhAttrs[j] = false;
                        }
                }
-
-               /*
-                * Check for inherited CHECK constraints.  We assume a constraint is
-                * inherited if its name matches the name of any constraint in the
-                * parent.      Originally this code tried to compare the expression
-                * texts, but that can fail if the parent and child tables are in
-                * different schemas, because reverse-listing of function calls may
-                * produce different text (schema-qualified or not) depending on
-                * search path.  We really need a more bulletproof way of detecting
-                * inherited constraints --- pg_constraint should record this
-                * explicitly!
-                */
-               for (j = 0; j < tbinfo->ncheck; j++)
-               {
-                       ConstraintInfo *constr;
-
-                       constr = &(tbinfo->checkexprs[j]);
-
-                       for (k = 0; k < numParents; k++)
-                       {
-                               int                     l;
-
-                               parent = parents[k];
-                               for (l = 0; l < parent->ncheck; l++)
-                               {
-                                       ConstraintInfo *pconstr = &(parent->checkexprs[l]);
-
-                                       if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
-                                       {
-                                               constr->coninherited = true;
-                                               break;
-                                       }
-                               }
-                               if (constr->coninherited)
-                                       break;
-                       }
-               }
        }
 }
 
index f7ac1706de83193ec8f994f6ea6f141cae6fd2e9..18e83f4a9ee03162de236401f47372a84f1f03b1 100644 (file)
@@ -12,7 +12,7 @@
  *     by PostgreSQL
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.489 2008/05/03 23:32:32 adunstan Exp $
+ *       $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.490 2008/05/09 23:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -120,6 +120,7 @@ static void expand_table_name_patterns(SimpleStringList *patterns,
                                                   SimpleOidList *oids);
 static NamespaceInfo *findNamespace(Oid nsoid, Oid objoid);
 static void dumpTableData(Archive *fout, TableDataInfo *tdinfo);
+static void guessConstraintInheritance(TableInfo *tblinfo, int numTables);
 static void dumpComment(Archive *fout, const char *target,
                        const char *namespace, const char *owner,
                        CatalogId catalogId, int subid, DumpId dumpId);
@@ -645,6 +646,9 @@ main(int argc, char **argv)
         */
        tblinfo = getSchemaData(&numTables);
 
+       if (g_fout->remoteVersion < 80400)
+               guessConstraintInheritance(tblinfo, numTables);
+
        if (!schemaOnly)
                getTableData(tblinfo, numTables, oids);
 
@@ -1383,6 +1387,81 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids)
 }
 
 
+/*
+ * guessConstraintInheritance:
+ *     In pre-8.4 databases, we can't tell for certain which constraints
+ *     are inherited.  We assume a CHECK constraint is inherited if its name
+ *     matches the name of any constraint in the parent.  Originally this code
+ *     tried to compare the expression texts, but that can fail for various
+ *     reasons --- for example, if the parent and child tables are in different
+ *     schemas, reverse-listing of function calls may produce different text
+ *     (schema-qualified or not) depending on search path.
+ *
+ *     In 8.4 and up we can rely on the conislocal field to decide which
+ *     constraints must be dumped; much safer.
+ *
+ *     This function assumes all conislocal flags were initialized to TRUE.
+ *     It clears the flag on anything that seems to be inherited.
+ */
+static void
+guessConstraintInheritance(TableInfo *tblinfo, int numTables)
+{
+       int                     i,
+                               j,
+                               k;
+
+       for (i = 0; i < numTables; i++)
+       {
+               TableInfo  *tbinfo = &(tblinfo[i]);
+               int                     numParents;
+               TableInfo **parents;
+               TableInfo  *parent;
+
+               /* Sequences and views never have parents */
+               if (tbinfo->relkind == RELKIND_SEQUENCE ||
+                       tbinfo->relkind == RELKIND_VIEW)
+                       continue;
+
+               /* Don't bother computing anything for non-target tables, either */
+               if (!tbinfo->dobj.dump)
+                       continue;
+
+               numParents = tbinfo->numParents;
+               parents = tbinfo->parents;
+
+               if (numParents == 0)
+                       continue;                       /* nothing to see here, move along */
+
+               /* scan for inherited CHECK constraints */
+               for (j = 0; j < tbinfo->ncheck; j++)
+               {
+                       ConstraintInfo *constr;
+
+                       constr = &(tbinfo->checkexprs[j]);
+
+                       for (k = 0; k < numParents; k++)
+                       {
+                               int                     l;
+
+                               parent = parents[k];
+                               for (l = 0; l < parent->ncheck; l++)
+                               {
+                                       ConstraintInfo *pconstr = &(parent->checkexprs[l]);
+
+                                       if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
+                                       {
+                                               constr->conislocal = false;
+                                               break;
+                                       }
+                               }
+                               if (!constr->conislocal)
+                                       break;
+                       }
+               }
+       }
+}
+
+
 /*
  * dumpDatabase:
  *     dump the database definition
@@ -3522,7 +3601,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
                                constrinfo[j].contype = contype;
                                constrinfo[j].condef = NULL;
                                constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
-                               constrinfo[j].coninherited = false;
+                               constrinfo[j].conislocal = true;
                                constrinfo[j].separate = true;
 
                                indxinfo[j].indexconstraint = constrinfo[j].dobj.dumpId;
@@ -3623,7 +3702,7 @@ getConstraints(TableInfo tblinfo[], int numTables)
                        constrinfo[j].contype = 'f';
                        constrinfo[j].condef = strdup(PQgetvalue(res, j, i_condef));
                        constrinfo[j].conindex = 0;
-                       constrinfo[j].coninherited = false;
+                       constrinfo[j].conislocal = true;
                        constrinfo[j].separate = true;
                }
 
@@ -3706,7 +3785,7 @@ getDomainConstraints(TypeInfo *tinfo)
                constrinfo[i].contype = 'c';
                constrinfo[i].condef = strdup(PQgetvalue(res, i, i_consrc));
                constrinfo[i].conindex = 0;
-               constrinfo[i].coninherited = false;
+               constrinfo[i].conislocal = true;
                constrinfo[i].separate = false;
 
                /*
@@ -4586,10 +4665,22 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
                                                  tbinfo->dobj.name);
 
                        resetPQExpBuffer(q);
-                       if (g_fout->remoteVersion >= 70400)
+                       if (g_fout->remoteVersion >= 80400)
+                       {
+                               appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
+                                                       "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
+                                                                 "conislocal "
+                                                                 "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 >= 70400)
                        {
                                appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
-                                                       "pg_catalog.pg_get_constraintdef(oid) AS consrc "
+                                                       "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
+                                                                 "true as conislocal "
                                                                  "FROM pg_catalog.pg_constraint "
                                                                  "WHERE conrelid = '%u'::pg_catalog.oid "
                                                                  "   AND contype = 'c' "
@@ -4600,7 +4691,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
                        {
                                /* no pg_get_constraintdef, must use consrc */
                                appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
-                                                                 "'CHECK (' || consrc || ')' AS consrc "
+                                                                 "'CHECK (' || consrc || ')' AS consrc, "
+                                                                 "true as conislocal "
                                                                  "FROM pg_catalog.pg_constraint "
                                                                  "WHERE conrelid = '%u'::pg_catalog.oid "
                                                                  "   AND contype = 'c' "
@@ -4612,7 +4704,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
                                /* 7.2 did not have OIDs in pg_relcheck */
                                appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, "
                                                                  "rcname AS conname, "
-                                                                 "'CHECK (' || rcsrc || ')' AS consrc "
+                                                                 "'CHECK (' || rcsrc || ')' AS consrc, "
+                                                                 "true as conislocal "
                                                                  "FROM pg_relcheck "
                                                                  "WHERE rcrelid = '%u'::oid "
                                                                  "ORDER BY rcname",
@@ -4622,7 +4715,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
                        {
                                appendPQExpBuffer(q, "SELECT tableoid, oid, "
                                                                  "rcname AS conname, "
-                                                                 "'CHECK (' || rcsrc || ')' AS consrc "
+                                                                 "'CHECK (' || rcsrc || ')' AS consrc, "
+                                                                 "true as conislocal "
                                                                  "FROM pg_relcheck "
                                                                  "WHERE rcrelid = '%u'::oid "
                                                                  "ORDER BY rcname",
@@ -4634,7 +4728,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
                                appendPQExpBuffer(q, "SELECT "
                                                                  "(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
                                                                  "oid, rcname AS conname, "
-                                                                 "'CHECK (' || rcsrc || ')' AS consrc "
+                                                                 "'CHECK (' || rcsrc || ')' AS consrc, "
+                                                                 "true as conislocal "
                                                                  "FROM pg_relcheck "
                                                                  "WHERE rcrelid = '%u'::oid "
                                                                  "ORDER BY rcname",
@@ -4668,7 +4763,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
                                constrs[j].contype = 'c';
                                constrs[j].condef = strdup(PQgetvalue(res, j, 3));
                                constrs[j].conindex = 0;
-                               constrs[j].coninherited = false;
+                               constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't');
                                constrs[j].separate = false;
 
                                constrs[j].dobj.dump = tbinfo->dobj.dump;
@@ -4684,8 +4779,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
 
                                /*
                                 * If the constraint is inherited, this will be detected
-                                * later.  We also detect later if the constraint must be
-                                * split out from the table definition.
+                                * later (in pre-8.4 databases).  We also detect later if the
+                                * constraint must be split out from the table definition.
                                 */
                        }
                        PQclear(res);
@@ -8840,7 +8935,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
                {
                        ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
 
-                       if (constr->coninherited || constr->separate)
+                       if (constr->separate || !constr->conislocal)
                                continue;
 
                        if (actual_atts > 0)
@@ -8955,7 +9050,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
        {
                ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
 
-               if (constr->coninherited || constr->separate)
+               if (constr->separate || !constr->conislocal)
                        continue;
 
                dumpTableConstraintComment(fout, constr);
index d4b40e9d887b9518db72c54d86e9407a41aa587e..8fbe98221b6c8e9e67cd1589e7d8d9a5517c683d 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.139 2008/01/01 19:45:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.140 2008/05/09 23:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -353,7 +353,7 @@ typedef struct _constraintInfo
        char            contype;
        char       *condef;                     /* definition, if CHECK or FOREIGN KEY */
        DumpId          conindex;               /* identifies associated index if any */
-       bool            coninherited;   /* TRUE if appears to be inherited */
+       bool            conislocal;             /* TRUE if constraint has local definition */
        bool            separate;               /* TRUE if must dump as separate item */
 } ConstraintInfo;
 
index 9b0cb80e174c4765c3d39390eb8f05dcd1856137..fcc9c6c2340a87b9a50af41707306e21224eb5f9 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.457 2008/05/08 08:58:59 mha Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.458 2008/05/09 23:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200805081
+#define CATALOG_VERSION_NO     200805091
 
 #endif
index 727af295c769f47f4e96a34863b8080faa464eb0..52347a65522401d143d20c5d30041e3bd024cb73 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.87 2008/01/01 19:45:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.88 2008/05/09 23:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,8 @@ typedef struct CookedConstraint
        char       *name;                       /* name, or NULL if none */
        AttrNumber      attnum;                 /* which attr (only for DEFAULT) */
        Node       *expr;                       /* transformed default or check expr */
+       bool            is_local;               /* constraint has local (non-inherited) def */
+       int                     inhcount;               /* number of times constraint is inherited */
 } CookedConstraint;
 
 extern Relation heap_create(const char *relname,
@@ -46,6 +48,7 @@ extern Oid heap_create_with_catalog(const char *relname,
                                                 Oid relid,
                                                 Oid ownerid,
                                                 TupleDesc tupdesc,
+                                                List *cooked_constraints,
                                                 char relkind,
                                                 bool shared_relation,
                                                 bool oidislocal,
@@ -67,11 +70,13 @@ extern void InsertPgClassTuple(Relation pg_class_desc,
                                   Oid new_rel_oid,
                                   Datum reloptions);
 
-extern List *AddRelationRawConstraints(Relation rel,
-                                                 List *rawColDefaults,
-                                                 List *rawConstraints);
+extern List *AddRelationNewConstraints(Relation rel,
+                                                 List *newColDefaults,
+                                                 List *newConstraints,
+                                                 bool allow_merge,
+                                                 bool is_local);
 
-extern void StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin);
+extern void StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr);
 
 extern Node *cookDefault(ParseState *pstate,
                        Node *raw_default,
@@ -79,9 +84,6 @@ extern Node *cookDefault(ParseState *pstate,
                        int32 atttypmod,
                        char *attname);
 
-extern int RemoveRelConstraints(Relation rel, const char *constrName,
-                                        DropBehavior behavior);
-
 extern void DeleteRelationTuple(Oid relid);
 extern void DeleteAttributeTuples(Oid relid);
 extern void RemoveAttributeById(Oid relid, AttrNumber attnum);
index a05e2a4a40ee876a594d69b4dfd8a1e14ee25cc4..e94067ae95322d4dc8040a34b9136105109567e1 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.28 2008/03/27 03:57:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.29 2008/05/09 23:32:04 tgl Exp $
  *
  * NOTES
  *       the genbki.sh script reads this file and generates .bki
@@ -71,6 +71,12 @@ CATALOG(pg_constraint,2606)
        char            confdeltype;    /* foreign key's ON DELETE action */
        char            confmatchtype;  /* foreign key's match type */
 
+       /* Has a local definition (hence, do not drop when coninhcount is 0) */
+       bool            conislocal;
+
+       /* Number of times inherited from direct parent relation(s) */
+       int4            coninhcount;
+
        /*
         * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
         */
@@ -125,7 +131,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
  *             compiler constants for pg_constraint
  * ----------------
  */
-#define Natts_pg_constraint                                    18
+#define Natts_pg_constraint                                    20
 #define Anum_pg_constraint_conname                     1
 #define Anum_pg_constraint_connamespace                2
 #define Anum_pg_constraint_contype                     3
@@ -137,13 +143,15 @@ typedef FormData_pg_constraint *Form_pg_constraint;
 #define Anum_pg_constraint_confupdtype         9
 #define Anum_pg_constraint_confdeltype         10
 #define Anum_pg_constraint_confmatchtype       11
-#define Anum_pg_constraint_conkey                      12
-#define Anum_pg_constraint_confkey                     13
-#define Anum_pg_constraint_conpfeqop           14
-#define Anum_pg_constraint_conppeqop           15
-#define Anum_pg_constraint_conffeqop           16
-#define Anum_pg_constraint_conbin                      17
-#define Anum_pg_constraint_consrc                      18
+#define Anum_pg_constraint_conislocal          12
+#define Anum_pg_constraint_coninhcount         13
+#define Anum_pg_constraint_conkey                      14
+#define Anum_pg_constraint_confkey                     15
+#define Anum_pg_constraint_conpfeqop           16
+#define Anum_pg_constraint_conppeqop           17
+#define Anum_pg_constraint_conffeqop           18
+#define Anum_pg_constraint_conbin                      19
+#define Anum_pg_constraint_consrc                      20
 
 
 /* Valid values for contype */
@@ -192,7 +200,9 @@ extern Oid CreateConstraintEntry(const char *constraintName,
                                          Oid indexRelId,
                                          Node *conExpr,
                                          const char *conBin,
-                                         const char *conSrc);
+                                         const char *conSrc,
+                                         bool conIsLocal,
+                                         int conInhCount);
 
 extern void RemoveConstraintById(Oid conId);
 extern void RenameConstraintById(Oid conId, const char *newname);
index 61c3df539139769a6c1ed7949aae85a8fb8f0418..0875b99a2248771d93f350f57f42a3b9101be8f8 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.364 2008/04/29 20:44:49 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.365 2008/05/09 23:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -892,11 +892,11 @@ typedef enum AlterTableType
        AT_AddIndex,                            /* add index */
        AT_ReAddIndex,                          /* internal to commands/tablecmds.c */
        AT_AddConstraint,                       /* add constraint */
+       AT_AddConstraintRecurse,        /* internal to commands/tablecmds.c */
        AT_ProcessedConstraint,         /* pre-processed add constraint (local in
                                                                 * parser/parse_utilcmd.c) */
        AT_DropConstraint,                      /* drop constraint */
-       AT_DropConstraintQuietly,       /* drop constraint, no error/warning (local in
-                                                                * commands/tablecmds.c) */
+       AT_DropConstraintRecurse,       /* internal to commands/tablecmds.c */
        AT_AlterColumnType,                     /* alter column type */
        AT_ChangeOwner,                         /* change owner */
        AT_ClusterOn,                           /* CLUSTER ON */
index 51d5afa81fd9a771576c2e42e27a7c77db3c071d..b5af16c558ec65b75539b008b8b6cdd47d83310b 100644 (file)
@@ -378,19 +378,21 @@ drop table atacc2 cascade;
 NOTICE:  drop cascades to table atacc3
 NOTICE:  drop cascades to constraint foo on table atacc3
 drop table atacc1;
--- let's try only to add only to the parent
+-- adding only to a parent is disallowed as of 8.4
 create table atacc1 (test int);
-create table atacc2 (test2 int);
-create table atacc3 (test3 int) inherits (atacc1, atacc2);
-alter table only atacc2 add constraint foo check (test2>0);
--- fail and then succeed on atacc2
-insert into atacc2 (test2) values (-3);
+create table atacc2 (test2 int) inherits (atacc1);
+-- fail:
+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
+insert into atacc1 (test) values (-3);
+insert into atacc1 (test) values (3);
+-- check constraint is there on child
+insert into atacc2 (test) values (-3);
 ERROR:  new row for relation "atacc2" violates check constraint "foo"
-insert into atacc2 (test2) values (3);
--- both succeed on atacc3
-insert into atacc3 (test2) values (-3);
-insert into atacc3 (test2) values (3);
-drop table atacc3;
+insert into atacc2 (test) values (3);
 drop table atacc2;
 drop table atacc1;
 -- test unique constraint adding
@@ -1230,7 +1232,7 @@ alter table p1 add column f2 text;
 NOTICE:  merging definition of column "f2" for child "c1"
 insert into p1 values (1,2,'abc');
 insert into c1 values(11,'xyz',33,0); -- should fail
-ERROR:  new row for relation "c1" violates check constraint "c1_a1_check"
+ERROR:  new row for relation "c1" violates check constraint "p1_a1_check"
 insert into c1 values(11,'xyz',33,22);
 select * from p1;
  f1 | a1 | f2  
@@ -1249,7 +1251,7 @@ select * from p1;
 
 drop table p1 cascade;
 NOTICE:  drop cascades to table c1
-NOTICE:  drop cascades to constraint c1_a1_check on table c1
+NOTICE:  drop cascades to constraint p1_a1_check on table c1
 -- test that operations with a dropped column do not try to reference
 -- its datatype
 create domain mytype as text;
index f81776fe80463dac11247b8cab439d3415f96030..e3acd03c17db689f6e5d4d30aa7c2b059a686be9 100644 (file)
@@ -692,3 +692,220 @@ drop function p2text(p2);
 drop table c1;
 drop table p2;
 drop table p1;
+CREATE TABLE ac (aa TEXT);
+alter table ac add constraint ac_check check (aa is not null);
+CREATE TABLE bc (bb TEXT) INHERITS (ac);
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+ relname | conname  | contype | conislocal | coninhcount |      consrc      
+---------+----------+---------+------------+-------------+------------------
+ ac      | ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ bc      | ac_check | c       | f          |           1 | (aa IS NOT NULL)
+(2 rows)
+
+insert into ac (aa) values (NULL);
+ERROR:  new row for relation "ac" violates check constraint "ac_check"
+insert into bc (aa) values (NULL);
+ERROR:  new row for relation "bc" violates check constraint "ac_check"
+alter table bc drop constraint ac_check;  -- fail, disallowed
+ERROR:  cannot drop inherited constraint "ac_check" of relation "bc"
+alter table ac drop constraint ac_check;
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+ relname | conname | contype | conislocal | coninhcount | consrc 
+---------+---------+---------+------------+-------------+--------
+(0 rows)
+
+-- try the unnamed-constraint case
+alter table ac add check (aa is not null);
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+ relname |   conname   | contype | conislocal | coninhcount |      consrc      
+---------+-------------+---------+------------+-------------+------------------
+ ac      | ac_aa_check | c       | t          |           0 | (aa IS NOT NULL)
+ bc      | ac_aa_check | c       | f          |           1 | (aa IS NOT NULL)
+(2 rows)
+
+insert into ac (aa) values (NULL);
+ERROR:  new row for relation "ac" violates check constraint "ac_aa_check"
+insert into bc (aa) values (NULL);
+ERROR:  new row for relation "bc" violates check constraint "ac_aa_check"
+alter table bc drop constraint ac_aa_check;  -- fail, disallowed
+ERROR:  cannot drop inherited constraint "ac_aa_check" of relation "bc"
+alter table ac drop constraint ac_aa_check;
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+ relname | conname | contype | conislocal | coninhcount | consrc 
+---------+---------+---------+------------+-------------+--------
+(0 rows)
+
+alter table ac add constraint ac_check check (aa is not null);
+alter table bc no inherit ac;
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+ relname | conname  | contype | conislocal | coninhcount |      consrc      
+---------+----------+---------+------------+-------------+------------------
+ ac      | ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ bc      | ac_check | c       | t          |           0 | (aa IS NOT NULL)
+(2 rows)
+
+alter table bc drop constraint ac_check;
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+ relname | conname  | contype | conislocal | coninhcount |      consrc      
+---------+----------+---------+------------+-------------+------------------
+ ac      | ac_check | c       | t          |           0 | (aa IS NOT NULL)
+(1 row)
+
+alter table ac drop constraint ac_check;
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+ relname | conname | contype | conislocal | coninhcount | consrc 
+---------+---------+---------+------------+-------------+--------
+(0 rows)
+
+drop table bc;
+drop table ac;
+create table ac (a int constraint check_a check (a <> 0));
+create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
+NOTICE:  merging column "a" with inherited definition
+NOTICE:  merging constraint "check_a" with inherited definition
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+ relname | conname | contype | conislocal | coninhcount |  consrc  
+---------+---------+---------+------------+-------------+----------
+ ac      | check_a | c       | t          |           0 | (a <> 0)
+ bc      | check_a | c       | t          |           1 | (a <> 0)
+ bc      | check_b | c       | t          |           0 | (b <> 0)
+(3 rows)
+
+drop table bc;
+drop table ac;
+create table ac (a int constraint check_a check (a <> 0));
+create table bc (b int constraint check_b check (b <> 0));
+create table cc (c int constraint check_c check (c <> 0)) inherits (ac, bc);
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc', 'cc') order by 1,2;
+ relname | conname | contype | conislocal | coninhcount |  consrc  
+---------+---------+---------+------------+-------------+----------
+ ac      | check_a | c       | t          |           0 | (a <> 0)
+ bc      | check_b | c       | t          |           0 | (b <> 0)
+ cc      | check_a | c       | f          |           1 | (a <> 0)
+ cc      | check_b | c       | f          |           1 | (b <> 0)
+ cc      | check_c | c       | t          |           0 | (c <> 0)
+(5 rows)
+
+alter table cc no inherit bc;
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc', 'cc') order by 1,2;
+ relname | conname | contype | conislocal | coninhcount |  consrc  
+---------+---------+---------+------------+-------------+----------
+ ac      | check_a | c       | t          |           0 | (a <> 0)
+ bc      | check_b | c       | t          |           0 | (b <> 0)
+ cc      | check_a | c       | f          |           1 | (a <> 0)
+ cc      | check_b | c       | t          |           0 | (b <> 0)
+ cc      | check_c | c       | t          |           0 | (c <> 0)
+(5 rows)
+
+drop table cc;
+drop table bc;
+drop table ac;
+create table p1(f1 int);
+create table p2(f2 int);
+create table c1(f3 int) inherits(p1,p2);
+insert into c1 values(1,-1,2);
+alter table p2 add constraint cc check (f2>0);  -- fail
+ERROR:  check constraint "cc" is violated by some row
+alter table p2 add check (f2>0);  -- check it without a name, too
+ERROR:  check constraint "p2_f2_check" is violated by some row
+delete from c1;
+insert into c1 values(1,1,2);
+alter table p2 add check (f2>0);
+insert into c1 values(1,-1,2);  -- fail
+ERROR:  new row for relation "c1" violates check constraint "p2_f2_check"
+create table c2(f3 int) inherits(p1,p2);
+\d c2
+      Table "public.c2"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ f1     | integer |          
+ f2     | integer |          
+ f3     | integer |          
+Check constraints:
+    "p2_f2_check" CHECK (f2 > 0)
+Inherits: p1,
+          p2
+
+create table c3 (f4 int) inherits(c1,c2);
+NOTICE:  merging multiple inherited definitions of column "f1"
+NOTICE:  merging multiple inherited definitions of column "f2"
+NOTICE:  merging multiple inherited definitions of column "f3"
+\d c3
+      Table "public.c3"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ f1     | integer |          
+ f2     | integer |          
+ f3     | integer |          
+ f4     | integer |          
+Check constraints:
+    "p2_f2_check" CHECK (f2 > 0)
+Inherits: c1,
+          c2
+
+drop table p1 cascade;
+NOTICE:  drop cascades to table c2
+NOTICE:  drop cascades to table c3
+NOTICE:  drop cascades to constraint p2_f2_check on table c3
+NOTICE:  drop cascades to constraint p2_f2_check on table c2
+NOTICE:  drop cascades to table c1
+NOTICE:  drop cascades to constraint p2_f2_check on table c1
+drop table p2 cascade;
+create table pp1 (f1 int);
+create table cc1 (f2 text, f3 int) inherits (pp1);
+alter table pp1 add column a1 int check (a1 > 0);
+\d cc1
+      Table "public.cc1"
+ Column |  Type   | Modifiers 
+--------+---------+-----------
+ f1     | integer |          
+ f2     | text    |          
+ f3     | integer |          
+ a1     | integer |          
+Check constraints:
+    "pp1_a1_check" CHECK (a1 > 0)
+Inherits: pp1
+
+create table cc2(f4 float) inherits(pp1,cc1);
+NOTICE:  merging multiple inherited definitions of column "f1"
+NOTICE:  merging multiple inherited definitions of column "a1"
+\d cc2
+          Table "public.cc2"
+ Column |       Type       | Modifiers 
+--------+------------------+-----------
+ f1     | integer          |          
+ a1     | integer          |          
+ f2     | text             |          
+ f3     | integer          |          
+ f4     | double precision |          
+Check constraints:
+    "pp1_a1_check" CHECK (a1 > 0)
+Inherits: pp1,
+          cc1
+
+alter table pp1 add column a2 int check (a2 > 0);
+NOTICE:  merging definition of column "a2" for child "cc2"
+NOTICE:  merging constraint "pp1_a2_check" with inherited definition
+\d cc2
+          Table "public.cc2"
+ Column |       Type       | Modifiers 
+--------+------------------+-----------
+ f1     | integer          |          
+ a1     | integer          |          
+ f2     | text             |          
+ f3     | integer          |          
+ f4     | double precision |          
+ a2     | integer          |          
+Check constraints:
+    "pp1_a1_check" CHECK (a1 > 0)
+    "pp1_a2_check" CHECK (a2 > 0)
+Inherits: pp1,
+          cc1
+
+drop table pp1 cascade;
+NOTICE:  drop cascades to table cc2
+NOTICE:  drop cascades to constraint pp1_a1_check on table cc2
+NOTICE:  drop cascades to constraint pp1_a2_check on table cc2
+NOTICE:  drop cascades to table cc1
+NOTICE:  drop cascades to constraint pp1_a1_check on table cc1
+NOTICE:  drop cascades to constraint pp1_a2_check on table cc1
index 81cc70612d5ae5194edbf16413ec761aa2b2d713..46aacd1bef8bef8b461f4cbcd28987eb6dc729e5 100644 (file)
@@ -389,19 +389,20 @@ select test2 from atacc2;
 drop table atacc2 cascade;
 drop table atacc1;
 
--- let's try only to add only to the parent
+-- adding only to a parent is disallowed as of 8.4
 
 create table atacc1 (test int);
-create table atacc2 (test2 int);
-create table atacc3 (test3 int) inherits (atacc1, atacc2);
-alter table only atacc2 add constraint foo check (test2>0);
--- fail and then succeed on atacc2
-insert into atacc2 (test2) values (-3);
-insert into atacc2 (test2) values (3);
--- both succeed on atacc3
-insert into atacc3 (test2) values (-3);
-insert into atacc3 (test2) values (3);
-drop table atacc3;
+create table atacc2 (test2 int) inherits (atacc1);
+-- fail:
+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
+insert into atacc1 (test) values (-3);
+insert into atacc1 (test) values (3);
+-- check constraint is there on child
+insert into atacc2 (test) values (-3);
+insert into atacc2 (test) values (3);
 drop table atacc2;
 drop table atacc1;
 
index b0499a649284df366d8974fca70e92423d1a4fae..1730a485756a83c63cd5355b395695964b736182 100644 (file)
@@ -196,3 +196,83 @@ drop function p2text(p2);
 drop table c1;
 drop table p2;
 drop table p1;
+
+CREATE TABLE ac (aa TEXT);
+alter table ac add constraint ac_check check (aa is not null);
+CREATE TABLE bc (bb TEXT) INHERITS (ac);
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+
+insert into ac (aa) values (NULL);
+insert into bc (aa) values (NULL);
+
+alter table bc drop constraint ac_check;  -- fail, disallowed
+alter table ac drop constraint ac_check;
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+
+-- try the unnamed-constraint case
+alter table ac add check (aa is not null);
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+
+insert into ac (aa) values (NULL);
+insert into bc (aa) values (NULL);
+
+alter table bc drop constraint ac_aa_check;  -- fail, disallowed
+alter table ac drop constraint ac_aa_check;
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+
+alter table ac add constraint ac_check check (aa is not null);
+alter table bc no inherit ac;
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+alter table bc drop constraint ac_check;
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+alter table ac drop constraint ac_check;
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+
+drop table bc;
+drop table ac;
+
+create table ac (a int constraint check_a check (a <> 0));
+create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2;
+
+drop table bc;
+drop table ac;
+
+create table ac (a int constraint check_a check (a <> 0));
+create table bc (b int constraint check_b check (b <> 0));
+create table cc (c int constraint check_c check (c <> 0)) inherits (ac, bc);
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc', 'cc') order by 1,2;
+
+alter table cc no inherit bc;
+select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc', 'cc') order by 1,2;
+
+drop table cc;
+drop table bc;
+drop table ac;
+
+create table p1(f1 int);
+create table p2(f2 int);
+create table c1(f3 int) inherits(p1,p2);
+insert into c1 values(1,-1,2);
+alter table p2 add constraint cc check (f2>0);  -- fail
+alter table p2 add check (f2>0);  -- check it without a name, too
+delete from c1;
+insert into c1 values(1,1,2);
+alter table p2 add check (f2>0);
+insert into c1 values(1,-1,2);  -- fail
+create table c2(f3 int) inherits(p1,p2);
+\d c2
+create table c3 (f4 int) inherits(c1,c2);
+\d c3
+drop table p1 cascade;
+drop table p2 cascade;
+
+create table pp1 (f1 int);
+create table cc1 (f2 text, f3 int) inherits (pp1);
+alter table pp1 add column a1 int check (a1 > 0);
+\d cc1
+create table cc2(f4 float) inherits(pp1,cc1);
+\d cc2
+alter table pp1 add column a2 int check (a2 > 0);
+\d cc2
+drop table pp1 cascade;