Add DOMAIN check constraints.
authorBruce Momjian <bruce@momjian.us>
Fri, 15 Nov 2002 02:50:21 +0000 (02:50 +0000)
committerBruce Momjian <bruce@momjian.us>
Fri, 15 Nov 2002 02:50:21 +0000 (02:50 +0000)
Rod Taylor

29 files changed:
doc/src/sgml/ref/create_domain.sgml
src/backend/catalog/heap.c
src/backend/catalog/pg_constraint.c
src/backend/commands/tablecmds.c
src/backend/commands/typecmds.c
src/backend/executor/execQual.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/util/clauses.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/parser/parse_clause.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_node.c
src/backend/parser/parse_target.c
src/backend/utils/adt/ruleutils.c
src/include/catalog/indexing.h
src/include/catalog/pg_constraint.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/optimizer/var.h
src/include/parser/parse_expr.h
src/test/regress/expected/domain.out
src/test/regress/sql/domain.sql

index b7d5e606d3e602019fc358deba84020527df96bd..bd4114688f6347d40009cad51a6a8ede8227b0b2 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_domain.sgml,v 1.6 2002/09/20 03:39:15 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_domain.sgml,v 1.7 2002/11/15 02:50:05 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -200,16 +200,6 @@ CREATE TABLE countrylist (id INT4, country country_code);
   </para>
  </refsect1>
 
- <refsect1 id="SQL-CREATEDOMAIN-compatibility">
-  <title>Compatibility</title>
-
-  <para>
-   SQL99 defines CREATE DOMAIN, but says that the only allowed constraint
-   type is CHECK constraints.  CHECK constraints for domains are not yet
-   supported by <productname>PostgreSQL</productname>.
-  </para>
- </refsect1>
-
  <refsect1 id="SQL-CREATEDOMAIN-see-also">
   <title>See Also</title>
 
