diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index c53ac42b26f70c282c8d2a4f849029fe0c4f2e8c..30f5b0e378f3406ab985c605180ede27d0c009e5 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.105 2002/08/31 19:10:08 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.106 2002/08/31 22:10:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -69,8 +69,9 @@ static Datum ExecEvalNullTest(NullTest *ntest, ExprContext *econtext,
 				 bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
 					bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalConstraint(Constraint *constraint, ExprContext *econtext,
-								bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalConstraintTest(ConstraintTest *constraint,
+									ExprContext *econtext,
+									bool *isNull, ExprDoneCond *isDone);
 
 
 /*----------
@@ -1465,43 +1466,6 @@ ExecEvalNullTest(NullTest *ntest,
 	}
 }
 
-/*
- * ExecEvalConstraint
- *
- * Test the constraint against the data provided.  If the data fits
- * within the constraint specifications, pass it through (return the
- * datum) otherwise throw an error.
- */
-static Datum
-ExecEvalConstraint(Constraint *constraint, ExprContext *econtext,
-				   bool *isNull, ExprDoneCond *isDone)
-{
-	Datum		result;
-
-	result = ExecEvalExpr(constraint->raw_expr, econtext, isNull, isDone);
-
-	/* Test for the constraint type */
-	switch(constraint->contype)
-	{
-		case CONSTR_NOTNULL:
-			if (*isNull)
-			{
-				elog(ERROR, "Domain %s does not allow NULL values", constraint->name);
-			}
-			break;
-		case CONSTR_CHECK:
-
-				elog(ERROR, "ExecEvalConstraint: Domain CHECK Constraints not yet implemented");
-			break;
-		default:
-			elog(ERROR, "ExecEvalConstraint: Constraint type unknown");
-			break;
-	}
-
-	/* If all has gone well (constraint did not fail) return the datum */
-	return result;
-}
-
 /* ----------------------------------------------------------------
  *		ExecEvalBooleanTest
  *
@@ -1582,6 +1546,41 @@ ExecEvalBooleanTest(BooleanTest *btest,
 	}
 }
 
+/*
+ * ExecEvalConstraintTest
+ *
+ * Test the constraint against the data provided.  If the data fits
+ * within the constraint specifications, pass it through (return the
+ * datum) otherwise throw an error.
+ */
+static Datum
+ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext,
+					   bool *isNull, ExprDoneCond *isDone)
+{
+	Datum		result;
+
+	result = ExecEvalExpr(constraint->arg, econtext, isNull, isDone);
+
+	switch (constraint->testtype)
+	{
+		case CONSTR_TEST_NOTNULL:
+			if (*isNull)
+				elog(ERROR, "Domain %s does not allow NULL values",
+					 constraint->name);
+			break;
+		case CONSTR_TEST_CHECK:
+			/* TODO: Add CHECK Constraints to domains */
+			elog(ERROR, "Domain CHECK Constraints not yet implemented");
+			break;
+		default:
+			elog(ERROR, "ExecEvalConstraintTest: Constraint type unknown");
+			break;
+	}
+
+	/* If all has gone well (constraint did not fail) return the datum */
+	return result;
+}
+
 /* ----------------------------------------------------------------
  *		ExecEvalFieldSelect
  *
@@ -1749,12 +1748,6 @@ ExecEvalExpr(Node *expression,
 									isNull,
 									isDone);
 			break;
-		case T_Constraint:
-			retDatum = ExecEvalConstraint((Constraint *) expression,
-										 econtext,
-										 isNull,
-										 isDone);
-			break;
 		case T_CaseExpr:
 			retDatum = ExecEvalCase((CaseExpr *) expression,
 									econtext,
@@ -1773,6 +1766,12 @@ ExecEvalExpr(Node *expression,
 										   isNull,
 										   isDone);
 			break;
+		case T_ConstraintTest:
+			retDatum = ExecEvalConstraintTest((ConstraintTest *) expression,
+											  econtext,
+											  isNull,
+											  isDone);
+			break;
 
 		default:
 			elog(ERROR, "ExecEvalExpr: unknown expression type %d",
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5b35eea170a39c2086a2ac366fcb565f87fb2aaf..1938e7f4738ef7e0b9066eaa6d261b43ab3c2a53 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.208 2002/08/30 19:23:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.209 2002/08/31 22:10:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -973,10 +973,6 @@ _copyJoinExpr(JoinExpr *from)
 	return newnode;
 }
 
-/* ----------------
- *		_copyCaseExpr
- * ----------------
- */
 static CaseExpr *
 _copyCaseExpr(CaseExpr *from)
 {
@@ -994,10 +990,6 @@ _copyCaseExpr(CaseExpr *from)
 	return newnode;
 }
 
-/* ----------------
- *		_copyCaseWhen
- * ----------------
- */
 static CaseWhen *
 _copyCaseWhen(CaseWhen *from)
 {
@@ -1012,10 +1004,6 @@ _copyCaseWhen(CaseWhen *from)
 	return newnode;
 }
 
-/* ----------------
- *		_copyNullTest
- * ----------------
- */
 static NullTest *
 _copyNullTest(NullTest *from)
 {
@@ -1030,10 +1018,6 @@ _copyNullTest(NullTest *from)
 	return newnode;
 }
 
-/* ----------------
- *		_copyBooleanTest
- * ----------------
- */
 static BooleanTest *
 _copyBooleanTest(BooleanTest *from)
 {
@@ -1048,6 +1032,23 @@ _copyBooleanTest(BooleanTest *from)
 	return newnode;
 }
 
+static ConstraintTest *
+_copyConstraintTest(ConstraintTest *from)
+{
+	ConstraintTest *newnode = makeNode(ConstraintTest);
+
+	/*
+	 * copy remainder of node
+	 */
+	Node_Copy(from, newnode, arg);
+	newnode->testtype = from->testtype;
+	if (from->name)
+		newnode->name = pstrdup(from->name);
+	Node_Copy(from, newnode, check_expr);
+
+	return newnode;
+}
+
 static ArrayRef *
 _copyArrayRef(ArrayRef *from)
 {
@@ -3206,6 +3207,9 @@ copyObject(void *from)
 		case T_BooleanTest:
 			retval = _copyBooleanTest(from);
 			break;
+		case T_ConstraintTest:
+			retval = _copyConstraintTest(from);
+			break;
 		case T_FkConstraint:
 			retval = _copyFkConstraint(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 7c9127dbf4f6ab31147fa1aa34d24b27199329cf..10b8e79933d7847a4dc2a220efdd4e0e4b30b54d 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.156 2002/08/30 19:23:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.157 2002/08/31 22:10:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1924,6 +1924,20 @@ _equalBooleanTest(BooleanTest *a, BooleanTest *b)
 	return true;
 }
 
+static bool
+_equalConstraintTest(ConstraintTest *a, ConstraintTest *b)
+{
+	if (!equal(a->arg, b->arg))
+		return false;
+	if (a->testtype != b->testtype)
+		return false;
+	if (!equalstr(a->name, b->name))
+		return false;
+	if (!equal(a->check_expr, b->check_expr))
+		return false;
+	return true;
+}
+
 /*
  * Stuff from pg_list.h
  */
@@ -2380,6 +2394,9 @@ equal(void *a, void *b)
 		case T_BooleanTest:
 			retval = _equalBooleanTest(a, b);
 			break;
+		case T_ConstraintTest:
+			retval = _equalConstraintTest(a, b);
+			break;
 		case T_FkConstraint:
 			retval = _equalFkConstraint(a, b);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index a92750caef194311007d812d6d0d690df97ecb71..8c255058068974b0a48fadeec3cf2c7de90533db 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.171 2002/08/30 19:23:19 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.172 2002/08/31 22:10:43 tgl Exp $
  *
  * NOTES
  *	  Every (plan) node in POSTGRES has an associated "out" routine which
@@ -1471,7 +1471,6 @@ _outNullTest(StringInfo str, NullTest *node)
 {
 	appendStringInfo(str, " NULLTEST :arg ");
 	_outNode(str, node->arg);
-
 	appendStringInfo(str, " :nulltesttype %d ",
 					 (int) node->nulltesttype);
 }
@@ -1484,11 +1483,25 @@ _outBooleanTest(StringInfo str, BooleanTest *node)
 {
 	appendStringInfo(str, " BOOLEANTEST :arg ");
 	_outNode(str, node->arg);
-
 	appendStringInfo(str, " :booltesttype %d ",
 					 (int) node->booltesttype);
 }
 
+/*
+ *	ConstraintTest
+ */
+static void
+_outConstraintTest(StringInfo str, ConstraintTest *node)
+{
+	appendStringInfo(str, " CONSTRAINTTEST :arg ");
+	_outNode(str, node->arg);
+	appendStringInfo(str, " :testtype %d :name ",
+					 (int) node->testtype);
+	_outToken(str, node->name);
+	appendStringInfo(str, " :check_expr ");
+	_outNode(str, node->check_expr);
+}
+
 /*
  * _outNode -
  *	  converts a Node into ascii string and append it to 'str'
@@ -1750,6 +1763,9 @@ _outNode(StringInfo str, void *obj)
 			case T_BooleanTest:
 				_outBooleanTest(str, obj);
 				break;
+			case T_ConstraintTest:
+				_outConstraintTest(str, obj);
+				break;
 			case T_FuncCall:
 				_outFuncCall(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 2799bb746044b97b4aaad5644b42d9380ebc0a81..4d4001d21316e28a50520a4d48d201755cbe7191 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.130 2002/08/30 19:23:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.131 2002/08/31 22:10:43 tgl Exp $
  *
  * NOTES
  *	  Most of the read functions for plan nodes are tested. (In fact, they
@@ -931,6 +931,38 @@ _readBooleanTest(void)
 	return local_node;
 }
 
+/* ----------------
+ *		_readConstraintTest
+ *
+ *	ConstraintTest is a subclass of Node
+ * ----------------
+ */
+static ConstraintTest *
+_readConstraintTest(void)
+{
+	ConstraintTest *local_node;
+	char	   *token;
+	int			length;
+
+	local_node = makeNode(ConstraintTest);
+
+	token = pg_strtok(&length); /* eat :arg */
+	local_node->arg = nodeRead(true);	/* now read it */
+
+	token = pg_strtok(&length); /* eat :testtype */
+	token = pg_strtok(&length); /* get testtype */
+	local_node->testtype = (ConstraintTestType) atoi(token);
+
+	token = pg_strtok(&length); /* get :name */
+	token = pg_strtok(&length); /* now read it */
+	local_node->name = nullable_string(token, length);
+
+	token = pg_strtok(&length); /* eat :check_expr */
+	local_node->check_expr = nodeRead(true);	/* now read it */
+
+	return local_node;
+}
+
 /* ----------------
  *		_readVar
  *
@@ -2222,6 +2254,8 @@ parsePlanString(void)
 		return_value = _readNullTest();
 	else if (length == 11 && strncmp(token, "BOOLEANTEST", length) == 0)
 		return_value = _readBooleanTest();
+	else if (length == 14 && strncmp(token, "CONSTRAINTTEST", length) == 0)
+		return_value = _readConstraintTest();
 	else
 		elog(ERROR, "badly formatted planstring \"%.10s\"...", token);
 
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index b7c0bac12c8124934344e55d96381df6640015da..41f9b2f9478f976017efb41e83b587108a124d36 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.54 2002/08/02 18:15:06 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.55 2002/08/31 22:10:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,6 +27,7 @@
 #include "nodes/makefuncs.h"
 #include "optimizer/prep.h"
 #include "parser/parsetree.h"
+#include "parser/parse_coerce.h"
 
 
 static List *expand_targetlist(List *tlist, int command_type,
@@ -162,6 +163,8 @@ expand_targetlist(List *tlist, int command_type,
 			 *
 			 * For INSERT, generate a NULL constant.  (We assume the
 			 * rewriter would have inserted any available default value.)
+			 * Also, if the column isn't dropped, apply any domain constraints
+			 * that might exist --- this is to catch domain NOT NULL.
 			 *
 			 * For UPDATE, generate a Var reference to the existing value of
 			 * the attribute, so that it gets copied to the new tuple.
@@ -182,6 +185,9 @@ expand_targetlist(List *tlist, int command_type,
 												  att_tup->attbyval,
 												  false,	/* not a set */
 												  false);
+					if (!att_tup->attisdropped)
+						new_expr = coerce_type_constraints(NULL, new_expr,
+														   atttype, false);
 					break;
 				case CMD_UPDATE:
 					/* Insert NULLs for dropped columns */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 9e8372139efc2f55410e68934c540fb117d729c5..3b5e6988396d23e10eb07266ec903024babb7bb0 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.106 2002/07/20 05:16:58 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.107 2002/08/31 22:10:43 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -1882,12 +1882,14 @@ expression_tree_walker(Node *node,
 					return true;
 			}
 			break;
-		case T_Constraint:
-			return walker(((Constraint *) node)->raw_expr, context);
 		case T_NullTest:
 			return walker(((NullTest *) node)->arg, context);
 		case T_BooleanTest:
 			return walker(((BooleanTest *) node)->arg, context);
+		case T_ConstraintTest:
+			if (walker(((ConstraintTest *) node)->arg, context))
+				return true;
+			return walker(((ConstraintTest *) node)->check_expr, context);
 		case T_SubLink:
 			{
 				SubLink    *sublink = (SubLink *) node;
@@ -2238,20 +2240,6 @@ expression_tree_mutator(Node *node,
 				return (Node *) newnode;
 			}
 			break;
-		case T_Constraint:
-			{
-				/*
-				 * Used for confirming domains.  Only needed fields
-				 * within the executor are the name, raw expression
-				 * and constraint type.
-				 */
-				Constraint *con = (Constraint *) node;
-				Constraint *newnode;
-
-				FLATCOPY(newnode, con, Constraint);
-				MUTATE(newnode->raw_expr, con->raw_expr, Node *);
-				return (Node *) newnode;
-			}
 		case T_NullTest:
 			{
 				NullTest   *ntest = (NullTest *) node;
@@ -2272,6 +2260,17 @@ expression_tree_mutator(Node *node,
 				return (Node *) newnode;
 			}
 			break;
+		case T_ConstraintTest:
+			{
+				ConstraintTest *ctest = (ConstraintTest *) node;
+				ConstraintTest *newnode;
+
+				FLATCOPY(newnode, ctest, ConstraintTest);
+				MUTATE(newnode->arg, ctest->arg, Node *);
+				MUTATE(newnode->check_expr, ctest->check_expr, Node *);
+				return (Node *) newnode;
+			}
+			break;
 		case T_SubLink:
 			{
 				/*
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 1016c782d29d11f741e7693219233abe319ff8ff..397bd4b8b079e84482b2f5570f9a2c000009baba 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.80 2002/08/22 00:01:42 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.81 2002/08/31 22:10:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,7 +35,7 @@ static Node *build_func_call(Oid funcid, Oid rettype, List *args);
 static Oid	find_coercion_function(Oid targetTypeId, Oid sourceTypeId,
 								   bool isExplicit);
 static Oid	find_typmod_coercion_function(Oid typeId);
-static Node	*TypeConstraints(Node *arg, Oid typeId);
+
 
 /* coerce_type()
  * Convert a function argument to a different type.
@@ -49,7 +49,7 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 	if (targetTypeId == inputTypeId ||
 		node == NULL)
 	{
-		/* no conversion needed, but constraints may need to be applied */
+		/* no conversion needed */
 		result = node;
 	}
 	else if (inputTypeId == UNKNOWNOID && IsA(node, Const))
@@ -69,11 +69,12 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 		 * postpone evaluation of the function call until runtime. But
 		 * there is no way to represent a typinput function call as an
 		 * expression tree, because C-string values are not Datums.
+		 * (XXX This *is* possible as of 7.3, do we want to do it?)
 		 */
 		Const	   *con = (Const *) node;
 		Const	   *newcon = makeNode(Const);
 		Type		targetType = typeidType(targetTypeId);
-		Oid			baseTypeId = getBaseType(targetTypeId);
+		char		targetTyptype = typeTypType(targetType);
 
 		newcon->consttype = targetTypeId;
 		newcon->constlen = typeLen(targetType);
@@ -85,16 +86,31 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 		{
 			char	   *val = DatumGetCString(DirectFunctionCall1(unknownout,
 													   con->constvalue));
+
+			/*
+			 * If target is a domain, use the typmod it applies to the base
+			 * type.  Note that we call stringTypeDatum using the domain's
+			 * pg_type row, though.  This works because the domain row has
+			 * the same typinput and typelem as the base type --- ugly...
+			 */
+			if (targetTyptype == 'd')
+				atttypmod = getBaseTypeMod(targetTypeId, atttypmod);
+
 			newcon->constvalue = stringTypeDatum(targetType, val, atttypmod);
 			pfree(val);
 		}
 
-		ReleaseSysCache(targetType);
-
-		/* Test for domain, and apply appropriate constraints */
 		result = (Node *) newcon;
-		if (targetTypeId != baseTypeId)
-			result = (Node *) TypeConstraints(result, targetTypeId);
+
+		/*
+		 * If target is a domain, apply constraints (except for typmod,
+		 * which we assume the input routine took care of).
+		 */
+		if (targetTyptype == 'd')
+			result = coerce_type_constraints(pstate, result, targetTypeId,
+											 false);
+
+		ReleaseSysCache(targetType);
 	}
 	else if (targetTypeId == ANYOID ||
 			 targetTypeId == ANYARRAYOID)
@@ -109,21 +125,18 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 		 * attach a RelabelType node so that the expression will be seen
 		 * to have the intended type when inspected by higher-level code.
 		 *
+		 * Also, domains may have value restrictions beyond the base type
+		 * that must be accounted for.
+		 */
+		result = coerce_type_constraints(pstate, node, targetTypeId, true);
+		/*
 		 * XXX could we label result with exprTypmod(node) instead of
 		 * default -1 typmod, to save a possible length-coercion later?
 		 * Would work if both types have same interpretation of typmod,
-		 * which is likely but not certain.
-		 *
-		 * Domains may have value restrictions beyond the base type that
-		 * must be accounted for.
+		 * which is likely but not certain (wrong if target is a domain,
+		 * in any case).
 		 */
-		Oid			baseTypeId = getBaseType(targetTypeId);
-		result = node;
-		if (targetTypeId != baseTypeId)
-			result = (Node *) TypeConstraints(result, targetTypeId);
-
 		result = (Node *) makeRelabelType(result, targetTypeId, -1);
-
 	}
 	else if (typeInheritsFrom(inputTypeId, targetTypeId))
 	{
@@ -144,8 +157,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 		 *
 		 * For domains, we use the coercion function for the base type.
 		 */
-		Oid			funcId;
 		Oid			baseTypeId = getBaseType(targetTypeId);
+		Oid			funcId;
 
 		funcId = find_coercion_function(baseTypeId,
 										getBaseType(inputTypeId),
@@ -157,11 +170,15 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 		result = build_func_call(funcId, baseTypeId, makeList1(node));
 
 		/*
-		 * If domain, relabel with domain type ID and test against domain
-		 * constraints
+		 * If domain, test against domain constraints and relabel with
+		 * domain type ID
 		 */
 		if (targetTypeId != baseTypeId)
-			result = (Node *) TypeConstraints(result, targetTypeId);
+		{
+			result = coerce_type_constraints(pstate, result, targetTypeId,
+											 true);
+			result = (Node *) makeRelabelType(result, targetTypeId, -1);
+		}
 
 		/*
 		 * If the input is a constant, apply the type conversion function
@@ -306,28 +323,21 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids,
  * function should be invoked to do that.
  *
  * "bpchar" (ie, char(N)) and "numeric" are examples of such types.
+ *
+ * This mechanism may seem pretty grotty and in need of replacement by
+ * something in pg_cast, but since typmod is only interesting for datatypes
+ * that have special handling in the grammar, there's not really much
+ * percentage in making it any easier to apply such coercions ...
+ *
+ * NOTE: this does not need to work on domain types, because any typmod
+ * coercion for a domain is considered to be part of the type coercion
+ * needed to produce the domain value in the first place.
  */
 Node *
 coerce_type_typmod(ParseState *pstate, Node *node,
 				   Oid targetTypeId, int32 atttypmod)
 {
-	Oid			baseTypeId;
 	Oid			funcId;
-	int32		domainTypMod;
-
-	/* If given type is a domain, use base type instead */
-	baseTypeId = getBaseTypeTypeMod(targetTypeId, &domainTypMod);
-
-
-	/*
-	 * Use the domain typmod rather than what was supplied if the
-	 * domain was empty.  atttypmod will always be -1 if domains are in use.
-	 */
-	if (baseTypeId != targetTypeId)
-	{
-		Assert(atttypmod < 0);
-		atttypmod = domainTypMod;
-	}
 
 	/*
 	 * A negative typmod is assumed to mean that no coercion is wanted.
@@ -335,7 +345,8 @@ coerce_type_typmod(ParseState *pstate, Node *node,
 	if (atttypmod < 0 || atttypmod == exprTypmod(node))
 		return node;
 
-	funcId = find_typmod_coercion_function(baseTypeId);
+	funcId = find_typmod_coercion_function(targetTypeId);
+
 	if (OidIsValid(funcId))
 	{
 		Const	   *cons;
@@ -348,7 +359,7 @@ coerce_type_typmod(ParseState *pstate, Node *node,
 						 false,
 						 false);
 
-		node = build_func_call(funcId, baseTypeId, makeList2(node, cons));
+		node = build_func_call(funcId, targetTypeId, makeList2(node, cons));
 	}
 
 	return node;
@@ -869,12 +880,15 @@ build_func_call(Oid funcid, Oid rettype, List *args)
 
 /*
  * Create an expression tree to enforce the constraints (if any)
- * which should be applied by the type.
+ * that should be applied by the type.  Currently this is only
+ * interesting for domain types.
  */
-static Node *
-TypeConstraints(Node *arg, Oid typeId)
+Node *
+coerce_type_constraints(ParseState *pstate, Node *arg,
+						Oid typeId, bool applyTypmod)
 {
 	char   *notNull = NULL;
+	int32	typmod = -1;
 
 	for (;;)
 	{
@@ -885,12 +899,13 @@ TypeConstraints(Node *arg, Oid typeId)
 							 ObjectIdGetDatum(typeId),
 							 0, 0, 0);
 		if (!HeapTupleIsValid(tup))
-			elog(ERROR, "getBaseType: failed to lookup type %u", typeId);
+			elog(ERROR, "coerce_type_constraints: failed to lookup type %u",
+				 typeId);
 		typTup = (Form_pg_type) GETSTRUCT(tup);
 
 		/* Test for NOT NULL Constraint */
 		if (typTup->typnotnull && notNull == NULL)
-			notNull = NameStr(typTup->typname);
+			notNull = pstrdup(NameStr(typTup->typname));
 
 		/* TODO: Add CHECK Constraints to domains */
 
@@ -901,20 +916,32 @@ TypeConstraints(Node *arg, Oid typeId)
 			break;
 		}
 
+		Assert(typmod < 0);
+
 		typeId = typTup->typbasetype;
+		typmod = typTup->typtypmod;
 		ReleaseSysCache(tup);
 	}
 
+	/*
+	 * If domain applies a typmod to its base type, do length coercion.
+	 */
+	if (applyTypmod && typmod >= 0)
+		arg = coerce_type_typmod(pstate, arg, typeId, typmod);
+
 	/*
 	 * Only need to add one NOT NULL check regardless of how many 
-	 * domains in the tree request it.
+	 * domains in the stack request it.  The topmost domain that
+	 * requested it is used as the constraint name.
 	 */
-	if (notNull != NULL) {
-		Constraint *r = makeNode(Constraint);
+	if (notNull)
+	{
+		ConstraintTest *r = makeNode(ConstraintTest);
 
-		r->raw_expr = arg;
-		r->contype = CONSTR_NOTNULL;
-		r->name	= notNull; 
+		r->arg = arg;
+		r->testtype = CONSTR_TEST_NOTNULL;
+		r->name	= notNull;
+		r->check_expr = NULL;
 
 		arg = (Node *) r;
 	}	
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 1a7acd22527a5c7f014ba8e045a3d353ad7b946a..de07d39b4d0b199336b5017b734d5832d2e44514 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.126 2002/08/26 17:53:58 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.127 2002/08/31 22:10:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -640,6 +640,7 @@ transformExpr(ParseState *pstate, Node *expr)
 		case T_ArrayRef:
 		case T_FieldSelect:
 		case T_RelabelType:
+		case T_ConstraintTest:
 			{
 				result = (Node *) expr;
 				break;
@@ -919,15 +920,15 @@ exprType(Node *expr)
 		case T_CaseWhen:
 			type = exprType(((CaseWhen *) expr)->result);
 			break;
-		case T_Constraint:
-			type = exprType(((Constraint *) expr)->raw_expr);
-			break;
 		case T_NullTest:
 			type = BOOLOID;
 			break;
 		case T_BooleanTest:
 			type = BOOLOID;
 			break;
+		case T_ConstraintTest:
+			type = exprType(((ConstraintTest *) expr)->arg);
+			break;
 		default:
 			elog(ERROR, "exprType: Do not know how to get type for %d node",
 				 nodeTag(expr));
@@ -978,10 +979,8 @@ exprTypmod(Node *expr)
 			break;
 		case T_FieldSelect:
 			return ((FieldSelect *) expr)->resulttypmod;
-			break;
 		case T_RelabelType:
 			return ((RelabelType *) expr)->resulttypmod;
-			break;
 		case T_CaseExpr:
 			{
 				/*
@@ -1013,6 +1012,9 @@ exprTypmod(Node *expr)
 				return typmod;
 			}
 			break;
+		case T_ConstraintTest:
+			return exprTypmod(((ConstraintTest *) expr)->arg);
+
 		default:
 			break;
 	}
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 0eb29045b1f62f27996430dbf29ff9b0951afef9..e75c193eff5b65f5a0ab54a576e4a56754a772a5 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.48 2002/08/08 01:22:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.49 2002/08/31 22:10:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -277,6 +277,16 @@ typeByVal(Type t)
 	return typ->typbyval;
 }
 
+/* given type (as type struct), return the value of its 'typtype' attribute.*/
+char
+typeTypType(Type t)
+{
+	Form_pg_type typ;
+
+	typ = (Form_pg_type) GETSTRUCT(t);
+	return typ->typtype;
+}
+
 /* given type (as type struct), return the name of type */
 char *
 typeTypeName(Type t)
@@ -434,7 +444,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
 	 * paranoia is justified since the string might contain anything.
 	 */
 	if (length(raw_parsetree_list) != 1)
-		elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+		elog(ERROR, "Invalid type name '%s'", str);
 	stmt = (SelectStmt *) lfirst(raw_parsetree_list);
 	if (stmt == NULL ||
 		!IsA(stmt, SelectStmt) ||
@@ -450,25 +460,26 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
 		stmt->limitCount != NULL ||
 		stmt->forUpdate != NIL ||
 		stmt->op != SETOP_NONE)
-		elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+		elog(ERROR, "Invalid type name '%s'", str);
 	if (length(stmt->targetList) != 1)
-		elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+		elog(ERROR, "Invalid type name '%s'", str);
 	restarget = (ResTarget *) lfirst(stmt->targetList);
 	if (restarget == NULL ||
 		!IsA(restarget, ResTarget) ||
 		restarget->name != NULL ||
 		restarget->indirection != NIL)
-		elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+		elog(ERROR, "Invalid type name '%s'", str);
 	typecast = (TypeCast *) restarget->val;
 	if (typecast == NULL ||
 		!IsA(typecast, TypeCast) ||
 		typecast->arg == NULL ||
 		!IsA(typecast->arg, A_Const))
-		elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+		elog(ERROR, "Invalid type name '%s'", str);
 	typename = typecast->typename;
 	if (typename == NULL ||
 		!IsA(typename, TypeName))
-		elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+		elog(ERROR, "Invalid type name '%s'", str);
+
 	*type_id = typenameTypeId(typename);
 	*typmod = typename->typmod;
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 458ab68749dd25e9a6437d497fda51fc8d8980f8..c7da14ad7eabd294891b7c57179d96cc65395661 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *				back to source text
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.119 2002/08/29 01:19:41 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.120 2002/08/31 22:10:46 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -2187,6 +2187,18 @@ get_rule_expr(Node *node, deparse_context *context)
 			}
 			break;
 
+		case T_ConstraintTest:
+			{
+				ConstraintTest *ctest = (ConstraintTest *) node;
+
+				/*
+				 * We assume that the operations of the constraint node
+				 * need not be explicitly represented in the output.
+				 */
+				get_rule_expr(ctest->arg, context);
+			}
+			break;
+
 		case T_SubLink:
 			get_sublink_expr(node, context);
 			break;
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 66dc58d6c4b4935706ce3aa003da7e36d0d556cd..4054b2920e6430e44ab70dec30b2caaac27d5b50 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.81 2002/08/29 00:17:05 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.82 2002/08/31 22:10:47 tgl Exp $
  *
  * NOTES
  *	  Eventually, the index information should go through here, too.
@@ -1074,12 +1074,12 @@ getBaseType(Oid typid)
 }
 
 /*
- * getBaseTypeTypeMod
- *		If the given type is a domain, return its base type;
- *		otherwise return the type's own OID.  Also return base typmod.
+ * getBaseTypeMod
+ *		If the given type is a domain, return the typmod it applies to
+ *		its base type; otherwise return the specified original typmod.
  */
-Oid
-getBaseTypeTypeMod(Oid typid, int32 *typmod)
+int32
+getBaseTypeMod(Oid typid, int32 typmod)
 {
 	/*
 	 * We loop to find the bottom base type in a stack of domains.
@@ -1093,7 +1093,7 @@ getBaseTypeTypeMod(Oid typid, int32 *typmod)
 							 ObjectIdGetDatum(typid),
 							 0, 0, 0);
 		if (!HeapTupleIsValid(tup))
-			elog(ERROR, "getBaseTypeTypeMod: failed to lookup type %u", typid);
+			elog(ERROR, "getBaseTypeMod: failed to lookup type %u", typid);
 		typTup = (Form_pg_type) GETSTRUCT(tup);
 		if (typTup->typtype != 'd')
 		{
@@ -1102,12 +1102,20 @@ getBaseTypeTypeMod(Oid typid, int32 *typmod)
 			break;
 		}
 
+		/*
+		 * The typmod applied to a domain should always be -1.
+		 *
+		 * We substitute the domain's typmod as we switch attention to
+		 * the base type.
+		 */
+		Assert(typmod < 0);
+
 		typid = typTup->typbasetype;
-		*typmod = typTup->typtypmod;
+		typmod = typTup->typtypmod;
 		ReleaseSysCache(tup);
 	}
 
-	return typid;
+	return typmod;
 }
 
 /*
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 3f5f6d744998d40c868a95d727132d14c8e159b2..ee472203e68fd3073d391639d004fee40b25cbd7 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.117 2002/08/27 04:55:11 tgl Exp $
+ * $Id: nodes.h,v 1.118 2002/08/31 22:10:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -229,6 +229,7 @@ typedef enum NodeTag
 	T_GroupClause,
 	T_NullTest,
 	T_BooleanTest,
+	T_ConstraintTest,
 	T_CaseExpr,
 	T_CaseWhen,
 	T_FkConstraint,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 19d52d72175a812ae5ae4d11de868f2e08325e19..a426aeba020fd0f373b4908a7c875b03b6b47271 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.203 2002/08/30 19:23:20 tgl Exp $
+ * $Id: parsenodes.h,v 1.204 2002/08/31 22:10:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -233,14 +233,13 @@ typedef struct NullTest
 	NullTestType nulltesttype;	/* IS NULL, IS NOT NULL */
 } NullTest;
 
-/* ----------------
+/*
  * BooleanTest
  *
  * BooleanTest represents the operation of determining whether a boolean
  * is TRUE, FALSE, or UNKNOWN (ie, NULL).  All six meaningful combinations
  * are supported.  Note that a NULL input does *not* cause a NULL result.
  * The appropriate test is performed and returned as a boolean Datum.
- * ----------------
  */
 
 typedef enum BoolTestType
@@ -255,6 +254,29 @@ typedef struct BooleanTest
 	BoolTestType booltesttype;	/* test type */
 } BooleanTest;
 
+/*
+ * ConstraintTest
+ *
+ * ConstraintTest represents the operation of testing a value to see whether
+ * it meets a constraint.  If so, the input value is returned as the result;
+ * if not, an error is raised.
+ */
+
+typedef enum ConstraintTestType
+{
+	CONSTR_TEST_NOTNULL,
+	CONSTR_TEST_CHECK
+} ConstraintTestType;
+
+typedef struct ConstraintTest
+{
+	NodeTag		type;
+	Node	   *arg;			/* input expression */
+	ConstraintTestType testtype; /* test type */
+	char	   *name;			/* name of constraint (for error msgs) */
+	Node	   *check_expr;		/* for CHECK test, a boolean expression */
+} ConstraintTest;
+
 /*
  * ColumnDef - column definition (used in various creates)
  *
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index e306493fa3616c627ab7e6bbc48eb6d35b764564..328332aafd2e5affa823332bc18e19ff7187cc17 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_coerce.h,v 1.44 2002/06/20 20:29:51 momjian Exp $
+ * $Id: parse_coerce.h,v 1.45 2002/08/31 22:10:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,8 @@ extern Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 			Oid targetTypeId, int32 atttypmod, bool isExplicit);
 extern Node *coerce_type_typmod(ParseState *pstate, Node *node,
 				   Oid targetTypeId, int32 atttypmod);
+extern Node *coerce_type_constraints(ParseState *pstate, Node *arg,
+									 Oid typeId, bool applyTypmod);
 
 extern Node *coerce_to_boolean(Node *node, const char *constructName);
 
diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h
index f348a32d4162e77a31c292f40afd17065b6bb8bb..86c453882bc13ffcd63eb793f898f1cc069ef89e 100644
--- a/src/include/parser/parse_type.h
+++ b/src/include/parser/parse_type.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_type.h,v 1.23 2002/06/20 20:29:52 momjian Exp $
+ * $Id: parse_type.h,v 1.24 2002/08/31 22:10:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,6 +31,7 @@ extern Type typeidType(Oid id);
 extern Oid	typeTypeId(Type tp);
 extern int16 typeLen(Type t);
 extern bool typeByVal(Type t);
+extern char typeTypType(Type t);
 extern char *typeTypeName(Type t);
 extern char typeTypeFlag(Type t);
 extern Oid	typeTypeRelid(Type typ);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 2681d67139f97767c5a6d65ccaee25d25f45cec0..3c442bbd202d8e8f7f786a4932a136dac57f7e83 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lsyscache.h,v 1.60 2002/08/29 00:17:06 tgl Exp $
+ * $Id: lsyscache.h,v 1.61 2002/08/31 22:10:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -58,7 +58,7 @@ extern void getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem);
 extern bool getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
 							  bool *typIsVarlena);
 extern Oid getBaseType(Oid typid);
-extern Oid getBaseTypeTypeMod(Oid typid, int32 *typmod);
+extern int32 getBaseTypeMod(Oid typid, int32 typmod);
 extern int32 get_typavgwidth(Oid typid, int32 typmod);
 extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);
 extern bool get_attstatsslot(HeapTuple statstuple,
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index a67559129c5171905f818b8d5a59655b656a4227..522c28121ff27ef071895d5b017394ac133732f8 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -41,8 +41,7 @@ select * from basictest;
 (2 rows)
 
 -- check that domains inherit operations from base types
--- XXX shouldn't have to quote the constant here
-select testtext || testvarchar as concat, testnumeric + '42' as sum
+select testtext || testvarchar as concat, testnumeric + 42 as sum
 from basictest;
   concat   |  sum   
 -----------+--------
@@ -99,7 +98,7 @@ create table nulltest
            , col4 dnull
            );
 INSERT INTO nulltest DEFAULT VALUES;
-ERROR:  ExecInsert: Fail to add null value in not null attribute col3
+ERROR:  Domain dnotnull does not allow NULL values
 INSERT INTO nulltest values ('a', 'b', 'c', 'd');  -- Good
 INSERT INTO nulltest values (NULL, 'b', 'c', 'd');
 ERROR:  Domain dnotnull does not allow NULL values
@@ -147,7 +146,7 @@ create table defaulttest
             , col5 ddef1 NOT NULL DEFAULT NULL
             , col6 ddef2 DEFAULT '88'
             , col7 ddef4 DEFAULT 8000
-		, col8 ddef5
+            , col8 ddef5
             );
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index 'defaulttest_pkey' for table 'defaulttest'
 insert into defaulttest default values;
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index 4c2e7e31aca3590223f4c281a6358dbd12f9afad..9627870934fe16e533adb5c3f7d3bc86288e551c 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -38,8 +38,7 @@ INSERT INTO basictest values ('88', 'haha', 'short', '123.1212');    -- Truncate
 select * from basictest;
 
 -- check that domains inherit operations from base types
--- XXX shouldn't have to quote the constant here
-select testtext || testvarchar as concat, testnumeric + '42' as sum
+select testtext || testvarchar as concat, testnumeric + 42 as sum
 from basictest;
 
 drop table basictest;