aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/copy.c24
-rw-r--r--src/backend/commands/typecmds.c129
-rw-r--r--src/backend/executor/execQual.c82
-rw-r--r--src/backend/nodes/copyfuncs.c33
-rw-r--r--src/backend/nodes/equalfuncs.c28
-rw-r--r--src/backend/nodes/outfuncs.c25
-rw-r--r--src/backend/nodes/readfuncs.c33
-rw-r--r--src/backend/optimizer/path/clausesel.c10
-rw-r--r--src/backend/optimizer/prep/preptlist.c9
-rw-r--r--src/backend/optimizer/util/clauses.c21
-rw-r--r--src/backend/parser/parse_coerce.c212
-rw-r--r--src/backend/parser/parse_expr.c22
-rw-r--r--src/backend/utils/adt/ruleutils.c46
-rw-r--r--src/backend/utils/cache/lsyscache.c29
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/commands/typecmds.h4
-rw-r--r--src/include/nodes/execnodes.h34
-rw-r--r--src/include/nodes/nodes.h9
-rw-r--r--src/include/nodes/primnodes.h34
-rw-r--r--src/include/parser/parse_coerce.h6
-rw-r--r--src/include/utils/lsyscache.h3
-rw-r--r--src/pl/plpgsql/src/pl_exec.c5
-rw-r--r--src/test/regress/expected/domain.out41
-rw-r--r--src/test/regress/sql/domain.sql20
24 files changed, 524 insertions, 339 deletions
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 91386eeb2cc..baaf57a70ac 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 379e4bb9b45..ad954605373 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;
@@ -1670,6 +1672,125 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
}
/*
+ * 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
*
* Eventually this should allow changing ownership of other kinds of types,
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index c13e1e1e4d8..a2583fcc4c9 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 667e4f30c20..8437ed82a5a 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 19829736967..7916ca0fca6 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 fd18c957d9b..ea6305512ce 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 457e04eb9f6..fe4831ee9f6 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 9df0a794782..8fe67ec4757 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 87d3c983a70..dc8faa9d837 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 253c9e88138..f0b8ab1262d 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 af184eca6d1..46d191a9158 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 5ee64cf38ee..95b3a6297ae 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 df5890358b8..10ee725b30a 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 a6c838974c0..abc00a9204e 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.
@@ -1013,6 +1013,33 @@ get_typstorage(Oid typid)
}
/*
+ * 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 d234eb32895..df4347633f2 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 fde284efeaf..0d2e319ed44 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 98c6f1ddfbd..ccb25320662 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 bf8bb1719ed..a218790194e 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 b187c98fdc7..ad1c7361255 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 ecc61ea716a..ae12f46f621 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 8200730f6a2..d6f9447d190 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 c9fd9418b44..7d4785ef7d5 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 e48120a68d8..1aaa4a85ef4 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 76060e99c8b..00a38f449cd 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;