diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml
index 488f86811c787d7e228d1c55dc8d8d1c2aadc29e..0d44434b3e5bf9d709dfc4c65aa9abe62c756c19 100644
--- a/doc/src/sgml/ref/create_domain.sgml
+++ b/doc/src/sgml/ref/create_domain.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_domain.sgml,v 1.8 2002/11/21 23:34:43 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_domain.sgml,v 1.9 2002/12/12 20:35:07 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -30,7 +30,7 @@ CREATE DOMAIN <replaceable class="parameter">domainname</replaceable> [AS] <repl
 where <replaceable class="PARAMETER">constraint</replaceable> is:
 
 [ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
-{ NOT NULL | NULL }
+{ NOT NULL | NULL | CHECK (<replaceable class="PARAMETER">expression</replaceable>) }
   </synopsis>
 
   <refsect2 id="R2-SQL-CREATEDOMAIN-1">
@@ -128,6 +128,25 @@ where <replaceable class="PARAMETER">constraint</replaceable> is:
       </listitem>
      </varlistentry>
 
+   <varlistentry>
+    <term><literal>CHECK (<replaceable class="PARAMETER">expression</replaceable>)</literal></term>
+    <listitem>
+     <para>
+      <literal>CHECK</> clauses specify integrity constraints or tests
+      which values of the domain must satisfy.
+      Each constraint must be an expression
+      producing a Boolean result.  It should use the name <literal>VALUE</>
+      to refer to the value being tested.
+     </para>
+
+     <para>
+      Currently, <literal>CHECK</literal> expressions cannot contain
+      subselects nor refer to variables other than <literal>VALUE</>.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
     </variablelist>
    </para>
   </refsect2>
diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml
index b43db923e214322b05410b728b30c48ee31876e0..ce54952e02a1f2de28f39c5f7abe4cdad68d6add 100644
--- a/doc/src/sgml/release.sgml
+++ b/doc/src/sgml/release.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.168 2002/11/26 22:04:03 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.169 2002/12/12 20:35:07 tgl Exp $
 -->
 
 <appendix id="release">
@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
 worries about funny characters.
 -->
 <literallayout><![CDATA[
+Domains now support CHECK constraints
 ]]></literallayout>
 
  </sect1>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index bd068271acdcc7d9cafa06e97f9d764bda523d68..424684d74d77f53953a3438c0cd5d9bc00629378 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.236 2002/12/12 15:49:23 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.237 2002/12/12 20:35:08 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1567,7 +1567,7 @@ AddRelationRawConstraints(Relation rel,
 		/*
 		 * Transform raw parsetree to executable expression.
 		 */
-		expr = transformExpr(pstate, cdef->raw_expr, NULL);
+		expr = transformExpr(pstate, cdef->raw_expr);
 
 		/*
 		 * Make sure it yields a boolean result.
@@ -1691,7 +1691,7 @@ cookDefault(ParseState *pstate,
 	/*
 	 * Transform raw parsetree to executable expression.
 	 */
-	expr = transformExpr(pstate, raw_default, NULL);
+	expr = transformExpr(pstate, raw_default);
 
 	/*
 	 * 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 8c274caf3b7ab9cf0c7e934fc9292e1ccc4a1265..e724034969cde3a5eedfd123c055bb44296855f5 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.11 2002/12/06 05:00:10 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.12 2002/12/12 20:35:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -439,18 +439,16 @@ 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 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.
+	 * Special processing depending on what the constraint is for.
 	 */
 	if (OidIsValid(con->conrelid))
 	{
 		Relation	rel;
 
+		/*
+		 * If the constraint is for a relation, open and exclusive-lock the
+		 * relation it's for.
+		 */
 		rel = heap_open(con->conrelid, AccessExclusiveLock);
 
 		/*
@@ -490,34 +488,14 @@ 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;
-		int			nkeys = 0;
-
-		typRel = heap_openr(TypeRelationName, RowExclusiveLock);
-
-		ScanKeyEntryInitialize(&typKey[nkeys++], 0x0,
-							   ObjectIdAttributeNumber, F_OIDEQ,
-							   ObjectIdGetDatum(con->contypid));
-
-		typScan = systable_beginscan(typRel, TypeOidIndex, true,
-									 SnapshotNow, nkeys, 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);
+		/*
+		 * XXX for now, do nothing special when dropping a domain constraint
+		 *
+		 * Probably there should be some form of locking on the domain type,
+		 * but we have no such concept at the moment.
+		 */
 	}
 	else
 	{
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 19687737337c897dc539b28345c60f61ed0b89f0..9f5d89a87fbe9d94c4701eb5b9381c19789a8363 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.58 2002/12/12 15:49:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.59 2002/12/12 20:35:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2737,7 +2737,7 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr)
 	/*
 	 * Convert the A_EXPR in raw_expr into an EXPR
 	 */
-	expr = transformExpr(pstate, constr->raw_expr, NULL);
+	expr = transformExpr(pstate, constr->raw_expr);
 
 	/*
 	 * Make sure it yields a boolean result.
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index e16942acd72d43b9df817b128dbae4d46b677b55..989bc36ee82aadb095d7c0022fbab39b0f83c41b 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.22 2002/12/12 15:49:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.23 2002/12/12 20:35:12 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -562,14 +562,14 @@ DefineDomain(CreateDomainStmt *stmt)
 				break;
 
 			case CONSTR_NOTNULL:
-				if (nullDefined)
+				if (nullDefined && !typNotNull)
 					elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
 				typNotNull = true;
 				nullDefined = true;
 				break;
 
 			case CONSTR_NULL:
-				if (nullDefined)
+				if (nullDefined && typNotNull)
 					elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
 				typNotNull = false;
 				nullDefined = true;
@@ -644,14 +644,9 @@ DefineDomain(CreateDomainStmt *stmt)
 		switch (constr->contype)
 		{
 		  	case CONSTR_CHECK:
-				{
-					char   *junk;
-
-					/* Returns the cooked constraint which is not needed during creation */
-					junk = domainAddConstraint(domainoid, domainNamespace,
-											   basetypeoid, stmt->typename->typmod,
-											   constr, &counter, domainName);
-				}
+				domainAddConstraint(domainoid, domainNamespace,
+									basetypeoid, stmt->typename->typmod,
+									constr, &counter, domainName);
 		  		break;
 
 			/* Other constraint types were fully processed above */
@@ -1247,6 +1242,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
 	List   *rels;
 	List   *rt;
 	Form_pg_type	typTup;
+	ExprContext *econtext;
 	char   *ccbin;
 	Node   *expr;
 	int		counter = 0;
@@ -1261,7 +1257,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
 	/* Lock the type table */
 	rel = heap_openr(TypeRelationName, RowExclusiveLock);
 
-	/* Use LookupTypeName here so that shell types can be removed. */
+	/* Use LookupTypeName here so that shell types can be found. */
 	domainoid = LookupTypeName(typename);
 	if (!OidIsValid(domainoid))
 		elog(ERROR, "Type \"%s\" does not exist",
@@ -1328,10 +1324,10 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
 
 	/*
 	 * Since all other constraint types throw errors, this must be
-	 * a check constraint.
+	 * a check constraint.  First, process the constraint expression
+	 * and add an entry to pg_constraint.
 	 */
 
-	/* Returns the cooked constraint which is not needed during creation */
 	ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
 								typTup->typbasetype, typTup->typtypmod,
 								constr, &counter, NameStr(typTup->typname));
@@ -1342,61 +1338,46 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
 	 */
 	expr = stringToNode(ccbin);
 	fix_opfuncids(expr);
+
+	/* Make an expression context for ExecQual */
+	econtext = MakeExprContext(NULL, CurrentMemoryContext);
+
 	rels = get_rels_with_domain(domainoid);
 
 	foreach (rt, rels)
 	{
-		Relation	typrel;
+		relToCheck *rtc = (relToCheck *) lfirst(rt);
+		Relation	testrel;
+		TupleDesc	tupdesc;
 		HeapTuple	tuple;
 		HeapScanDesc scan;
-		TupleDesc	tupdesc;
-		ExprContext *econtext;
-		TupleTableSlot *slot;
-		relToCheck *rtc = (relToCheck *) lfirst(rt);
-
-		/* Lock relation */
-		typrel = heap_open(rtc->relOid, ExclusiveLock);
 
-		/* Test attributes */
-		tupdesc = RelationGetDescr(typrel);
+		/* Lock relation against changes */
+		testrel = heap_open(rtc->relOid, ShareLock);
 
-		/* Make tuple slot to hold tuples */
-		slot = MakeTupleTableSlot();
-		ExecSetSlotDescriptor(slot, RelationGetDescr(typrel), false);
-
-		/* Make an expression context for ExecQual */
-		econtext = MakeExprContext(slot, CurrentMemoryContext);
+		tupdesc = RelationGetDescr(testrel);
 
 		/* Scan through table */
-		scan = heap_beginscan(typrel, SnapshotNow, 0, NULL);
+		scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
 		while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
 		{
 			int		i;
 
-			ExecStoreTuple(tuple, slot, InvalidBuffer, false);
-
 			/* Loop through each attribute of the tuple with a domain */
 			for (i = 0; i < rtc->natts; i++)
 			{
 				Datum	d;
 				bool	isNull;
 				Datum   conResult;
-				ExprDoneCond   isDone;
 
 				d = heap_getattr(tuple, rtc->atts[i], tupdesc, &isNull);
 
-				if (isNull)
-					elog(ERROR, "ALTER DOMAIN: Relation \"%s\" Attribute \"%s\" "
-						 "contains NULL values",
-						 RelationGetRelationName(typrel),
-						 NameStr(*attnumAttName(typrel, rtc->atts[i])));
-
 				econtext->domainValue_datum = d;
 				econtext->domainValue_isNull = isNull;
 
-				conResult = ExecEvalExpr(expr, econtext, &isNull, &isDone);
+				conResult = ExecEvalExpr(expr, econtext, &isNull, NULL);
 
-				if (!DatumGetBool(conResult))
+				if (!isNull && !DatumGetBool(conResult))
 					elog(ERROR, "AlterDomainAddConstraint: Domain %s constraint %s failed",
 						 NameStr(typTup->typname), constr->name);
 			}
@@ -1406,13 +1387,12 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
 
 		heap_endscan(scan);
 
-		FreeExprContext(econtext);
-		pfree(slot);
-
-		/* Hold type lock */
-		heap_close(typrel, NoLock);
+		/* Hold relation lock till commit (XXX bad for concurrency) */
+		heap_close(testrel, NoLock);
 	}
 
+	FreeExprContext(econtext);
+
 	/* Clean up */
 	heap_close(rel, NoLock);
 }
@@ -1524,11 +1504,12 @@ domainPermissionCheck(HeapTuple tup, TypeName *typename)
 
 
 /*
- * domainAddConstraint
+ * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
  */
-char *
+static char *
 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
-					int typMod, Constraint *constr, int *counter, char *domainName)
+					int typMod, Constraint *constr,
+					int *counter, char *domainName)
 {
 	Node	   *expr;
 	char	   *ccsrc;
@@ -1556,26 +1537,24 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
 											  counter);
 
 	/*
-	 * Convert the A_EXPR in raw_expr into an
-	 * EXPR
+	 * 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.
+	 * Set up a ConstraintTestValue 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->typeId = baseTypeOid;
 	domVal->typeMod = typMod;
 
-	expr = transformExpr(pstate, constr->raw_expr, domVal);
+	pstate->p_value_substitute = (Node *) domVal;
 
-	/*
-	 * Domains don't allow var clauses
-	 */
-	if (contain_var_clause(expr))
-		elog(ERROR, "cannot use column references in domain CHECK clause");
+	expr = transformExpr(pstate, constr->raw_expr);
 
 	/*
 	 * Make sure it yields a boolean result.
@@ -1589,6 +1568,13 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
 	if (length(pstate->p_rtable) != 0)
 		elog(ERROR, "Relations cannot be referenced in domain CHECK constraint");
 
+	/*
+	 * Domains don't allow var clauses (this should be redundant with the
+	 * above check, but make it anyway)
+	 */
+	if (contain_var_clause(expr))
+		elog(ERROR, "cannot use column references in domain CHECK clause");
+
 	/*
 	 * No subplans or aggregates, either...
 	 */
