Determine the set of constraints applied to a domain at executor
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 3 Feb 2003 21:15:45 +0000 (21:15 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 3 Feb 2003 21:15:45 +0000 (21:15 +0000)
startup, not in the parser; this allows ALTER DOMAIN to work correctly
with domain constraint operations stored in rules.  Rod Taylor;
code review by Tom Lane.

24 files changed:
src/backend/commands/copy.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/path/clausesel.c
src/backend/optimizer/prep/preptlist.c
src/backend/optimizer/util/clauses.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_expr.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/cache/lsyscache.c
src/include/catalog/catversion.h
src/include/commands/typecmds.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/primnodes.h
src/include/parser/parse_coerce.h
src/include/utils/lsyscache.h
src/pl/plpgsql/src/pl_exec.c
src/test/regress/expected/domain.out
src/test/regress/sql/domain.sql

index 91386eeb2ccfa154e25c954926a839ebcffab6f9..baaf57a70ac0af0206576daf58cb4e57c0970a74 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.188 2003/01/10 22:03:27 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.189 2003/02/03 21:15:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -853,7 +853,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                        }
                }
 
-               /* If it's a domain type, get info on domain constraints */
+               /* If it's a domain type, set up to check domain constraints */
                if (get_typtype(attr[i]->atttypid) == 'd')
                {
                        Param      *prm;
@@ -863,25 +863,23 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                         * Easiest way to do this is to use parse_coerce.c to set up
                         * an expression that checks the constraints.  (At present,
                         * the expression might contain a length-coercion-function call
-                        * and/or ConstraintTest nodes.)  The bottom of the expression
+                        * and/or CoerceToDomain nodes.)  The bottom of the expression
                         * is a Param node so that we can fill in the actual datum during
                         * the data input loop.
                         */
                        prm = makeNode(Param);
                        prm->paramkind = PARAM_EXEC;
                        prm->paramid = 0;
-                       prm->paramtype = attr[i]->atttypid;
+                       prm->paramtype = getBaseType(attr[i]->atttypid);
 
-                       node = coerce_type_constraints((Node *) prm, attr[i]->atttypid,
-                                                                                  COERCE_IMPLICIT_CAST);
+                       node = coerce_to_domain((Node *) prm,
+                                                                       prm->paramtype,
+                                                                       attr[i]->atttypid,
+                                                                       COERCE_IMPLICIT_CAST);
 
-                       /* check whether any constraints actually found */
-                       if (node != (Node *) prm)
-                       {
-                               constraintexprs[i] = ExecPrepareExpr((Expr *) node,
-                                                                                                        estate);
-                               hasConstraints = true;
-                       }
+                       constraintexprs[i] = ExecPrepareExpr((Expr *) node,
+                                                                                                estate);
+                       hasConstraints = true;
                }
        }
 
index 379e4bb9b45eee930b245fc097c17a92a730b8b6..ad954605373b25b2ca82ab96d6ca910f6204d0df 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.29 2003/01/08 22:06:23 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.30 2003/02/03 21:15:43 tgl Exp $
  *
  * DESCRIPTION
  *       The "DefineFoo" routines take the parse tree and pick out the
 #include "commands/typecmds.h"
 #include "executor/executor.h"
 #include "miscadmin.h"
+#include "nodes/execnodes.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"
@@ -1555,7 +1557,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
        char       *ccsrc;
        char       *ccbin;
        ParseState *pstate;
-       ConstraintTestValue  *domVal;
+       CoerceToDomainValue  *domVal;
 
        /*
         * Assign or validate constraint name
@@ -1582,13 +1584,13 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
        pstate = make_parsestate(NULL);
 
        /*
-        * Set up a ConstraintTestValue to represent the occurrence of VALUE
+        * Set up a CoerceToDomainValue to represent the occurrence of VALUE
         * in the expression.  Note that it will appear to have the type of the
         * base type, not the domain.  This seems correct since within the
         * check expression, we should not assume the input value can be considered
         * a member of the domain.
         */
-       domVal = makeNode(ConstraintTestValue);
+       domVal = makeNode(CoerceToDomainValue);
        domVal->typeId = baseTypeOid;
        domVal->typeMod = typMod;
 
@@ -1669,6 +1671,125 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
        return ccbin;
 }
 
+/*
+ * GetDomainConstraints - get a list of the current constraints of domain
+ *
+ * Returns a possibly-empty list of DomainConstraintState nodes.
+ *
+ * This is called by the executor during plan startup for a CoerceToDomain
+ * expression node.  The given constraints will be checked for each value
+ * passed through the node.
+ */
+List *
+GetDomainConstraints(Oid typeOid)
+{
+       List       *result = NIL;
+       bool            notNull = false;
+       Relation        conRel;
+
+       conRel = heap_openr(ConstraintRelationName, AccessShareLock);
+
+       for (;;)
+       {
+               HeapTuple       tup;
+               HeapTuple       conTup;
+               Form_pg_type typTup;
+               ScanKeyData key[1];
+               SysScanDesc scan;
+               
+               tup = SearchSysCache(TYPEOID,
+                                                        ObjectIdGetDatum(typeOid),
+                                                        0, 0, 0);
+               if (!HeapTupleIsValid(tup))
+                       elog(ERROR, "GetDomainConstraints: failed to lookup type %u",
+                                typeOid);
+               typTup = (Form_pg_type) GETSTRUCT(tup);
+
+               /* Test for NOT NULL Constraint */
+               if (typTup->typnotnull)
+                       notNull = true;
+
+               /* Look for CHECK Constraints on this domain */
+               ScanKeyEntryInitialize(&key[0], 0x0,
+                                                          Anum_pg_constraint_contypid, F_OIDEQ,
+                                                          ObjectIdGetDatum(typeOid));
+
+               scan = systable_beginscan(conRel, ConstraintTypidIndex, true,
+                                                                 SnapshotNow, 1, key);
+
+               while (HeapTupleIsValid(conTup = systable_getnext(scan)))
+               {
+                       Form_pg_constraint      c = (Form_pg_constraint) GETSTRUCT(conTup);
+                       Datum   val;
+                       bool    isNull;
+                       Expr   *check_expr;
+                       DomainConstraintState *r;
+
+                       /* Ignore non-CHECK constraints (presently, shouldn't be any) */
+                       if (c->contype != CONSTRAINT_CHECK)
+                               continue;
+
+                       /* 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, "GetDomainConstraints: domain %s constraint %s has NULL conbin",
+                                        NameStr(typTup->typname), NameStr(c->conname));
+
+                       check_expr = (Expr *)
+                               stringToNode(DatumGetCString(DirectFunctionCall1(textout,
+                                                                                                                                val)));
+
+                       /* ExecInitExpr assumes we already fixed opfuncids */
+                       fix_opfuncids((Node *) check_expr);
+
+                       r = makeNode(DomainConstraintState);
+                       r->constrainttype = DOM_CONSTRAINT_CHECK;
+                       r->name = pstrdup(NameStr(c->conname));
+                       r->check_expr = ExecInitExpr(check_expr, NULL);
+
+                       /*
+                        * use lcons() here because constraints of lower domains should
+                        * be applied earlier.
+                        */
+                       result = lcons(r, result);
+               }
+
+               systable_endscan(scan);
+
+               if (typTup->typtype != 'd')
+               {
+                       /* Not a domain, so done */
+                       ReleaseSysCache(tup);
+                       break;
+               }
+
+               /* else loop to next domain in stack */
+               typeOid = typTup->typbasetype;
+               ReleaseSysCache(tup);
+       }
+
+       heap_close(conRel, AccessShareLock);
+
+       /*
+        * Only need to add one NOT NULL check regardless of how many domains
+        * in the stack request it.
+        */
+       if (notNull)
+       {
+               DomainConstraintState *r = makeNode(DomainConstraintState);
+
+               r->constrainttype = DOM_CONSTRAINT_NOTNULL;
+               r->name = pstrdup("NOT NULL");
+               r->check_expr = NULL;
+
+               /* lcons to apply the nullness check FIRST */
+               result = lcons(r, result);
+       }
+
+       return result;
+}
+
 /*
  * ALTER DOMAIN .. OWNER TO
  *
index c13e1e1e4d8c078db72b2868f68bbadf7d281583..a2583fcc4c9569f08192abce50e443fe4729b556 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.123 2003/01/12 04:03:34 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.124 2003/02/03 21:15:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,6 +36,7 @@
 
 #include "access/heapam.h"
 #include "catalog/pg_type.h"
+#include "commands/typecmds.h"
 #include "executor/execdebug.h"
 #include "executor/functions.h"
 #include "executor/nodeSubplan.h"
@@ -80,10 +81,10 @@ static Datum ExecEvalNullTest(GenericExprState *nstate,
 static Datum ExecEvalBooleanTest(GenericExprState *bstate,
                                                                 ExprContext *econtext,
                                                                 bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalConstraintTest(ConstraintTestState *cstate,
+static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate,
                                           ExprContext *econtext,
                                           bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalConstraintTestValue(ConstraintTestValue *conVal,
+static Datum ExecEvalCoerceToDomainValue(CoerceToDomainValue *conVal,
                                           ExprContext *econtext, bool *isNull);
 static Datum ExecEvalFieldSelect(GenericExprState *fstate,
                                                                 ExprContext *econtext,
@@ -1559,32 +1560,37 @@ ExecEvalBooleanTest(GenericExprState *bstate,
 }
 
 /*
- * ExecEvalConstraintTest
+ * ExecEvalCoerceToDomain
  *
- * Test the constraint against the data provided.  If the data fits
- * within the constraint specifications, pass it through (return the
+ * Test the provided data against the domain constraint(s).  If the data
+ * passes the constraint specifications, pass it through (return the
  * datum) otherwise throw an error.
  */
 static Datum