index 9956672c12af6f96b7baef284e24429a7bff078d..f8f667e24a9d8f01f4c89b27ca86b66aba61cb09 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.234 2002/11/11 22:19:21 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.235 2002/11/15 02:50:05 momjian Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1500,7 +1500,8 @@ AddRelationRawConstraints(Relation rel,
 
                        ccname = cdef->name;
                        /* Check against pre-existing constraints */
-                       if (ConstraintNameIsUsed(RelationGetRelid(rel),
+                       if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
+                                                                        RelationGetRelid(rel),
                                                                         RelationGetNamespace(rel),
                                                                         ccname))
                                elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
@@ -1534,7 +1535,8 @@ AddRelationRawConstraints(Relation rel,
                                 * pre-existing constraints, nor with any auto-generated
                                 * names so far.
                                 */
-                               ccname = GenerateConstraintName(RelationGetRelid(rel),
+                               ccname = GenerateConstraintName(CONSTRAINT_RELATION,
+                                                                                               RelationGetRelid(rel),
                                                                                                RelationGetNamespace(rel),
                                                                                                &constr_name_ctr);
 
@@ -1565,7 +1567,7 @@ AddRelationRawConstraints(Relation rel,
                /*
                 * Transform raw parsetree to executable expression.
                 */
-               expr = transformExpr(pstate, cdef->raw_expr);
+               expr = transformExpr(pstate, cdef->raw_expr, NULL);
 
                /*
                 * Make sure it yields a boolean result.
@@ -1694,7 +1696,7 @@ cookDefault(ParseState *pstate,
        /*
         * Transform raw parsetree to executable expression.
         */
-       expr = transformExpr(pstate, raw_default);
+       expr = transformExpr(pstate, raw_default, NULL);
 
        /*
         * Make sure default expr does not refer to any vars.
index e03e545beb37f5c4ea8929c8ad9cb24c08e5dffb..7cd105928c331309c96539c82ef20bbc9a30d3f0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.7 2002/09/22 00:37:09 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.8 2002/11/15 02:50:05 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -190,6 +190,19 @@ CreateConstraintEntry(const char *constraintName,
                }
        }
 
+       if (OidIsValid(domainId))
+       {
+               /*
+                * Register auto dependency from constraint to owning domain
+                */
+               ObjectAddress   domobject;
+
+               domobject.classId = RelOid_pg_type;
+               domobject.objectId = domainId;
+
+               recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
+       }
+
        if (OidIsValid(foreignRelId))
        {
                /*
@@ -262,7 +275,7 @@ CreateConstraintEntry(const char *constraintName,
  * this test is not very meaningful.
  */
 bool
-ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
+ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, const char *cname)
 {
        bool            found;
        Relation        conDesc;
@@ -280,7 +293,7 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
 
        ScanKeyEntryInitialize(&skey[1], 0x0,
                                                   Anum_pg_constraint_connamespace, F_OIDEQ,
-                                                  ObjectIdGetDatum(relNamespace));
+                                                  ObjectIdGetDatum(objNamespace));
 
        conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
                                                                 SnapshotNow, 2, skey);
@@ -289,7 +302,12 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
        {
                Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
 
-               if (con->conrelid == relId)
+               if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
+               {
+                       found = true;
+                       break;
+               }
+               else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
                {
                        found = true;
                        break;
@@ -314,7 +332,7 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
  * someone else might choose the same name concurrently!
  */
 char *
-GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
+GenerateConstraintName(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, int *counter)
 {
        bool            found;
        Relation        conDesc;
@@ -347,7 +365,7 @@ GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
 
                ScanKeyEntryInitialize(&skey[1], 0x0,
                                                           Anum_pg_constraint_connamespace, F_OIDEQ,
-                                                          ObjectIdGetDatum(relNamespace));
+                                                          ObjectIdGetDatum(objNamespace));
 
                conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
                                                                         SnapshotNow, 2, skey);
@@ -356,7 +374,12 @@ GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
                {
                        Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
 
-                       if (con->conrelid == relId)
+                       if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
+                       {
+                               found = true;
+                               break;
+                       }
+                       else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
                        {
                                found = true;
                                break;
@@ -415,10 +438,13 @@ RemoveConstraintById(Oid conId)
        con = (Form_pg_constraint) GETSTRUCT(tup);
 
        /*
-        * If the constraint is for a relation, open and exclusive-lock the
-        * relation it's for.
+        * If the constraint is for a relation, open and exclusive-lock
+        * the relation it's for.
+        *
+        * If the constraint is for a domain, open and lock the pg_type entry
+        * tye constraint is used on.
         *
-        * XXX not clear what we should lock, if anything, for other constraints.
+        * XXX not clear what we should lock, if anything, for assert constraints.
         */
        if (OidIsValid(con->conrelid))
        {
@@ -463,6 +489,34 @@ RemoveConstraintById(Oid conId)
                /* Keep lock on constraint's rel until end of xact */
                heap_close(rel, NoLock);
        }
+       /* Lock the domain row in pg_type */
+       else if (OidIsValid(con->contypid))
+       {
+               Relation        typRel;
+               HeapTuple       typTup;
+               ScanKeyData typKey[1];
+               SysScanDesc typScan;
+
+               typRel = heap_openr(TypeRelationName, RowExclusiveLock);
+
+               ScanKeyEntryInitialize(&typKey[0], 0x0,
+                                                          Anum_pg_constraint_contypid, F_OIDEQ,
+                                                          ObjectIdGetDatum(con->contypid));
+
+               typScan = systable_beginscan(typRel, TypeOidIndex, true,
+                                                                        SnapshotNow, 1, typKey);
+
+               typTup = systable_getnext(typScan);
+
+               if (!HeapTupleIsValid(typTup))
+                       elog(ERROR, "RemoveConstraintById: Type %d does not exist",
+                                con->contypid);
+
+               systable_endscan(typScan);
+
+               /* Keep lock on domain type until end of xact */
+               heap_close(typRel, NoLock);
+       }
 
        /* Fry the constraint itself */
        simple_heap_delete(conDesc, &tup->t_self);
index 8023ba834204b070580e314b0d7ebd30549e3dd2..cda8687e448c872b2185eb44e8589c52b2f48119 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.53 2002/11/11 22:19:21 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.54 2002/11/15 02:50:05 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2632,14 +2632,16 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
                                         */
                                        if (constr->name)
                                        {
-                                               if (ConstraintNameIsUsed(RelationGetRelid(rel),
+                                               if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
+                                                                                                RelationGetRelid(rel),
                                                                                                 RelationGetNamespace(rel),
                                                                                                 constr->name))
                                                        elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
                                                                 constr->name, RelationGetRelationName(rel));
                                        }
                                        else
-                                               constr->name = GenerateConstraintName(RelationGetRelid(rel),
+                                               constr->name = GenerateConstraintName(CONSTRAINT_RELATION,
+                                                                                                                         RelationGetRelid(rel),
                                                                                                                          RelationGetNamespace(rel),
                                                                                                                          &counter);
 
@@ -2668,7 +2670,8 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
                                         */
                                        if (fkconstraint->constr_name)
                                        {
-                                               if (ConstraintNameIsUsed(RelationGetRelid(rel),
+                                               if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
+                                                                                                  RelationGetRelid(rel),
                                                                                           RelationGetNamespace(rel),
                                                                                          fkconstraint->constr_name))
                                                        elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
@@ -2676,7 +2679,8 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
                                                                 RelationGetRelationName(rel));
                                        }
                                        else
-                                               fkconstraint->constr_name = GenerateConstraintName(RelationGetRelid(rel),
+                                               fkconstraint->constr_name = GenerateConstraintName(CONSTRAINT_RELATION,
+                                                                                                  RelationGetRelid(rel),
                                                                                           RelationGetNamespace(rel),
                                                                                                                           &counter);
 
@@ -2734,7 +2738,7 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr)
        /*
         * Convert the A_EXPR in raw_expr into an EXPR
         */
-       expr = transformExpr(pstate, constr->raw_expr);
+       expr = transformExpr(pstate, constr->raw_expr, NULL);
 
        /*
         * Make sure it yields a boolean result.
index a573cac27def844350bafebb51f94965780f2fd4..ab0608a08a9cdeba90c52d98959e2e3a30d6c142 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.16 2002/11/11 22:19:22 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.17 2002/11/15 02:50:06 momjian Exp $
  *
  * DESCRIPTION
  *       The "DefineFoo" routines take the parse tree and pick out the
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/tablecmds.h"
 #include "miscadmin.h"
+#include "nodes/nodes.h"
+#include "optimizer/clauses.h"
+#include "optimizer/planmain.h"
+#include "optimizer/var.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_expr.h"
 #include "parser/parse_func.h"
 #include "parser/parse_type.h"
 #include "utils/acl.h"
@@ -406,7 +413,8 @@ DefineDomain(CreateDomainStmt *stmt)
        List       *listptr;
        Oid                     basetypeoid;
        Oid                     domainoid;
-       Form_pg_type baseType;
+       Form_pg_type    baseType;
+       int                     counter = 0;
 
        /* Convert list of names to a name and namespace */
        domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
@@ -484,17 +492,21 @@ DefineDomain(CreateDomainStmt *stmt)
        basetypelem = baseType->typelem;
 
        /*
-        * Run through constraints manually to avoid the additional processing
-        * conducted by DefineRelation() and friends.
-        *
-        * Besides, we don't want any constraints to be cooked.  We'll do that
-        * when the table is created via MergeDomainAttributes().
+        * Run through constraints manually to avoid the additional
+        * processing conducted by DefineRelation() and friends.
         */
        foreach(listptr, schema)
        {
-               Constraint *colDef = lfirst(listptr);
+               Node       *newConstraint = lfirst(listptr);
+               Constraint *colDef;
                ParseState *pstate;
 
+               /* Prior to processing, confirm that it is not a foreign key constraint */
+               if (nodeTag(newConstraint) == T_FkConstraint)
+                       elog(ERROR, "CREATE DOMAIN / FOREIGN KEY constraints not supported");
+
+               colDef = (Constraint *) newConstraint;
+
                switch (colDef->contype)
                {
                                /*
@@ -546,26 +558,26 @@ DefineDomain(CreateDomainStmt *stmt)
                                        elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
                                typNotNull = false;
                                nullDefined = true;
-                               break;
+                               break;
 
-                       case CONSTR_UNIQUE:
-                               elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported");
-                               break;
+                       case CONSTR_UNIQUE:
+                               elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported");
+                               break;
 
-                       case CONSTR_PRIMARY:
-                               elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported");
-                               break;
+                       case CONSTR_PRIMARY:
+                               elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported");
+                               break;
 
-                       case CONSTR_CHECK:
-                               elog(ERROR, "DefineDomain: CHECK Constraints not supported");
-                               break;
+                       /* Check constraints are handled after domain creation */
+                       case CONSTR_CHECK:
+                               break;
 
-                       case CONSTR_ATTR_DEFERRABLE:
-                       case CONSTR_ATTR_NOT_DEFERRABLE:
-                       case CONSTR_ATTR_DEFERRED:
-                       case CONSTR_ATTR_IMMEDIATE:
-                               elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
-                               break;
+                       case CONSTR_ATTR_DEFERRABLE:
+                       case CONSTR_ATTR_NOT_DEFERRABLE:
+                       case CONSTR_ATTR_DEFERRED:
+                       case CONSTR_ATTR_IMMEDIATE:
+                               elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
+                               break;
 
                        default:
                                elog(ERROR, "DefineDomain: unrecognized constraint node type");
@@ -591,12 +603,139 @@ DefineDomain(CreateDomainStmt *stmt)
                                   basetypeoid, /* base type ID */
                                   defaultValue,        /* default type value (text) */
                                   defaultValueBin,             /* default type value (binary) */
-                                  byValue,             /* passed by value */
-                                  alignment,   /* required alignment */
-                                  storage,             /* TOAST strategy */
-                                  stmt->typename->typmod,              /* typeMod value */
-                                  typNDims,    /* Array dimensions for base type */
-                                  typNotNull); /* Type NOT NULL */
+                                  byValue,                             /* passed by value */
+                                  alignment,                   /* required alignment */
+                                  storage,                             /* TOAST strategy */
+                                  stmt->typename->typmod, /* typeMod value */
+                                  typNDims,                    /* Array dimensions for base type */
+                                  typNotNull);                 /* Type NOT NULL */
+
+       /*
+        * Process constraints which refer to the domain ID returned by TypeCreate
+        */
+       foreach(listptr, schema)
+       {
+               Constraint *constr = lfirst(listptr);
+               ParseState *pstate;
+
+               switch (constr->contype)
+               {
+                       case CONSTR_CHECK:
+                               {
+                                       Node       *expr;
+                                       char       *ccsrc;
+                                       char       *ccbin;
+                                       ConstraintTestValue  *domVal;
+
+                                       /*
+                                        * Assign or validate constraint name
+                                        */
+                                       if (constr->name)
+                                       {
+                                               if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
+                                                                                                domainoid,
+                                                                                                domainNamespace,
+                                                                                                constr->name))
+                                                       elog(ERROR, "constraint \"%s\" already exists for domain \"%s\"",
+                                                                       constr->name,
+                                                                       domainName);
+                                       }
+                                       else
+                                               constr->name = GenerateConstraintName(CONSTRAINT_DOMAIN,
+                                                                                                                         domainoid,
+                                                                                                                         domainNamespace,
+                                                                                                                         &counter);
+
+                                       /*
+                                        * Convert the A_EXPR in raw_expr into an
+                                        * EXPR
+                                        */
+                                       pstate = make_parsestate(NULL);
+
+                                       /*
+                                        * We want to have the domain VALUE node type filled in so
+                                        * that proper casting can occur.
+                                        */
+                                       domVal = makeNode(ConstraintTestValue);
+                                       domVal->typeId = basetypeoid;
+                                       domVal->typeMod = stmt->typename->typmod;
+
+                                       expr = transformExpr(pstate, constr->raw_expr, domVal);
+
+                                       /*
+                                        * Domains don't allow var clauses
+                                        */
+                                       if (contain_var_clause(expr))
+                                               elog(ERROR, "cannot use column references in domain CHECK clause");
+
+                                       /*
+                                        * Make sure it yields a boolean result.
+                                        */
+                                       expr = coerce_to_boolean(expr, "CHECK");
+
+                                       /*
+                                        * Make sure no outside relations are
+                                        * referred to.
+                                        */
+                                       if (length(pstate->p_rtable) != 0)
+                                               elog(ERROR, "Relations cannot be referenced in domain CHECK constraint");
+
+                                       /*
+                                        * No subplans or aggregates, either...
+                                        */
+                                       if (contain_subplans(expr))
+                                               elog(ERROR, "cannot use subselect in CHECK constraint expression");
+                                       if (contain_agg_clause(expr))
+                                               elog(ERROR, "cannot use aggregate function in CHECK constraint expression");
+
+                                       /*
+                                        * Might as well try to reduce any constant expressions.
+                                        */
+                                       expr = eval_const_expressions(expr);
+
+                                       /*
+                                        * Must fix opids in operator clauses.
+                                        */
+                                       fix_opids(expr);
+
+                                       ccbin = nodeToString(expr);
+
+                                       /*
+                                        * Deparse it.  Since VARNOs aren't allowed in domain
+                                        * constraints, relation context isn't required as anything
+                                        * other than a shell.
+                                        */
+                                       ccsrc = deparse_expression(expr,
+                                                               deparse_context_for(domainName,
+                                                                                                       InvalidOid),
+                                                                                                  false, false);
+
+                                       /* Write the constraint */
+                                       CreateConstraintEntry(constr->name,             /* Constraint Name */
+                                                                                 domainNamespace,      /* namespace */
+                                                                                 CONSTRAINT_CHECK,             /* Constraint Type */
+                                                                                 false,        /* Is Deferrable */
+                                                                                 false,        /* Is Deferred */
+                                                                                 InvalidOid,           /* not a relation constraint */
+                                                                                 NULL, 
+                                                                                 0,
+                                                                                 domainoid,    /* domain constraint */
+                                                                                 InvalidOid,   /* Foreign key fields */
+                                                                                 NULL,
+                                                                                 0,
+                                                                                 ' ',
+                                                                                 ' ',
+                                                                                 ' ',
+                                                                                 InvalidOid,
+                                                                                 expr,         /* Tree form check constraint */
+                                                                                 ccbin,        /* Binary form check constraint */
+                                                                                 ccsrc);       /* Source form check constraint */
+                               }
+                               break;
+                       default:
+                               break;
+               }
+       }
 
        /*
         * Add any dependencies needed for the default expression.
index 5718983ca101fb794c55b0a36fd4d6943b77e950..1612a2d9ea2b3dd474a4ea5cd391e0a36e1926cd 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.108 2002/09/04 20:31:17 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.109 2002/11/15 02:50:06 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -72,6 +72,9 @@ static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
 static Datum ExecEvalConstraintTest(ConstraintTest *constraint,
                                           ExprContext *econtext,
                                           bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalConstraintTestValue(ConstraintTestValue *conVal,
+                                          ExprContext *econtext,
+                                          bool *isNull, ExprDoneCond *isDone);
 
 
 /*----------
@@ -1551,6 +1554,23 @@ ExecEvalBooleanTest(BooleanTest *btest,
        }
 }
 
+/*
+ * ExecEvalConstraintTestValue
+ *
+ * Return the value stored by constraintTest.
+ */
+static Datum
+ExecEvalConstraintTestValue(ConstraintTestValue *conVal, ExprContext *econtext,
+                                                       bool *isNull, ExprDoneCond *isDone)
+{
+       /*
+        * If the Datum hasn't been set, then it's ExecEvalConstraintTest
+        * hasn't been called.
+        */
+       *isNull = econtext->domainValue_isNull;
+       return econtext->domainValue_datum;
+}
+
 /*
  * ExecEvalConstraintTest
  *
@@ -1571,11 +1591,22 @@ ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext,
                case CONSTR_TEST_NOTNULL:
                        if (*isNull)
                                elog(ERROR, "Domain %s does not allow NULL values",
-                                        constraint->name);
+                                        constraint->domname);
                        break;
                case CONSTR_TEST_CHECK:
-                       /* TODO: Add CHECK Constraints to domains */
-                       elog(ERROR, "Domain CHECK Constraints not yet implemented");
+                       {
+                               Datum   conResult;
+
+                               /* Var with attnum == UnassignedAttrNum uses the result */
+                               econtext->domainValue_datum = result;
+                               econtext->domainValue_isNull = *isNull;
+
+                               conResult = ExecEvalExpr(constraint->check_expr, econtext, isNull, isDone);
+
+                               if (!DatumGetBool(conResult))
+                                       elog(ERROR, "Domain %s constraint %s failed",
+                                                constraint->name, constraint->domname);
+                       }
                        break;
                default:
                        elog(ERROR, "ExecEvalConstraintTest: Constraint type unknown");
@@ -1777,7 +1808,12 @@ ExecEvalExpr(Node *expression,
                                                                                          isNull,
                                                                                          isDone);
                        break;
-
+               case T_ConstraintTestValue:
+                       retDatum = ExecEvalConstraintTestValue((ConstraintTestValue *) expression,
+                                                                                                 econtext,
+                                                                                                 isNull,
+                                                                                                 isDone);
+                       break;
                default:
                        elog(ERROR, "ExecEvalExpr: unknown expression type %d",
                                 nodeTag(expression));
index c354abf5dad8490b2fa3da910fae467bda7ab994..9dc29584e82ddc2fce048508073b8a6223b42c17 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.217 2002/11/11 22:19:22 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.218 2002/11/15 02:50:06 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1056,11 +1056,35 @@ _copyConstraintTest(ConstraintTest *from)
        newnode->testtype = from->testtype;
        if (from->name)
                newnode->name = pstrdup(from->name);
+       if (from->domname)
+               newnode->domname = pstrdup(from->domname);
        Node_Copy(from, newnode, check_expr);
 
        return newnode;
 }
 
+static ConstraintTestValue *
+_copyConstraintTestValue(ConstraintTestValue *from)
+{
+       ConstraintTestValue *newnode = makeNode(ConstraintTestValue);
+
+       /*
+        * copy remainder of node
+        */
+       newnode->typeId = from->typeId;
+       newnode->typeMod = from->typeMod;
+
+       return newnode;
+}
+
+static DomainConstraintValue *
+_copyDomainConstraintValue(DomainConstraintValue *from)
+{
+       DomainConstraintValue *newnode = makeNode(DomainConstraintValue);
+
+       return newnode;
+}
+
 static ArrayRef *
 _copyArrayRef(ArrayRef *from)
 {
@@ -3252,6 +3276,9 @@ copyObject(void *from)
                case T_ConstraintTest:
                        retval = _copyConstraintTest(from);
                        break;
+               case T_ConstraintTestValue:
+                       retval = _copyConstraintTestValue(from);
+                       break;
                case T_FkConstraint:
                        retval = _copyFkConstraint(from);
                        break;
@@ -3264,6 +3291,9 @@ copyObject(void *from)
                case T_InsertDefault:
                        retval = _copyInsertDefault(from);
                        break;
+               case T_DomainConstraintValue:
+                       retval = _copyDomainConstraintValue(from);
+                       break;
 
                default:
                        elog(ERROR, "copyObject: don't know how to copy node type %d",
index ab84f5d3d2021094e6fde8f165643559c3e97a09..68e93e48b08c47bb7ed291c30ddb82cfe2e4aca6 100644 (file)
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.163 2002/11/11 22:19:22 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.164 2002/11/15 02:50:06 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1971,15 +1971,32 @@ _equalConstraintTest(ConstraintTest *a, ConstraintTest *b)
                return false;
        if (!equalstr(a->name, b->name))
                return false;
+       if (!equalstr(a->domname, b->domname))
+               return false;
        if (!equal(a->check_expr, b->check_expr))
                return false;
        return true;
 }
 
+static bool
+_equalConstraintTestValue(ConstraintTestValue *a, ConstraintTestValue *b)
+{
+       if (a->typeId != b->typeId)
+               return false;
+       if (a->typeMod != b->typeMod)
+               return false;
+       return true;
+}
+
+static bool
+_equalDomainConstraintValue(DomainConstraintValue *a, DomainConstraintValue *b)
+{
+       return true;
+}
+
 /*
  * Stuff from pg_list.h
  */
-
 static bool
 _equalValue(Value *a, Value *b)
 {
@@ -2438,6 +2455,9 @@ equal(void *a, void *b)
                case T_ConstraintTest:
                        retval = _equalConstraintTest(a, b);
                        break;
+               case T_ConstraintTestValue:
+                       retval = _equalConstraintTestValue(a, b);
+                       break;
                case T_FkConstraint:
                        retval = _equalFkConstraint(a, b);
                        break;
@@ -2450,6 +2470,9 @@ equal(void *a, void *b)
                case T_InsertDefault:
                        retval = _equalInsertDefault(a, b);
                        break;
+               case T_DomainConstraintValue:
+                       retval = _equalDomainConstraintValue(a, b);
+                       break;
 
                default:
                        elog(WARNING, "equal: don't know whether nodes of type %d are equal",
index c5b5a493583669774389e247dc3f4d9d424cd633..2d1f2236c9bfb108902b23bac2669f8476783b6f 100644 (file)
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.179 2002/11/11 22:19:22 tgl Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.180 2002/11/15 02:50:07 momjian Exp $
  *
  * NOTES
  *       Every (plan) node in POSTGRES has an associated "out" routine which
@@ -1525,10 +1525,32 @@ _outConstraintTest(StringInfo str, ConstraintTest *node)
        appendStringInfo(str, " :testtype %d :name ",
                                         (int) node->testtype);
        _outToken(str, node->name);
+       appendStringInfo(str, " :domain ");
+       _outToken(str, node->domname);
        appendStringInfo(str, " :check_expr ");
        _outNode(str, node->check_expr);
 }
 
+/*
+ *     ConstraintTestValue
+ */
+static void
+_outConstraintTestValue(StringInfo str, ConstraintTestValue *node)
+{
+       appendStringInfo(str, " CONSTRAINTTESTVALUE :typeid %u :typemod %d ",
+                                        node->typeId,
+                                        node->typeMod);
+}
+
+/*
+ *     DomainConstraintValue
+ */
+static void
+_outDomainConstraintValue(StringInfo str, DomainConstraintValue *node)
+{
+       appendStringInfo(str, " DOMAINCONSTRAINTVALUE ");
+}
+
 /*
  * _outNode -
  *       converts a Node into ascii string and append it to 'str'
@@ -1796,9 +1818,15 @@ _outNode(StringInfo str, void *obj)
                        case T_ConstraintTest:
                                _outConstraintTest(str, obj);
                                break;
+                       case T_ConstraintTestValue:
+                               _outConstraintTestValue(str, obj);
+                               break;
                        case T_FuncCall:
                                _outFuncCall(str, obj);
                                break;
+                       case T_DomainConstraintValue:
+                               _outDomainConstraintValue(str, obj);
+                               break;
 
                        default:
                                elog(WARNING, "_outNode: don't know how to print type %d ",
index 568bf8ee1e4fbea1831b7d37dea48037798b7d2f..9b2198ec5a57eb965a03625a4c3556d4381f8d89 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.136 2002/11/06 00:00:44 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.137 2002/11/15 02:50:07 momjian Exp $
  *
  * NOTES
  *       Most of the read functions for plan nodes are tested. (In fact, they
@@ -949,12 +949,56 @@ _readConstraintTest(void)
        token = pg_strtok(&length); /* now read it */
        local_node->name = nullable_string(token, length);
 
+       token = pg_strtok(&length); /* get :domname */
+       token = pg_strtok(&length); /* get domname */
+       local_node->domname = nullable_string(token, length);
+
        token = pg_strtok(&length); /* eat :check_expr */
        local_node->check_expr = nodeRead(true);        /* now read it */
 
        return local_node;
 }
 
+/* ----------------
+ *             _readConstraintTestValue
+ *
+ *     ConstraintTestValue is a subclass of Node
+ * ----------------
+ */
+static ConstraintTestValue *
+_readConstraintTestValue(void)
+{
+       ConstraintTestValue *local_node;
+       char   *token;
+       int             length;
+
+       local_node = makeNode(ConstraintTestValue);
+       token = pg_strtok(&length); /* eat :typeid */
+       token = pg_strtok(&length); /* get typeid */
+       local_node->typeId = atooid(token);
+       token = pg_strtok(&length); /* eat :typemod */
+       token = pg_strtok(&length); /* get typemod */
+       local_node->typeMod = atoi(token);
+
+       return local_node;
+}
+
+/* ----------------
+ *             _readDomainConstraintValue
+ *
+ *     DomainConstraintValue is a subclass of Node
+ * ----------------
+ */
+static DomainConstraintValue *
+_readDomainConstraintValue(void)
+{
+       DomainConstraintValue *local_node;
+
+       local_node = makeNode(DomainConstraintValue);
+
+       return local_node;
+}
+
 /* ----------------
  *             _readVar
  *
@@ -2300,6 +2344,10 @@ parsePlanString(void)
                return_value = _readBooleanTest();
        else if (length == 14 && strncmp(token, "CONSTRAINTTEST", length) == 0)
                return_value = _readConstraintTest();
+       else if (length == 21 && strncmp(token, "DOMAINCONSTRAINTVALUE", length) == 0)
+               return_value = _readDomainConstraintValue();
+       else if (length == 19 && strncmp(token, "CONSTRAINTTESTVALUE", length) == 0)
+               return_value = _readConstraintTestValue();
        else
                elog(ERROR, "badly formatted planstring \"%.10s\"...", token);
 
index f55c988bfc5d4a32d8fc85c1cce7fdfb8b8de712..e7b4e3b5dbc6476dad2a87a833e1eb3310eec49a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.110 2002/11/06 22:31:24 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.111 2002/11/15 02:50:07 momjian Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -1926,6 +1926,8 @@ expression_tree_walker(Node *node,
                        if (walker(((ConstraintTest *) node)->arg, context))
                                return true;
                        return walker(((ConstraintTest *) node)->check_expr, context);
+               case T_ConstraintTestValue:
+                       break;
                case T_SubLink:
                        {
                                SubLink    *sublink = (SubLink *) node;
@@ -2310,6 +2312,15 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
+               case T_ConstraintTestValue:
+                       {
+                               ConstraintTestValue *ctest = (ConstraintTestValue *) node;
+                               ConstraintTestValue *newnode;
+
+                               FLATCOPY(newnode, ctest, ConstraintTestValue);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_SubLink:
                        {
                                /*
index 30c8e8f15b999e0f375571ba38947ad7e4aae802..18144f7b94136c904258afdf9e31e3ce8529b696 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.253 2002/10/21 22:06:19 tgl Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.254 2002/11/15 02:50:07 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2401,7 +2401,7 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
                        Oid                     expected_type_id,
                                                given_type_id;
 
-                       expr = transformExpr(pstate, expr);
+                       expr = transformExpr(pstate, expr, NULL);
 
                        /* Cannot contain subselects or aggregates */
                        if (contain_subplans(expr))
index 724424220b671f3d79cb56483919e3a26efc68c8..b3ca71013c99cf0ce2dab4bceeb556d2c8d88c07 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.377 2002/11/13 00:44:08 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.378 2002/11/15 02:50:08 momjian Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -393,7 +393,7 @@ static void doNegateFloat(Value *v);
        UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
        UPDATE USAGE USER USING
 
-       VACUUM VALID VALIDATOR VALUES VARCHAR VARYING
+       VACUUM VALID VALIDATOR VALUE VALUES VARCHAR VARYING
        VERBOSE VERSION VIEW VOLATILE
 
        WHEN WHERE WITH WITHOUT WORK WRITE
@@ -6406,6 +6406,11 @@ c_expr:          columnref                                                               { $$ = (Node *) $1; }
                                        n->subselect = $2;
                                        $$ = (Node *)n;
                                }
+                       | VALUE
+                               {
+                                       DomainConstraintValue *n = makeNode(DomainConstraintValue);
+                                       $$ = (Node *)n;
+                               }
                ;
 
 /*
@@ -7315,6 +7320,7 @@ reserved_keyword:
                        | UNIQUE
                        | USER
                        | USING
+                       | VALUE
                        | WHEN
                        | WHERE
                ;
index b86ffa522e4e3f45d3ba4996c8e1f11c11f010a9..de8a6e09b1a664d89cd7ae5c21fd1582bf436d55 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.130 2002/11/13 00:44:09 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.131 2002/11/15 02:50:08 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -314,6 +314,7 @@ static const ScanKeyword ScanKeywords[] = {
        {"vacuum", VACUUM},
        {"valid", VALID},
        {"validator", VALIDATOR},
+       {"value", VALUE},
        {"values", VALUES},
        {"varchar", VARCHAR},
        {"varying", VARYING},
index 245c0ba422bab57987c85830876c8158c9947898..d9638753746f7db869102ea2f6ea2731d0dcf5ed 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.98 2002/09/18 21:35:22 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.99 2002/11/15 02:50:08 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -283,7 +283,7 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
         * transformJoinOnClause() does.  Just invoke transformExpr() to fix
         * up the operators, and we're done.
         */
-       result = transformExpr(pstate, result);
+       result = transformExpr(pstate, result, NULL);
 
        result = coerce_to_boolean(result, "JOIN/USING");
 
@@ -317,7 +317,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
        pstate->p_namespace = makeList2(j->larg, j->rarg);
 
        /* This part is just like transformWhereClause() */
-       result = transformExpr(pstate, j->quals);
+       result = transformExpr(pstate, j->quals, NULL);
 
        result = coerce_to_boolean(result, "JOIN/ON");
 
@@ -478,7 +478,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
        save_namespace = pstate->p_namespace;
        pstate->p_namespace = NIL;
 
-       funcexpr = transformExpr(pstate, r->funccallnode);
+       funcexpr = transformExpr(pstate, r->funccallnode, NULL);
 
        pstate->p_namespace = save_namespace;
 
@@ -961,7 +961,7 @@ transformWhereClause(ParseState *pstate, Node *clause)
        if (clause == NULL)
                return NULL;
 
-       qual = transformExpr(pstate, clause);
+       qual = transformExpr(pstate, clause, NULL);
 
        qual = coerce_to_boolean(qual, "WHERE");
 
@@ -1104,7 +1104,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
         * willing to match a resjunk target here, though the above cases must
         * ignore resjunk targets.
         */
-       expr = transformExpr(pstate, node);
+       expr = transformExpr(pstate, node, NULL);
 
        foreach(tl, tlist)
        {
index 4870b24de07fe892e91f8a1a8d0bcba021fed836..a24af2de3e1a1c4767f5c12017f51ddb08df33ca 100644 (file)
@@ -8,13 +8,18 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.85 2002/10/24 22:09:00 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.86 2002/11/15 02:50:09 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "catalog/catname.h"
+#include "catalog/indexing.h"
 #include "catalog/pg_cast.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_proc.h"
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
@@ -405,8 +410,14 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
        for (;;)
        {
                HeapTuple       tup;
+               HeapTuple       conTup;
                Form_pg_type typTup;
 
+               ScanKeyData key[1];
+               int                     nkeys = 0;
+               SysScanDesc scan;
+               Relation        conRel;
+               
                tup = SearchSysCache(TYPEOID,
                                                         ObjectIdGetDatum(typeId),
                                                         0, 0, 0);
@@ -419,7 +430,45 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
                if (typTup->typnotnull && notNull == NULL)
                        notNull = pstrdup(NameStr(typTup->typname));
 
-               /* TODO: Add CHECK Constraints to domains */
+               /* Add CHECK Constraints to domains */
+               conRel = heap_openr(ConstraintRelationName, RowShareLock);
+
+               ScanKeyEntryInitialize(&key[nkeys++], 0x0,
+                                                          Anum_pg_constraint_contypid, F_OIDEQ,
+                                                          ObjectIdGetDatum(typeId));
+
+               scan = systable_beginscan(conRel, ConstraintTypidIndex, true,
+                                                                 SnapshotNow, nkeys, key);
+
+               while (HeapTupleIsValid(conTup = systable_getnext(scan)))
+               {
+                       Datum   val;
+                       bool    isNull;
+                       ConstraintTest *r = makeNode(ConstraintTest);
+                       Form_pg_constraint      c = (Form_pg_constraint) GETSTRUCT(conTup);
+
+                       /* Not expecting conbin to be NULL, but we'll test for it anyway */
+                       val = fastgetattr(conTup,
+                                                         Anum_pg_constraint_conbin,
+                                                         conRel->rd_att, &isNull);
+
+                       if (isNull)
+                               elog(ERROR, "coerce_type_constraints: domain %s constraint %s has NULL conbin",
+                                        NameStr(typTup->typname), NameStr(c->conname));
+
+                       r->arg = arg;
+                       r->testtype = CONSTR_TEST_CHECK;
+                       r->name = NameStr(c->conname);
+                       r->domname = NameStr(typTup->typname);
+                       r->check_expr = stringToNode(MemoryContextStrdup(CacheMemoryContext,
+                                                                                DatumGetCString(DirectFunctionCall1(textout,
+                                                                                                                                                        val))));
+
+                       arg = (Node *) r;
+               }
+
+               systable_endscan(scan);
+               heap_close(conRel, RowShareLock);
 
                if (typTup->typtype != 'd')
                {
@@ -452,7 +501,8 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
 
                r->arg = arg;
                r->testtype = CONSTR_TEST_NOTNULL;
-               r->name = notNull;
+               r->name = "NOT NULL";
+               r->domname = notNull;
                r->check_expr = NULL;
 
                arg = (Node *) r;
index 3873fd37f0de436dd1669b0a7602f9c862724480..9a4a9c8bc91e33fb226d0a883db5fef03782bd32 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.129 2002/09/18 21:35:22 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.130 2002/11/15 02:50:09 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,6 +20,7 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/params.h"
+#include "optimizer/clauses.h"
 #include "parser/analyze.h"
 #include "parser/gramparse.h"
 #include "parser/parse.h"
@@ -83,7 +84,7 @@ parse_expr_init(void)
  * input and output of transformExpr; see SubLink for example.
  */
 Node *
-transformExpr(ParseState *pstate, Node *expr)
+transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 {
        Node       *result = NULL;
 
@@ -152,7 +153,7 @@ transformExpr(ParseState *pstate, Node *expr)
                                ExprFieldSelect *efs = (ExprFieldSelect *) expr;
                                List       *fields;
 
-                               result = transformExpr(pstate, efs->arg);
+                               result = transformExpr(pstate, efs->arg, domVal);
                                /* handle qualification, if any */
                                foreach(fields, efs->fields)
                                {
@@ -169,7 +170,7 @@ transformExpr(ParseState *pstate, Node *expr)
                case T_TypeCast:
                        {
                                TypeCast   *tc = (TypeCast *) expr;
-                               Node       *arg = transformExpr(pstate, tc->arg);
+                               Node       *arg = transformExpr(pstate, tc->arg, domVal);
 
                                result = typecast_expression(arg, tc->typename);
                                break;
@@ -204,14 +205,14 @@ transformExpr(ParseState *pstate, Node *expr)
                                                                        n->arg = a->lexpr;
 
                                                                result = transformExpr(pstate,
-                                                                                                          (Node *) n);
+                                                                                                          (Node *) n, domVal);
                                                        }
                                                        else
                                                        {
                                                                Node       *lexpr = transformExpr(pstate,
-                                                                                                                          a->lexpr);
+                                                                                                                          a->lexpr, domVal);
                                                                Node       *rexpr = transformExpr(pstate,
-                                                                                                                          a->rexpr);
+                                                                                                                          a->rexpr, domVal);
 
                                                                result = (Node *) make_op(a->name,
                                                                                                                  lexpr,
@@ -222,9 +223,9 @@ transformExpr(ParseState *pstate, Node *expr)
                                        case AND:
                                                {
                                                        Node       *lexpr = transformExpr(pstate,
-                                                                                                                         a->lexpr);
+                                                                                                                         a->lexpr, domVal);
                                                        Node       *rexpr = transformExpr(pstate,
-                                                                                                                         a->rexpr);
+                                                                                                                         a->rexpr, domVal);
                                                        Expr       *expr = makeNode(Expr);
 
                                                        lexpr = coerce_to_boolean(lexpr, "AND");
@@ -239,9 +240,9 @@ transformExpr(ParseState *pstate, Node *expr)
                                        case OR:
                                                {
                                                        Node       *lexpr = transformExpr(pstate,
-                                                                                                                         a->lexpr);
+                                                                                                                         a->lexpr, domVal);
                                                        Node       *rexpr = transformExpr(pstate,
-                                                                                                                         a->rexpr);
+                                                                                                                         a->rexpr, domVal);
                                                        Expr       *expr = makeNode(Expr);
 
                                                        lexpr = coerce_to_boolean(lexpr, "OR");
@@ -256,7 +257,7 @@ transformExpr(ParseState *pstate, Node *expr)
                                        case NOT:
                                                {
                                                        Node       *rexpr = transformExpr(pstate,
-                                                                                                                         a->rexpr);
+                                                                                                                         a->rexpr, domVal);
                                                        Expr       *expr = makeNode(Expr);
 
                                                        rexpr = coerce_to_boolean(rexpr, "NOT");
@@ -270,9 +271,9 @@ transformExpr(ParseState *pstate, Node *expr)
                                        case DISTINCT:
                                                {
                                                        Node       *lexpr = transformExpr(pstate,
-                                                                                                                         a->lexpr);
+                                                                                                                         a->lexpr, domVal);
                                                        Node       *rexpr = transformExpr(pstate,
-                                                                                                                         a->rexpr);
+                                                                                                                         a->rexpr, domVal);
 
                                                        result = (Node *) make_op(a->name,
                                                                                                          lexpr,
@@ -293,7 +294,7 @@ transformExpr(ParseState *pstate, Node *expr)
                                                         * Will result in a boolean constant node.
                                                         */
                                                        Node       *lexpr = transformExpr(pstate,
-                                                                                                                         a->lexpr);
+                                                                                                                         a->lexpr, domVal);
 
                                                        ltype = exprType(lexpr);
                                                        foreach(telem, (List *) a->rexpr)
@@ -317,7 +318,7 @@ transformExpr(ParseState *pstate, Node *expr)
                                                        n->val.val.str = (matched ? "t" : "f");
                                                        n->typename = SystemTypeName("bool");
 
-                                                       result = transformExpr(pstate, (Node *) n);
+                                                       result = transformExpr(pstate, (Node *) n, domVal);
                                                }
                                                break;
                                }