@@ -1618,7 +1604,9 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
 												   InvalidOid),
 							   false, false);
 
-	/* Write the constraint */
+	/*
+	 * Store the constraint in pg_constraint
+	 */
 	CreateConstraintEntry(constr->name,		/* Constraint Name */
 						  domainNamespace,	/* namespace */
 						  CONSTRAINT_CHECK,		/* Constraint Type */
@@ -1640,8 +1628,8 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
 						  ccsrc);	/* Source form check constraint */
 
 	/*
-	 * Return the constraint so the calling routine can perform any additional
-	 * required tests.
+	 * Return the compiled constraint expression so the calling routine can
+	 * perform any additional required tests.
 	 */
 	return ccbin;
 }
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d96e983fa30ab6eae2a7aec79d126f17b0b285e5..79796f1c0b27ad3aa01c3f671abd892ff781fc97 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.117 2002/12/12 15:49:28 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.118 2002/12/12 20:35:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -77,8 +77,7 @@ static Datum ExecEvalConstraintTest(ConstraintTest *constraint,
 					   ExprContext *econtext,
 					   bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalConstraintTestValue(ConstraintTestValue *conVal,
-					   ExprContext *econtext,
-					   bool *isNull, ExprDoneCond *isDone);
+					   ExprContext *econtext, bool *isNull);
 
 
 /*----------
@@ -1553,23 +1552,6 @@ 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
  *
@@ -1585,6 +1567,9 @@ ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext,
 
 	result = ExecEvalExpr((Node *) constraint->arg, econtext, isNull, isDone);
 
+	if (isDone && *isDone == ExprEndResult)
+		return result;			/* nothing to check */
+
 	switch (constraint->testtype)
 	{
 		case CONSTR_TEST_NOTNULL:
@@ -1595,16 +1580,32 @@ ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext,
 		case CONSTR_TEST_CHECK:
 			{
 				Datum	conResult;
+				bool	conIsNull;
+				Datum	save_datum;
+				bool	save_isNull;
+
+				/*
+				 * Set up value to be returned by ConstraintTestValue 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.
+				 */
+				save_datum = econtext->domainValue_datum;
+				save_isNull = econtext->domainValue_isNull;
 
-				/* Var with attnum == UnassignedAttrNum uses the result */
 				econtext->domainValue_datum = result;
 				econtext->domainValue_isNull = *isNull;
 
-				conResult = ExecEvalExpr((Node *) constraint->check_expr, econtext, isNull, isDone);
+				conResult = ExecEvalExpr((Node *) constraint->check_expr,
+										 econtext, &conIsNull, NULL);
 
-				if (!DatumGetBool(conResult))
+				if (!conIsNull &&
+					!DatumGetBool(conResult))
 					elog(ERROR, "ExecEvalConstraintTest: Domain %s constraint %s failed",
 						 constraint->domname, constraint->name);
+
+				econtext->domainValue_datum = save_datum;
+				econtext->domainValue_isNull = save_isNull;
 			}
 			break;
 		default:
@@ -1616,6 +1617,19 @@ ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext,
 	return result;
 }
 