-ExecEvalConstraintTest(ConstraintTestState *cstate, ExprContext *econtext,
+ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
                                           bool *isNull, ExprDoneCond *isDone)
 {
-       ConstraintTest *constraint = (ConstraintTest *) cstate->xprstate.expr;
+       CoerceToDomain *ctest = (CoerceToDomain *) cstate->xprstate.expr;
        Datum           result;
+       List       *l;
 
        result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
 
        if (isDone && *isDone == ExprEndResult)
                return result;                  /* nothing to check */
 
-       switch (constraint->testtype)
+       foreach(l, cstate->constraints)
        {
-               case CONSTR_TEST_NOTNULL:
-                       if (*isNull)
-                               elog(ERROR, "Domain %s does not allow NULL values",
-                                        constraint->domname);
-                       break;
-               case CONSTR_TEST_CHECK:
+               DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+
+               switch (con->constrainttype)
+               {
+                       case DOM_CONSTRAINT_NOTNULL:
+                               if (*isNull)
+                                       elog(ERROR, "Domain %s does not allow NULL values",
+                                                format_type_be(ctest->resulttype));
+                               break;
+                       case DOM_CONSTRAINT_CHECK:
                        {
                                Datum   conResult;
                                bool    conIsNull;
@@ -1592,7 +1598,7 @@ ExecEvalConstraintTest(ConstraintTestState *cstate, ExprContext *econtext,
                                bool    save_isNull;
 
                                /*
-                                * Set up value to be returned by ConstraintTestValue nodes.
+                                * Set up value to be returned by CoerceToDomainValue nodes.
                                 * We must save and restore prior setting of econtext's
                                 * domainValue fields, in case this node is itself within
                                 * a check expression for another domain.
@@ -1603,35 +1609,37 @@ ExecEvalConstraintTest(ConstraintTestState *cstate, ExprContext *econtext,
                                econtext->domainValue_datum = result;
                                econtext->domainValue_isNull = *isNull;
 
-                               conResult = ExecEvalExpr(cstate->check_expr,
+                               conResult = ExecEvalExpr(con->check_expr,
                                                                                 econtext, &conIsNull, NULL);
 
                                if (!conIsNull &&
                                        !DatumGetBool(conResult))
-                                       elog(ERROR, "ExecEvalConstraintTest: Domain %s constraint %s failed",
-                                                constraint->domname, constraint->name);
+                                       elog(ERROR, "ExecEvalCoerceToDomain: Domain %s constraint %s failed",
+                                                format_type_be(ctest->resulttype), con->name);
 
                                econtext->domainValue_datum = save_datum;
                                econtext->domainValue_isNull = save_isNull;
+
+                               break;
                        }
-                       break;
-               default:
-                       elog(ERROR, "ExecEvalConstraintTest: Constraint type unknown");
-                       break;
+                       default:
+                               elog(ERROR, "ExecEvalCoerceToDomain: Constraint type unknown");
+                               break;
+               }
        }
 
-       /* If all has gone well (constraint did not fail) return the datum */
+       /* If all has gone well (constraints did not fail) return the datum */
        return result;
 }
 
 /*
- * ExecEvalConstraintTestValue
+ * ExecEvalCoerceToDomainValue
  *
- * Return the value stored by constraintTest.
+ * Return the value stored by CoerceToDomain.
  */
 static Datum
-ExecEvalConstraintTestValue(ConstraintTestValue *conVal, ExprContext *econtext,
-                                                       bool *isNull)
+ExecEvalCoerceToDomainValue(CoerceToDomainValue *conVal,
+                                                       ExprContext *econtext, bool *isNull)
 {
        *isNull = econtext->domainValue_isNull;
        return econtext->domainValue_datum;
@@ -1830,14 +1838,14 @@ ExecEvalExpr(ExprState *expression,
                                                                                   isNull,
                                                                                   isDone);
                        break;
-               case T_ConstraintTest:
-                       retDatum = ExecEvalConstraintTest((ConstraintTestState *) expression,
+               case T_CoerceToDomain:
+                       retDatum = ExecEvalCoerceToDomain((CoerceToDomainState *) expression,
                                                                                          econtext,
                                                                                          isNull,
                                                                                          isDone);
                        break;
-               case T_ConstraintTestValue:
-                       retDatum = ExecEvalConstraintTestValue((ConstraintTestValue *) expr,
+               case T_CoerceToDomainValue:
+                       retDatum = ExecEvalCoerceToDomainValue((CoerceToDomainValue *) expr,
                                                                                                   econtext,
                                                                                                   isNull);
                        break;
@@ -1915,7 +1923,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
                case T_Var:
                case T_Const:
                case T_Param:
-               case T_ConstraintTestValue:
+               case T_CoerceToDomainValue:
                        /* No special setup needed for these node types */
                        state = (ExprState *) makeNode(ExprState);
                        break;
@@ -2092,13 +2100,13 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) gstate;
                        }
                        break;
-               case T_ConstraintTest:
+               case T_CoerceToDomain:
                        {
-                               ConstraintTest   *ctest = (ConstraintTest *) node;
-                               ConstraintTestState *cstate = makeNode(ConstraintTestState);
+                               CoerceToDomain   *ctest = (CoerceToDomain *) node;
+                               CoerceToDomainState *cstate = makeNode(CoerceToDomainState);
 
                                cstate->arg = ExecInitExpr(ctest->arg, parent);
-                               cstate->check_expr = ExecInitExpr(ctest->check_expr, parent);
+                               cstate->constraints = GetDomainConstraints(ctest->resulttype);
                                state = (ExprState *) cstate;
                        }
                        break;
index 667e4f30c20c77bb98dfee96b6f1dc72641bc1af..8437ed82a5a1de43a229074b0c018a7074d2d621 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.238 2003/01/23 23:38:56 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.239 2003/02/03 21:15:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -933,29 +933,28 @@ _copyBooleanTest(BooleanTest *from)
 }
 
 /*
- * _copyConstraintTest
+ * _copyCoerceToDomain
  */
-static ConstraintTest *
-_copyConstraintTest(ConstraintTest *from)
+static CoerceToDomain *
+_copyCoerceToDomain(CoerceToDomain *from)
 {
-       ConstraintTest *newnode = makeNode(ConstraintTest);
+       CoerceToDomain *newnode = makeNode(CoerceToDomain);
 
        COPY_NODE_FIELD(arg);
-       COPY_SCALAR_FIELD(testtype);
-       COPY_STRING_FIELD(name);
-       COPY_STRING_FIELD(domname);
-       COPY_NODE_FIELD(check_expr);
+       COPY_SCALAR_FIELD(resulttype);
+       COPY_SCALAR_FIELD(resulttypmod);
+       COPY_SCALAR_FIELD(coercionformat);
 
        return newnode;
 }
 
 /*
- * _copyConstraintTestValue
+ * _copyCoerceToDomainValue
  */
-static ConstraintTestValue *
-_copyConstraintTestValue(ConstraintTestValue *from)
+static CoerceToDomainValue *
+_copyCoerceToDomainValue(CoerceToDomainValue *from)
 {
-       ConstraintTestValue *newnode = makeNode(ConstraintTestValue);
+       CoerceToDomainValue *newnode = makeNode(CoerceToDomainValue);
 
        COPY_SCALAR_FIELD(typeId);
        COPY_SCALAR_FIELD(typeMod);
@@ -2476,11 +2475,11 @@ copyObject(void *from)
                case T_BooleanTest:
                        retval = _copyBooleanTest(from);
                        break;
-               case T_ConstraintTest:
-                       retval = _copyConstraintTest(from);
+               case T_CoerceToDomain:
+                       retval = _copyCoerceToDomain(from);
                        break;
-               case T_ConstraintTestValue:
-                       retval = _copyConstraintTestValue(from);
+               case T_CoerceToDomainValue:
+                       retval = _copyCoerceToDomainValue(from);
                        break;
                case T_TargetEntry:
                        retval = _copyTargetEntry(from);
index 19829736967694c7ecd39e258761996e7c558f74..7916ca0fca6537755e912a46356598a37fc19383 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.182 2003/01/23 23:38:56 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.183 2003/02/03 21:15:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -383,19 +383,25 @@ _equalBooleanTest(BooleanTest *a, BooleanTest *b)
 }
 
 static bool
-_equalConstraintTest(ConstraintTest *a, ConstraintTest *b)
+_equalCoerceToDomain(CoerceToDomain *a, CoerceToDomain *b)
 {
        COMPARE_NODE_FIELD(arg);
-       COMPARE_SCALAR_FIELD(testtype);
-       COMPARE_STRING_FIELD(name);
-       COMPARE_STRING_FIELD(domname);
-       COMPARE_NODE_FIELD(check_expr);
+       COMPARE_SCALAR_FIELD(resulttype);
+       COMPARE_SCALAR_FIELD(resulttypmod);
+       /*
+        * Special-case COERCE_DONTCARE, so that pathkeys can build coercion
+        * nodes that are equal() to both explicit and implicit coercions.
+        */
+       if (a->coercionformat != b->coercionformat &&
+               a->coercionformat != COERCE_DONTCARE &&
+               b->coercionformat != COERCE_DONTCARE)
+               return false;
 
        return true;
 }
 
 static bool
-_equalConstraintTestValue(ConstraintTestValue *a, ConstraintTestValue *b)
+_equalCoerceToDomainValue(CoerceToDomainValue *a, CoerceToDomainValue *b)
 {
        COMPARE_SCALAR_FIELD(typeId);
        COMPARE_SCALAR_FIELD(typeMod);
@@ -1599,11 +1605,11 @@ equal(void *a, void *b)
                case T_BooleanTest:
                        retval = _equalBooleanTest(a, b);
                        break;
-               case T_ConstraintTest:
-                       retval = _equalConstraintTest(a, b);
+               case T_CoerceToDomain:
+                       retval = _equalCoerceToDomain(a, b);
                        break;
-               case T_ConstraintTestValue:
-                       retval = _equalConstraintTestValue(a, b);
+               case T_CoerceToDomainValue:
+                       retval = _equalCoerceToDomainValue(a, b);
                        break;
                case T_TargetEntry:
                        retval = _equalTargetEntry(a, b);
index fd18c957d9be0d3b732481b8c47f47d1dc4098e8..ea6305512ce800e1a78f3d89a9c8c9b21116ec1e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.194 2003/01/20 18:54:47 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.195 2003/02/03 21:15:44 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -745,21 +745,20 @@ _outBooleanTest(StringInfo str, BooleanTest *node)
 }
 
 static void
-_outConstraintTest(StringInfo str, ConstraintTest *node)
+_outCoerceToDomain(StringInfo str, CoerceToDomain *node)
 {
-       WRITE_NODE_TYPE("CONSTRAINTTEST");
+       WRITE_NODE_TYPE("COERCETODOMAIN");
 
        WRITE_NODE_FIELD(arg);
-       WRITE_ENUM_FIELD(testtype, ConstraintTestType);
-       WRITE_STRING_FIELD(name);
-       WRITE_STRING_FIELD(domname);
-       WRITE_NODE_FIELD(check_expr);
+       WRITE_OID_FIELD(resulttype);
+       WRITE_INT_FIELD(resulttypmod);
+       WRITE_ENUM_FIELD(coercionformat, CoercionForm);
 }
 
 static void
-_outConstraintTestValue(StringInfo str, ConstraintTestValue *node)
+_outCoerceToDomainValue(StringInfo str, CoerceToDomainValue *node)
 {
-       WRITE_NODE_TYPE("CONSTRAINTTESTVALUE");
+       WRITE_NODE_TYPE("COERCETODOMAINVALUE");
 
        WRITE_OID_FIELD(typeId);
        WRITE_INT_FIELD(typeMod);
@@ -1548,11 +1547,11 @@ _outNode(StringInfo str, void *obj)
                        case T_BooleanTest:
                                _outBooleanTest(str, obj);
                                break;
-                       case T_ConstraintTest:
-                               _outConstraintTest(str, obj);
+                       case T_CoerceToDomain:
+                               _outCoerceToDomain(str, obj);
                                break;
-                       case T_ConstraintTestValue:
-                               _outConstraintTestValue(str, obj);
+                       case T_CoerceToDomainValue:
+                               _outCoerceToDomainValue(str, obj);
                                break;
                        case T_TargetEntry:
                                _outTargetEntry(str, obj);
index 457e04eb9f66daf1add4456a70c2ca55fe8d68ce..fe4831ee9f6891047f468ad54b2e7cb6b0528529 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.146 2003/01/10 21:08:11 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.147 2003/02/03 21:15:44 tgl Exp $
  *
  * NOTES
  *       Path and Plan nodes do not have any readfuncs support, because we
@@ -635,29 +635,28 @@ _readBooleanTest(void)
 }
 
 /*
- * _readConstraintTest
+ * _readCoerceToDomain
  */
-static ConstraintTest *
-_readConstraintTest(void)
+static CoerceToDomain *
+_readCoerceToDomain(void)
 {
-       READ_LOCALS(ConstraintTest);
+       READ_LOCALS(CoerceToDomain);
 
        READ_NODE_FIELD(arg);
-       READ_ENUM_FIELD(testtype, ConstraintTestType);
-       READ_STRING_FIELD(name);
-       READ_STRING_FIELD(domname);
-       READ_NODE_FIELD(check_expr);
+       READ_OID_FIELD(resulttype);
+       READ_INT_FIELD(resulttypmod);
+       READ_ENUM_FIELD(coercionformat, CoercionForm);
 
        READ_DONE();
 }
 
 /*
- * _readConstraintTestValue
+ * _readCoerceToDomainValue
  */
-static ConstraintTestValue *
-_readConstraintTestValue(void)
+static CoerceToDomainValue *
+_readCoerceToDomainValue(void)
 {
-       READ_LOCALS(ConstraintTestValue);
+       READ_LOCALS(CoerceToDomainValue);
 
        READ_OID_FIELD(typeId);
        READ_INT_FIELD(typeMod);
@@ -900,10 +899,10 @@ parseNodeString(void)
                return_value = _readNullTest();
        else if (MATCH("BOOLEANTEST", 11))
                return_value = _readBooleanTest();
-       else if (MATCH("CONSTRAINTTEST", 14))
-               return_value = _readConstraintTest();
-       else if (MATCH("CONSTRAINTTESTVALUE", 19))
-               return_value = _readConstraintTestValue();
+       else if (MATCH("COERCETODOMAIN", 14))
+               return_value = _readCoerceToDomain();
+       else if (MATCH("COERCETODOMAINVALUE", 19))
+               return_value = _readCoerceToDomainValue();
        else if (MATCH("TARGETENTRY", 11))
                return_value = _readTargetEntry();
        else if (MATCH("RANGETBLREF", 11))
index 9df0a79478230f77bc95e13b30676e05c2e2f6a6..8fe67ec4757cf062a79cf994e911cab1a82b38f5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.56 2003/01/28 22:13:29 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.57 2003/02/03 21:15:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -540,6 +540,14 @@ clause_selectivity(Query *root,
                                                                varRelid,
                                                                jointype);
        }
+       else if (IsA(clause, CoerceToDomain))
+       {
+               /* Not sure this case is needed, but it can't hurt */
+               s1 = clause_selectivity(root,
+                                                               (Node *) ((CoerceToDomain *) clause)->arg,
+                                                               varRelid,
+                                                               jointype);
+       }
 
 #ifdef SELECTIVITY_DEBUG
        elog(DEBUG3, "clause_selectivity: s1 %f", s1);
index 87d3c983a70830684c9be6547a63fb568e48d26f..dc8faa9d837c2caa1be22ecbf0dfe8a5387e7d08 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.59 2002/12/12 15:49:32 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.60 2003/02/03 21:15:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -185,9 +185,10 @@ expand_targetlist(List *tlist, int command_type,
                                                                                                  true, /* isnull */
                                                                                                  att_tup->attbyval);
                                        if (!att_tup->attisdropped)
-                                               new_expr = coerce_type_constraints(new_expr,
-                                                                                                                  atttype,
-                                                                                                                  COERCE_IMPLICIT_CAST);
+                                               new_expr = coerce_to_domain(new_expr,
+                                                                                                       InvalidOid,
+                                                                                                       atttype,
+                                                                                                       COERCE_IMPLICIT_CAST);
                                        break;
                                case CMD_UPDATE:
                                        /* Insert NULLs for dropped columns */
index 253c9e8813880a2b76958d3da91fba18cfda3f42..f0b8ab1262d473844bab0818ea690e48fe64c964 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.125 2003/01/20 18:54:54 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.126 2003/02/03 21:15:44 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -2030,7 +2030,7 @@ expression_tree_walker(Node *node,
                case T_Var:
                case T_Const:
                case T_Param:
-               case T_ConstraintTestValue:
+               case T_CoerceToDomainValue:
                case T_RangeTblRef:
                        /* primitive node types with no subnodes */
                        break;
@@ -2148,10 +2148,8 @@ expression_tree_walker(Node *node,
                        return walker(((NullTest *) node)->arg, context);
                case T_BooleanTest:
                        return walker(((BooleanTest *) node)->arg, context);
-               case T_ConstraintTest:
-                       if (walker(((ConstraintTest *) node)->arg, context))
-                               return true;
-                       return walker(((ConstraintTest *) node)->check_expr, context);
+               case T_CoerceToDomain:
+                       return walker(((CoerceToDomain *) node)->arg, context);
                case T_TargetEntry:
                        return walker(((TargetEntry *) node)->expr, context);
                case T_Query:
@@ -2374,7 +2372,7 @@ expression_tree_mutator(Node *node,
                case T_Var:
                case T_Const:
                case T_Param:
-               case T_ConstraintTestValue:
+               case T_CoerceToDomainValue:
                case T_RangeTblRef:
                        /* primitive node types with no subnodes */
                        return (Node *) copyObject(node);
@@ -2538,14 +2536,13 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
-               case T_ConstraintTest:
+               case T_CoerceToDomain:
                        {
-                               ConstraintTest *ctest = (ConstraintTest *) node;
-                               ConstraintTest *newnode;
+                               CoerceToDomain *ctest = (CoerceToDomain *) node;
+                               CoerceToDomain *newnode;
 
-                               FLATCOPY(newnode, ctest, ConstraintTest);
+                               FLATCOPY(newnode, ctest, CoerceToDomain);
                                MUTATE(newnode->arg, ctest->arg, Expr *);
-                               MUTATE(newnode->check_expr, ctest->check_expr, Expr *);
                                return (Node *) newnode;
                        }
                        break;
index af184eca6d1106fdaafe19b15c649f136498ea57..46d191a91587e944be6f48d8e42ac8231d176182 100644 (file)
@@ -8,18 +8,13 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.91 2002/12/12 20:35:13 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.92 2003/02/03 21:15:44 tgl 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"
@@ -35,7 +30,7 @@
 
 static Node *coerce_type_typmod(Node *node,
                                                                Oid targetTypeId, int32 targetTypMod,
-                                                               CoercionForm cformat);
+                                                               CoercionForm cformat, bool isExplicit);
 static Oid     PreferredType(CATEGORY category, Oid type);
 static Node *build_func_call(Oid funcid, Oid rettype, List *args,
                                                         CoercionForm fformat);
@@ -103,7 +98,9 @@ coerce_to_target_type(Node *expr, Oid exprtype,
         * as well as a type coercion.
         */
        if (expr != NULL)
-               expr = coerce_type_typmod(expr, targettype, targettypmod, cformat);
+               expr = coerce_type_typmod(expr, targettype, targettypmod,
+                                                                 cformat,
+                                                                 (cformat != COERCE_IMPLICIT_CAST));
 
        return expr;
 }
@@ -119,7 +116,7 @@ coerce_to_target_type(Node *expr, Oid exprtype,
  * No coercion to a typmod (length) is performed here.  The caller must
  * call coerce_type_typmod as well, if a typmod constraint is wanted.
  * (But if the target type is a domain, it may internally contain a
- * typmod constraint, which will be applied inside coerce_type_constraints.)
+ * typmod constraint, which will be applied inside coerce_to_domain.)
  */
 Node *
 coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
@@ -186,15 +183,8 @@ coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
 
                /* If target is a domain, apply constraints. */
                if (targetTyptype == 'd')
-               {
-                       result = coerce_type_constraints(result, targetTypeId,
-                                                                                        cformat);
-                       /* We might now need a RelabelType. */
-                       if (exprType(result) != targetTypeId)
-                               result = (Node *) makeRelabelType((Expr *) result,
-                                                                                                 targetTypeId, -1,
-                                                                                                 cformat);
-               }
+                       result = coerce_to_domain(result, InvalidOid, targetTypeId,
+                                                                         cformat);
 
                ReleaseSysCache(targetType);
        }
@@ -222,17 +212,12 @@ coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
                                                                         cformat);
 
                        /*
-                        * If domain, test against domain constraints and relabel with
+                        * If domain, coerce to the domain type and relabel with
                         * domain type ID
                         */
                        if (targetTypeId != baseTypeId)
-                       {
-                               result = coerce_type_constraints(result, targetTypeId,
-                                                                                                cformat);
-                               result = (Node *) makeRelabelType((Expr *) result,
-                                                                                                 targetTypeId, -1,
-                                                                                                 cformat);
-                       }
+                               result = coerce_to_domain(result, baseTypeId, targetTypeId,
+                                                                                 cformat);
 
                        /*
                         * If the input is a constant, apply the type conversion
@@ -257,21 +242,23 @@ coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
                         * higher-level code.
                         *
                         * Also, domains may have value restrictions beyond the base type
-                        * that must be accounted for.
+                        * that must be accounted for.  If the destination is a domain
+                        * then we won't need a RelabelType node.
                         */
-                       result = coerce_type_constraints(node, targetTypeId,
-                                                                                        cformat);
-
-                       /*
-                        * XXX could we label result with exprTypmod(node) instead of
-                        * default -1 typmod, to save a possible length-coercion
-                        * later? Would work if both types have same interpretation of
-                        * typmod, which is likely but not certain (wrong if target is
-                        * a domain, in any case).
-                        */
-                       result = (Node *) makeRelabelType((Expr *) result,
-                                                                                         targetTypeId, -1,
-                                                                                         cformat);
+                       result = coerce_to_domain(node, InvalidOid, targetTypeId,
+                                                                         cformat);
+                       if (result == node)
+                       {
+                               /*
+                                * XXX could we label result with exprTypmod(node) instead of
+                                * default -1 typmod, to save a possible length-coercion
+                                * later? Would work if both types have same interpretation of
+                                * typmod, which is likely but not certain.
+                                */
+                               result = (Node *) makeRelabelType((Expr *) result,
+                                                                                                 targetTypeId, -1,
+                                                                                                 cformat);
+                       }
                }
        }
        else if (typeInheritsFrom(inputTypeId, targetTypeId))
@@ -392,120 +379,61 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
 
 
 /*
- * Create an expression tree to enforce the constraints (if any)
- * that should be applied by the type. Currently this is only
- * interesting for domain types.
+ * Create an expression tree to represent coercion to a domain type.
+ *
+ * 'arg': input expression
+ * 'baseTypeId': base type of domain, if known (pass InvalidOid if caller
+ *             has not bothered to look this up)
+ * 'typeId': target type to coerce to
+ * 'cformat': coercion format
  *
- * NOTE: result tree is not guaranteed to show the correct exprType() for
- * the domain; it may show the base type.  Caller must relabel if needed.
+ * If the target type isn't a domain, the given 'arg' is returned as-is.
  */
 Node *
-coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
+coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat)
 {
-       char       *notNull = NULL;
-       int32           typmod = -1;
-
-       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);
-               if (!HeapTupleIsValid(tup))
-                       elog(ERROR, "coerce_type_constraints: failed to lookup type %u",
-                                typeId);
-               typTup = (Form_pg_type) GETSTRUCT(tup);
-
-               /* Test for NOT NULL Constraint */
-               if (typTup->typnotnull && notNull == NULL)
-                       notNull = pstrdup(NameStr(typTup->typname));
-
-               /* 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 = (Expr *) arg;
-                       r->testtype = CONSTR_TEST_CHECK;
-                       r->name = NameStr(c->conname);
-                       r->domname = NameStr(typTup->typname);
-                       r->check_expr = stringToNode(DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                                                                        val)));
-
-                       arg = (Node *) r;
-               }
-
-               systable_endscan(scan);
-               heap_close(conRel, RowShareLock);
-
-               if (typTup->typtype != 'd')
-               {
-                       /* Not a domain, so done */
-                       ReleaseSysCache(tup);
-                       break;
-               }
+       CoerceToDomain *result;
+       int32   typmod;
 
