]> granicus.if.org Git - postgresql/commitdiff
Code review for domain-constraints patch. Use a new ConstraintTest node
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 31 Aug 2002 22:10:48 +0000 (22:10 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 31 Aug 2002 22:10:48 +0000 (22:10 +0000)
type for runtime constraint checks, instead of misusing the parse-time
Constraint node for the purpose.  Fix some damage introduced into type
coercion logic; in particular ensure that a coerced expression tree will
read out the correct result type when inspected (patch had broken some
RelabelType cases).  Enforce domain NOT NULL constraints against columns
that are omitted from an INSERT.

19 files changed:
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/prep/preptlist.c
src/backend/optimizer/util/clauses.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_type.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/cache/lsyscache.c
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/parse_coerce.h
src/include/parser/parse_type.h
src/include/utils/lsyscache.h
src/test/regress/expected/domain.out
src/test/regress/sql/domain.sql

index c53ac42b26f70c282c8d2a4f849029fe0c4f2e8c..30f5b0e378f3406ab985c605180ede27d0c009e5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.105 2002/08/31 19:10:08 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.106 2002/08/31 22:10:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -69,8 +69,9 @@ static Datum ExecEvalNullTest(NullTest *ntest, ExprContext *econtext,
                                 bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
                                        bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalConstraint(Constraint *constraint, ExprContext *econtext,
-                                                               bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalConstraintTest(ConstraintTest *constraint,
+                                                                       ExprContext *econtext,
+                                                                       bool *isNull, ExprDoneCond *isDone);
 
 
 /*----------
@@ -1465,43 +1466,6 @@ ExecEvalNullTest(NullTest *ntest,
        }
 }
 
-/*
- * ExecEvalConstraint
- *
- * Test the constraint against the data provided.  If the data fits
- * within the constraint specifications, pass it through (return the
- * datum) otherwise throw an error.
- */
-static Datum
-ExecEvalConstraint(Constraint *constraint, ExprContext *econtext,
-                                  bool *isNull, ExprDoneCond *isDone)
-{
-       Datum           result;
-
-       result = ExecEvalExpr(constraint->raw_expr, econtext, isNull, isDone);
-
-       /* Test for the constraint type */
-       switch(constraint->contype)
-       {
-               case CONSTR_NOTNULL:
-                       if (*isNull)
-                       {
-                               elog(ERROR, "Domain %s does not allow NULL values", constraint->name);
-                       }
-                       break;
-               case CONSTR_CHECK:
-
-                               elog(ERROR, "ExecEvalConstraint: Domain CHECK Constraints not yet implemented");
-                       break;
-               default:
-                       elog(ERROR, "ExecEvalConstraint: Constraint type unknown");
-                       break;
-       }
-
-       /* If all has gone well (constraint did not fail) return the datum */
-       return result;
-}
-
 /* ----------------------------------------------------------------
  *             ExecEvalBooleanTest
  *
@@ -1582,6 +1546,41 @@ ExecEvalBooleanTest(BooleanTest *btest,
        }
 }
 
+/*
+ * ExecEvalConstraintTest
+ *
+ * Test the constraint against the data provided.  If the data fits
+ * within the constraint specifications, pass it through (return the
+ * datum) otherwise throw an error.
+ */
+static Datum
+ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext,
+                                          bool *isNull, ExprDoneCond *isDone)
+{
+       Datum           result;
+
+       result = ExecEvalExpr(constraint->arg, econtext, isNull, isDone);
+
+       switch (constraint->testtype)
+       {
+               case CONSTR_TEST_NOTNULL:
+                       if (*isNull)
+                               elog(ERROR, "Domain %s does not allow NULL values",
+                                        constraint->name);
+                       break;
+               case CONSTR_TEST_CHECK:
+                       /* TODO: Add CHECK Constraints to domains */
+                       elog(ERROR, "Domain CHECK Constraints not yet implemented");
+                       break;
+               default:
+                       elog(ERROR, "ExecEvalConstraintTest: Constraint type unknown");
+                       break;
+       }
+
+       /* If all has gone well (constraint did not fail) return the datum */
+       return result;
+}
+
 /* ----------------------------------------------------------------
  *             ExecEvalFieldSelect
  *
@@ -1749,12 +1748,6 @@ ExecEvalExpr(Node *expression,
                                                                        isNull,
                                                                        isDone);
                        break;
-               case T_Constraint:
-                       retDatum = ExecEvalConstraint((Constraint *) expression,
-                                                                                econtext,
-                                                                                isNull,
-                                                                                isDone);
-                       break;
                case T_CaseExpr:
                        retDatum = ExecEvalCase((CaseExpr *) expression,
                                                                        econtext,
@@ -1773,6 +1766,12 @@ ExecEvalExpr(Node *expression,
                                                                                   isNull,
                                                                                   isDone);
                        break;
+               case T_ConstraintTest:
+                       retDatum = ExecEvalConstraintTest((ConstraintTest *) expression,
+                                                                                         econtext,
+                                                                                         isNull,
+                                                                                         isDone);
+                       break;
 
                default:
                        elog(ERROR, "ExecEvalExpr: unknown expression type %d",
index 5b35eea170a39c2086a2ac366fcb565f87fb2aaf..1938e7f4738ef7e0b9066eaa6d261b43ab3c2a53 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.208 2002/08/30 19:23:19 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.209 2002/08/31 22:10:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -973,10 +973,6 @@ _copyJoinExpr(JoinExpr *from)
        return newnode;
 }
 
-/* ----------------
- *             _copyCaseExpr
- * ----------------
- */
 static CaseExpr *
 _copyCaseExpr(CaseExpr *from)
 {
@@ -994,10 +990,6 @@ _copyCaseExpr(CaseExpr *from)
        return newnode;
 }
 
-/* ----------------
- *             _copyCaseWhen
- * ----------------
- */
 static CaseWhen *
 _copyCaseWhen(CaseWhen *from)
 {
@@ -1012,10 +1004,6 @@ _copyCaseWhen(CaseWhen *from)
        return newnode;
 }
 
-/* ----------------
- *             _copyNullTest
- * ----------------
- */
 static NullTest *
 _copyNullTest(NullTest *from)
 {
@@ -1030,10 +1018,6 @@ _copyNullTest(NullTest *from)
        return newnode;
 }
 
-/* ----------------
- *             _copyBooleanTest
- * ----------------
- */
 static BooleanTest *
 _copyBooleanTest(BooleanTest *from)
 {
@@ -1048,6 +1032,23 @@ _copyBooleanTest(BooleanTest *from)
        return newnode;
 }
 
+static ConstraintTest *
+_copyConstraintTest(ConstraintTest *from)
+{
+       ConstraintTest *newnode = makeNode(ConstraintTest);
+
+       /*
+        * copy remainder of node
+        */
+       Node_Copy(from, newnode, arg);
+       newnode->testtype = from->testtype;
+       if (from->name)
+               newnode->name = pstrdup(from->name);
+       Node_Copy(from, newnode, check_expr);
+
+       return newnode;
+}
+
 static ArrayRef *
 _copyArrayRef(ArrayRef *from)
 {
@@ -3206,6 +3207,9 @@ copyObject(void *from)
                case T_BooleanTest:
                        retval = _copyBooleanTest(from);
                        break;
+               case T_ConstraintTest:
+                       retval = _copyConstraintTest(from);
+                       break;
                case T_FkConstraint:
                        retval = _copyFkConstraint(from);
                        break;
index 7c9127dbf4f6ab31147fa1aa34d24b27199329cf..10b8e79933d7847a4dc2a220efdd4e0e4b30b54d 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.156 2002/08/30 19:23:19 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.157 2002/08/31 22:10:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1924,6 +1924,20 @@ _equalBooleanTest(BooleanTest *a, BooleanTest *b)
        return true;
 }
 
+static bool
+_equalConstraintTest(ConstraintTest *a, ConstraintTest *b)
+{
+       if (!equal(a->arg, b->arg))
+               return false;
+       if (a->testtype != b->testtype)
+               return false;
+       if (!equalstr(a->name, b->name))
+               return false;
+       if (!equal(a->check_expr, b->check_expr))
+               return false;
+       return true;
+}
+
 /*
  * Stuff from pg_list.h
  */
@@ -2380,6 +2394,9 @@ equal(void *a, void *b)
                case T_BooleanTest:
                        retval = _equalBooleanTest(a, b);
                        break;
+               case T_ConstraintTest:
+                       retval = _equalConstraintTest(a, b);
+                       break;
                case T_FkConstraint:
                        retval = _equalFkConstraint(a, b);
                        break;
index a92750caef194311007d812d6d0d690df97ecb71..8c255058068974b0a48fadeec3cf2c7de90533db 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.171 2002/08/30 19:23:19 tgl Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.172 2002/08/31 22:10:43 tgl Exp $
  *
  * NOTES
  *       Every (plan) node in POSTGRES has an associated "out" routine which
@@ -1471,7 +1471,6 @@ _outNullTest(StringInfo str, NullTest *node)
 {
        appendStringInfo(str, " NULLTEST :arg ");
        _outNode(str, node->arg);
-
        appendStringInfo(str, " :nulltesttype %d ",
                                         (int) node->nulltesttype);
 }
@@ -1484,11 +1483,25 @@ _outBooleanTest(StringInfo str, BooleanTest *node)
 {
        appendStringInfo(str, " BOOLEANTEST :arg ");
        _outNode(str, node->arg);
-
        appendStringInfo(str, " :booltesttype %d ",
                                         (int) node->booltesttype);
 }
 
+/*
+ *     ConstraintTest
+ */
+static void
+_outConstraintTest(StringInfo str, ConstraintTest *node)
+{
+       appendStringInfo(str, " CONSTRAINTTEST :arg ");
+       _outNode(str, node->arg);
+       appendStringInfo(str, " :testtype %d :name ",
+                                        (int) node->testtype);
+       _outToken(str, node->name);
+       appendStringInfo(str, " :check_expr ");
+       _outNode(str, node->check_expr);
+}
+
 /*
  * _outNode -
  *       converts a Node into ascii string and append it to 'str'
@@ -1750,6 +1763,9 @@ _outNode(StringInfo str, void *obj)
                        case T_BooleanTest:
                                _outBooleanTest(str, obj);
                                break;
+                       case T_ConstraintTest:
+                               _outConstraintTest(str, obj);
+                               break;
                        case T_FuncCall:
                                _outFuncCall(str, obj);
                                break;
index 2799bb746044b97b4aaad5644b42d9380ebc0a81..4d4001d21316e28a50520a4d48d201755cbe7191 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.130 2002/08/30 19:23:19 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.131 2002/08/31 22:10:43 tgl Exp $
  *
  * NOTES
  *       Most of the read functions for plan nodes are tested. (In fact, they
@@ -931,6 +931,38 @@ _readBooleanTest(void)
        return local_node;
 }
 
+/* ----------------
+ *             _readConstraintTest
+ *
+ *     ConstraintTest is a subclass of Node
+ * ----------------
+ */
+static ConstraintTest *
+_readConstraintTest(void)
+{
+       ConstraintTest *local_node;
+       char       *token;
+       int                     length;
+
+       local_node = makeNode(ConstraintTest);
+
+       token = pg_strtok(&length); /* eat :arg */
+       local_node->arg = nodeRead(true);       /* now read it */
+
+       token = pg_strtok(&length); /* eat :testtype */
+       token = pg_strtok(&length); /* get testtype */
+       local_node->testtype = (ConstraintTestType) atoi(token);
+
+       token = pg_strtok(&length); /* get :name */
+       token = pg_strtok(&length); /* now read it */
+       local_node->name = nullable_string(token, length);
+
+       token = pg_strtok(&length); /* eat :check_expr */
+       local_node->check_expr = nodeRead(true);        /* now read it */
+
+       return local_node;
+}
+
 /* ----------------
  *             _readVar
  *
@@ -2222,6 +2254,8 @@ parsePlanString(void)
                return_value = _readNullTest();
        else if (length == 11 && strncmp(token, "BOOLEANTEST", length) == 0)
                return_value = _readBooleanTest();
+       else if (length == 14 && strncmp(token, "CONSTRAINTTEST", length) == 0)
+               return_value = _readConstraintTest();
        else
                elog(ERROR, "badly formatted planstring \"%.10s\"...", token);
 
index b7c0bac12c8124934344e55d96381df6640015da..41f9b2f9478f976017efb41e83b587108a124d36 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.54 2002/08/02 18:15:06 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.55 2002/08/31 22:10:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,6 +27,7 @@
 #include "nodes/makefuncs.h"
 #include "optimizer/prep.h"
 #include "parser/parsetree.h"
+#include "parser/parse_coerce.h"
 
 
 static List *expand_targetlist(List *tlist, int command_type,
@@ -162,6 +163,8 @@ expand_targetlist(List *tlist, int command_type,
                         *
                         * For INSERT, generate a NULL constant.  (We assume the
                         * rewriter would have inserted any available default value.)
+                        * Also, if the column isn't dropped, apply any domain constraints
+                        * that might exist --- this is to catch domain NOT NULL.
                         *
                         * For UPDATE, generate a Var reference to the existing value of
                         * the attribute, so that it gets copied to the new tuple.
@@ -182,6 +185,9 @@ expand_targetlist(List *tlist, int command_type,
                                                                                                  att_tup->attbyval,
                                                                                                  false,        /* not a set */
                                                                                                  false);
+                                       if (!att_tup->attisdropped)
+                                               new_expr = coerce_type_constraints(NULL, new_expr,
+                                                                                                                  atttype, false);
                                        break;
                                case CMD_UPDATE:
                                        /* Insert NULLs for dropped columns */
index 9e8372139efc2f55410e68934c540fb117d729c5..3b5e6988396d23e10eb07266ec903024babb7bb0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.106 2002/07/20 05:16:58 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.107 2002/08/31 22:10:43 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -1882,12 +1882,14 @@ expression_tree_walker(Node *node,
                                        return true;
                        }
                        break;
-               case T_Constraint:
-                       return walker(((Constraint *) node)->raw_expr, context);
                case T_NullTest:
                        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_SubLink:
                        {
                                SubLink    *sublink = (SubLink *) node;
@@ -2238,20 +2240,6 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
-               case T_Constraint:
-                       {
-                               /*
-                                * Used for confirming domains.  Only needed fields
-                                * within the executor are the name, raw expression
-                                * and constraint type.
-                                */
-                               Constraint *con = (Constraint *) node;
-                               Constraint *newnode;
-
-                               FLATCOPY(newnode, con, Constraint);
-                               MUTATE(newnode->raw_expr, con->raw_expr, Node *);
-                               return (Node *) newnode;
-                       }
                case T_NullTest:
                        {
                                NullTest   *ntest = (NullTest *) node;
@@ -2272,6 +2260,17 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
+               case T_ConstraintTest:
+                       {
+                               ConstraintTest *ctest = (ConstraintTest *) node;
+                               ConstraintTest *newnode;
+
+                               FLATCOPY(newnode, ctest, ConstraintTest);
+                               MUTATE(newnode->arg, ctest->arg, Node *);
+                               MUTATE(newnode->check_expr, ctest->check_expr, Node *);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_SubLink:
                        {
                                /*
index 1016c782d29d11f741e7693219233abe319ff8ff..397bd4b8b079e84482b2f5570f9a2c000009baba 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.80 2002/08/22 00:01:42 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.81 2002/08/31 22:10:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,7 +35,7 @@ static Node *build_func_call(Oid funcid, Oid rettype, List *args);
 static Oid     find_coercion_function(Oid targetTypeId, Oid sourceTypeId,
                                                                   bool isExplicit);
 static Oid     find_typmod_coercion_function(Oid typeId);
-static Node    *TypeConstraints(Node *arg, Oid typeId);
+
 
 /* coerce_type()
  * Convert a function argument to a different type.
@@ -49,7 +49,7 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
        if (targetTypeId == inputTypeId ||
                node == NULL)
        {
-               /* no conversion needed, but constraints may need to be applied */
+               /* no conversion needed */
                result = node;
        }
        else if (inputTypeId == UNKNOWNOID && IsA(node, Const))
@@ -69,11 +69,12 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
                 * postpone evaluation of the function call until runtime. But
                 * there is no way to represent a typinput function call as an
                 * expression tree, because C-string values are not Datums.
+                * (XXX This *is* possible as of 7.3, do we want to do it?)
                 */
                Const      *con = (Const *) node;
                Const      *newcon = makeNode(Const);
                Type            targetType = typeidType(targetTypeId);
-               Oid                     baseTypeId = getBaseType(targetTypeId);
+               char            targetTyptype = typeTypType(targetType);
 
                newcon->consttype = targetTypeId;
                newcon->constlen = typeLen(targetType);
@@ -85,16 +86,31 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
                {
                        char       *val = DatumGetCString(DirectFunctionCall1(unknownout,
                                                                                                           con->constvalue));
+
+                       /*
+                        * If target is a domain, use the typmod it applies to the base
+                        * type.  Note that we call stringTypeDatum using the domain's
+                        * pg_type row, though.  This works because the domain row has
+                        * the same typinput and typelem as the base type --- ugly...
+                        */
+                       if (targetTyptype == 'd')
+                               atttypmod = getBaseTypeMod(targetTypeId, atttypmod);
+
                        newcon->constvalue = stringTypeDatum(targetType, val, atttypmod);
                        pfree(val);
                }
 
-               ReleaseSysCache(targetType);
-
-               /* Test for domain, and apply appropriate constraints */
                result = (Node *) newcon;
-               if (targetTypeId != baseTypeId)
-                       result = (Node *) TypeConstraints(result, targetTypeId);
+
+               /*
+                * If target is a domain, apply constraints (except for typmod,
+                * which we assume the input routine took care of).
+                */
+               if (targetTyptype == 'd')
+                       result = coerce_type_constraints(pstate, result, targetTypeId,
+                                                                                        false);
+
+               ReleaseSysCache(targetType);
        }
        else if (targetTypeId == ANYOID ||
                         targetTypeId == ANYARRAYOID)
@@ -109,21 +125,18 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
                 * attach a RelabelType node so that the expression will be seen
                 * to have the intended type when inspected by higher-level code.
                 *
+                * Also, domains may have value restrictions beyond the base type
+                * that must be accounted for.
+                */
+               result = coerce_type_constraints(pstate, node, targetTypeId, true);
+               /*
                 * 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.
-                *
-                * Domains may have value restrictions beyond the base type that
-                * must be accounted for.
+                * which is likely but not certain (wrong if target is a domain,
+                * in any case).
                 */
-               Oid                     baseTypeId = getBaseType(targetTypeId);
-               result = node;
-               if (targetTypeId != baseTypeId)
-                       result = (Node *) TypeConstraints(result, targetTypeId);
-
                result = (Node *) makeRelabelType(result, targetTypeId, -1);
-
        }
        else if (typeInheritsFrom(inputTypeId, targetTypeId))
        {
@@ -144,8 +157,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
                 *
                 * For domains, we use the coercion function for the base type.
                 */
-               Oid                     funcId;
                Oid                     baseTypeId = getBaseType(targetTypeId);
+               Oid                     funcId;
 
                funcId = find_coercion_function(baseTypeId,
                                                                                getBaseType(inputTypeId),
@@ -157,11 +170,15 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
                result = build_func_call(funcId, baseTypeId, makeList1(node));
 
                /*
-                * If domain, relabel with domain type ID and test against domain
-                * constraints
+                * If domain, test against domain constraints and relabel with
+                * domain type ID
                 */
                if (targetTypeId != baseTypeId)
-                       result = (Node *) TypeConstraints(result, targetTypeId);
+               {
+                       result = coerce_type_constraints(pstate, result, targetTypeId,
+                                                                                        true);
+                       result = (Node *) makeRelabelType(result, targetTypeId, -1);
+               }
 
                /*
                 * If the input is a constant, apply the type conversion function
@@ -306,28 +323,21 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids,
  * function should be invoked to do that.
  *
  * "bpchar" (ie, char(N)) and "numeric" are examples of such types.
+ *
+ * This mechanism may seem pretty grotty and in need of replacement by
+ * something in pg_cast, but since typmod is only interesting for datatypes
+ * that have special handling in the grammar, there's not really much
+ * percentage in making it any easier to apply such coercions ...
+ *
+ * NOTE: this does not need to work on domain types, because any typmod
+ * coercion for a domain is considered to be part of the type coercion
+ * needed to produce the domain value in the first place.
  */
 Node *
 coerce_type_typmod(ParseState *pstate, Node *node,
                                   Oid targetTypeId, int32 atttypmod)
 {
-       Oid                     baseTypeId;
        Oid                     funcId;
-       int32           domainTypMod;
-
-       /* If given type is a domain, use base type instead */
-       baseTypeId = getBaseTypeTypeMod(targetTypeId, &domainTypMod);
-
-
-       /*
-        * Use the domain typmod rather than what was supplied if the
-        * domain was empty.  atttypmod will always be -1 if domains are in use.
-        */
-       if (baseTypeId != targetTypeId)
-       {
-               Assert(atttypmod < 0);
-               atttypmod = domainTypMod;
-       }
 
        /*
         * A negative typmod is assumed to mean that no coercion is wanted.
@@ -335,7 +345,8 @@ coerce_type_typmod(ParseState *pstate, Node *node,
        if (atttypmod < 0 || atttypmod == exprTypmod(node))
                return node;
 
-       funcId = find_typmod_coercion_function(baseTypeId);
+       funcId = find_typmod_coercion_function(targetTypeId);
+
        if (OidIsValid(funcId))
        {
                Const      *cons;
@@ -348,7 +359,7 @@ coerce_type_typmod(ParseState *pstate, Node *node,
                                                 false,
                                                 false);
 
-               node = build_func_call(funcId, baseTypeId, makeList2(node, cons));
+               node = build_func_call(funcId, targetTypeId, makeList2(node, cons));
        }
 
        return node;
@@ -869,12 +880,15 @@ build_func_call(Oid funcid, Oid rettype, List *args)
 
 /*
  * Create an expression tree to enforce the constraints (if any)
- * which should be applied by the type.
+ * that should be applied by the type.  Currently this is only
+ * interesting for domain types.
  */
-static Node *
-TypeConstraints(Node *arg, Oid typeId)
+Node *
+coerce_type_constraints(ParseState *pstate, Node *arg,
+                                               Oid typeId, bool applyTypmod)
 {
        char   *notNull = NULL;
+       int32   typmod = -1;
 
        for (;;)
        {
@@ -885,12 +899,13 @@ TypeConstraints(Node *arg, Oid typeId)
                                                         ObjectIdGetDatum(typeId),
                                                         0, 0, 0);
                if (!HeapTupleIsValid(tup))
-                       elog(ERROR, "getBaseType: failed to lookup type %u", typeId);
+                       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 = NameStr(typTup->typname);
+                       notNull = pstrdup(NameStr(typTup->typname));
 
                /* TODO: Add CHECK Constraints to domains */
 
@@ -901,20 +916,32 @@ TypeConstraints(Node *arg, Oid typeId)
                        break;
                }
 
+               Assert(typmod < 0);
+
                typeId = typTup->typbasetype;
+               typmod = typTup->typtypmod;
                ReleaseSysCache(tup);
        }
 
+       /*
+        * If domain applies a typmod to its base type, do length coercion.
+        */
+       if (applyTypmod && typmod >= 0)
+               arg = coerce_type_typmod(pstate, arg, typeId, typmod);
+
        /*
         * Only need to add one NOT NULL check regardless of how many 
-        * domains in the tree request it.
+        * domains in the stack request it.  The topmost domain that
+        * requested it is used as the constraint name.
         */
-       if (notNull != NULL) {
-               Constraint *r = makeNode(Constraint);
+       if (notNull)
+       {
+               ConstraintTest *r = makeNode(ConstraintTest);
 
-               r->raw_expr = arg;
-               r->contype = CONSTR_NOTNULL;
-               r->name = notNull; 
+               r->arg = arg;
+               r->testtype = CONSTR_TEST_NOTNULL;
+               r->name = notNull;
+               r->check_expr = NULL;
 
                arg = (Node *) r;
        }       
index 1a7acd22527a5c7f014ba8e045a3d353ad7b946a..de07d39b4d0b199336b5017b734d5832d2e44514 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.126 2002/08/26 17:53:58 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.127 2002/08/31 22:10:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -640,6 +640,7 @@ transformExpr(ParseState *pstate, Node *expr)
                case T_ArrayRef:
                case T_FieldSelect:
                case T_RelabelType:
+               case T_ConstraintTest:
                        {
                                result = (Node *) expr;
                                break;
@@ -919,15 +920,15 @@ exprType(Node *expr)
                case T_CaseWhen:
                        type = exprType(((CaseWhen *) expr)->result);
                        break;
-               case T_Constraint:
-                       type = exprType(((Constraint *) expr)->raw_expr);
-                       break;
                case T_NullTest:
                        type = BOOLOID;
                        break;
                case T_BooleanTest:
                        type = BOOLOID;
                        break;
+               case T_ConstraintTest:
+                       type = exprType(((ConstraintTest *) expr)->arg);
+                       break;
                default:
                        elog(ERROR, "exprType: Do not know how to get type for %d node",
                                 nodeTag(expr));
@@ -978,10 +979,8 @@ exprTypmod(Node *expr)
                        break;
                case T_FieldSelect:
                        return ((FieldSelect *) expr)->resulttypmod;
-                       break;
                case T_RelabelType:
                        return ((RelabelType *) expr)->resulttypmod;
-                       break;
                case T_CaseExpr:
                        {
                                /*
@@ -1013,6 +1012,9 @@ exprTypmod(Node *expr)
                                return typmod;
                        }
                        break;
+               case T_ConstraintTest:
+                       return exprTypmod(((ConstraintTest *) expr)->arg);
+
                default:
                        break;
        }
index 0eb29045b1f62f27996430dbf29ff9b0951afef9..e75c193eff5b65f5a0ab54a576e4a56754a772a5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.48 2002/08/08 01:22:35 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.49 2002/08/31 22:10:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -277,6 +277,16 @@ typeByVal(Type t)
        return typ->typbyval;
 }
 
+/* given type (as type struct), return the value of its 'typtype' attribute.*/
+char
+typeTypType(Type t)
+{
+       Form_pg_type typ;
+
+       typ = (Form_pg_type) GETSTRUCT(t);
+       return typ->typtype;
+}
+
 /* given type (as type struct), return the name of type */
 char *
 typeTypeName(Type t)
@@ -434,7 +444,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
         * paranoia is justified since the string might contain anything.
         */
        if (length(raw_parsetree_list) != 1)
-               elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+               elog(ERROR, "Invalid type name '%s'", str);
        stmt = (SelectStmt *) lfirst(raw_parsetree_list);
        if (stmt == NULL ||
                !IsA(stmt, SelectStmt) ||
@@ -450,25 +460,26 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
                stmt->limitCount != NULL ||
                stmt->forUpdate != NIL ||
                stmt->op != SETOP_NONE)
-               elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+               elog(ERROR, "Invalid type name '%s'", str);
        if (length(stmt->targetList) != 1)
-               elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+               elog(ERROR, "Invalid type name '%s'", str);
        restarget = (ResTarget *) lfirst(stmt->targetList);
        if (restarget == NULL ||
                !IsA(restarget, ResTarget) ||
                restarget->name != NULL ||
                restarget->indirection != NIL)
-               elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+               elog(ERROR, "Invalid type name '%s'", str);
        typecast = (TypeCast *) restarget->val;
        if (typecast == NULL ||
                !IsA(typecast, TypeCast) ||
                typecast->arg == NULL ||
                !IsA(typecast->arg, A_Const))
-               elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+               elog(ERROR, "Invalid type name '%s'", str);
        typename = typecast->typename;
        if (typename == NULL ||
                !IsA(typename, TypeName))
-               elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+               elog(ERROR, "Invalid type name '%s'", str);
+
        *type_id = typenameTypeId(typename);
        *typmod = typename->typmod;
 
index 458ab68749dd25e9a6437d497fda51fc8d8980f8..c7da14ad7eabd294891b7c57179d96cc65395661 100644 (file)
@@ -3,7 +3,7 @@
  *                             back to source text
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.119 2002/08/29 01:19:41 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.120 2002/08/31 22:10:46 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -2187,6 +2187,18 @@ get_rule_expr(Node *node, deparse_context *context)
                        }
                        break;
 
+               case T_ConstraintTest:
+                       {
+                               ConstraintTest *ctest = (ConstraintTest *) node;
+
+                               /*
+                                * We assume that the operations of the constraint node
+                                * need not be explicitly represented in the output.
+                                */
+                               get_rule_expr(ctest->arg, context);
+                       }
+                       break;
+
                case T_SubLink:
                        get_sublink_expr(node, context);
                        break;
index 66dc58d6c4b4935706ce3aa003da7e36d0d556cd..4054b2920e6430e44ab70dec30b2caaac27d5b50 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.81 2002/08/29 00:17:05 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.82 2002/08/31 22:10:47 tgl Exp $
  *
  * NOTES
  *       Eventually, the index information should go through here, too.
@@ -1074,12 +1074,12 @@ getBaseType(Oid typid)
 }
 
 /*
- * getBaseTypeTypeMod
- *             If the given type is a domain, return its base type;
- *             otherwise return the type's own OID.  Also return base typmod.
+ * getBaseTypeMod
+ *             If the given type is a domain, return the typmod it applies to
+ *             its base type; otherwise return the specified original typmod.
  */
-Oid
-getBaseTypeTypeMod(Oid typid, int32 *typmod)
+int32
+getBaseTypeMod(Oid typid, int32 typmod)
 {
        /*
         * We loop to find the bottom base type in a stack of domains.
@@ -1093,7 +1093,7 @@ getBaseTypeTypeMod(Oid typid, int32 *typmod)
                                                         ObjectIdGetDatum(typid),
                                                         0, 0, 0);
                if (!HeapTupleIsValid(tup))
-                       elog(ERROR, "getBaseTypeTypeMod: failed to lookup type %u", typid);
+                       elog(ERROR, "getBaseTypeMod: failed to lookup type %u", typid);
                typTup = (Form_pg_type) GETSTRUCT(tup);
                if (typTup->typtype != 'd')
                {
@@ -1102,12 +1102,20 @@ getBaseTypeTypeMod(Oid typid, int32 *typmod)
                        break;
                }
 
+               /*
+                * The typmod applied to a domain should always be -1.
+                *
+                * We substitute the domain's typmod as we switch attention to
+                * the base type.
+                */
+               Assert(typmod < 0);
+
                typid = typTup->typbasetype;
-               *typmod = typTup->typtypmod;
+               typmod = typTup->typtypmod;
                ReleaseSysCache(tup);
        }
 
-       return typid;
+       return typmod;
 }
 
 /*
index 3f5f6d744998d40c868a95d727132d14c8e159b2..ee472203e68fd3073d391639d004fee40b25cbd7 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.117 2002/08/27 04:55:11 tgl Exp $
+ * $Id: nodes.h,v 1.118 2002/08/31 22:10:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -229,6 +229,7 @@ typedef enum NodeTag
        T_GroupClause,
        T_NullTest,
        T_BooleanTest,
+       T_ConstraintTest,
        T_CaseExpr,
        T_CaseWhen,
        T_FkConstraint,
index 19d52d72175a812ae5ae4d11de868f2e08325e19..a426aeba020fd0f373b4908a7c875b03b6b47271 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.203 2002/08/30 19:23:20 tgl Exp $
+ * $Id: parsenodes.h,v 1.204 2002/08/31 22:10:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -233,14 +233,13 @@ typedef struct NullTest
        NullTestType nulltesttype;      /* IS NULL, IS NOT NULL */
 } NullTest;
 
-/* ----------------
+/*
  * BooleanTest
  *
  * BooleanTest represents the operation of determining whether a boolean
  * is TRUE, FALSE, or UNKNOWN (ie, NULL).  All six meaningful combinations
  * are supported.  Note that a NULL input does *not* cause a NULL result.
  * The appropriate test is performed and returned as a boolean Datum.
- * ----------------
  */
 
 typedef enum BoolTestType
@@ -255,6 +254,29 @@ typedef struct BooleanTest
        BoolTestType booltesttype;      /* test type */
 } BooleanTest;
 
+/*
+ * ConstraintTest
+ *
+ * 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.
+ */
+
+typedef enum ConstraintTestType
+{
+       CONSTR_TEST_NOTNULL,
+       CONSTR_TEST_CHECK
+} ConstraintTestType;
+
+typedef struct ConstraintTest
+{
+       NodeTag         type;
+       Node       *arg;                        /* input expression */
+       ConstraintTestType testtype; /* test type */
+       char       *name;                       /* name of constraint (for error msgs) */
+       Node       *check_expr;         /* for CHECK test, a boolean expression */
+} ConstraintTest;
+
 /*
  * ColumnDef - column definition (used in various creates)
  *
index e306493fa3616c627ab7e6bbc48eb6d35b764564..328332aafd2e5affa823332bc18e19ff7187cc17 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.44 2002/06/20 20:29:51 momjian Exp $
+ * $Id: parse_coerce.h,v 1.45 2002/08/31 22:10:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,8 @@ extern Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
                        Oid targetTypeId, int32 atttypmod, bool isExplicit);
 extern Node *coerce_type_typmod(ParseState *pstate, Node *node,
                                   Oid targetTypeId, int32 atttypmod);
+extern Node *coerce_type_constraints(ParseState *pstate, Node *arg,
+                                                                        Oid typeId, bool applyTypmod);
 
 extern Node *coerce_to_boolean(Node *node, const char *constructName);
 
index f348a32d4162e77a31c292f40afd17065b6bb8bb..86c453882bc13ffcd63eb793f898f1cc069ef89e 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_type.h,v 1.23 2002/06/20 20:29:52 momjian Exp $
+ * $Id: parse_type.h,v 1.24 2002/08/31 22:10:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,6 +31,7 @@ extern Type typeidType(Oid id);
 extern Oid     typeTypeId(Type tp);
 extern int16 typeLen(Type t);
 extern bool typeByVal(Type t);
+extern char typeTypType(Type t);
 extern char *typeTypeName(Type t);
 extern char typeTypeFlag(Type t);
 extern Oid     typeTypeRelid(Type typ);
index 2681d67139f97767c5a6d65ccaee25d25f45cec0..3c442bbd202d8e8f7f786a4932a136dac57f7e83 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.60 2002/08/29 00:17:06 tgl Exp $
+ * $Id: lsyscache.h,v 1.61 2002/08/31 22:10:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -58,7 +58,7 @@ extern void getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem);
 extern bool getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
                                                          bool *typIsVarlena);
 extern Oid getBaseType(Oid typid);
-extern Oid getBaseTypeTypeMod(Oid typid, int32 *typmod);
+extern int32 getBaseTypeMod(Oid typid, int32 typmod);
 extern int32 get_typavgwidth(Oid typid, int32 typmod);
 extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);
 extern bool get_attstatsslot(HeapTuple statstuple,
index a67559129c5171905f818b8d5a59655b656a4227..522c28121ff27ef071895d5b017394ac133732f8 100644 (file)
@@ -41,8 +41,7 @@ select * from basictest;
 (2 rows)
 
 -- check that domains inherit operations from base types
--- XXX shouldn't have to quote the constant here
-select testtext || testvarchar as concat, testnumeric + '42' as sum
+select testtext || testvarchar as concat, testnumeric + 42 as sum
 from basictest;
   concat   |  sum   
 -----------+--------
@@ -99,7 +98,7 @@ create table nulltest
            , col4 dnull
            );
 INSERT INTO nulltest DEFAULT VALUES;
-ERROR:  ExecInsert: Fail to add null value in not null attribute col3
+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');
 ERROR:  Domain dnotnull does not allow NULL values
@@ -147,7 +146,7 @@ create table defaulttest
             , col5 ddef1 NOT NULL DEFAULT NULL
             , col6 ddef2 DEFAULT '88'
             , col7 ddef4 DEFAULT 8000
-               , col8 ddef5
+            , col8 ddef5
             );
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index 'defaulttest_pkey' for table 'defaulttest'
 insert into defaulttest default values;
index 4c2e7e31aca3590223f4c281a6358dbd12f9afad..9627870934fe16e533adb5c3f7d3bc86288e551c 100644 (file)
@@ -38,8 +38,7 @@ INSERT INTO basictest values ('88', 'haha', 'short', '123.1212');    -- Truncate
 select * from basictest;
 
 -- check that domains inherit operations from base types
--- XXX shouldn't have to quote the constant here
-select testtext || testvarchar as concat, testnumeric + '42' as sum
+select testtext || testvarchar as concat, testnumeric + 42 as sum
 from basictest;
 
 drop table basictest;