+/*
+ * ExecEvalConstraintTestValue
+ *
+ * Return the value stored by constraintTest.
+ */
+static Datum
+ExecEvalConstraintTestValue(ConstraintTestValue *conVal, ExprContext *econtext,
+							bool *isNull)
+{
+	*isNull = econtext->domainValue_isNull;
+	return econtext->domainValue_datum;
+}
+
 /* ----------------------------------------------------------------
  *		ExecEvalFieldSelect
  *
@@ -1811,9 +1825,8 @@ ExecEvalExpr(Node *expression,
 			break;
 		case T_ConstraintTestValue:
 			retDatum = ExecEvalConstraintTestValue((ConstraintTestValue *) expression,
-												  econtext,
-												  isNull,
-												  isDone);
+												   econtext,
+												   isNull);
 			break;
 		default:
 			elog(ERROR, "ExecEvalExpr: unknown expression type %d",
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f7aa8fcb7ec83771c8310905551f563e5e0925fd..258e1a1188c04414b2b275fab89eebb7de57d881 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.230 2002/12/12 15:49:28 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.231 2002/12/12 20:35:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1297,14 +1297,6 @@ _copyTypeName(TypeName *from)
 	return newnode;
 }
 
-static DomainConstraintValue *
-_copyDomainConstraintValue(DomainConstraintValue *from)
-{
-	DomainConstraintValue *newnode = makeNode(DomainConstraintValue);
-
-	return newnode;
-}
-
 static SortGroupBy *
 _copySortGroupBy(SortGroupBy *from)
 {
@@ -2763,9 +2755,6 @@ copyObject(void *from)
 		case T_TypeCast:
 			retval = _copyTypeCast(from);
 			break;
-		case T_DomainConstraintValue:
-			retval = _copyDomainConstraintValue(from);
-			break;
 		case T_SortGroupBy:
 			retval = _copySortGroupBy(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3b5103820688fc065bcb7bbd08087b7eae9eddf1..3fc924024e21c69ce47f3accb678c6984738d5b0 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.175 2002/12/12 15:49:28 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.176 2002/12/12 20:35:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -648,12 +648,6 @@ _equalInsertDefault(InsertDefault *a, InsertDefault *b)
 	return true;
 }
 
-static bool
-_equalDomainConstraintValue(DomainConstraintValue *a, DomainConstraintValue *b)
-{
-	return true;
-}
-
 static bool
 _equalClosePortalStmt(ClosePortalStmt *a, ClosePortalStmt *b)
 {
@@ -1931,9 +1925,6 @@ 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 bd44816e6fab60020e9e05814a5308382e70195b..9ae71555095f95e3ebf52df7514c712a454cd9a4 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.187 2002/12/12 15:49:28 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.188 2002/12/12 20:35:12 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -1299,12 +1299,6 @@ _outExprFieldSelect(StringInfo str, ExprFieldSelect *node)
 	WRITE_NODE_FIELD(indirection);
 }
 
-static void
-_outDomainConstraintValue(StringInfo str, DomainConstraintValue *node)
-{
-	WRITE_NODE_TYPE("DOMAINCONSTRAINTVALUE");
-}
-
 static void
 _outConstraint(StringInfo str, Constraint *node)
 {
@@ -1637,9 +1631,6 @@ _outNode(StringInfo str, void *obj)
 			case T_ExprFieldSelect:
 				_outExprFieldSelect(str, obj);
 				break;
-			case T_DomainConstraintValue:
-				_outDomainConstraintValue(str, obj);
-				break;
 			case T_Constraint:
 				_outConstraint(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index bb4a565a8b54213895f102f84d9ff28240da9811..ba46a4f088cd245003ed9147cbb430cba21bddac 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.141 2002/12/12 15:49:28 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.142 2002/12/12 20:35:12 tgl Exp $
  *
  * NOTES
  *	  Path and Plan nodes do not have any readfuncs support, because we
@@ -792,17 +792,6 @@ _readExprFieldSelect(void)
 	READ_DONE();
 }
 
-/*
- * _readDomainConstraintValue
- */
-static DomainConstraintValue *
-_readDomainConstraintValue(void)
-{
-	READ_LOCALS_NO_FIELDS(DomainConstraintValue);
-
-	READ_DONE();
-}
-
 /*
  * _readRangeTblEntry
  */