@@ -331,7 +332,7 @@ transformExpr(ParseState *pstate, Node *expr)
                                /* transform the list of arguments */
                                foreach(args, fn->args)
                                        lfirst(args) = transformExpr(pstate,
-                                                                                                (Node *) lfirst(args));
+                                                                                                (Node *) lfirst(args), domVal);
                                result = ParseFuncOrColumn(pstate,
                                                                                   fn->funcname,
                                                                                   fn->args,
@@ -405,7 +406,7 @@ transformExpr(ParseState *pstate, Node *expr)
                                        List       *elist;
 
                                        foreach(elist, left_list)
-                                               lfirst(elist) = transformExpr(pstate, lfirst(elist));
+                                               lfirst(elist) = transformExpr(pstate, lfirst(elist), domVal);
 
                                        Assert(IsA(sublink->oper, A_Expr));
                                        op = ((A_Expr *) sublink->oper)->name;
@@ -504,7 +505,7 @@ transformExpr(ParseState *pstate, Node *expr)
                                                warg = (Node *) makeSimpleA_Expr(OP, "=",
                                                                                                                 c->arg, warg);
                                        }
-                                       neww->expr = transformExpr(pstate, warg);
+                                       neww->expr = transformExpr(pstate, warg, domVal);
 
                                        neww->expr = coerce_to_boolean(neww->expr, "CASE/WHEN");
 
