From 3752e85bad45e98383ea1c9e7e7dbaf13f41da7e Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 3 Feb 2003 21:15:45 +0000 Subject: [PATCH] Determine the set of constraints applied to a domain at executor 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. --- src/backend/commands/copy.c | 24 ++- src/backend/commands/typecmds.c | 129 ++++++++++++++- src/backend/executor/execQual.c | 82 +++++----- src/backend/nodes/copyfuncs.c | 33 ++-- src/backend/nodes/equalfuncs.c | 28 ++-- src/backend/nodes/outfuncs.c | 25 ++- src/backend/nodes/readfuncs.c | 33 ++-- src/backend/optimizer/path/clausesel.c | 10 +- src/backend/optimizer/prep/preptlist.c | 9 +- src/backend/optimizer/util/clauses.c | 21 ++- src/backend/parser/parse_coerce.c | 212 ++++++++----------------- src/backend/parser/parse_expr.c | 22 +-- src/backend/utils/adt/ruleutils.c | 46 ++++-- src/backend/utils/cache/lsyscache.c | 29 +++- src/include/catalog/catversion.h | 4 +- src/include/commands/typecmds.h | 4 +- src/include/nodes/execnodes.h | 34 +++- src/include/nodes/nodes.h | 9 +- src/include/nodes/primnodes.h | 34 ++-- src/include/parser/parse_coerce.h | 6 +- src/include/utils/lsyscache.h | 3 +- src/pl/plpgsql/src/pl_exec.c | 5 +- src/test/regress/expected/domain.out | 41 ++++- src/test/regress/sql/domain.sql | 20 +++ 24 files changed, 524 insertions(+), 339 deletions(-) diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 91386eeb2c..baaf57a70a 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -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; } } diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 379e4bb9b4..ad95460537 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -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 @@ -46,8 +46,10 @@ #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 * diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index c13e1e1e4d..a2583fcc4c 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.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; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 667e4f30c2..8437ed82a5 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.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); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 1982973696..7916ca0fca 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index fd18c957d9..ea6305512c 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -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); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 457e04eb9f..fe4831ee9f 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.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)) diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 9df0a79478..8fe67ec475 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -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); diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 87d3c983a7..dc8faa9d83 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.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 */ diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 253c9e8813..f0b8ab1262 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.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; diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index af184eca6d..46d191a915 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -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); diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 5ee64cf38e..95b3a6297a 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.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; } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index df5890358b..10ee725b30 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.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) diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index a6c838974c..abc00a9204 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.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. diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index d234eb3289..df4347633f 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -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 diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h index fde284efea..0d2e319ed4 100644 --- a/src/include/commands/typecmds.h +++ b/src/include/commands/typecmds.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: 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 */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 98c6f1ddfb..ccb2532066 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.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: 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; /* ---------------------------------------------------------------- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index bf8bb1719e..a218790194 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.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) diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index b187c98fdc..ad1c736125 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -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; /* diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index ecc61ea716..ae12f46f62 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.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); diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 8200730f6a..d6f9447d19 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.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); diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index c9fd9418b4..7d4785ef7d 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -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; diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index e48120a68d..1aaa4a85ef 100644 --- a/src/test/regress/expected/domain.out +++ b/src/test/regress/expected/domain.out @@ -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; diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql index 76060e99c8..00a38f449c 100644 --- a/src/test/regress/sql/domain.sql +++ b/src/test/regress/sql/domain.sql @@ -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; -- 2.40.0