@@ -935,8 +924,6 @@ parseNodeString(void)
 		return_value = _readTypeName();
 	else if (MATCH("EXPRFIELDSELECT", 15))
 		return_value = _readExprFieldSelect();
-	else if (MATCH("DOMAINCONSTRAINTVALUE", 21))
-		return_value = _readDomainConstraintValue();
 	else if (MATCH("RTE", 3))
 		return_value = _readRangeTblEntry();
 	else
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index ef317c5b2231ee007affa2b8eacdfafd700b8ec0..83c05a5d5fddd793aeef9a465f6e867d55915a5f 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.116 2002/12/12 15:49:32 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.117 2002/12/12 20:35:12 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -2129,6 +2129,7 @@ expression_tree_walker(Node *node,
 		case T_Var:
 		case T_Const:
 		case T_Param:
+		case T_ConstraintTestValue:
 		case T_RangeTblRef:
 			/* primitive node types with no subnodes */
 			break;
@@ -2265,8 +2266,6 @@ 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_TargetEntry:
 			return walker(((TargetEntry *) node)->expr, context);
 		case T_Query:
@@ -2474,6 +2473,7 @@ expression_tree_mutator(Node *node,
 		case T_Var:
 		case T_Const:
 		case T_Param:
+		case T_ConstraintTestValue:
 		case T_RangeTblRef:
 			/* primitive node types with no subnodes */
 			return (Node *) copyObject(node);
@@ -2651,15 +2651,6 @@ 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_TargetEntry:
 			{
 				/*
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 56f0fdd955a4f79ecca798a55624d36ead4f5569..a7ead103188cda84b92ed7defd61c470ce003053 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.255 2002/12/12 15:49:33 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.256 2002/12/12 20:35:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2401,7 +2401,7 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
 			Oid			expected_type_id,
 						given_type_id;
 
-			expr = transformExpr(pstate, expr, NULL);
+			expr = transformExpr(pstate, expr);
 
 			/* Cannot contain subselects or aggregates */
 			if (contain_subplans(expr))
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 53e1a5999fed52e5a032f11057b35c2460331c29..bb94d1f8e821b0840a69a945f62d2ffa2d65de2a 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.387 2002/12/12 15:49:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.388 2002/12/12 20:35:13 tgl 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 VALUE VALUES VARCHAR VARYING
+	VACUUM VALID VALIDATOR VALUES VARCHAR VARYING
 	VERBOSE VERSION VIEW VOLATILE
 
 	WHEN WHERE WITH WITHOUT WORK WRITE
@@ -6464,11 +6464,6 @@ c_expr:		columnref								{ $$ = (Node *) $1; }
 					n->subselect = $2;
 					$$ = (Node *)n;
 				}
-			| VALUE
-				{
-					DomainConstraintValue *n = makeNode(DomainConstraintValue);
-					$$ = (Node *)n;
-				}
 		;
 
 /*
@@ -7378,7 +7373,6 @@ reserved_keyword:
 			| UNIQUE
 			| USER
 			| USING
-			| VALUE
 			| WHEN
 			| WHERE
 		;
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index de8a6e09b1a664d89cd7ae5c21fd1582bf436d55..5373b974cbe71ad7dbfbdcaa7fe6f1e2d0f04efd 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.131 2002/11/15 02:50:08 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.132 2002/12/12 20:35:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -314,7 +314,6 @@ 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 6f05ee329cd1127ffd06f5760452ff92f291f358..71375f62c97e812b5d4bbdd2516431f21ef0d5a1 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.101 2002/12/12 15:49:38 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.102 2002/12/12 20:35:13 tgl 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, NULL);
+	result = transformExpr(pstate, result);
 
 	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, NULL);
+	result = transformExpr(pstate, j->quals);
 
 	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, NULL);
+	funcexpr = transformExpr(pstate, r->funccallnode);
 
 	pstate->p_namespace = save_namespace;
 
@@ -950,7 +950,7 @@ transformWhereClause(ParseState *pstate, Node *clause)
 	if (clause == NULL)
 		return NULL;
 
-	qual = transformExpr(pstate, clause, NULL);
+	qual = transformExpr(pstate, clause);
 
 	qual = coerce_to_boolean(qual, "WHERE");
 
@@ -1093,7 +1093,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, NULL);
+	expr = transformExpr(pstate, node);
 
 	foreach(tl, tlist)
 	{
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 3856939d404a87d83ded1857a4c37120fec36587..af184eca6d1106fdaafe19b15c649f136498ea57 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.90 2002/12/12 15:49:38 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.91 2002/12/12 20:35:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -458,9 +458,8 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
 			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))));
+			r->check_expr =	stringToNode(DatumGetCString(DirectFunctionCall1(textout,
+																			 val)));
 
 			arg = (Node *) r;
 		}
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 7dcbf59a4e397a77ee7773a61c71352476892208..db0667ef16ec6c6370720158e216ad11035ffcf0 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.136 2002/12/12 15:49:39 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.137 2002/12/12 20:35:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,6 @@
 #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"
@@ -84,7 +83,7 @@ parse_expr_init(void)
  * input and output of transformExpr; see SubLink for example.
  */
 Node *
-transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
+transformExpr(ParseState *pstate, Node *expr)
 {
 	Node	   *result = NULL;
 
@@ -152,7 +151,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 				ExprFieldSelect *efs = (ExprFieldSelect *) expr;
 				List	   *fields;
 
-				result = transformExpr(pstate, efs->arg, domVal);
+				result = transformExpr(pstate, efs->arg);
 				/* handle qualification, if any */
 				foreach(fields, efs->fields)
 				{
@@ -169,7 +168,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 		case T_TypeCast:
 			{
 				TypeCast   *tc = (TypeCast *) expr;
-				Node	   *arg = transformExpr(pstate, tc->arg, domVal);
+				Node	   *arg = transformExpr(pstate, tc->arg);
 
 				result = typecast_expression(arg, tc->typename);
 				break;
@@ -204,14 +203,14 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 									n->arg = (Expr *) a->lexpr;
 
 								result = transformExpr(pstate,
-													   (Node *) n, domVal);
+													   (Node *) n);
 							}
 							else
 							{
 								Node	   *lexpr = transformExpr(pstate,
-															   a->lexpr, domVal);
+																  a->lexpr);
 								Node	   *rexpr = transformExpr(pstate,
-															   a->rexpr, domVal);
+																  a->rexpr);
 
 								result = (Node *) make_op(a->name,
 														  lexpr,
@@ -222,9 +221,9 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 					case AND:
 						{
 							Node	   *lexpr = transformExpr(pstate,
-															  a->lexpr, domVal);
+															  a->lexpr);
 							Node	   *rexpr = transformExpr(pstate,
-															  a->rexpr, domVal);
+															  a->rexpr);
 
 							lexpr = coerce_to_boolean(lexpr, "AND");
 							rexpr = coerce_to_boolean(rexpr, "AND");
@@ -237,9 +236,9 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 					case OR:
 						{
 							Node	   *lexpr = transformExpr(pstate,
-															  a->lexpr, domVal);
+															  a->lexpr);
 							Node	   *rexpr = transformExpr(pstate,
-															  a->rexpr, domVal);
+															  a->rexpr);
 
 							lexpr = coerce_to_boolean(lexpr, "OR");
 							rexpr = coerce_to_boolean(rexpr, "OR");
@@ -252,7 +251,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 					case NOT:
 						{
 							Node	   *rexpr = transformExpr(pstate,
-															  a->rexpr, domVal);
+															  a->rexpr);
 
 							rexpr = coerce_to_boolean(rexpr, "NOT");
 
@@ -263,9 +262,9 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 					case DISTINCT:
 						{
 							Node	   *lexpr = transformExpr(pstate,
-															  a->lexpr, domVal);
+															  a->lexpr);
 							Node	   *rexpr = transformExpr(pstate,
-															  a->rexpr, domVal);
+															  a->rexpr);
 
 							result = (Node *) make_op(a->name,
 													  lexpr,
@@ -291,7 +290,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 							 * Will result in a boolean constant node.
 							 */
 							Node	   *lexpr = transformExpr(pstate,
-															  a->lexpr, domVal);
+															  a->lexpr);
 
 							ltype = exprType(lexpr);
 							foreach(telem, (List *) a->rexpr)