@@ -520,7 +521,7 @@ transformExpr(ParseState *pstate, Node *expr)
                                                n->val.type = T_Null;
                                                warg = (Node *) n;
                                        }
-                                       neww->result = transformExpr(pstate, warg);
+                                       neww->result = transformExpr(pstate, warg, domVal);
 
                                        newargs = lappend(newargs, neww);
                                        typeids = lappendi(typeids, exprType(neww->result));
@@ -544,7 +545,7 @@ transformExpr(ParseState *pstate, Node *expr)
                                        n->val.type = T_Null;
                                        defresult = (Node *) n;
                                }
-                               newc->defresult = transformExpr(pstate, defresult);
+                               newc->defresult = transformExpr(pstate, defresult, domVal);
 
                                /*
                                 * Note: default result is considered the most significant
@@ -580,7 +581,7 @@ transformExpr(ParseState *pstate, Node *expr)
                        {
                                NullTest   *n = (NullTest *) expr;
 
-                               n->arg = transformExpr(pstate, n->arg);
+                               n->arg = transformExpr(pstate, n->arg, domVal);
                                /* the argument can be any type, so don't coerce it */
                                result = expr;
                                break;
@@ -617,7 +618,7 @@ transformExpr(ParseState *pstate, Node *expr)
                                                clausename = NULL;              /* keep compiler quiet */
                                }
 
