diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml
index b7d5e606d3e..bd4114688f6 100644
--- a/doc/src/sgml/ref/create_domain.sgml
+++ b/doc/src/sgml/ref/create_domain.sgml
@@ -1,5 +1,5 @@
@@ -200,16 +200,6 @@ CREATE TABLE countrylist (id INT4, country country_code);
-
- Compatibility
-
-
- SQL99 defines CREATE DOMAIN, but says that the only allowed constraint
- type is CHECK constraints. CHECK constraints for domains are not yet
- supported by PostgreSQL.
-
-
-
See Also
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 9956672c12a..f8f667e24a9 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.234 2002/11/11 22:19:21 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.235 2002/11/15 02:50:05 momjian Exp $
*
*
* INTERFACE ROUTINES
@@ -1500,7 +1500,8 @@ AddRelationRawConstraints(Relation rel,
ccname = cdef->name;
/* Check against pre-existing constraints */
- if (ConstraintNameIsUsed(RelationGetRelid(rel),
+ if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
+ RelationGetRelid(rel),
RelationGetNamespace(rel),
ccname))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
@@ -1534,7 +1535,8 @@ AddRelationRawConstraints(Relation rel,
* pre-existing constraints, nor with any auto-generated
* names so far.
*/
- ccname = GenerateConstraintName(RelationGetRelid(rel),
+ ccname = GenerateConstraintName(CONSTRAINT_RELATION,
+ RelationGetRelid(rel),
RelationGetNamespace(rel),
&constr_name_ctr);
@@ -1565,7 +1567,7 @@ AddRelationRawConstraints(Relation rel,
/*
* Transform raw parsetree to executable expression.
*/
- expr = transformExpr(pstate, cdef->raw_expr);
+ expr = transformExpr(pstate, cdef->raw_expr, NULL);
/*
* Make sure it yields a boolean result.
@@ -1694,7 +1696,7 @@ cookDefault(ParseState *pstate,
/*
* Transform raw parsetree to executable expression.
*/
- expr = transformExpr(pstate, raw_default);
+ expr = transformExpr(pstate, raw_default, NULL);
/*
* Make sure default expr does not refer to any vars.
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index e03e545beb3..7cd105928c3 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.7 2002/09/22 00:37:09 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.8 2002/11/15 02:50:05 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -190,6 +190,19 @@ CreateConstraintEntry(const char *constraintName,
}
}
+ if (OidIsValid(domainId))
+ {
+ /*
+ * Register auto dependency from constraint to owning domain
+ */
+ ObjectAddress domobject;
+
+ domobject.classId = RelOid_pg_type;
+ domobject.objectId = domainId;
+
+ recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
+ }
+
if (OidIsValid(foreignRelId))
{
/*
@@ -262,7 +275,7 @@ CreateConstraintEntry(const char *constraintName,
* this test is not very meaningful.
*/
bool
-ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
+ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, const char *cname)
{
bool found;
Relation conDesc;
@@ -280,7 +293,7 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
ScanKeyEntryInitialize(&skey[1], 0x0,
Anum_pg_constraint_connamespace, F_OIDEQ,
- ObjectIdGetDatum(relNamespace));
+ ObjectIdGetDatum(objNamespace));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
SnapshotNow, 2, skey);
@@ -289,7 +302,12 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
- if (con->conrelid == relId)
+ if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
+ {
+ found = true;
+ break;
+ }
+ else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
{
found = true;
break;
@@ -314,7 +332,7 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
* someone else might choose the same name concurrently!
*/
char *
-GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
+GenerateConstraintName(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, int *counter)
{
bool found;
Relation conDesc;
@@ -347,7 +365,7 @@ GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
ScanKeyEntryInitialize(&skey[1], 0x0,
Anum_pg_constraint_connamespace, F_OIDEQ,
- ObjectIdGetDatum(relNamespace));
+ ObjectIdGetDatum(objNamespace));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
SnapshotNow, 2, skey);
@@ -356,7 +374,12 @@ GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
- if (con->conrelid == relId)
+ if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
+ {
+ found = true;
+ break;
+ }
+ else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
{
found = true;
break;
@@ -415,10 +438,13 @@ RemoveConstraintById(Oid conId)
con = (Form_pg_constraint) GETSTRUCT(tup);
/*
- * If the constraint is for a relation, open and exclusive-lock the
- * relation it's for.
+ * If the constraint is for a relation, open and exclusive-lock
+ * the relation it's for.
*
- * XXX not clear what we should lock, if anything, for other constraints.
+ * If the constraint is for a domain, open and lock the pg_type entry
+ * tye constraint is used on.
+ *
+ * XXX not clear what we should lock, if anything, for assert constraints.
*/
if (OidIsValid(con->conrelid))
{
@@ -463,6 +489,34 @@ RemoveConstraintById(Oid conId)
/* Keep lock on constraint's rel until end of xact */
heap_close(rel, NoLock);
}
+ /* Lock the domain row in pg_type */
+ else if (OidIsValid(con->contypid))
+ {
+ Relation typRel;
+ HeapTuple typTup;
+ ScanKeyData typKey[1];
+ SysScanDesc typScan;
+
+ typRel = heap_openr(TypeRelationName, RowExclusiveLock);
+
+ ScanKeyEntryInitialize(&typKey[0], 0x0,
+ Anum_pg_constraint_contypid, F_OIDEQ,
+ ObjectIdGetDatum(con->contypid));
+
+ typScan = systable_beginscan(typRel, TypeOidIndex, true,
+ SnapshotNow, 1, typKey);
+
+ typTup = systable_getnext(typScan);
+
+ if (!HeapTupleIsValid(typTup))
+ elog(ERROR, "RemoveConstraintById: Type %d does not exist",
+ con->contypid);
+
+ systable_endscan(typScan);
+
+ /* Keep lock on domain type until end of xact */
+ heap_close(typRel, NoLock);
+ }
/* Fry the constraint itself */
simple_heap_delete(conDesc, &tup->t_self);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 8023ba83420..cda8687e448 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.53 2002/11/11 22:19:21 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.54 2002/11/15 02:50:05 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2632,14 +2632,16 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
*/
if (constr->name)
{
- if (ConstraintNameIsUsed(RelationGetRelid(rel),
+ if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
+ RelationGetRelid(rel),
RelationGetNamespace(rel),
constr->name))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
constr->name, RelationGetRelationName(rel));
}
else
- constr->name = GenerateConstraintName(RelationGetRelid(rel),
+ constr->name = GenerateConstraintName(CONSTRAINT_RELATION,
+ RelationGetRelid(rel),
RelationGetNamespace(rel),
&counter);
@@ -2668,7 +2670,8 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
*/
if (fkconstraint->constr_name)
{
- if (ConstraintNameIsUsed(RelationGetRelid(rel),
+ if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
+ RelationGetRelid(rel),
RelationGetNamespace(rel),
fkconstraint->constr_name))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
@@ -2676,7 +2679,8 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
RelationGetRelationName(rel));
}
else
- fkconstraint->constr_name = GenerateConstraintName(RelationGetRelid(rel),
+ fkconstraint->constr_name = GenerateConstraintName(CONSTRAINT_RELATION,
+ RelationGetRelid(rel),
RelationGetNamespace(rel),
&counter);
@@ -2734,7 +2738,7 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr)
/*
* Convert the A_EXPR in raw_expr into an EXPR
*/
- expr = transformExpr(pstate, constr->raw_expr);
+ expr = transformExpr(pstate, constr->raw_expr, NULL);
/*
* Make sure it yields a boolean result.
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index a573cac27de..ab0608a08a9 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.16 2002/11/11 22:19:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.17 2002/11/15 02:50:06 momjian Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -36,10 +36,17 @@
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
+#include "catalog/pg_constraint.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
#include "miscadmin.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"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
@@ -406,7 +413,8 @@ DefineDomain(CreateDomainStmt *stmt)
List *listptr;
Oid basetypeoid;
Oid domainoid;
- Form_pg_type baseType;
+ Form_pg_type baseType;
+ int counter = 0;
/* Convert list of names to a name and namespace */
domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
@@ -484,17 +492,21 @@ DefineDomain(CreateDomainStmt *stmt)
basetypelem = baseType->typelem;
/*
- * Run through constraints manually to avoid the additional processing
- * conducted by DefineRelation() and friends.
- *
- * Besides, we don't want any constraints to be cooked. We'll do that
- * when the table is created via MergeDomainAttributes().
+ * Run through constraints manually to avoid the additional
+ * processing conducted by DefineRelation() and friends.
*/
foreach(listptr, schema)
{
- Constraint *colDef = lfirst(listptr);
+ Node *newConstraint = lfirst(listptr);
+ Constraint *colDef;
ParseState *pstate;
+ /* Prior to processing, confirm that it is not a foreign key constraint */
+ if (nodeTag(newConstraint) == T_FkConstraint)
+ elog(ERROR, "CREATE DOMAIN / FOREIGN KEY constraints not supported");
+
+ colDef = (Constraint *) newConstraint;
+
switch (colDef->contype)
{
/*
@@ -546,26 +558,26 @@ DefineDomain(CreateDomainStmt *stmt)
elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
typNotNull = false;
nullDefined = true;
- break;
+ break;
- case CONSTR_UNIQUE:
- elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported");
- break;
+ case CONSTR_UNIQUE:
+ elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported");
+ break;
- case CONSTR_PRIMARY:
- elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported");
- break;
+ case CONSTR_PRIMARY:
+ elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported");
+ break;
- case CONSTR_CHECK:
- elog(ERROR, "DefineDomain: CHECK Constraints not supported");
- break;
+ /* Check constraints are handled after domain creation */
+ case CONSTR_CHECK:
+ break;
- case CONSTR_ATTR_DEFERRABLE:
- case CONSTR_ATTR_NOT_DEFERRABLE:
- case CONSTR_ATTR_DEFERRED:
- case CONSTR_ATTR_IMMEDIATE:
- elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
- break;
+ case CONSTR_ATTR_DEFERRABLE:
+ case CONSTR_ATTR_NOT_DEFERRABLE:
+ case CONSTR_ATTR_DEFERRED:
+ case CONSTR_ATTR_IMMEDIATE:
+ elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
+ break;
default:
elog(ERROR, "DefineDomain: unrecognized constraint node type");
@@ -591,12 +603,139 @@ DefineDomain(CreateDomainStmt *stmt)
basetypeoid, /* base type ID */
defaultValue, /* default type value (text) */
defaultValueBin, /* default type value (binary) */
- byValue, /* passed by value */
- alignment, /* required alignment */
- storage, /* TOAST strategy */
- stmt->typename->typmod, /* typeMod value */
- typNDims, /* Array dimensions for base type */
- typNotNull); /* Type NOT NULL */
+ byValue, /* passed by value */
+ alignment, /* required alignment */
+ storage, /* TOAST strategy */
+ stmt->typename->typmod, /* typeMod value */
+ typNDims, /* Array dimensions for base type */
+ typNotNull); /* Type NOT NULL */
+
+ /*
+ * Process constraints which refer to the domain ID returned by TypeCreate
+ */
+ foreach(listptr, schema)
+ {
+ Constraint *constr = lfirst(listptr);
+ ParseState *pstate;
+
+ switch (constr->contype)
+ {
+ case CONSTR_CHECK:
+ {
+ Node *expr;
+ char *ccsrc;
+ char *ccbin;
+ ConstraintTestValue *domVal;
+
+ /*
+ * Assign or validate constraint name
+ */
+ if (constr->name)
+ {
+ if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
+ domainoid,
+ domainNamespace,
+ constr->name))
+ elog(ERROR, "constraint \"%s\" already exists for domain \"%s\"",
+ constr->name,
+ domainName);
+ }
+ else
+ constr->name = GenerateConstraintName(CONSTRAINT_DOMAIN,
+ domainoid,
+ domainNamespace,
+ &counter);
+
+ /*
+ * Convert the A_EXPR in raw_expr into an
+ * EXPR
+ */
+ pstate = make_parsestate(NULL);
+
+ /*
+ * We want to have the domain VALUE node type filled in so
+ * that proper casting can occur.
+ */
+ domVal = makeNode(ConstraintTestValue);
+ domVal->typeId = basetypeoid;
+ domVal->typeMod = stmt->typename->typmod;
+
+ expr = transformExpr(pstate, constr->raw_expr, domVal);
+
+ /*
+ * Domains don't allow var clauses
+ */
+ if (contain_var_clause(expr))
+ elog(ERROR, "cannot use column references in domain CHECK clause");
+
+ /*
+ * Make sure it yields a boolean result.
+ */
+ expr = coerce_to_boolean(expr, "CHECK");
+
+ /*
+ * Make sure no outside relations are
+ * referred to.
+ */
+ if (length(pstate->p_rtable) != 0)
+ elog(ERROR, "Relations cannot be referenced in domain CHECK constraint");
+
+ /*
+ * No subplans or aggregates, either...
+ */
+ if (contain_subplans(expr))
+ elog(ERROR, "cannot use subselect in CHECK constraint expression");
+ if (contain_agg_clause(expr))
+ elog(ERROR, "cannot use aggregate function in CHECK constraint expression");
+
+ /*
+ * Might as well try to reduce any constant expressions.
+ */
+ expr = eval_const_expressions(expr);
+
+ /*
+ * Must fix opids in operator clauses.
+ */
+ fix_opids(expr);
+
+ ccbin = nodeToString(expr);
+
+ /*
+ * Deparse it. Since VARNOs aren't allowed in domain
+ * constraints, relation context isn't required as anything
+ * other than a shell.
+ */
+ ccsrc = deparse_expression(expr,
+ deparse_context_for(domainName,
+ InvalidOid),
+ false, false);
+
+ /* Write the constraint */
+ CreateConstraintEntry(constr->name, /* Constraint Name */
+ domainNamespace, /* namespace */
+ CONSTRAINT_CHECK, /* Constraint Type */
+ false, /* Is Deferrable */
+ false, /* Is Deferred */
+ InvalidOid, /* not a relation constraint */
+ NULL,
+ 0,
+ domainoid, /* domain constraint */
+ InvalidOid, /* Foreign key fields */
+ NULL,
+ 0,
+ ' ',
+ ' ',
+ ' ',
+ InvalidOid,
+ expr, /* Tree form check constraint */
+ ccbin, /* Binary form check constraint */
+ ccsrc); /* Source form check constraint */
+ }
+ break;
+ default:
+ break;
+ }
+ }
/*
* Add any dependencies needed for the default expression.
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 5718983ca10..1612a2d9ea2 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.108 2002/09/04 20:31:17 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.109 2002/11/15 02:50:06 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -72,6 +72,9 @@ static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
static Datum ExecEvalConstraintTest(ConstraintTest *constraint,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalConstraintTestValue(ConstraintTestValue *conVal,
+ ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone);
/*----------
@@ -1551,6 +1554,23 @@ ExecEvalBooleanTest(BooleanTest *btest,
}
}
+/*
+ * ExecEvalConstraintTestValue
+ *
+ * Return the value stored by constraintTest.
+ */
+static Datum
+ExecEvalConstraintTestValue(ConstraintTestValue *conVal, ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone)
+{
+ /*
+ * If the Datum hasn't been set, then it's ExecEvalConstraintTest
+ * hasn't been called.
+ */
+ *isNull = econtext->domainValue_isNull;
+ return econtext->domainValue_datum;
+}
+
/*
* ExecEvalConstraintTest
*
@@ -1571,11 +1591,22 @@ ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext,
case CONSTR_TEST_NOTNULL:
if (*isNull)
elog(ERROR, "Domain %s does not allow NULL values",
- constraint->name);
+ constraint->domname);
break;
case CONSTR_TEST_CHECK:
- /* TODO: Add CHECK Constraints to domains */
- elog(ERROR, "Domain CHECK Constraints not yet implemented");
+ {
+ Datum conResult;
+
+ /* Var with attnum == UnassignedAttrNum uses the result */
+ econtext->domainValue_datum = result;
+ econtext->domainValue_isNull = *isNull;
+
+ conResult = ExecEvalExpr(constraint->check_expr, econtext, isNull, isDone);
+
+ if (!DatumGetBool(conResult))
+ elog(ERROR, "Domain %s constraint %s failed",
+ constraint->name, constraint->domname);
+ }
break;
default:
elog(ERROR, "ExecEvalConstraintTest: Constraint type unknown");
@@ -1777,7 +1808,12 @@ ExecEvalExpr(Node *expression,
isNull,
isDone);
break;
-
+ case T_ConstraintTestValue:
+ retDatum = ExecEvalConstraintTestValue((ConstraintTestValue *) expression,
+ econtext,
+ isNull,
+ isDone);
+ break;
default:
elog(ERROR, "ExecEvalExpr: unknown expression type %d",
nodeTag(expression));
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c354abf5dad..9dc29584e82 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.217 2002/11/11 22:19:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.218 2002/11/15 02:50:06 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1056,11 +1056,35 @@ _copyConstraintTest(ConstraintTest *from)
newnode->testtype = from->testtype;
if (from->name)
newnode->name = pstrdup(from->name);
+ if (from->domname)
+ newnode->domname = pstrdup(from->domname);
Node_Copy(from, newnode, check_expr);
return newnode;
}
+static ConstraintTestValue *
+_copyConstraintTestValue(ConstraintTestValue *from)
+{
+ ConstraintTestValue *newnode = makeNode(ConstraintTestValue);
+
+ /*
+ * copy remainder of node
+ */
+ newnode->typeId = from->typeId;
+ newnode->typeMod = from->typeMod;
+
+ return newnode;
+}
+
+static DomainConstraintValue *
+_copyDomainConstraintValue(DomainConstraintValue *from)
+{
+ DomainConstraintValue *newnode = makeNode(DomainConstraintValue);
+
+ return newnode;
+}
+
static ArrayRef *
_copyArrayRef(ArrayRef *from)
{
@@ -3252,6 +3276,9 @@ copyObject(void *from)
case T_ConstraintTest:
retval = _copyConstraintTest(from);
break;
+ case T_ConstraintTestValue:
+ retval = _copyConstraintTestValue(from);
+ break;
case T_FkConstraint:
retval = _copyFkConstraint(from);
break;
@@ -3264,6 +3291,9 @@ copyObject(void *from)
case T_InsertDefault:
retval = _copyInsertDefault(from);
break;
+ case T_DomainConstraintValue:
+ retval = _copyDomainConstraintValue(from);
+ break;
default:
elog(ERROR, "copyObject: don't know how to copy node type %d",
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index ab84f5d3d20..68e93e48b08 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.163 2002/11/11 22:19:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.164 2002/11/15 02:50:06 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1971,15 +1971,32 @@ _equalConstraintTest(ConstraintTest *a, ConstraintTest *b)
return false;
if (!equalstr(a->name, b->name))
return false;
+ if (!equalstr(a->domname, b->domname))
+ return false;
if (!equal(a->check_expr, b->check_expr))
return false;
return true;
}
+static bool
+_equalConstraintTestValue(ConstraintTestValue *a, ConstraintTestValue *b)
+{
+ if (a->typeId != b->typeId)
+ return false;
+ if (a->typeMod != b->typeMod)
+ return false;
+ return true;
+}
+
+static bool
+_equalDomainConstraintValue(DomainConstraintValue *a, DomainConstraintValue *b)
+{
+ return true;
+}
+
/*
* Stuff from pg_list.h
*/
-
static bool
_equalValue(Value *a, Value *b)
{
@@ -2438,6 +2455,9 @@ equal(void *a, void *b)
case T_ConstraintTest:
retval = _equalConstraintTest(a, b);
break;
+ case T_ConstraintTestValue:
+ retval = _equalConstraintTestValue(a, b);
+ break;
case T_FkConstraint:
retval = _equalFkConstraint(a, b);
break;
@@ -2450,6 +2470,9 @@ equal(void *a, void *b)
case T_InsertDefault:
retval = _equalInsertDefault(a, b);
break;
+ case T_DomainConstraintValue:
+ retval = _equalDomainConstraintValue(a, b);
+ break;
default:
elog(WARNING, "equal: don't know whether nodes of type %d are equal",
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c5b5a493583..2d1f2236c9b 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.179 2002/11/11 22:19:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.180 2002/11/15 02:50:07 momjian Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@@ -1525,10 +1525,32 @@ _outConstraintTest(StringInfo str, ConstraintTest *node)
appendStringInfo(str, " :testtype %d :name ",
(int) node->testtype);
_outToken(str, node->name);
+ appendStringInfo(str, " :domain ");
+ _outToken(str, node->domname);
appendStringInfo(str, " :check_expr ");
_outNode(str, node->check_expr);
}
+/*
+ * ConstraintTestValue
+ */
+static void
+_outConstraintTestValue(StringInfo str, ConstraintTestValue *node)
+{
+ appendStringInfo(str, " CONSTRAINTTESTVALUE :typeid %u :typemod %d ",
+ node->typeId,
+ node->typeMod);
+}
+
+/*
+ * DomainConstraintValue
+ */
+static void
+_outDomainConstraintValue(StringInfo str, DomainConstraintValue *node)
+{
+ appendStringInfo(str, " DOMAINCONSTRAINTVALUE ");
+}
+
/*
* _outNode -
* converts a Node into ascii string and append it to 'str'
@@ -1796,9 +1818,15 @@ _outNode(StringInfo str, void *obj)
case T_ConstraintTest:
_outConstraintTest(str, obj);
break;
+ case T_ConstraintTestValue:
+ _outConstraintTestValue(str, obj);
+ break;
case T_FuncCall:
_outFuncCall(str, obj);
break;
+ case T_DomainConstraintValue:
+ _outDomainConstraintValue(str, obj);
+ break;
default:
elog(WARNING, "_outNode: don't know how to print type %d ",
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 568bf8ee1e4..9b2198ec5a5 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.136 2002/11/06 00:00:44 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.137 2002/11/15 02:50:07 momjian Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
@@ -949,12 +949,56 @@ _readConstraintTest(void)
token = pg_strtok(&length); /* now read it */
local_node->name = nullable_string(token, length);
+ token = pg_strtok(&length); /* get :domname */
+ token = pg_strtok(&length); /* get domname */
+ local_node->domname = nullable_string(token, length);
+
token = pg_strtok(&length); /* eat :check_expr */
local_node->check_expr = nodeRead(true); /* now read it */
return local_node;
}
+/* ----------------
+ * _readConstraintTestValue
+ *
+ * ConstraintTestValue is a subclass of Node
+ * ----------------
+ */
+static ConstraintTestValue *
+_readConstraintTestValue(void)
+{
+ ConstraintTestValue *local_node;
+ char *token;
+ int length;
+
+ local_node = makeNode(ConstraintTestValue);
+ token = pg_strtok(&length); /* eat :typeid */
+ token = pg_strtok(&length); /* get typeid */
+ local_node->typeId = atooid(token);
+ token = pg_strtok(&length); /* eat :typemod */
+ token = pg_strtok(&length); /* get typemod */
+ local_node->typeMod = atoi(token);
+
+ return local_node;
+}
+
+/* ----------------
+ * _readDomainConstraintValue
+ *
+ * DomainConstraintValue is a subclass of Node
+ * ----------------
+ */
+static DomainConstraintValue *
+_readDomainConstraintValue(void)
+{
+ DomainConstraintValue *local_node;
+
+ local_node = makeNode(DomainConstraintValue);
+
+ return local_node;
+}
+
/* ----------------
* _readVar
*
@@ -2300,6 +2344,10 @@ parsePlanString(void)
return_value = _readBooleanTest();
else if (length == 14 && strncmp(token, "CONSTRAINTTEST", length) == 0)
return_value = _readConstraintTest();
+ else if (length == 21 && strncmp(token, "DOMAINCONSTRAINTVALUE", length) == 0)
+ return_value = _readDomainConstraintValue();
+ else if (length == 19 && strncmp(token, "CONSTRAINTTESTVALUE", length) == 0)
+ return_value = _readConstraintTestValue();
else
elog(ERROR, "badly formatted planstring \"%.10s\"...", token);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index f55c988bfc5..e7b4e3b5dbc 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.110 2002/11/06 22:31:24 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.111 2002/11/15 02:50:07 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -1926,6 +1926,8 @@ expression_tree_walker(Node *node,
if (walker(((ConstraintTest *) node)->arg, context))
return true;
return walker(((ConstraintTest *) node)->check_expr, context);
+ case T_ConstraintTestValue:
+ break;
case T_SubLink:
{
SubLink *sublink = (SubLink *) node;
@@ -2310,6 +2312,15 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
+ case T_ConstraintTestValue:
+ {
+ ConstraintTestValue *ctest = (ConstraintTestValue *) node;
+ ConstraintTestValue *newnode;
+
+ FLATCOPY(newnode, ctest, ConstraintTestValue);
+ return (Node *) newnode;
+ }
+ break;
case T_SubLink:
{
/*
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 30c8e8f15b9..18144f7b941 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.253 2002/10/21 22:06:19 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.254 2002/11/15 02:50:07 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2401,7 +2401,7 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
Oid expected_type_id,
given_type_id;
- expr = transformExpr(pstate, expr);
+ expr = transformExpr(pstate, expr, NULL);
/* Cannot contain subselects or aggregates */
if (contain_subplans(expr))
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 724424220b6..b3ca71013c9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.377 2002/11/13 00:44:08 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.378 2002/11/15 02:50:08 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -393,7 +393,7 @@ static void doNegateFloat(Value *v);
UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
UPDATE USAGE USER USING
- VACUUM VALID VALIDATOR VALUES VARCHAR VARYING
+ VACUUM VALID VALIDATOR VALUE VALUES VARCHAR VARYING
VERBOSE VERSION VIEW VOLATILE
WHEN WHERE WITH WITHOUT WORK WRITE
@@ -6406,6 +6406,11 @@ c_expr: columnref { $$ = (Node *) $1; }
n->subselect = $2;
$$ = (Node *)n;
}
+ | VALUE
+ {
+ DomainConstraintValue *n = makeNode(DomainConstraintValue);
+ $$ = (Node *)n;
+ }
;
/*
@@ -7315,6 +7320,7 @@ reserved_keyword:
| UNIQUE
| USER
| USING
+ | VALUE
| WHEN
| WHERE
;
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index b86ffa522e4..de8a6e09b1a 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.130 2002/11/13 00:44:09 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.131 2002/11/15 02:50:08 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -314,6 +314,7 @@ static const ScanKeyword ScanKeywords[] = {
{"vacuum", VACUUM},
{"valid", VALID},
{"validator", VALIDATOR},
+ {"value", VALUE},
{"values", VALUES},
{"varchar", VARCHAR},
{"varying", VARYING},
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 245c0ba422b..d9638753746 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.98 2002/09/18 21:35:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.99 2002/11/15 02:50:08 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -283,7 +283,7 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
* transformJoinOnClause() does. Just invoke transformExpr() to fix
* up the operators, and we're done.
*/
- result = transformExpr(pstate, result);
+ result = transformExpr(pstate, result, NULL);
result = coerce_to_boolean(result, "JOIN/USING");
@@ -317,7 +317,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
pstate->p_namespace = makeList2(j->larg, j->rarg);
/* This part is just like transformWhereClause() */
- result = transformExpr(pstate, j->quals);
+ result = transformExpr(pstate, j->quals, NULL);
result = coerce_to_boolean(result, "JOIN/ON");
@@ -478,7 +478,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
save_namespace = pstate->p_namespace;
pstate->p_namespace = NIL;
- funcexpr = transformExpr(pstate, r->funccallnode);
+ funcexpr = transformExpr(pstate, r->funccallnode, NULL);
pstate->p_namespace = save_namespace;
@@ -961,7 +961,7 @@ transformWhereClause(ParseState *pstate, Node *clause)
if (clause == NULL)
return NULL;
- qual = transformExpr(pstate, clause);
+ qual = transformExpr(pstate, clause, NULL);
qual = coerce_to_boolean(qual, "WHERE");
@@ -1104,7 +1104,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
* willing to match a resjunk target here, though the above cases must
* ignore resjunk targets.
*/
- expr = transformExpr(pstate, node);
+ expr = transformExpr(pstate, node, NULL);
foreach(tl, tlist)
{
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 4870b24de07..a24af2de3e1 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,13 +8,18 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.85 2002/10/24 22:09:00 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.86 2002/11/15 02:50:09 momjian 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"
@@ -405,8 +410,14 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
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);
@@ -419,7 +430,45 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
if (typTup->typnotnull && notNull == NULL)
notNull = pstrdup(NameStr(typTup->typname));
- /* TODO: Add CHECK Constraints to domains */
+ /* 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 = arg;
+ r->testtype = CONSTR_TEST_CHECK;
+ r->name = NameStr(c->conname);
+ r->domname = NameStr(typTup->typname);
+ r->check_expr = stringToNode(MemoryContextStrdup(CacheMemoryContext,
+ DatumGetCString(DirectFunctionCall1(textout,
+ val))));
+
+ arg = (Node *) r;
+ }
+
+ systable_endscan(scan);
+ heap_close(conRel, RowShareLock);
if (typTup->typtype != 'd')
{
@@ -452,7 +501,8 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
r->arg = arg;
r->testtype = CONSTR_TEST_NOTNULL;
- r->name = notNull;
+ r->name = "NOT NULL";
+ r->domname = notNull;
r->check_expr = NULL;
arg = (Node *) r;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 3873fd37f0d..9a4a9c8bc91 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.129 2002/09/18 21:35:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.130 2002/11/15 02:50:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,6 +20,7 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/params.h"
+#include "optimizer/clauses.h"
#include "parser/analyze.h"
#include "parser/gramparse.h"
#include "parser/parse.h"
@@ -83,7 +84,7 @@ parse_expr_init(void)
* input and output of transformExpr; see SubLink for example.
*/
Node *
-transformExpr(ParseState *pstate, Node *expr)
+transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
{
Node *result = NULL;
@@ -152,7 +153,7 @@ transformExpr(ParseState *pstate, Node *expr)
ExprFieldSelect *efs = (ExprFieldSelect *) expr;
List *fields;
- result = transformExpr(pstate, efs->arg);
+ result = transformExpr(pstate, efs->arg, domVal);
/* handle qualification, if any */
foreach(fields, efs->fields)
{
@@ -169,7 +170,7 @@ transformExpr(ParseState *pstate, Node *expr)
case T_TypeCast:
{
TypeCast *tc = (TypeCast *) expr;
- Node *arg = transformExpr(pstate, tc->arg);
+ Node *arg = transformExpr(pstate, tc->arg, domVal);
result = typecast_expression(arg, tc->typename);
break;
@@ -204,14 +205,14 @@ transformExpr(ParseState *pstate, Node *expr)
n->arg = a->lexpr;
result = transformExpr(pstate,
- (Node *) n);
+ (Node *) n, domVal);
}
else
{
Node *lexpr = transformExpr(pstate,
- a->lexpr);
+ a->lexpr, domVal);
Node *rexpr = transformExpr(pstate,
- a->rexpr);
+ a->rexpr, domVal);
result = (Node *) make_op(a->name,
lexpr,
@@ -222,9 +223,9 @@ transformExpr(ParseState *pstate, Node *expr)
case AND:
{
Node *lexpr = transformExpr(pstate,
- a->lexpr);
+ a->lexpr, domVal);
Node *rexpr = transformExpr(pstate,
- a->rexpr);
+ a->rexpr, domVal);
Expr *expr = makeNode(Expr);
lexpr = coerce_to_boolean(lexpr, "AND");
@@ -239,9 +240,9 @@ transformExpr(ParseState *pstate, Node *expr)
case OR:
{
Node *lexpr = transformExpr(pstate,
- a->lexpr);
+ a->lexpr, domVal);
Node *rexpr = transformExpr(pstate,
- a->rexpr);
+ a->rexpr, domVal);
Expr *expr = makeNode(Expr);
lexpr = coerce_to_boolean(lexpr, "OR");
@@ -256,7 +257,7 @@ transformExpr(ParseState *pstate, Node *expr)
case NOT:
{
Node *rexpr = transformExpr(pstate,
- a->rexpr);
+ a->rexpr, domVal);
Expr *expr = makeNode(Expr);
rexpr = coerce_to_boolean(rexpr, "NOT");
@@ -270,9 +271,9 @@ transformExpr(ParseState *pstate, Node *expr)
case DISTINCT:
{
Node *lexpr = transformExpr(pstate,
- a->lexpr);
+ a->lexpr, domVal);
Node *rexpr = transformExpr(pstate,
- a->rexpr);
+ a->rexpr, domVal);
result = (Node *) make_op(a->name,
lexpr,
@@ -293,7 +294,7 @@ transformExpr(ParseState *pstate, Node *expr)
* Will result in a boolean constant node.
*/
Node *lexpr = transformExpr(pstate,
- a->lexpr);
+ a->lexpr, domVal);
ltype = exprType(lexpr);
foreach(telem, (List *) a->rexpr)
@@ -317,7 +318,7 @@ transformExpr(ParseState *pstate, Node *expr)
n->val.val.str = (matched ? "t" : "f");
n->typename = SystemTypeName("bool");
- result = transformExpr(pstate, (Node *) n);
+ result = transformExpr(pstate, (Node *) n, domVal);
}
break;
}
@@ -331,7 +332,7 @@ transformExpr(ParseState *pstate, Node *expr)
/* transform the list of arguments */
foreach(args, fn->args)
lfirst(args) = transformExpr(pstate,
- (Node *) lfirst(args));
+ (Node *) lfirst(args), domVal);
result = ParseFuncOrColumn(pstate,
fn->funcname,
fn->args,
@@ -405,7 +406,7 @@ transformExpr(ParseState *pstate, Node *expr)
List *elist;
foreach(elist, left_list)
- lfirst(elist) = transformExpr(pstate, lfirst(elist));
+ lfirst(elist) = transformExpr(pstate, lfirst(elist), domVal);
Assert(IsA(sublink->oper, A_Expr));
op = ((A_Expr *) sublink->oper)->name;
@@ -504,7 +505,7 @@ transformExpr(ParseState *pstate, Node *expr)
warg = (Node *) makeSimpleA_Expr(OP, "=",
c->arg, warg);
}
- neww->expr = transformExpr(pstate, warg);
+ neww->expr = transformExpr(pstate, warg, domVal);
neww->expr = coerce_to_boolean(neww->expr, "CASE/WHEN");
@@ -520,7 +521,7 @@ transformExpr(ParseState *pstate, Node *expr)
n->val.type = T_Null;
warg = (Node *) n;
}
- neww->result = transformExpr(pstate, warg);
+ neww->result = transformExpr(pstate, warg, domVal);
newargs = lappend(newargs, neww);
typeids = lappendi(typeids, exprType(neww->result));
@@ -544,7 +545,7 @@ transformExpr(ParseState *pstate, Node *expr)
n->val.type = T_Null;
defresult = (Node *) n;
}
- newc->defresult = transformExpr(pstate, defresult);
+ newc->defresult = transformExpr(pstate, defresult, domVal);
/*
* Note: default result is considered the most significant
@@ -580,7 +581,7 @@ transformExpr(ParseState *pstate, Node *expr)
{
NullTest *n = (NullTest *) expr;
- n->arg = transformExpr(pstate, n->arg);
+ n->arg = transformExpr(pstate, n->arg, domVal);
/* the argument can be any type, so don't coerce it */
result = expr;
break;
@@ -617,7 +618,7 @@ transformExpr(ParseState *pstate, Node *expr)
clausename = NULL; /* keep compiler quiet */
}
- b->arg = transformExpr(pstate, b->arg);
+ b->arg = transformExpr(pstate, b->arg, domVal);
b->arg = coerce_to_boolean(b->arg, clausename);
@@ -625,6 +626,13 @@ transformExpr(ParseState *pstate, Node *expr)
break;
}
+ case T_DomainConstraintValue:
+ {
+ result = (Node *) copyObject(domVal);
+
+ break;
+ }
+
/*********************************************
* Quietly accept node types that may be presented when we are
* called on an already-transformed tree.
@@ -936,6 +944,9 @@ exprType(Node *expr)
case T_ConstraintTest:
type = exprType(((ConstraintTest *) expr)->arg);
break;
+ case T_ConstraintTestValue:
+ type = ((ConstraintTestValue *) expr)->typeId;
+ break;
default:
elog(ERROR, "exprType: Do not know how to get type for %d node",
nodeTag(expr));
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index f0bb1856ab3..608a67921c1 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.72 2002/11/13 00:39:47 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.73 2002/11/15 02:50:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -277,7 +277,7 @@ transformArraySubscripts(ParseState *pstate,
{
if (ai->lidx)
{
- subexpr = transformExpr(pstate, ai->lidx);
+ subexpr = transformExpr(pstate, ai->lidx, NULL);
/* If it's not int4 already, try to coerce */
subexpr = coerce_to_target_type(subexpr, exprType(subexpr),
INT4OID, -1,
@@ -299,7 +299,7 @@ transformArraySubscripts(ParseState *pstate,
}
lowerIndexpr = lappend(lowerIndexpr, subexpr);
}
- subexpr = transformExpr(pstate, ai->uidx);
+ subexpr = transformExpr(pstate, ai->uidx, NULL);
/* If it's not int4 already, try to coerce */
subexpr = coerce_to_target_type(subexpr, exprType(subexpr),
INT4OID, -1,
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index c03db4f8b4b..1a2da6da1eb 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.91 2002/09/28 20:00:19 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.92 2002/11/15 02:50:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -56,7 +56,7 @@ transformTargetEntry(ParseState *pstate,
/* Transform the node if caller didn't do it already */
if (expr == NULL)
- expr = transformExpr(pstate, node);
+ expr = transformExpr(pstate, node, NULL);
if (IsA(expr, RangeVar))
elog(ERROR, "You can't use relation names alone in the target list, try relation.*.");
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 796eaa05069..9250faff27a 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.124 2002/09/19 23:40:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.125 2002/11/15 02:50:09 momjian Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -2226,6 +2226,12 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
+ case T_ConstraintTestValue:
+ {
+ appendStringInfo(buf, "VALUE");
+ }
+ break;
+
case T_SubLink:
get_sublink_expr(node, context);
break;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index fce84e72ae9..4a76a27dec6 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: indexing.h,v 1.76 2002/10/18 20:33:57 tgl Exp $
+ * $Id: indexing.h,v 1.77 2002/11/15 02:50:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -40,6 +40,7 @@
#define ConstraintNameNspIndex "pg_constraint_conname_nsp_index"
#define ConstraintOidIndex "pg_constraint_oid_index"
#define ConstraintRelidIndex "pg_constraint_conrelid_index"
+#define ConstraintTypidIndex "pg_constraint_contypid_index"
#define ConversionDefaultIndex "pg_conversion_default_index"
#define ConversionNameNspIndex "pg_conversion_name_nsp_index"
#define ConversionOidIndex "pg_conversion_oid_index"
@@ -129,6 +130,8 @@ DECLARE_UNIQUE_INDEX(pg_class_relname_nsp_index on pg_class using btree(relname
DECLARE_INDEX(pg_constraint_conname_nsp_index on pg_constraint using btree(conname name_ops, connamespace oid_ops));
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_constraint_conrelid_index on pg_constraint using btree(conrelid oid_ops));
+/* This following index is not used for a cache and is not unique */
+DECLARE_INDEX(pg_constraint_contypid_index on pg_constraint using btree(contypid oid_ops));
DECLARE_UNIQUE_INDEX(pg_constraint_oid_index on pg_constraint using btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_conversion_default_index on pg_conversion using btree(connamespace oid_ops, conforencoding int4_ops, contoencoding int4_ops, oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_conversion_name_nsp_index on pg_conversion using btree(conname name_ops, connamespace oid_ops));
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ffe6a740ca8..80ff185579e 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_constraint.h,v 1.4 2002/09/22 00:37:09 tgl Exp $
+ * $Id: pg_constraint.h,v 1.5 2002/11/15 02:50:10 momjian Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -140,6 +140,15 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
*/
+/*
+ * Used for constraint support functions where the
+ * and conrelid, contypid columns being looked up
+ */
+typedef enum CONSTRAINTCATEGORY {
+ CONSTRAINT_RELATION,
+ CONSTRAINT_DOMAIN,
+ CONSTRAINT_ASSERTION
+} CONSTRAINTCATEGORY;
/*
* prototypes for functions in pg_constraint.c
@@ -166,10 +175,10 @@ extern Oid CreateConstraintEntry(const char *constraintName,
extern void RemoveConstraintById(Oid conId);
-extern bool ConstraintNameIsUsed(Oid relId, Oid relNamespace,
- const char *cname);
-extern char *GenerateConstraintName(Oid relId, Oid relNamespace,
- int *counter);
+extern bool ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace,
+ const char *cname);
+extern char *GenerateConstraintName(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace,
+ int *counter);
extern bool ConstraintNameIsGenerated(const char *cname);
#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index f62d1cb8159..f955815926d 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.77 2002/11/06 22:31:24 tgl Exp $
+ * $Id: execnodes.h,v 1.78 2002/11/15 02:50:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -113,6 +113,13 @@ typedef struct ExprContext
Datum *ecxt_aggvalues; /* precomputed values for Aggref nodes */
bool *ecxt_aggnulls; /* null flags for Aggref nodes */
+ /*
+ * Carry the domain value through the executor for application
+ * in a domain constraint
+ */
+ Datum domainValue_datum;
+ bool domainValue_isNull;
+
/* Functions to call back when ExprContext is shut down */
ExprContext_CB *ecxt_callbacks;
} ExprContext;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index cec75309210..112eac34680 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.122 2002/11/10 02:17:25 momjian Exp $
+ * $Id: nodes.h,v 1.123 2002/11/15 02:50:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -171,6 +171,7 @@ typedef enum NodeTag
T_ViewStmt,
T_LoadStmt,
T_CreateDomainStmt,
+ T_DomainConstraintValue,
T_CreatedbStmt,
T_DropdbStmt,
T_VacuumStmt,
@@ -231,6 +232,7 @@ typedef enum NodeTag
T_NullTest,
T_BooleanTest,
T_ConstraintTest,
+ T_ConstraintTestValue,
T_CaseExpr,
T_CaseWhen,
T_FkConstraint,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 962452992e8..1198a81de5e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parsenodes.h,v 1.213 2002/11/13 00:44:09 momjian Exp $
+ * $Id: parsenodes.h,v 1.214 2002/11/15 02:50:12 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -285,9 +285,26 @@ typedef struct ConstraintTest
Node *arg; /* input expression */
ConstraintTestType testtype; /* test type */
char *name; /* name of constraint (for error msgs) */
+ char *domname; /* name of domain (for error messages) */
Node *check_expr; /* for CHECK test, a boolean expression */
} ConstraintTest;
+/*
+ * Placeholder node for the value to be processed by a domains
+ * check constraint.
+ */
+typedef struct DomainConstraintValue
+{
+ NodeTag type;
+} DomainConstraintValue;
+
+typedef struct ConstraintTestValue
+{
+ NodeTag type;
+ Oid typeId;
+ int32 typeMod;
+} ConstraintTestValue;
+
/*
* ColumnDef - column definition (used in various creates)
*
diff --git a/src/include/optimizer/var.h b/src/include/optimizer/var.h
index a153c4d0570..68ffc8e373a 100644
--- a/src/include/optimizer/var.h
+++ b/src/include/optimizer/var.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: var.h,v 1.21 2002/06/20 20:29:51 momjian Exp $
+ * $Id: var.h,v 1.22 2002/11/15 02:50:21 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -22,6 +22,7 @@ extern bool contain_var_reference(Node *node, int varno, int varattno,
int levelsup);
extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
extern bool contain_var_clause(Node *node);
+extern bool contain_var_tuple_clause(Node *node);
extern List *pull_var_clause(Node *node, bool includeUpperVars);
extern Node *flatten_join_alias_vars(Node *node, List *rtable, bool force);
diff --git a/src/include/parser/parse_expr.h b/src/include/parser/parse_expr.h
index a7af335dd12..bcf84912acf 100644
--- a/src/include/parser/parse_expr.h
+++ b/src/include/parser/parse_expr.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_expr.h,v 1.28 2002/06/20 20:29:51 momjian Exp $
+ * $Id: parse_expr.h,v 1.29 2002/11/15 02:50:21 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,7 +21,8 @@
extern int max_expr_depth;
extern bool Transform_null_equals;
-extern Node *transformExpr(ParseState *pstate, Node *expr);
+
+extern Node *transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal);
extern Oid exprType(Node *expr);
extern int32 exprTypmod(Node *expr);
extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index e82ce6fdf08..92c9cc2cc00 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -103,35 +103,43 @@ drop domain domainint4arr restrict;
drop domain domaintextarr restrict;
create domain dnotnull varchar(15) NOT NULL;
create domain dnull varchar(15);
+create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd');
create table nulltest
( col1 dnotnull
, col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden
, col3 dnull NOT NULL
, col4 dnull
+ , col5 dcheck CHECK (col5 IN ('c', 'd'))
);
INSERT INTO nulltest DEFAULT VALUES;
ERROR: Domain dnotnull does not allow NULL values
-INSERT INTO nulltest values ('a', 'b', 'c', 'd'); -- Good
-INSERT INTO nulltest values (NULL, 'b', 'c', 'd');
+INSERT INTO nulltest values ('a', 'b', 'c', 'd', 'c'); -- Good
+insert into nulltest values ('a', 'b', 'c', 'd', NULL);
+ERROR: Domain $1 constraint dcheck failed
+insert into nulltest values ('a', 'b', 'c', 'd', 'a');
+ERROR: ExecInsert: rejected due to CHECK constraint "nulltest_col5" on "nulltest"
+INSERT INTO nulltest values (NULL, 'b', 'c', 'd', 'd');
ERROR: Domain dnotnull does not allow NULL values
-INSERT INTO nulltest values ('a', NULL, 'c', 'd');
+INSERT INTO nulltest values ('a', NULL, 'c', 'd', 'c');
ERROR: Domain dnotnull does not allow NULL values
-INSERT INTO nulltest values ('a', 'b', NULL, 'd');
+INSERT INTO nulltest values ('a', 'b', NULL, 'd', 'c');
ERROR: ExecInsert: Fail to add null value in not null attribute col3
-INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good
+INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
-- Test copy
COPY nulltest FROM stdin; --fail
-ERROR: copy: line 1, CopyFrom: Fail to add null value in not null attribute col3
+ERROR: copy: line 1, Domain $1 constraint dcheck failed
lost synchronization with server, resetting connection
SET autocommit TO 'on';
+-- Last row is bad
COPY nulltest FROM stdin;
+ERROR: copy: line 3, CopyFrom: rejected due to CHECK constraint "nulltest_col5" on "nulltest"
+lost synchronization with server, resetting connection
select * from nulltest;
- col1 | col2 | col3 | col4
-------+------+------+------
- a | b | c | d
- a | b | c |
- a | b | c |
-(3 rows)
+ col1 | col2 | col3 | col4 | col5
+------+------+------+------+------
+ a | b | c | d | c
+ a | b | c | | d
+(2 rows)
-- Test out coerced (casted) constraints
SELECT cast('1' as dnotnull);
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index 4d210cd4aa7..65fba7466fd 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -83,29 +83,36 @@ drop domain domaintextarr restrict;
create domain dnotnull varchar(15) NOT NULL;
create domain dnull varchar(15);
+create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd');
create table nulltest
( col1 dnotnull
, col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden
, col3 dnull NOT NULL
, col4 dnull
+ , col5 dcheck CHECK (col5 IN ('c', 'd'))
);
INSERT INTO nulltest DEFAULT VALUES;
-INSERT INTO nulltest values ('a', 'b', 'c', 'd'); -- Good
-INSERT INTO nulltest values (NULL, 'b', 'c', 'd');
-INSERT INTO nulltest values ('a', NULL, 'c', 'd');
-INSERT INTO nulltest values ('a', 'b', NULL, 'd');
-INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good
+INSERT INTO nulltest values ('a', 'b', 'c', 'd', 'c'); -- Good
+insert into nulltest values ('a', 'b', 'c', 'd', NULL);
+insert into nulltest values ('a', 'b', 'c', 'd', 'a');
+INSERT INTO nulltest values (NULL, 'b', 'c', 'd', 'd');
+INSERT INTO nulltest values ('a', NULL, 'c', 'd', 'c');
+INSERT INTO nulltest values ('a', 'b', NULL, 'd', 'c');
+INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
-- Test copy
COPY nulltest FROM stdin; --fail
-a b \N d
+a b \N d \N
\.
SET autocommit TO 'on';
+-- Last row is bad
COPY nulltest FROM stdin;
-a b c \N
+a b c \N c
+a b c \N d
+a b c \N a
\.
select * from nulltest;