@@ -315,7 +314,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 							n->val.val.str = (matched ? "t" : "f");
 							n->typename = SystemTypeName("bool");
 
-							result = transformExpr(pstate, (Node *) n, domVal);
+							result = transformExpr(pstate, (Node *) n);
 						}
 						break;
 				}
@@ -329,7 +328,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 				/* transform the list of arguments */
 				foreach(args, fn->args)
 					lfirst(args) = transformExpr(pstate,
-												 (Node *) lfirst(args), domVal);
+												 (Node *) lfirst(args));
 				result = ParseFuncOrColumn(pstate,
 										   fn->funcname,
 										   fn->args,
@@ -403,7 +402,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 					List	   *elist;
 
 					foreach(elist, left_list)
-						lfirst(elist) = transformExpr(pstate, lfirst(elist), domVal);
+						lfirst(elist) = transformExpr(pstate, lfirst(elist));
 
 					Assert(IsA(sublink->oper, A_Expr));
 					op = ((A_Expr *) sublink->oper)->name;
@@ -507,7 +506,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 														 (Node *) c->arg,
 														 warg);
 					}
-					neww->expr = (Expr *) transformExpr(pstate, warg, domVal);
+					neww->expr = (Expr *) transformExpr(pstate, warg);
 
 					neww->expr = (Expr *) coerce_to_boolean((Node *) neww->expr,
 															"CASE/WHEN");