-                               b->arg = transformExpr(pstate, b->arg);
+                               b->arg = transformExpr(pstate, b->arg, domVal);
 
                                b->arg = coerce_to_boolean(b->arg, clausename);
 
@@ -625,6 +626,13 @@ transformExpr(ParseState *pstate, Node *expr)
                                break;
                        }
 
+               case T_DomainConstraintValue:
+                       {
+                               result = (Node *) copyObject(domVal);
+
+                               break;
+                       }
+
                        /*********************************************
                         * Quietly accept node types that may be presented when we are
                         * called on an already-transformed tree.
@@ -936,6 +944,9 @@ exprType(Node *expr)
                case T_ConstraintTest:
                        type = exprType(((ConstraintTest *) expr)->arg);
                        break;
+               case T_ConstraintTestValue:
+                       type = ((ConstraintTestValue *) expr)->typeId;
+                       break;
                default:
                        elog(ERROR, "exprType: Do not know how to get type for %d node",
                                 nodeTag(expr));
index f0bb1856ab38c9b0e97c24d8fc54ad2d72a78a7b..608a67921c128863a8f4a1784745468f52fb5e56 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.72 2002/11/13 00:39:47 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.73 2002/11/15 02:50:09 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -277,7 +277,7 @@ transformArraySubscripts(ParseState *pstate,
                {
                        if (ai->lidx)
                        {
-                               subexpr = transformExpr(pstate, ai->lidx);
+                               subexpr = transformExpr(pstate, ai->lidx, NULL);
                                /* If it's not int4 already, try to coerce */
                                subexpr = coerce_to_target_type(subexpr, exprType(subexpr),
                                                                                                INT4OID, -1,
@@ -299,7 +299,7 @@ transformArraySubscripts(ParseState *pstate,
                        }
                        lowerIndexpr = lappend(lowerIndexpr, subexpr);
                }