-               Assert(typmod < 0);
+       /* Get the base type if it hasn't been supplied */
+       if (baseTypeId == InvalidOid)
+               baseTypeId = getBaseType(typeId);
 
-               typeId = typTup->typbasetype;
-               typmod = typTup->typtypmod;
-               ReleaseSysCache(tup);
-       }
+       /* If it isn't a domain, return the node as it was passed in */
+       if (baseTypeId == typeId)
+               return arg;
 
        /*
-        * If domain applies a typmod to its base type, do length coercion.
+        * If the domain applies a typmod to its base type, build the appropriate
+        * coercion step.  Mark it implicit for display purposes, because we don't
+        * want it shown separately by ruleutils.c; but the isExplicit flag passed
+        * to the conversion function depends on the manner in which the domain
+        * coercion is invoked, so that the semantics of implicit and explicit
+        * coercion differ.  (Is that really the behavior we want?)
+        *
+        * NOTE: because we apply this as part of the fixed expression structure,
+        * ALTER DOMAIN cannot alter the typtypmod.  But it's unclear that that
+        * would be safe to do anyway, without lots of knowledge about what the
+        * base type thinks the typmod means.
         */
+       typmod = get_typtypmod(typeId);
        if (typmod >= 0)
-               arg = coerce_type_typmod(arg, typeId, typmod, cformat);
+               arg = coerce_type_typmod(arg, baseTypeId, typmod,
+                                                                COERCE_IMPLICIT_CAST,
+                                                                (cformat != COERCE_IMPLICIT_CAST));
 
        /*
-        * Only need to add one NOT NULL check regardless of how many domains
-        * in the stack request it.  The topmost domain that requested it is
-        * used as the constraint name.
+        * Now build the domain coercion node.  This represents run-time checking
+        * of any constraints currently attached to the domain.  This also
+        * ensures that the expression is properly labeled as to result type.
         */