@@ -524,7 +523,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 						n->val.type = T_Null;
 						warg = (Node *) n;
 					}
-					neww->result = (Expr *) transformExpr(pstate, warg, domVal);
+					neww->result = (Expr *) transformExpr(pstate, warg);
 
 					newargs = lappend(newargs, neww);
 					typeids = lappendi(typeids, exprType((Node *) neww->result));
@@ -548,7 +547,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 					n->val.type = T_Null;
 					defresult = (Node *) n;
 				}
-				newc->defresult = (Expr *) transformExpr(pstate, defresult, domVal);
+				newc->defresult = (Expr *) transformExpr(pstate, defresult);
 
 				/*
 				 * Note: default result is considered the most significant
@@ -586,7 +585,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 			{
 				NullTest   *n = (NullTest *) expr;
 
-				n->arg = (Expr *) transformExpr(pstate, (Node *) n->arg, domVal);
+				n->arg = (Expr *) transformExpr(pstate, (Node *) n->arg);
 				/* the argument can be any type, so don't coerce it */
 				result = expr;
 				break;
@@ -623,7 +622,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 						clausename = NULL;		/* keep compiler quiet */
 				}
 
-				b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg, domVal);
+				b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg);
 
 				b->arg = (Expr *) coerce_to_boolean((Node *) b->arg, clausename);
 