-               subexpr = transformExpr(pstate, ai->uidx);
+               subexpr = transformExpr(pstate, ai->uidx, NULL);
                /* If it's not int4 already, try to coerce */
                subexpr = coerce_to_target_type(subexpr, exprType(subexpr),
                                                                                INT4OID, -1,
index c03db4f8b4b36fbf65e84a1c2190be0a3a1bb74c..1a2da6da1eb354fe1723a90e20951056314b4961 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.91 2002/09/28 20:00:19 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.92 2002/11/15 02:50:09 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,7 +56,7 @@ transformTargetEntry(ParseState *pstate,
 
        /* Transform the node if caller didn't do it already */
        if (expr == NULL)
-               expr = transformExpr(pstate, node);
+               expr = transformExpr(pstate, node, NULL);
 
        if (IsA(expr, RangeVar))
                elog(ERROR, "You can't use relation names alone in the target list, try relation.*.");
index 796eaa05069b07e56d32d9d7324033e32752878c..9250faff27a7ab274d0f18293212f32bd0207445 100644 (file)
@@ -3,7 +3,7 @@
  *                             back to source text
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.124 2002/09/19 23:40:56 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.125 2002/11/15 02:50:09 momjian Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -2226,6 +2226,12 @@ get_rule_expr(Node *node, deparse_context *context,
                        }
                        break;
 
+               case T_ConstraintTestValue:
+                       {
+                               appendStringInfo(buf, "VALUE");
+                       }
+                       break;
+
                case T_SubLink:
                        get_sublink_expr(node, context);
                        break;
index fce84e72ae98912ef4736966cefec336abab522b..4a76a27dec6529d429b29e7c4f0eeeffe17ad4a4 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: indexing.h,v 1.76 2002/10/18 20:33:57 tgl Exp $
+ * $Id: indexing.h,v 1.77 2002/11/15 02:50:10 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,6 +40,7 @@
 #define ConstraintNameNspIndex         "pg_constraint_conname_nsp_index"
 #define ConstraintOidIndex                     "pg_constraint_oid_index"
 #define ConstraintRelidIndex           "pg_constraint_conrelid_index"
+#define ConstraintTypidIndex           "pg_constraint_contypid_index"
 #define ConversionDefaultIndex         "pg_conversion_default_index"
 #define ConversionNameNspIndex         "pg_conversion_name_nsp_index"
 #define ConversionOidIndex                     "pg_conversion_oid_index"
@@ -129,6 +130,8 @@ DECLARE_UNIQUE_INDEX(pg_class_relname_nsp_index on pg_class using btree(relname
 DECLARE_INDEX(pg_constraint_conname_nsp_index on pg_constraint using btree(conname name_ops, connamespace oid_ops));
 /* This following index is not used for a cache and is not unique */
 DECLARE_INDEX(pg_constraint_conrelid_index on pg_constraint using btree(conrelid oid_ops));
+/* This following index is not used for a cache and is not unique */
+DECLARE_INDEX(pg_constraint_contypid_index on pg_constraint using btree(contypid oid_ops));
 DECLARE_UNIQUE_INDEX(pg_constraint_oid_index on pg_constraint using btree(oid oid_ops));
 DECLARE_UNIQUE_INDEX(pg_conversion_default_index on pg_conversion using btree(connamespace oid_ops, conforencoding int4_ops, contoencoding int4_ops, oid oid_ops));
 DECLARE_UNIQUE_INDEX(pg_conversion_name_nsp_index on pg_conversion using btree(conname name_ops, connamespace oid_ops));
index ffe6a740ca80ba47014a4a548935c5db07c88bb0..80ff185579ed25a9cd4de6bdfd5d858930b6a3fb 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_constraint.h,v 1.4 2002/09/22 00:37:09 tgl Exp $
+ * $Id: pg_constraint.h,v 1.5 2002/11/15 02:50:10 momjian Exp $
  *
  * NOTES
  *       the genbki.sh script reads this file and generates .bki
@@ -140,6 +140,15 @@ typedef FormData_pg_constraint *Form_pg_constraint;
  * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
  */
 
+/*
+ * Used for constraint support functions where the 
+ * and conrelid, contypid columns being looked up
+ */
+typedef enum CONSTRAINTCATEGORY {
+       CONSTRAINT_RELATION,
+       CONSTRAINT_DOMAIN,
+       CONSTRAINT_ASSERTION
+} CONSTRAINTCATEGORY;
 
 /*
  * prototypes for functions in pg_constraint.c
@@ -166,10 +175,10 @@ extern Oid CreateConstraintEntry(const char *constraintName,
 
 extern void RemoveConstraintById(Oid conId);
 
-extern bool ConstraintNameIsUsed(Oid relId, Oid relNamespace,
-                                        const char *cname);
-extern char *GenerateConstraintName(Oid relId, Oid relNamespace,
-                                          int *counter);
+extern bool ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace,
+                                                                const char *cname);
+extern char *GenerateConstraintName(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace,
+                                                                       int *counter);
 extern bool ConstraintNameIsGenerated(const char *cname);
 
 #endif   /* PG_CONSTRAINT_H */
index f62d1cb8159f06a649b609765666ce85d99742bd..f955815926d90586eebfb40e98be1d57088ad1ed 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.77 2002/11/06 22:31:24 tgl Exp $
+ * $Id: execnodes.h,v 1.78 2002/11/15 02:50:10 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -113,6 +113,13 @@ typedef struct ExprContext
        Datum      *ecxt_aggvalues; /* precomputed values for Aggref nodes */
        bool       *ecxt_aggnulls;      /* null flags for Aggref nodes */
 
+       /*
+        * Carry the domain value through the executor for application
+        * in a domain constraint
+        */
+       Datum           domainValue_datum;
+       bool            domainValue_isNull;
+
        /* Functions to call back when ExprContext is shut down */
        ExprContext_CB *ecxt_callbacks;
 } ExprContext;
index cec753092108e48aaf4da4ba45018bc644883c9a..112eac34680bd8cbc9db92c6f17c836168f69904 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.122 2002/11/10 02:17:25 momjian Exp $
+ * $Id: nodes.h,v 1.123 2002/11/15 02:50:10 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -171,6 +171,7 @@ typedef enum NodeTag
        T_ViewStmt,
        T_LoadStmt,
        T_CreateDomainStmt,
+       T_DomainConstraintValue,
        T_CreatedbStmt,
        T_DropdbStmt,
        T_VacuumStmt,
@@ -231,6 +232,7 @@ typedef enum NodeTag
        T_NullTest,
        T_BooleanTest,
        T_ConstraintTest,
+       T_ConstraintTestValue,
        T_CaseExpr,
        T_CaseWhen,
        T_FkConstraint,
index 962452992e80c59b70f23563bcec6d61d1d1787f..1198a81de5e6fbb4cfc17d6dedea4649a4487ac8 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.213 2002/11/13 00:44:09 momjian Exp $
+ * $Id: parsenodes.h,v 1.214 2002/11/15 02:50:12 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -285,9 +285,26 @@ typedef struct ConstraintTest
        Node       *arg;                        /* input expression */
        ConstraintTestType testtype;    /* test type */
        char       *name;                       /* name of constraint (for error msgs) */
+       char       *domname;            /* name of domain (for error messages) */
        Node       *check_expr;         /* for CHECK test, a boolean expression */
 } ConstraintTest;
 
+/*
+ * Placeholder node for the value to be processed by a domains
+ * check constraint.
+ */
+typedef struct DomainConstraintValue
+{
+       NodeTag         type;
+} DomainConstraintValue;
+
+typedef struct ConstraintTestValue
+{
+       NodeTag         type;
+       Oid                     typeId;
+       int32           typeMod;
+} ConstraintTestValue;
+
 /*
  * ColumnDef - column definition (used in various creates)
  *
index a153c4d05709f27fb01aa9c5edc49656ab4363b8..68ffc8e373a7e48bfacf25a20a12550ffdc96a3e 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: var.h,v 1.21 2002/06/20 20:29:51 momjian Exp $
+ * $Id: var.h,v 1.22 2002/11/15 02:50:21 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,7 @@ extern bool contain_var_reference(Node *node, int varno, int varattno,
                                          int levelsup);
 extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
 extern bool contain_var_clause(Node *node);
+extern bool contain_var_tuple_clause(Node *node);
 extern List *pull_var_clause(Node *node, bool includeUpperVars);
 extern Node *flatten_join_alias_vars(Node *node, List *rtable, bool force);
 
index a7af335dd123961c442eb2dbd00fb4082d3888ae..bcf84912acf89e6aa4131f45bb455226b673222f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_expr.h,v 1.28 2002/06/20 20:29:51 momjian Exp $
+ * $Id: parse_expr.h,v 1.29 2002/11/15 02:50:21 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,7 +21,8 @@
 extern int     max_expr_depth;
 extern bool Transform_null_equals;
 
-extern Node *transformExpr(ParseState *pstate, Node *expr);
+
+extern Node *transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal);
 extern Oid     exprType(Node *expr);
 extern int32 exprTypmod(Node *expr);
 extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);
index e82ce6fdf08ecf2a95cb92096130db68c95a7c5e..92c9cc2cc00d9c7c8201b091d38cc2dfd33526ff 100644 (file)
@@ -103,35 +103,43 @@ drop domain domainint4arr restrict;
 drop domain domaintextarr restrict;
 create domain dnotnull varchar(15) NOT NULL;
 create domain dnull    varchar(15);
+create domain dcheck   varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd');
 create table nulltest
            ( col1 dnotnull
            , col2 dnotnull NULL  -- NOT NULL in the domain cannot be overridden
            , col3 dnull    NOT NULL
            , col4 dnull
+           , col5 dcheck CHECK (col5 IN ('c', 'd'))
            );
 INSERT INTO nulltest DEFAULT VALUES;
 ERROR:  Domain dnotnull does not allow NULL values
-INSERT INTO nulltest values ('a', 'b', 'c', 'd');  -- Good
-INSERT INTO nulltest values (NULL, 'b', 'c', 'd');
+INSERT INTO nulltest values ('a', 'b', 'c', 'd', 'c');  -- Good
+insert into nulltest values ('a', 'b', 'c', 'd', NULL);
+ERROR:  Domain $1 constraint dcheck failed
+insert into nulltest values ('a', 'b', 'c', 'd', 'a');
+ERROR:  ExecInsert: rejected due to CHECK constraint "nulltest_col5" on "nulltest"
+INSERT INTO nulltest values (NULL, 'b', 'c', 'd', 'd');
 ERROR:  Domain dnotnull does not allow NULL values
-INSERT INTO nulltest values ('a', NULL, 'c', 'd');
+INSERT INTO nulltest values ('a', NULL, 'c', 'd', 'c');
 ERROR:  Domain dnotnull does not allow NULL values
-INSERT INTO nulltest values ('a', 'b', NULL, 'd');
+INSERT INTO nulltest values ('a', 'b', NULL, 'd', 'c');
 ERROR:  ExecInsert: Fail to add null value in not null attribute col3
-INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good
+INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
 -- Test copy
 COPY nulltest FROM stdin; --fail
-ERROR:  copy: line 1, CopyFrom: Fail to add null value in not null attribute col3
+ERROR:  copy: line 1, Domain $1 constraint dcheck failed
 lost synchronization with server, resetting connection
 SET autocommit TO 'on';
+-- Last row is bad
 COPY nulltest FROM stdin;
+ERROR:  copy: line 3, CopyFrom: rejected due to CHECK constraint "nulltest_col5" on "nulltest"
+lost synchronization with server, resetting connection
 select * from nulltest;
- col1 | col2 | col3 | col4 
-------+------+------+------
- a    | b    | c    | d
- a    | b    | c    | 
- a    | b    | c    | 
-(3 rows)
+ col1 | col2 | col3 | col4 | col5 
+------+------+------+------+------
+ a    | b    | c    | d    | c
+ a    | b    | c    |      | d
+(2 rows)
 
 -- Test out coerced (casted) constraints
 SELECT cast('1' as dnotnull);
index 4d210cd4aa7fcbd2e9b83c0d0ebb8aaa57d4f612..65fba7466fd5bd5f2d58a7bc73b2a0257719cf9a 100644 (file)
@@ -83,29 +83,36 @@ drop domain domaintextarr restrict;
 
 create domain dnotnull varchar(15) NOT NULL;
 create domain dnull    varchar(15);
+create domain dcheck   varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd');
 
 create table nulltest
            ( col1 dnotnull
            , col2 dnotnull NULL  -- NOT NULL in the domain cannot be overridden
            , col3 dnull    NOT NULL
            , col4 dnull
+           , col5 dcheck CHECK (col5 IN ('c', 'd'))
            );
 INSERT INTO nulltest DEFAULT VALUES;
-INSERT INTO nulltest values ('a', 'b', 'c', 'd');  -- Good
-INSERT INTO nulltest values (NULL, 'b', 'c', 'd');
-INSERT INTO nulltest values ('a', NULL, 'c', 'd');
-INSERT INTO nulltest values ('a', 'b', NULL, 'd');
-INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good
+INSERT INTO nulltest values ('a', 'b', 'c', 'd', 'c');  -- Good
+insert into nulltest values ('a', 'b', 'c', 'd', NULL);
+insert into nulltest values ('a', 'b', 'c', 'd', 'a');
+INSERT INTO nulltest values (NULL, 'b', 'c', 'd', 'd');
+INSERT INTO nulltest values ('a', NULL, 'c', 'd', 'c');
+INSERT INTO nulltest values ('a', 'b', NULL, 'd', 'c');
+INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
 
 -- Test copy
 COPY nulltest FROM stdin; --fail
-a      b       \N      d
+a      b       \N      d       \N
 \.
 
 SET autocommit TO 'on';
 
+-- Last row is bad
 COPY nulltest FROM stdin;
-a      b       c       \N
+a      b       c       \N      c
+a      b       c       \N      d
+a      b       c       \N      a
 \.
 
 select * from nulltest;