-       if (notNull)
-       {
-               ConstraintTest *r = makeNode(ConstraintTest);
-
-               r->arg = (Expr *) arg;
-               r->testtype = CONSTR_TEST_NOTNULL;
-               r->name = "NOT NULL";
-               r->domname = notNull;
-               r->check_expr = NULL;
-
-               arg = (Node *) r;
-       }
+       result = makeNode(CoerceToDomain);
+       result->arg = (Expr *) arg;
+       result->resulttype = typeId;
+       result->resulttypmod = -1;      /* currently, always -1 for domains */
+       result->coercionformat = cformat;
 
-       return arg;
+       return (Node *) result;
 }
 
 
@@ -526,7 +454,7 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
  */
 static Node *
 coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
-                                  CoercionForm cformat)
+                                  CoercionForm cformat, bool isExplicit)
 {
        Oid                     funcId;
        int                     nargs;
@@ -559,7 +487,7 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
                        /* Pass it a boolean isExplicit parameter, too */
                        cons = makeConst(BOOLOID,
                                                         sizeof(bool),
-                                                        BoolGetDatum(cformat != COERCE_IMPLICIT_CAST),
+                                                        BoolGetDatum(isExplicit),
                                                         false,
                                                         true);
 
index 5ee64cf38ee6af025c1c5cba2a264fba2f296488..95b3a6297ae868a1da3e95f7eae439e269714d6d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.141 2003/01/13 00:18:51 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.142 2003/02/03 21:15:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -674,8 +674,8 @@ transformExpr(ParseState *pstate, Node *expr)
                case T_ArrayRef:
                case T_FieldSelect:
                case T_RelabelType:
-               case T_ConstraintTest:
-               case T_ConstraintTestValue:
+               case T_CoerceToDomain:
+               case T_CoerceToDomainValue:
                        {
                                result = (Node *) expr;
                                break;
@@ -1017,11 +1017,11 @@ exprType(Node *expr)
                case T_BooleanTest:
                        type = BOOLOID;
                        break;
-               case T_ConstraintTest:
-                       type = exprType((Node *) ((ConstraintTest *) expr)->arg);
+               case T_CoerceToDomain:
+                       type = ((CoerceToDomain *) expr)->resulttype;
                        break;
-               case T_ConstraintTestValue:
-                       type = ((ConstraintTestValue *) expr)->typeId;
+               case T_CoerceToDomainValue:
+                       type = ((CoerceToDomainValue *) expr)->typeId;
                        break;
                case T_RangeVar:
                        /*
@@ -1117,10 +1117,10 @@ exprTypmod(Node *expr)
                                return typmod;
                        }
                        break;
-               case T_ConstraintTest:
-                       return exprTypmod((Node *) ((ConstraintTest *) expr)->arg);
-               case T_ConstraintTestValue:
-                       return ((ConstraintTestValue *) expr)->typeMod;
+               case T_CoerceToDomain:
+                       return ((CoerceToDomain *) expr)->resulttypmod;
+               case T_CoerceToDomainValue:
+                       return ((CoerceToDomainValue *) expr)->typeMod;
                default:
                        break;
        }
index df5890358b83d32dbdd6c262c3ef258658607940..10ee725b30ab5bc46a2d5999001eb67a2ce44d80 100644 (file)
@@ -3,7 +3,7 @@
  *                             back to source text
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.133 2003/02/03 15:17:24 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.134 2003/02/03 21:15:44 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -638,11 +638,11 @@ pg_get_constraintdef(PG_FUNCTION_ARGS)
                                }
                                appendStringInfo(&buf, "%s", string);
 
-                               /* Add ON UPDATE and ON DELETE clauses */
+                               /* Add ON UPDATE and ON DELETE clauses, if needed */
                                switch (conForm->confupdtype)
                                {
                                        case FKCONSTR_ACTION_NOACTION:
-                                               string = "";
+                                               string = NULL; /* suppress default */
                                                break;
                                        case FKCONSTR_ACTION_RESTRICT:
                                                string = "RESTRICT";
@@ -659,16 +659,16 @@ pg_get_constraintdef(PG_FUNCTION_ARGS)
                                        default:
                                                elog(ERROR, "pg_get_constraintdef: Unknown confupdtype '%c' for constraint %u",
                                                         conForm->confupdtype, constraintId);
-                                               string = "";    /* keep compiler quiet */
+                                               string = NULL;  /* keep compiler quiet */
                                                break;
                                }
-                               if (strlen(string) != 0)
+                               if (string)
                                        appendStringInfo(&buf, " ON UPDATE %s", string);
 
                                switch (conForm->confdeltype)
                                {
                                        case FKCONSTR_ACTION_NOACTION:
-                                               string = "";
+                                               string = NULL; /* suppress default */
                                                break;
                                        case FKCONSTR_ACTION_RESTRICT:
                                                string = "RESTRICT";
@@ -685,10 +685,10 @@ pg_get_constraintdef(PG_FUNCTION_ARGS)
                                        default:
                                                elog(ERROR, "pg_get_constraintdef: Unknown confdeltype '%c' for constraint %u",
                                                         conForm->confdeltype, constraintId);
-                                               string = "";    /* keep compiler quiet */
+                                               string = NULL;  /* keep compiler quiet */
                                                break;
                                }
-                               if (strlen(string) != 0)
+                               if (string)
                                        appendStringInfo(&buf, " ON DELETE %s", string);
 
                                if (conForm->condeferrable)
@@ -2252,19 +2252,34 @@ get_rule_expr(Node *node, deparse_context *context,
                        }
                        break;
 
-               case T_ConstraintTest:
+               case T_CoerceToDomain:
                        {
-                               ConstraintTest *ctest = (ConstraintTest *) node;
+                               CoerceToDomain *ctest = (CoerceToDomain *) node;
+                               Node   *arg = (Node *) ctest->arg;
 
                                /*
-                                * We assume that the operations of the constraint node
-                                * need not be explicitly represented in the output.
+                                * Any implicit coercion at the top level of the argument
+                                * is presumably due to the domain's own internal typmod
+                                * coercion, so do not force it to be shown.
                                 */
-                               get_rule_expr((Node *) ctest->arg, context, showimplicit);
+                               if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
+                                       !showimplicit)
+                               {
+                                       /* don't show the implicit cast */
+                                       get_rule_expr(arg, context, false);
+                               }
+                               else
+                               {
+                                       appendStringInfoChar(buf, '(');
+                                       get_rule_expr(arg, context, false);
+                                       appendStringInfo(buf, ")::%s",
+                                                       format_type_with_typemod(ctest->resulttype,
+                                                                                                        ctest->resulttypmod));
+                               }
                        }
                        break;
 
-               case T_ConstraintTestValue:
+               case T_CoerceToDomainValue:
                        appendStringInfo(buf, "VALUE");
                        break;
 
@@ -2444,7 +2459,8 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
  * the expression tree has a length-coercion node atop a type-coercion node.
  *
  * Note: avoid stripping a length-coercion node, since two successive
- * coercions to different lengths aren't a no-op.
+ * coercions to different lengths aren't a no-op.  Also, never strip a
+ * CoerceToDomain node, even though it might be effectively just RelabelType.
  */
 static Node *
 strip_type_coercion(Node *expr, Oid resultType)
index a6c838974c0d30562d5853c95f9edfdf414a24d7..abc00a9204e02f1845f71f2f225d9cbbe8559ea2 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.89 2003/01/15 19:35:44 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.90 2003/02/03 21:15:44 tgl Exp $
  *
  * NOTES
  *       Eventually, the index information should go through here, too.
@@ -1012,6 +1012,33 @@ get_typstorage(Oid typid)
                return 'p';
 }
 