@@ -631,21 +630,6 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 				break;
 			}
 
-		case T_DomainConstraintValue:
-			{
-				/*
-				 * If domVal is NULL, we are not translating an expression that
-				 * can use it
-				 */
-				if (domVal == NULL)
-					elog(ERROR, "VALUE is not allowed in expression for node %d",
-						 nodeTag(expr));
-
-				result = (Node *) copyObject(domVal);
-
-				break;
-			}
-
 			/*********************************************
 			 * Quietly accept node types that may be presented when we are
 			 * called on an already-transformed tree.
@@ -663,6 +647,7 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
 		case T_FieldSelect:
 		case T_RelabelType:
 		case T_ConstraintTest:
+		case T_ConstraintTestValue:
 			{
 				result = (Node *) expr;
 				break;
@@ -700,6 +685,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 	int			numnames = length(cref->fields);
 	Node	   *node;
 	RangeVar   *rv;
+	int			levels_up;
 
 	/*----------
 	 * The allowed syntaxes are:
@@ -740,18 +726,30 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 				if (node == NULL)
 				{
 					/*
-					 * Not known as a column of any range-table entry, so
-					 * try to find the name as a relation ... but not if
+					 * Not known as a column of any range-table entry.
+					 *
+					 * Consider the possibility that it's VALUE in a domain
+					 * check expression.  (We handle VALUE as a name, not a
+					 * keyword, to avoid breaking a lot of applications that
+					 * have used VALUE as a column name in the past.)
+					 */
+					if (pstate->p_value_substitute != NULL &&
+						strcmp(name, "value") == 0)
+					{
+						node = (Node *) copyObject(pstate->p_value_substitute);
+						break;
+					}
+
+					/*
+					 * Try to find the name as a relation ... but not if
 					 * subscripts appear.  Note also that only relations
 					 * already entered into the rangetable will be
 					 * recognized.
 					 *
 					 * This is a hack for backwards compatibility with
-					 * PostQUEL- inspired syntax.  The preferred form now
+					 * PostQUEL-inspired syntax.  The preferred form now
 					 * is "rel.*".
 					 */
-					int			levels_up;
-
 					if (cref->indirection == NIL &&
 						refnameRangeTblEntry(pstate, NULL, name,
 											 &levels_up) != NULL)
@@ -1055,7 +1053,8 @@ exprTypmod(Node *expr)
 			break;
 		case T_ConstraintTest:
 			return exprTypmod((Node *) ((ConstraintTest *) expr)->arg);
-
+		case T_ConstraintTestValue:
+			return ((ConstraintTestValue *) expr)->typeMod;
 		default:
 			break;
 	}
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index a246344c7bd6a6c2a15efe956f7f139f89f07884..9de23d0cb8ee13519214d3bb40b3ee18c6e39087 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.75 2002/12/12 15:49:39 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.76 2002/12/12 20:35:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -272,7 +272,7 @@ transformArraySubscripts(ParseState *pstate,
 		{
 			if (ai->lidx)
 			{
-				subexpr = transformExpr(pstate, ai->lidx, NULL);
+				subexpr = transformExpr(pstate, ai->lidx);
 				/* If it's not int4 already, try to coerce */
 				subexpr = coerce_to_target_type(subexpr, exprType(subexpr),
 												INT4OID, -1,
@@ -292,7 +292,7 @@ transformArraySubscripts(ParseState *pstate,
 			}
 			lowerIndexpr = lappend(lowerIndexpr, subexpr);
 		}
-		subexpr = transformExpr(pstate, ai->uidx, NULL);
+		subexpr = transformExpr(pstate, ai->uidx);
 		/* 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 5e4e23db920686f1106eecf0229aa6034c134198..7e73b1b7b0c7c8aca9f460ff1b5b6680d54ee30f 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.93 2002/12/12 15:49:39 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.94 2002/12/12 20:35:13 tgl 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, NULL);
+		expr = transformExpr(pstate, node);
 
 	if (IsA(expr, RangeVar))
 		elog(ERROR, "You can't use relation names alone in the target list, try relation.*.");
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a4f10ef4c06e223cd2a1ccf6c84ef6fa346a0168..db8e431c7697bf43130f6984d6cc594159226b4e 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.82 2002/12/05 15:50:38 tgl Exp $
+ * $Id: execnodes.h,v 1.83 2002/12/12 20:35:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,7 +82,7 @@ typedef struct ExprContext_CB
  *	* ecxt_per_query_memory is a relatively long-lived context (such as
  *	  TransactionCommandContext); typically it's the same context the
  *	  ExprContext node itself is allocated in.	This context can be
- *	  used for purposes such as storing operator/function fcache nodes.
+ *	  used for purposes such as storing function call cache info.
  *	* ecxt_per_tuple_memory is a short-term context for expression results.
  *	  As the name suggests, it will typically be reset once per tuple,
  *	  before we begin to evaluate expressions for that tuple.  Each
@@ -112,10 +112,7 @@ 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
-	 */
+	/* Value to substitute for ConstraintTestValue nodes in expression */
 	Datum		domainValue_datum;
 	bool		domainValue_isNull;
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index aacdf60dd9ad396373fc8ca4507f01106541dcd8..0c031b830e14fe0c94cfcbb505ef45550495b96b 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.130 2002/12/12 15:49:40 tgl Exp $
+ * $Id: nodes.h,v 1.131 2002/12/12 20:35:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -252,7 +252,6 @@ typedef enum NodeTag
 	T_FuncWithArgs,
 	T_PrivTarget,
 	T_InsertDefault,
-	T_DomainConstraintValue,
 	T_CreateOpClassItem,
 	T_CompositeTypeStmt,
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f2d92431b71e993121713004b70f1bdd99fb3591..c409ec7ea847ffc7919a6cfae55ba4a5c2168539 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.222 2002/12/12 15:49:40 tgl Exp $
+ * $Id: parsenodes.h,v 1.223 2002/12/12 20:35:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -275,15 +275,6 @@ typedef struct InsertDefault
 	NodeTag		type;
 } InsertDefault;
 
-/*
- * Empty node used as raw-parse-tree representation of VALUE keyword
- * for domain check constraints.
- */
-typedef struct DomainConstraintValue
-{
-	NodeTag		type;
-} DomainConstraintValue;
-
 /*
  * SortGroupBy - for ORDER BY clause
  */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index ec0ad4235c96fb0a9d30d6dfee30a09859c27479..ae0df7e441c68c05b5a890e5e110a6e6024e2d30 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.72 2002/12/12 15:49:40 tgl Exp $
+ * $Id: primnodes.h,v 1.73 2002/12/12 20:35:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -599,15 +599,19 @@ typedef struct ConstraintTest
 } ConstraintTest;
 
 /*
- * Placeholder node for the value to be processed by a domains
- * check constraint.  This is effectively like a Param; could we use
- * a Param node instead?
+ * Placeholder node for the value to be processed by a domain's check
+ * constraint.  This is effectively like a Param, but can be implemented more
+ * simply since we need only one replacement value at a time.
+ *
+ * Note: the typeId/typeMod will be set from the domain's base type, not
+ * 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
 {
 	Expr		xpr;
-	Oid			typeId;
-	int32		typeMod;
+	Oid			typeId;			/* type for substituted value */
+	int32		typeMod;		/* typemod for substituted value */
 } ConstraintTestValue;
 
 
