diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index c53ac42b26f70c282c8d2a4f849029fe0c4f2e8c..30f5b0e378f3406ab985c605180ede27d0c009e5 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -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", diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 5b35eea170a39c2086a2ac366fcb565f87fb2aaf..1938e7f4738ef7e0b9066eaa6d261b43ab3c2a53 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 7c9127dbf4f6ab31147fa1aa34d24b27199329cf..10b8e79933d7847a4dc2a220efdd4e0e4b30b54d 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index a92750caef194311007d812d6d0d690df97ecb71..8c255058068974b0a48fadeec3cf2c7de90533db 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -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; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 2799bb746044b97b4aaad5644b42d9380ebc0a81..4d4001d21316e28a50520a4d48d201755cbe7191 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -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); diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index b7c0bac12c8124934344e55d96381df6640015da..41f9b2f9478f976017efb41e83b587108a124d36 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -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 */ diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 9e8372139efc2f55410e68934c540fb117d729c5..3b5e6988396d23e10eb07266ec903024babb7bb0 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -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: { /* diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 1016c782d29d11f741e7693219233abe319ff8ff..397bd4b8b079e84482b2f5570f9a2c000009baba 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -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; } diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 1a7acd22527a5c7f014ba8e045a3d353ad7b946a..de07d39b4d0b199336b5017b734d5832d2e44514 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -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; } diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 0eb29045b1f62f27996430dbf29ff9b0951afef9..e75c193eff5b65f5a0ab54a576e4a56754a772a5 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -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; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 458ab68749dd25e9a6437d497fda51fc8d8980f8..c7da14ad7eabd294891b7c57179d96cc65395661 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -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; diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 66dc58d6c4b4935706ce3aa003da7e36d0d556cd..4054b2920e6430e44ab70dec30b2caaac27d5b50 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -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; } /* diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 3f5f6d744998d40c868a95d727132d14c8e159b2..ee472203e68fd3073d391639d004fee40b25cbd7 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -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, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 19d52d72175a812ae5ae4d11de868f2e08325e19..a426aeba020fd0f373b4908a7c875b03b6b47271 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -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) * diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index e306493fa3616c627ab7e6bbc48eb6d35b764564..328332aafd2e5affa823332bc18e19ff7187cc17 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -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); diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h index f348a32d4162e77a31c292f40afd17065b6bb8bb..86c453882bc13ffcd63eb793f898f1cc069ef89e 100644 --- a/src/include/parser/parse_type.h +++ b/src/include/parser/parse_type.h @@ -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); diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 2681d67139f97767c5a6d65ccaee25d25f45cec0..3c442bbd202d8e8f7f786a4932a136dac57f7e83 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -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, diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index a67559129c5171905f818b8d5a59655b656a4227..522c28121ff27ef071895d5b017394ac133732f8 100644 --- a/src/test/regress/expected/domain.out +++ b/src/test/regress/expected/domain.out @@ -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; diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql index 4c2e7e31aca3590223f4c281a6358dbd12f9afad..9627870934fe16e533adb5c3f7d3bc86288e551c 100644 --- a/src/test/regress/sql/domain.sql +++ b/src/test/regress/sql/domain.sql @@ -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;