+/*
+ * get_typtypmod
+ *
+ *             Given the type OID, return the typtypmod field (domain's typmod
+ *             for base type)
+ */
+int32
+get_typtypmod(Oid typid)
+{
+       HeapTuple       tp;
+
+       tp = SearchSysCache(TYPEOID,
+                                               ObjectIdGetDatum(typid),
+                                               0, 0, 0);
+       if (HeapTupleIsValid(tp))
+       {
+               Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+               int32           result;
+
+               result = typtup->typtypmod;
+               ReleaseSysCache(tp);
+               return result;
+       }
+       else
+               return -1;
+}
+
 /*
  * get_typdefault
  *       Given a type OID, return the type's default value, if any.
index d234eb3289541aaa7e242862e64d1336df4a735a..df4347633f2699baad6861401d52ce69e3e82b26 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.174 2003/01/28 22:13:36 tgl Exp $
+ * $Id: catversion.h,v 1.175 2003/02/03 21:15:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200301281
+#define CATALOG_VERSION_NO     200302031
 
 #endif
index fde284efeaf8a199187769d697573914d1b83aaf..0d2e319ed44124426c3f9fd5e72dc526478a8419 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: typecmds.h,v 1.4 2003/01/09 18:00:24 tgl Exp $
+ * $Id: typecmds.h,v 1.5 2003/02/03 21:15:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +32,8 @@ extern void AlterDomainAddConstraint(List *names, Node *constr);
 extern void AlterDomainDropConstraint(List *names, const char *constrName,
                                                                          DropBehavior behavior);
 
+extern List *GetDomainConstraints(Oid typeOid);
+
 extern void AlterTypeOwner(List *names, AclId newOwnerSysId);
 
 #endif   /* TYPECMDS_H */