diff --git a/src/include/optimizer/var.h b/src/include/optimizer/var.h
index 68ffc8e373a7e48bfacf25a20a12550ffdc96a3e..102e928a544b87e99595f78d010d9efeaebb4c62 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.22 2002/11/15 02:50:21 momjian Exp $
+ * $Id: var.h,v 1.23 2002/12/12 20:35:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,7 +22,6 @@ 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 bcf84912acf89e6aa4131f45bb455226b673222f..6606d3913e51fe48a2579ce41a973e1ec34ece8a 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.29 2002/11/15 02:50:21 momjian Exp $
+ * $Id: parse_expr.h,v 1.30 2002/12/12 20:35:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,7 +22,7 @@ extern int	max_expr_depth;
 extern bool Transform_null_equals;
 
 
-extern Node *transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal);
+extern Node *transformExpr(ParseState *pstate, Node *expr);
 extern Oid	exprType(Node *expr);
 extern int32 exprTypmod(Node *expr);
 extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index beb16e2cc8e916cd452a49548014b7af4f5e1448..46c86e1f79661009dcbbf60c2268ba46111793e8 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.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: parse_node.h,v 1.32 2002/09/18 21:35:24 tgl Exp $
+ * $Id: parse_node.h,v 1.33 2002/12/12 20:35:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,6 +42,7 @@ typedef struct ParseState
 	List	   *p_namespace;	/* current lookup namespace (join items) */
 	int			p_last_resno;	/* last targetlist resno assigned */
 	List	   *p_forUpdate;	/* FOR UPDATE clause, if any (see gram.y) */
+	Node	   *p_value_substitute;	/* what to replace VALUE with, if any */
 	bool		p_hasAggs;
 	bool		p_hasSubLinks;
 	bool		p_is_insert;
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index f91191bddb69341fe96af8395240bd107d3c9752..cbbb9023794594911d205c9e6e63a8bdf72a1096 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -115,7 +115,7 @@ INSERT INTO nulltest DEFAULT VALUES;
 ERROR:  Domain dnotnull does not allow NULL values
 INSERT INTO nulltest values ('a', 'b', 'c', 'd', 'c');  -- Good
 insert into nulltest values ('a', 'b', 'c', 'd', NULL);
-ERROR:  ExecEvalConstraintTest: Domain dcheck constraint $1 failed
+ERROR:  Domain dcheck does not allow NULL values
 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');
@@ -127,7 +127,7 @@ ERROR:  ExecInsert: Fail to add null value in not null attribute col3
 INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
 -- Test copy
 COPY nulltest FROM stdin; --fail
-ERROR:  copy: line 1, ExecEvalConstraintTest: Domain dcheck constraint $1 failed
+ERROR:  copy: line 1, Domain dcheck does not allow NULL values
 lost synchronization with server, resetting connection
 SET autocommit TO 'on';
 -- Last row is bad