index 98c6f1ddfbd0b3f013da586e70f5485b12ed7e8b..ccb253206624c3b7f003a63a066ced472f940c11 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.92 2003/01/23 05:10:41 tgl Exp $
+ * $Id: execnodes.h,v 1.93 2003/02/03 21:15:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -114,7 +114,7 @@ typedef struct ExprContext
        Datum      *ecxt_aggvalues; /* precomputed values for Aggref nodes */
        bool       *ecxt_aggnulls;      /* null flags for Aggref nodes */
 
-       /* Value to substitute for ConstraintTestValue nodes in expression */
+       /* Value to substitute for CoerceToDomainValue nodes in expression */
        Datum           domainValue_datum;
        bool            domainValue_isNull;
 
@@ -539,15 +539,37 @@ typedef struct CaseWhenState
 } CaseWhenState;
 
 /* ----------------
- *             ConstraintTestState node
+ *             CoerceToDomainState node
  * ----------------
  */
-typedef struct ConstraintTestState
+typedef struct CoerceToDomainState
 {
        ExprState       xprstate;
        ExprState  *arg;                        /* input expression */
-       ExprState  *check_expr;         /* for CHECK test, a boolean expression */
-} ConstraintTestState;
+       /* Cached list of constraints that need to be checked */
+       List       *constraints;        /* list of DomainConstraintState nodes */
+} CoerceToDomainState;
+
+/*
+ * DomainConstraintState - one item to check during CoerceToDomain
+ *
+ * Note: this is just a Node, and not an ExprState, because it has no
+ * corresponding Expr to link to.  Nonetheless it is part of an ExprState
+ * tree, so we give it a name following the xxxState convention.
+ */
+typedef enum DomainConstraintType
+{
+       DOM_CONSTRAINT_NOTNULL,
+       DOM_CONSTRAINT_CHECK
+} DomainConstraintType;
+
+typedef struct DomainConstraintState
+{
+       NodeTag         type;
+       DomainConstraintType constrainttype; /* constraint type */
+       char       *name;                       /* name of constraint (for error msgs) */
+       ExprState  *check_expr;         /* for CHECK, a boolean expression */
+} DomainConstraintState;
 
 
 /* ----------------------------------------------------------------
index bf8bb1719edc773ba797ab9119e6a130bb1f9c0b..a218790194e0034f8eb41f5c65a4e8d1f51fcd8c 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.135 2003/01/20 18:55:00 tgl Exp $
+ * $Id: nodes.h,v 1.136 2003/02/03 21:15:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -114,8 +114,8 @@ typedef enum NodeTag
        T_CaseWhen,
        T_NullTest,
        T_BooleanTest,
-       T_ConstraintTest,
-       T_ConstraintTestValue,
+       T_CoerceToDomain,
+       T_CoerceToDomainValue,
        T_TargetEntry,
        T_RangeTblRef,
        T_JoinExpr,
@@ -136,7 +136,8 @@ typedef enum NodeTag
        T_SubPlanState,
        T_CaseExprState,
        T_CaseWhenState,
-       T_ConstraintTestState,
+       T_CoerceToDomainState,
+       T_DomainConstraintState,
 
        /*
         * TAGS FOR PLANNER NODES (relation.h)
index b187c98fdc735fdcce51bd7ca149136198a1fd87..ad1c73612554cfdf01215908943801816e2f1fd2 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.77 2003/01/10 21:08:15 tgl Exp $
+ * $Id: primnodes.h,v 1.78 2003/02/03 21:15:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -578,28 +578,22 @@ typedef struct BooleanTest
 } BooleanTest;
 
 /*
- * ConstraintTest
+ * CoerceToDomain
  *
- * ConstraintTest represents the operation of testing a value to see whether
- * it meets a constraint.  If so, the input value is returned as the result;
- * if not, an error is raised.
+ * CoerceToDomain represents the operation of coercing a value to a domain
+ * type.  At runtime (and not before) the precise set of constraints to be
+ * checked will be determined.  If the value passes, it is returned as the
+ * result; if not, an error is raised.  Note that this is equivalent to
+ * RelabelType in the scenario where no constraints are applied.
  */
-
-typedef enum ConstraintTestType
-{
-       CONSTR_TEST_NOTNULL,
-       CONSTR_TEST_CHECK
-} ConstraintTestType;
-
-typedef struct ConstraintTest
+typedef struct CoerceToDomain
 {
        Expr            xpr;
        Expr       *arg;                        /* input expression */
-       ConstraintTestType testtype;    /* test type */
-       char       *name;                       /* name of constraint (for error msgs) */
-       char       *domname;            /* name of domain (for error messages) */
-       Expr       *check_expr;         /* for CHECK test, a boolean expression */
-} ConstraintTest;
+       Oid                     resulttype;             /* domain type ID (result type) */
+       int32           resulttypmod;   /* output typmod (currently always -1) */
+       CoercionForm coercionformat; /* how to display this node */
+} CoerceToDomain;
 
 /*
  * Placeholder node for the value to be processed by a domain's check
@@ -610,12 +604,12 @@ typedef struct ConstraintTest
  * the domain itself.  This is because we shouldn't consider the value to
  * be a member of the domain if we haven't yet checked its constraints.
  */
-typedef struct ConstraintTestValue
+typedef struct CoerceToDomainValue
 {
        Expr            xpr;
        Oid                     typeId;                 /* type for substituted value */
        int32           typeMod;                /* typemod for substituted value */
-} ConstraintTestValue;
+} CoerceToDomainValue;
 
 
 /*
index ecc61ea716af99c0e621a9421fd7c671ec887dea..ae12f46f621e62f5f7b1f0b3a52e92ca34b7f613 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_coerce.h,v 1.48 2002/10/24 22:09:00 tgl Exp $
+ * $Id: parse_coerce.h,v 1.49 2003/02/03 21:15:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,8 +45,8 @@ extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
                                                        CoercionContext ccontext);
 extern Node *coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
                                                 CoercionContext ccontext, CoercionForm cformat);
-extern Node *coerce_type_constraints(Node *arg, Oid typeId,
-                                                                        CoercionForm cformat);
+extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId,
+                                                         CoercionForm cformat);
 
 extern Node *coerce_to_boolean(Node *node, const char *constructName);
 
index 8200730f6a25676031c670d7710f2a546022bf5d..d6f9447d190bb38759f556019d254b1b1357fdbd 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
  *
- * $Id: lsyscache.h,v 1.66 2003/01/15 19:35:47 tgl Exp $
+ * $Id: lsyscache.h,v 1.67 2003/02/03 21:15:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -54,6 +54,7 @@ extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
 extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
                                         char *typalign);
 extern char get_typstorage(Oid typid);
+extern int32 get_typtypmod(Oid typid);
 extern Node *get_typdefault(Oid typid);
 extern char get_typtype(Oid typid);
 extern Oid     get_typ_typrelid(Oid typid);
index c9fd9418b44d0e1d2d4425cd502f5f64d440f110..7d4785ef7d51bf9cc35bf73c1310f521b2f2ab89 100644 (file)
@@ -3,7 +3,7 @@
  *                       procedural language
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.77 2003/01/21 22:06:12 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.78 2003/02/03 21:15:45 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -3634,6 +3634,9 @@ exec_simple_check_node(Node *node)
                case T_BooleanTest:
                        return exec_simple_check_node((Node *) ((BooleanTest *) node)->arg);
 
+               case T_CoerceToDomain:
+                       return exec_simple_check_node((Node *) ((CoerceToDomain *) node)->arg);
+
                case T_List:
                        {
                                List       *expr = (List *) node;
index e48120a68d8ddd926c36db842341023cd786ea95..1aaa4a85ef43cbda576ba8a3fc2e362f021ab4f3 100644 (file)
@@ -257,14 +257,49 @@ ERROR:  ALTER DOMAIN: Relation "domcontest" attribute "col1" contains values tha
 alter domain con add constraint t check (VALUE < 34);
 alter domain con add check (VALUE > 0);
 insert into domcontest values (-5); -- fails
-ERROR:  ExecEvalConstraintTest: Domain con constraint $1 failed
+ERROR:  ExecEvalCoerceToDomain: Domain con constraint $1 failed
 insert into domcontest values (42); -- fails
-ERROR:  ExecEvalConstraintTest: Domain con constraint t failed
+ERROR:  ExecEvalCoerceToDomain: Domain con constraint t failed
 insert into domcontest values (5);
 alter domain con drop constraint t;
 insert into domcontest values (-5); --fails
-ERROR:  ExecEvalConstraintTest: Domain con constraint $1 failed
+ERROR:  ExecEvalCoerceToDomain: Domain con constraint $1 failed
 insert into domcontest values (42);
+-- Confirm ALTER DOMAIN with RULES.
+create table domtab (col1 integer);
+create domain dom as integer;
+create view domview as select cast(col1 as dom) from domtab;
+insert into domtab (col1) values (null);
+insert into domtab (col1) values (5);
+select * from domview;
+ col1 
+------
+ 5
+(2 rows)
+
+alter domain dom set not null;
+select * from domview; -- fail
+ERROR:  Domain dom does not allow NULL values
+alter domain dom drop not null;
+select * from domview;
+ col1 
+------
+ 5
+(2 rows)
+
+alter domain dom add constraint domchkgt6 check(value > 6);
+select * from domview; --fail
+ERROR:  ExecEvalCoerceToDomain: Domain dom constraint domchkgt6 failed
+alter domain dom drop constraint domchkgt6 restrict;
+select * from domview;
+ col1 
+------
+ 5
+(2 rows)
+
 -- cleanup
 drop domain ddef1 restrict;
 drop domain ddef2 restrict;
index 76060e99c8bd261b7dcf3945c6c8e4873b74daa9..00a38f449cd6ca379af82cdef8aed2198657a59d 100644 (file)
@@ -224,6 +224,26 @@ alter domain con drop constraint t;
 insert into domcontest values (-5); --fails
 insert into domcontest values (42);
 
+-- Confirm ALTER DOMAIN with RULES.
+create table domtab (col1 integer);
+create domain dom as integer;
+create view domview as select cast(col1 as dom) from domtab;
+insert into domtab (col1) values (null);
+insert into domtab (col1) values (5);
+select * from domview;
+
+alter domain dom set not null;
+select * from domview; -- fail
+
+alter domain dom drop not null;
+select * from domview;
+
+alter domain dom add constraint domchkgt6 check(value > 6);
+select * from domview; --fail
+
+alter domain dom drop constraint domchkgt6 restrict;
+select * from domview;
+
 -- cleanup
 drop domain ddef1 restrict;
 drop domain ddef2 restrict;