Index: src/backend/commands/copy.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/copy.c,v
retrieving revision 1.188
diff -c -r1.188 copy.c
*** src/backend/commands/copy.c	2003/01/10 22:03:27	1.188
--- src/backend/commands/copy.c	2003/01/17 18:16:46
***************
*** 872,879 ****
  			prm->paramid = 0;
  			prm->paramtype = attr[i]->atttypid;
  
! 			node = coerce_type_constraints((Node *) prm, attr[i]->atttypid,
! 										   COERCE_IMPLICIT_CAST);
  
  			/* check whether any constraints actually found */
  			if (node != (Node *) prm)
--- 872,879 ----
  			prm->paramid = 0;
  			prm->paramtype = attr[i]->atttypid;
  
! 			node = coerce_todomain((Node *) prm, InvalidOid,
! 								   attr[i]->atttypid, COERCE_IMPLICIT_CAST);
  
  			/* check whether any constraints actually found */
  			if (node != (Node *) prm)
Index: src/backend/executor/execQual.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/executor/execQual.c,v
retrieving revision 1.123
diff -c -r1.123 execQual.c
*** src/backend/executor/execQual.c	2003/01/12 04:03:34	1.123
--- src/backend/executor/execQual.c	2003/01/17 18:17:34
***************
*** 34,40 ****
--- 34,44 ----
  
  #include "postgres.h"
  
+ #include "access/genam.h"
  #include "access/heapam.h"
+ #include "catalog/catname.h"
+ #include "catalog/indexing.h"
+ #include "catalog/pg_constraint.h"
  #include "catalog/pg_type.h"
  #include "executor/execdebug.h"
  #include "executor/functions.h"
***************
*** 45,51 ****
--- 49,57 ----
  #include "utils/acl.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
  #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
  
  
  /* static function decls */
***************
*** 80,94 ****
  static Datum ExecEvalBooleanTest(GenericExprState *bstate,
  								 ExprContext *econtext,
  								 bool *isNull, ExprDoneCond *isDone);
- static Datum ExecEvalConstraintTest(ConstraintTestState *cstate,
- 					   ExprContext *econtext,
- 					   bool *isNull, ExprDoneCond *isDone);
  static Datum ExecEvalConstraintTestValue(ConstraintTestValue *conVal,
  					   ExprContext *econtext, bool *isNull);
  static Datum ExecEvalFieldSelect(GenericExprState *fstate,
  								 ExprContext *econtext,
  								 bool *isNull, ExprDoneCond *isDone);
  
  
  /*----------
   *	  ExecEvalArrayRef
--- 86,121 ----
  static Datum ExecEvalBooleanTest(GenericExprState *bstate,
  								 ExprContext *econtext,
  								 bool *isNull, ExprDoneCond *isDone);
  static Datum ExecEvalConstraintTestValue(ConstraintTestValue *conVal,
  					   ExprContext *econtext, bool *isNull);
  static Datum ExecEvalFieldSelect(GenericExprState *fstate,
  								 ExprContext *econtext,
  								 bool *isNull, ExprDoneCond *isDone);
+ static Datum ExecEvalCoerceToDomain(CoerceToDomainState *dstate,
+ 									ExprContext *econtext,
+ 									bool *isNull, ExprDoneCond *isDone);
  
+ /*
+  * ConstraintTest
+  *
+  * Used by ExecEvalCoerceToDomain to cache a list of constraints that
+  * need to be processed against each tuple in that State.
+  */
+ typedef enum ConstraintTestType
+ {
+ 	CONSTR_TEST_NOTNULL,
+ 	CONSTR_TEST_CHECK
+ } ConstraintTestType;
+ 
+ typedef struct ConstraintTest
+ {
+ 	Expr		xpr;
+ 	Expr	   *arg;			/* input expression */
+ 	ConstraintTestType testtype;	/* test type */
+ 	char	   *name;			/* name of constraint (for error msgs) */
+ 	char	   *domname; 		/* name of domain (for error messages) */
+ 	ExprState  *check_expr;		/* for CHECK test, a boolean expression */
+ } ConstraintTest;
  
  /*----------
   *	  ExecEvalArrayRef
***************
*** 1559,1640 ****
  }
  
  /*
!  * 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(ConstraintTestState *cstate, ExprContext *econtext,
  					   bool *isNull, ExprDoneCond *isDone)
  {
! 	ConstraintTest *constraint = (ConstraintTest *) cstate->xprstate.expr;
! 	Datum		result;
  
! 	result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
  
  	if (isDone && *isDone == ExprEndResult)
  		return result;			/* nothing to check */
  
! 	switch (constraint->testtype)
  	{
! 		case CONSTR_TEST_NOTNULL:
! 			if (*isNull)
! 				elog(ERROR, "Domain %s does not allow NULL values",
! 					 constraint->domname);
! 			break;
! 		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;
  
! 				econtext->domainValue_datum = result;
! 				econtext->domainValue_isNull = *isNull;
  
! 				conResult = ExecEvalExpr(cstate->check_expr,
! 										 econtext, &conIsNull, NULL);
  
! 				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:
! 			elog(ERROR, "ExecEvalConstraintTest: Constraint type unknown");
! 			break;
! 	}
  
! 	/* If all has gone well (constraint did not fail) return the datum */
! 	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;
  }
  
  /* ----------------------------------------------------------------
--- 1586,1797 ----
  }
  
  /*
!  * ExecEvalConstraintTestValue
   *
!  * Return the value stored by constraintTest.
   */
  static Datum
! ExecEvalConstraintTestValue(ConstraintTestValue *conVal, ExprContext *econtext,
! 							bool *isNull)
! {
! 	*isNull = econtext->domainValue_isNull;
! 	return econtext->domainValue_datum;
! }
! 
! /*
!  * ExecEvalCoerceToDomain
!  *
!  * Check the value being coerced is within the constraints
!  * that the domain enforces
!  */
! static Datum
! ExecEvalCoerceToDomain(CoerceToDomainState *dstate, ExprContext *econtext,
  					   bool *isNull, ExprDoneCond *isDone)
  {
! 	Datum	result;
! 	List   *cn = NULL;
  
! 	/*
! 	 * Process first to determine whether we even need to build a cache
! 	 */
! 	result = ExecEvalExpr(dstate->arg, econtext, isNull, isDone);
  
  	if (isDone && *isDone == ExprEndResult)
  		return result;			/* nothing to check */
+ 
  
! 	/* Initialize the cache if required */
! 	if (!dstate->constraints)
  	{
! 		CoerceToDomain *cdomain = (CoerceToDomain *) dstate->xprstate.expr;
! 		char	   *notNull = NULL;
! 		int32		typmod = -1;
! 		int32		typeOid = cdomain->domainOid;
! 		MemoryContext	oldcontext;
! 
! 		/* Ensure the cache is tied to the query, and not per tuple */
! 		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
! 
! 		/* Build the cache off expr */
! 		for (;;)
! 		{
! 			HeapTuple	tup;
! 			HeapTuple	conTup;
! 			Form_pg_type typTup;
! 
! 			ScanKeyData key[1];
! 			int			nkeys = 0;
! 			SysScanDesc scan;
! 			Relation	conRel;
! 		
! 			tup = SearchSysCache(TYPEOID,
! 								 ObjectIdGetDatum(typeOid),
! 								 0, 0, 0);
! 			if (!HeapTupleIsValid(tup))
! 				elog(ERROR, "coerce_type_constraints: failed to lookup type %u",
! 					 cdomain->domainOid);
! 			typTup = (Form_pg_type) GETSTRUCT(tup);
! 
! 			/* Test for NOT NULL Constraint */
! 			if (typTup->typnotnull && notNull == NULL)
! 				notNull = pstrdup(NameStr(typTup->typname));
! 
! 			/* Add CHECK Constraints to domains */
! 			conRel = heap_openr(ConstraintRelationName, RowShareLock);
! 
! 			ScanKeyEntryInitialize(&key[nkeys++], 0x0,
! 								   Anum_pg_constraint_contypid, F_OIDEQ,
! 								   ObjectIdGetDatum(typeOid));
! 
! 			scan = systable_beginscan(conRel, ConstraintTypidIndex, true,
! 									  SnapshotNow, nkeys, key);
! 
! 			while (HeapTupleIsValid(conTup = systable_getnext(scan)))
! 			{
! 				Datum	val;
! 				bool	isNull;
! 				Expr   *expr;
! 				Form_pg_constraint	c = (Form_pg_constraint) GETSTRUCT(conTup);
! 				ConstraintTest	   *r;
! 
! 				/* Not expecting conbin to be NULL, but we'll test for it anyway */
! 				val = fastgetattr(conTup,
! 								  Anum_pg_constraint_conbin,
! 								  conRel->rd_att, &isNull);
! 
! 				if (isNull)
! 					elog(ERROR, "coerce_type_constraints: domain %s constraint %s has NULL conbin",
! 						 NameStr(typTup->typname), NameStr(c->conname));
! 
! 				/* Create the ConstraintTest node */
! 				r = (ConstraintTest *) palloc(sizeof(ConstraintTest));
! 				r->testtype = CONSTR_TEST_CHECK;
! 				r->name = NameStr(c->conname);
! 				r->domname = NameStr(typTup->typname);
! 				expr = stringToNode(DatumGetCString(
! 											DirectFunctionCall1(textout,
! 																val)));
! 				fix_opfuncids((Node *) expr);
! 
! 				r->check_expr =	ExecInitExpr(expr, NULL);
! 
! 				dstate->constraints = lcons((void *) r, dstate->constraints);
! 			}
! 
! 			systable_endscan(scan);
! 			heap_close(conRel, RowShareLock);
! 
! 			if (typTup->typtype != 'd')
  			{
! 				/* Not a domain, so done */
! 				ReleaseSysCache(tup);
! 				break;
! 			}
  
! 			Assert(typmod < 0);
  
! 			typeOid = typTup->typbasetype;
! 			typmod = typTup->typtypmod;
! 			ReleaseSysCache(tup);
! 		}
  
! 		/*
! 		 * Only need to add one NOT NULL check regardless of how many domains
! 		 * in the stack request it.  The topmost domain that requested it is
! 		 * used as the constraint name.
! 		 */
! 		if (notNull)
! 		{
! 			ConstraintTest *r;
  
! 			/* Create the ConstraintTestState node */
! 			r = (ConstraintTest *) palloc(sizeof(ConstraintTest));
! 			r->testtype = CONSTR_TEST_NOTNULL;
! 			r->name = "NOT NULL";
! 			r->domname = notNull;
! 			r->check_expr = NULL;
  
! 			dstate->constraints = lcons((void *) r, dstate->constraints);
! 		}
  
! 		/* Switch back */
! 		MemoryContextSwitchTo(oldcontext);
! 	} /* if (!dstate->constraints) */
  
! 
! 	/*
! 	 * Process the cached constraints against Datum result to confirm
! 	 * domain coercion and legal
! 	 */
! 	foreach (cn, dstate->constraints)
! 	{
! 		ConstraintTest *con = (ConstraintTest *) lfirst(cn);
! 
! 		switch (con->testtype)
! 		{
! 			case CONSTR_TEST_NOTNULL:
! 				if (*isNull)
! 					elog(ERROR, "Domain %s does not allow NULL values",
! 						 con->domname);
! 				break;
! 			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;
! 
! 					econtext->domainValue_datum = result;
! 					econtext->domainValue_isNull = *isNull;
! 
! 					conResult = ExecEvalExpr(con->check_expr,
! 											 econtext, &conIsNull, NULL);
! 
! 					if (!conIsNull &&
! 						!DatumGetBool(conResult))
! 						elog(ERROR, "ExecEvalConstraintTest: Domain %s constraint %s failed",
! 							 con->domname, con->name);
! 
! 					econtext->domainValue_datum = save_datum;
! 					econtext->domainValue_isNull = save_isNull;
! 				}
! 				break;
! 			default:
! 				elog(ERROR, "ExecEvalConstraintTest: Constraint type unknown");
! 				break;
! 		}
! 	}
! 
! 	return(result);
  }
  
  /* ----------------------------------------------------------------
***************
*** 1829,1846 ****
  										   econtext,
  										   isNull,
  										   isDone);
! 			break;
! 		case T_ConstraintTest:
! 			retDatum = ExecEvalConstraintTest((ConstraintTestState *) expression,
! 											  econtext,
! 											  isNull,
! 											  isDone);
! 			break;
  		case T_ConstraintTestValue:
  			retDatum = ExecEvalConstraintTestValue((ConstraintTestValue *) expr,
  												   econtext,
  												   isNull);
  			break;
  		default:
  			elog(ERROR, "ExecEvalExpr: unknown expression type %d",
  				 nodeTag(expression));
--- 1986,2003 ----
  										   econtext,
  										   isNull,
  										   isDone);
! 			break; 
  		case T_ConstraintTestValue:
  			retDatum = ExecEvalConstraintTestValue((ConstraintTestValue *) expr,
  												   econtext,
  												   isNull);
  			break;
+ 		case T_CoerceToDomain:
+ 			retDatum = ExecEvalCoerceToDomain((CoerceToDomainState *) expression,
+ 											  econtext,
+ 											  isNull,
+ 											  isDone);
+ 			break;
  		default:
  			elog(ERROR, "ExecEvalExpr: unknown expression type %d",
  				 nodeTag(expression));
***************
*** 2092,2107 ****
  				state = (ExprState *) gstate;
  			}
  			break;
- 		case T_ConstraintTest:
- 			{
- 				ConstraintTest   *ctest = (ConstraintTest *) node;
- 				ConstraintTestState *cstate = makeNode(ConstraintTestState);
- 
- 				cstate->arg = ExecInitExpr(ctest->arg, parent);
- 				cstate->check_expr = ExecInitExpr(ctest->check_expr, parent);
- 				state = (ExprState *) cstate;
- 			}
- 			break;
  		case T_TargetEntry:
  			{
  				TargetEntry   *tle = (TargetEntry *) node;
--- 2249,2254 ----
***************
*** 2125,2130 ****
--- 2272,2287 ----
  				/* Don't fall through to the "common" code below */
  				return (ExprState *) outlist;
  			}
+ 		case T_CoerceToDomain:
+ 			{
+ 				CoerceToDomain *domainexpr = (CoerceToDomain *) node;
+ 				CoerceToDomainState *dstate = makeNode(CoerceToDomainState);
+ 
+ 				dstate->arg = ExecInitExpr((Expr *) domainexpr->arg, parent); 
+ 				dstate->constraints = NULL; /* not initialized */
+ 				state = (ExprState *) dstate;
+ 			}
+ 			break;
  		default:
  			elog(ERROR, "ExecInitExpr: unknown expression type %d",
  				 nodeTag(node));
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.236
diff -c -r1.236 copyfuncs.c
*** src/backend/nodes/copyfuncs.c	2003/01/15 19:35:35	1.236
--- src/backend/nodes/copyfuncs.c	2003/01/17 18:17:58
***************
*** 933,964 ****
  }
  
  /*
!  * _copyConstraintTest
   */
! static ConstraintTest *
! _copyConstraintTest(ConstraintTest *from)
  {
! 	ConstraintTest *newnode = makeNode(ConstraintTest);
  
! 	COPY_NODE_FIELD(arg);
! 	COPY_SCALAR_FIELD(testtype);
! 	COPY_STRING_FIELD(name);
! 	COPY_STRING_FIELD(domname);
! 	COPY_NODE_FIELD(check_expr);
  
  	return newnode;
  }
  
  /*
!  * _copyConstraintTestValue
   */
! static ConstraintTestValue *
! _copyConstraintTestValue(ConstraintTestValue *from)
  {
! 	ConstraintTestValue *newnode = makeNode(ConstraintTestValue);
  
! 	COPY_SCALAR_FIELD(typeId);
! 	COPY_SCALAR_FIELD(typeMod);
  
  	return newnode;
  }
--- 933,962 ----
  }
  
  /*
!  * _copyConstraintTestValue
   */
! static ConstraintTestValue *
! _copyConstraintTestValue(ConstraintTestValue *from)
  {
! 	ConstraintTestValue *newnode = makeNode(ConstraintTestValue);
  
! 	COPY_SCALAR_FIELD(typeId);
! 	COPY_SCALAR_FIELD(typeMod);
  
  	return newnode;
  }
  
  /*
!  * _copyCoerceToDomain
   */
! static CoerceToDomain *
! _copyCoerceToDomain(CoerceToDomain *from)
  {
! 	CoerceToDomain *newnode = makeNode(CoerceToDomain);
  
! 	COPY_NODE_FIELD(arg);
! 	COPY_SCALAR_FIELD(domainOid);
! 	COPY_SCALAR_FIELD(cformat);
  
  	return newnode;
  }
***************
*** 2459,2469 ****
  		case T_BooleanTest:
  			retval = _copyBooleanTest(from);
  			break;
- 		case T_ConstraintTest:
- 			retval = _copyConstraintTest(from);
- 			break;
  		case T_ConstraintTestValue:
  			retval = _copyConstraintTestValue(from);
  			break;
  		case T_TargetEntry:
  			retval = _copyTargetEntry(from);
--- 2457,2467 ----
  		case T_BooleanTest:
  			retval = _copyBooleanTest(from);
  			break;
  		case T_ConstraintTestValue:
  			retval = _copyConstraintTestValue(from);
+ 			break;
+ 		case T_CoerceToDomain:
+ 			retval = _copyCoerceToDomain(from);
  			break;
  		case T_TargetEntry:
  			retval = _copyTargetEntry(from);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.180
diff -c -r1.180 equalfuncs.c
*** src/backend/nodes/equalfuncs.c	2003/01/15 19:35:37	1.180
--- src/backend/nodes/equalfuncs.c	2003/01/17 18:18:23
***************
*** 383,404 ****
  }
  
  static bool
! _equalConstraintTest(ConstraintTest *a, ConstraintTest *b)
  {
! 	COMPARE_NODE_FIELD(arg);
! 	COMPARE_SCALAR_FIELD(testtype);
! 	COMPARE_STRING_FIELD(name);
! 	COMPARE_STRING_FIELD(domname);
! 	COMPARE_NODE_FIELD(check_expr);
  
  	return true;
  }
  
  static bool
! _equalConstraintTestValue(ConstraintTestValue *a, ConstraintTestValue *b)
  {
! 	COMPARE_SCALAR_FIELD(typeId);
! 	COMPARE_SCALAR_FIELD(typeMod);
  
  	return true;
  }
--- 383,409 ----
  }
  
  static bool
! _equalConstraintTestValue(ConstraintTestValue *a, ConstraintTestValue *b)
  {
! 	COMPARE_SCALAR_FIELD(typeId);
! 	COMPARE_SCALAR_FIELD(typeMod);
  
  	return true;
  }
  
  static bool
! _equalCoerceToDomain(CoerceToDomain *a, CoerceToDomain *b)
  {
! 	COMPARE_NODE_FIELD(arg);
! 	COMPARE_SCALAR_FIELD(domainOid);
! 	/*
! 	 * Special-case COERCE_DONTCARE, so that pathkeys can build coercion
! 	 * nodes that are equal() to both explicit and implicit coercions.
! 	 */
! 	if (a->cformat != b->cformat &&
! 		a->cformat != COERCE_DONTCARE &&
! 		b->cformat != COERCE_DONTCARE)
! 		return false;
  
  	return true;
  }
***************
*** 1587,1597 ****
  		case T_BooleanTest:
  			retval = _equalBooleanTest(a, b);
  			break;
- 		case T_ConstraintTest:
- 			retval = _equalConstraintTest(a, b);
- 			break;
  		case T_ConstraintTestValue:
  			retval = _equalConstraintTestValue(a, b);
  			break;
  		case T_TargetEntry:
  			retval = _equalTargetEntry(a, b);
--- 1592,1602 ----
  		case T_BooleanTest:
  			retval = _equalBooleanTest(a, b);
  			break;
  		case T_ConstraintTestValue:
  			retval = _equalConstraintTestValue(a, b);
+ 			break;
+ 		case T_CoerceToDomain:
+ 			retval = _equalCoerceToDomain(a, b);
  			break;
  		case T_TargetEntry:
  			retval = _equalTargetEntry(a, b);
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/nodes/outfuncs.c,v
retrieving revision 1.193
diff -c -r1.193 outfuncs.c
*** src/backend/nodes/outfuncs.c	2003/01/15 19:35:39	1.193
--- src/backend/nodes/outfuncs.c	2003/01/17 18:18:38
***************
*** 745,768 ****
  }
  
  static void
! _outConstraintTest(StringInfo str, ConstraintTest *node)
  {
! 	WRITE_NODE_TYPE("CONSTRAINTTEST");
  
! 	WRITE_NODE_FIELD(arg);
! 	WRITE_ENUM_FIELD(testtype, ConstraintTestType);
! 	WRITE_STRING_FIELD(name);
! 	WRITE_STRING_FIELD(domname);
! 	WRITE_NODE_FIELD(check_expr);
  }
  
  static void
! _outConstraintTestValue(StringInfo str, ConstraintTestValue *node)
  {
! 	WRITE_NODE_TYPE("CONSTRAINTTESTVALUE");
  
! 	WRITE_OID_FIELD(typeId);
! 	WRITE_INT_FIELD(typeMod);
  }
  
  static void
--- 745,766 ----
  }
  
  static void
! _outConstraintTestValue(StringInfo str, ConstraintTestValue *node)
  {
! 	WRITE_NODE_TYPE("CONSTRAINTTESTVALUE");
  
! 	WRITE_OID_FIELD(typeId);
! 	WRITE_INT_FIELD(typeMod);
  }
  
  static void
! _outCoerceToDomain(StringInfo str, CoerceToDomain *node)
  {
! 	WRITE_NODE_TYPE("COERCETODOMAIN");
  
! 	WRITE_NODE_FIELD(arg);
! 	WRITE_OID_FIELD(domainOid);
! 	WRITE_ENUM_FIELD(cformat, CoercionForm);
  }
  
  static void
***************
*** 1526,1536 ****
  			case T_BooleanTest:
  				_outBooleanTest(str, obj);
  				break;
- 			case T_ConstraintTest:
- 				_outConstraintTest(str, obj);
- 				break;
  			case T_ConstraintTestValue:
  				_outConstraintTestValue(str, obj);
  				break;
  			case T_TargetEntry:
  				_outTargetEntry(str, obj);
--- 1524,1534 ----
  			case T_BooleanTest:
  				_outBooleanTest(str, obj);
  				break;
  			case T_ConstraintTestValue:
  				_outConstraintTestValue(str, obj);
+ 				break;
+ 			case T_CoerceToDomain:
+ 				_outCoerceToDomain(str, obj);
  				break;
  			case T_TargetEntry:
  				_outTargetEntry(str, obj);
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/nodes/readfuncs.c,v
retrieving revision 1.146
diff -c -r1.146 readfuncs.c
*** src/backend/nodes/readfuncs.c	2003/01/10 21:08:11	1.146
--- src/backend/nodes/readfuncs.c	2003/01/17 18:18:52
***************
*** 635,657 ****
  }
  
  /*
-  * _readConstraintTest
-  */
- static ConstraintTest *
- _readConstraintTest(void)
- {
- 	READ_LOCALS(ConstraintTest);
- 
- 	READ_NODE_FIELD(arg);
- 	READ_ENUM_FIELD(testtype, ConstraintTestType);
- 	READ_STRING_FIELD(name);
- 	READ_STRING_FIELD(domname);
- 	READ_NODE_FIELD(check_expr);
- 
- 	READ_DONE();
- }
- 
- /*
   * _readConstraintTestValue
   */
  static ConstraintTestValue *
--- 635,640 ----
***************
*** 665,670 ****
--- 648,665 ----
  	READ_DONE();
  }
  
+ static CoerceToDomain *
+ _readCoerceToDomain(void)
+ {
+ 	READ_LOCALS(CoerceToDomain);
+ 
+ 	READ_NODE_FIELD(arg);
+ 	READ_OID_FIELD(domainOid);
+ 	READ_ENUM_FIELD(cformat, CoercionForm);
+ 
+ 	READ_DONE();
+ }
+ 
  /*
   * _readTargetEntry
   */
***************
*** 900,909 ****
  		return_value = _readNullTest();
  	else if (MATCH("BOOLEANTEST", 11))
  		return_value = _readBooleanTest();
- 	else if (MATCH("CONSTRAINTTEST", 14))
- 		return_value = _readConstraintTest();
  	else if (MATCH("CONSTRAINTTESTVALUE", 19))
  		return_value = _readConstraintTestValue();
  	else if (MATCH("TARGETENTRY", 11))
  		return_value = _readTargetEntry();
  	else if (MATCH("RANGETBLREF", 11))
--- 895,904 ----
  		return_value = _readNullTest();
  	else if (MATCH("BOOLEANTEST", 11))
  		return_value = _readBooleanTest();
  	else if (MATCH("CONSTRAINTTESTVALUE", 19))
  		return_value = _readConstraintTestValue();
+ 	else if (MATCH("COERCETODOMAIN", 14))
+ 		return_value = _readCoerceToDomain();
  	else if (MATCH("TARGETENTRY", 11))
  		return_value = _readTargetEntry();
  	else if (MATCH("RANGETBLREF", 11))
Index: src/backend/optimizer/prep/preptlist.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/prep/preptlist.c,v
retrieving revision 1.59
diff -c -r1.59 preptlist.c
*** src/backend/optimizer/prep/preptlist.c	2002/12/12 15:49:32	1.59
--- src/backend/optimizer/prep/preptlist.c	2003/01/17 18:19:00
***************
*** 179,193 ****
  			switch (command_type)
  			{
  				case CMD_INSERT:
! 					new_expr = (Node *) makeConst(atttype,
! 												  att_tup->attlen,
! 												  (Datum) 0,
! 												  true, /* isnull */
! 												  att_tup->attbyval);
! 					if (!att_tup->attisdropped)
! 						new_expr = coerce_type_constraints(new_expr,
! 														   atttype,
! 														   COERCE_IMPLICIT_CAST);
  					break;
  				case CMD_UPDATE:
  					/* Insert NULLs for dropped columns */
--- 179,199 ----
  			switch (command_type)
  			{
  				case CMD_INSERT:
! 					{
! 						Oid		baseTypeId = getBaseType(atttype);
! 
! 						new_expr = (Node *) makeConst(atttype,
! 													  att_tup->attlen,
! 													  (Datum) 0,
! 													  true, /* isnull */
! 													  att_tup->attbyval);
! 
! 						if (!att_tup->attisdropped && baseTypeId != atttype)
! 							new_expr = coerce_todomain(new_expr,
! 													   InvalidOid,
! 													   atttype,
! 													   COERCE_IMPLICIT_CAST);
! 					}
  					break;
  				case CMD_UPDATE:
  					/* Insert NULLs for dropped columns */
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.124
diff -c -r1.124 clauses.c
*** src/backend/optimizer/util/clauses.c	2003/01/17 03:25:03	1.124
--- src/backend/optimizer/util/clauses.c	2003/01/17 18:19:48
***************
*** 541,547 ****
  								  (void *) listptr);
  }
  
- 
  /*****************************************************************************
   *		Check clauses for mutable functions
   *****************************************************************************/
--- 541,546 ----
***************
*** 2148,2157 ****
  			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_TargetEntry:
  			return walker(((TargetEntry *) node)->expr, context);
  		case T_Query:
--- 2147,2154 ----
  			return walker(((NullTest *) node)->arg, context);
  		case T_BooleanTest:
  			return walker(((BooleanTest *) node)->arg, context);
! 		case T_CoerceToDomain:
! 			return walker(((CoerceToDomain *) node)->arg, context);
  		case T_TargetEntry:
  			return walker(((TargetEntry *) node)->expr, context);
  		case T_Query:
***************
*** 2527,2543 ****
  				return (Node *) newnode;
  			}
  			break;
! 		case T_ConstraintTest:
  			{
! 				ConstraintTest *ctest = (ConstraintTest *) node;
! 				ConstraintTest *newnode;
  
! 				FLATCOPY(newnode, ctest, ConstraintTest);
! 				MUTATE(newnode->arg, ctest->arg, Expr *);
! 				MUTATE(newnode->check_expr, ctest->check_expr, Expr *);
  				return (Node *) newnode;
  			}
- 			break;
  		case T_TargetEntry:
  			{
  				/*
--- 2524,2538 ----
  				return (Node *) newnode;
  			}
  			break;
! 		case T_CoerceToDomain:
  			{
! 				CoerceToDomain *cdomain = (CoerceToDomain *) node;
! 				CoerceToDomain *newnode;
  
! 				FLATCOPY(newnode, cdomain, CoerceToDomain);
! 				MUTATE(newnode->arg, cdomain->arg, Expr *);
  				return (Node *) newnode;
  			}
  		case T_TargetEntry:
  			{
  				/*
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.91
diff -c -r2.91 parse_coerce.c
*** src/backend/parser/parse_coerce.c	2002/12/12 20:35:13	2.91
--- src/backend/parser/parse_coerce.c	2003/01/17 18:20:11
***************
*** 14,25 ****
   */
  #include "postgres.h"
  
- #include "access/genam.h"
  #include "access/heapam.h"
- #include "catalog/catname.h"
- #include "catalog/indexing.h"
  #include "catalog/pg_cast.h"
- #include "catalog/pg_constraint.h"
  #include "catalog/pg_proc.h"
  #include "nodes/makefuncs.h"
  #include "optimizer/clauses.h"
--- 14,21 ----
***************
*** 40,46 ****
  static Node *build_func_call(Oid funcid, Oid rettype, List *args,
  							 CoercionForm fformat);
  
- 
  /*
   * coerce_to_target_type()
   *		Convert an expression to a target type and typmod.
--- 36,41 ----
***************
*** 187,194 ****
  		/* If target is a domain, apply constraints. */
  		if (targetTyptype == 'd')
  		{
! 			result = coerce_type_constraints(result, targetTypeId,
! 											 cformat);
  			/* We might now need a RelabelType. */
  			if (exprType(result) != targetTypeId)
  				result = (Node *) makeRelabelType((Expr *) result,
--- 182,189 ----
  		/* If target is a domain, apply constraints. */
  		if (targetTyptype == 'd')
  		{
! 			result = coerce_todomain(result, InvalidOid, targetTypeId, cformat);
! 
  			/* We might now need a RelabelType. */
  			if (exprType(result) != targetTypeId)
  				result = (Node *) makeRelabelType((Expr *) result,
***************
*** 208,213 ****
--- 203,210 ----
  	else if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
  								   &funcId))
  	{
+ 		Oid			baseTypeId = getBaseType(targetTypeId);
+ 
  		if (OidIsValid(funcId))
  		{
  			/*
***************
*** 216,234 ****
  			 * with a domain target type, the conversion function will
  			 * yield the base type.
  			 */
- 			Oid			baseTypeId = getBaseType(targetTypeId);
- 
  			result = build_func_call(funcId, baseTypeId, makeList1(node),
  									 cformat);
  
  			/*
! 			 * If domain, test against domain constraints and relabel with
  			 * domain type ID
  			 */
  			if (targetTypeId != baseTypeId)
  			{
! 				result = coerce_type_constraints(result, targetTypeId,
! 												 cformat);
  				result = (Node *) makeRelabelType((Expr *) result,
  												  targetTypeId, -1,
  												  cformat);
--- 213,229 ----
  			 * with a domain target type, the conversion function will
  			 * yield the base type.
  			 */
  			result = build_func_call(funcId, baseTypeId, makeList1(node),
  									 cformat);
  
  			/*
! 			 * If domain, coerce to the domain type and relabel with
  			 * domain type ID
  			 */
  			if (targetTypeId != baseTypeId)
  			{
! 				result = coerce_todomain(result, baseTypeId, targetTypeId, cformat);
! 
  				result = (Node *) makeRelabelType((Expr *) result,
  												  targetTypeId, -1,
  												  cformat);
***************
*** 250,268 ****
  		}
  		else
  		{
  			/*
  			 * We don't need to do a physical conversion, but we do need
  			 * to 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(node, targetTypeId,
! 											 cformat);
  
! 			/*
  			 * XXX could we label result with exprTypmod(node) instead of
  			 * default -1 typmod, to save a possible length-coercion
  			 * later? Would work if both types have same interpretation of
--- 245,265 ----
  		}
  		else
  		{
+ 			result = node;
+ 
  			/*
  			 * We don't need to do a physical conversion, but we do need
  			 * to attach a RelabelType node so that the expression will be
  			 * seen to have the intended type when inspected by
  			 * higher-level code.
  			 *
! 			 * If this is a domain, we need to process the domain constraints,
! 			 * which means a coercion.
  			 */
! 			if (targetTypeId != baseTypeId)
! 				result = coerce_todomain(result, baseTypeId, targetTypeId, cformat);
  
!   			/*
  			 * XXX could we label result with exprTypmod(node) instead of
  			 * default -1 typmod, to save a possible length-coercion
  			 * later? Would work if both types have same interpretation of
***************
*** 390,513 ****
  	return true;
  }
  
- 
  /*
!  * Create an expression tree to enforce the constraints (if any)
!  * that should be applied by the type.	Currently this is only
!  * interesting for domain types.
!  *
!  * NOTE: result tree is not guaranteed to show the correct exprType() for
!  * the domain; it may show the base type.  Caller must relabel if needed.
   */
  Node *
! coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
  {
! 	char	   *notNull = NULL;
! 	int32		typmod = -1;
! 
! 	for (;;)
! 	{
! 		HeapTuple	tup;
! 		HeapTuple	conTup;
! 		Form_pg_type typTup;
! 
! 		ScanKeyData key[1];
! 		int			nkeys = 0;
! 		SysScanDesc scan;
! 		Relation	conRel;
! 		
! 		tup = SearchSysCache(TYPEOID,
! 							 ObjectIdGetDatum(typeId),
! 							 0, 0, 0);
! 		if (!HeapTupleIsValid(tup))
! 			elog(ERROR, "coerce_type_constraints: failed to lookup type %u",
! 				 typeId);
! 		typTup = (Form_pg_type) GETSTRUCT(tup);
! 
! 		/* Test for NOT NULL Constraint */
! 		if (typTup->typnotnull && notNull == NULL)
! 			notNull = pstrdup(NameStr(typTup->typname));
! 
! 		/* Add CHECK Constraints to domains */
! 		conRel = heap_openr(ConstraintRelationName, RowShareLock);
! 
! 		ScanKeyEntryInitialize(&key[nkeys++], 0x0,
! 							   Anum_pg_constraint_contypid, F_OIDEQ,
! 							   ObjectIdGetDatum(typeId));
  
! 		scan = systable_beginscan(conRel, ConstraintTypidIndex, true,
! 								  SnapshotNow, nkeys, key);
! 
! 		while (HeapTupleIsValid(conTup = systable_getnext(scan)))
! 		{
! 			Datum	val;
! 			bool	isNull;
! 			ConstraintTest *r = makeNode(ConstraintTest);
! 			Form_pg_constraint	c = (Form_pg_constraint) GETSTRUCT(conTup);
! 
! 			/* Not expecting conbin to be NULL, but we'll test for it anyway */
! 			val = fastgetattr(conTup,
! 							  Anum_pg_constraint_conbin,
! 							  conRel->rd_att, &isNull);
! 
! 			if (isNull)
! 				elog(ERROR, "coerce_type_constraints: domain %s constraint %s has NULL conbin",
! 					 NameStr(typTup->typname), NameStr(c->conname));
! 
! 			r->arg = (Expr *) arg;
! 			r->testtype = CONSTR_TEST_CHECK;
! 			r->name = NameStr(c->conname);
! 			r->domname = NameStr(typTup->typname);
! 			r->check_expr =	stringToNode(DatumGetCString(DirectFunctionCall1(textout,
! 																			 val)));
! 
! 			arg = (Node *) r;
! 		}
  
! 		systable_endscan(scan);
! 		heap_close(conRel, RowShareLock);
! 
! 		if (typTup->typtype != 'd')
! 		{
! 			/* Not a domain, so done */
! 			ReleaseSysCache(tup);
! 			break;
! 		}
! 
! 		Assert(typmod < 0);
! 
! 		typeId = typTup->typbasetype;
! 		typmod = typTup->typtypmod;
! 		ReleaseSysCache(tup);
! 	}
! 
! 	/*
! 	 * If domain applies a typmod to its base type, do length coercion.
! 	 */
  	if (typmod >= 0)
- 		arg = coerce_type_typmod(arg, typeId, typmod, cformat);
- 
- 	/*
- 	 * Only need to add one NOT NULL check regardless of how many domains
- 	 * in the stack request it.  The topmost domain that requested it is
- 	 * used as the constraint name.
- 	 */
- 	if (notNull)
  	{
! 		ConstraintTest *r = makeNode(ConstraintTest);
  
! 		r->arg = (Expr *) arg;
! 		r->testtype = CONSTR_TEST_NOTNULL;
! 		r->name = "NOT NULL";
! 		r->domname = notNull;
! 		r->check_expr = NULL;
! 
! 		arg = (Node *) r;
  	}
  
! 	return arg;
! }
  
  
  /*
   * coerce_type_typmod()
--- 387,428 ----
  	return true;
  }
  
  /*
!  * Create an expression node to requestion coercion to the domain type.
!  * Domain constraints will be applied during execution.
   */
  Node *
! coerce_todomain(Node *arg, Oid baseTypeId, Oid domainOid, CoercionForm cformat)
  {
! 	int32	typmod;
! 	CoerceToDomain *node = makeNode(CoerceToDomain);
  
! 	/* Get the base type if it hasn't been supplied */
! 	if (baseTypeId == InvalidOid)
! 		baseTypeId = getBaseType(domainOid);
! 
! 	/* This isn't a domain -- return the node as it was passed in */
! 	if (baseTypeId == domainOid)
! 		return arg;
  
! 	/* Check the typmod of the domain */
! 	typmod = get_typtypmod(domainOid);
  	if (typmod >= 0)
  	{
! 		arg = coerce_type_typmod(arg, baseTypeId, typmod, cformat);
  
! 		arg = (Node *) makeRelabelType((Expr *) arg,
! 									   domainOid, -1,
! 									   cformat);
  	}
  
! 	/* Build the domain coercion node */
! 	node->arg = (Expr *) arg;
! 	node->domainOid = domainOid;
! 	node->cformat = cformat;
  
+ 	return (Node *) node;
+ }
  
  /*
   * coerce_type_typmod()
Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/parser/parse_expr.c,v
retrieving revision 1.141
diff -c -r1.141 parse_expr.c
*** src/backend/parser/parse_expr.c	2003/01/13 00:18:51	1.141
--- src/backend/parser/parse_expr.c	2003/01/17 18:20:22
***************
*** 674,681 ****
  		case T_ArrayRef:
  		case T_FieldSelect:
  		case T_RelabelType:
! 		case T_ConstraintTest:
! 		case T_ConstraintTestValue:
  			{
  				result = (Node *) expr;
  				break;
--- 674,680 ----
  		case T_ArrayRef:
  		case T_FieldSelect:
  		case T_RelabelType:
! 		case T_CoerceToDomain:
  			{
  				result = (Node *) expr;
  				break;
***************
*** 1017,1028 ****
  		case T_BooleanTest:
  			type = BOOLOID;
  			break;
- 		case T_ConstraintTest:
- 			type = exprType((Node *) ((ConstraintTest *) expr)->arg);
- 			break;
  		case T_ConstraintTestValue:
  			type = ((ConstraintTestValue *) expr)->typeId;
  			break;
  		case T_RangeVar:
  			/*
  			 * If someone uses a bare relation name in an expression,
--- 1016,1027 ----
  		case T_BooleanTest:
  			type = BOOLOID;
  			break;
  		case T_ConstraintTestValue:
  			type = ((ConstraintTestValue *) expr)->typeId;
  			break;
+ 		case T_CoerceToDomain:
+ 			type = ((CoerceToDomain *) expr)->domainOid;
+ 			break;
  		case T_RangeVar:
  			/*
  			 * If someone uses a bare relation name in an expression,
***************
*** 1117,1126 ****
  				return typmod;
  			}
  			break;
! 		case T_ConstraintTest:
! 			return exprTypmod((Node *) ((ConstraintTest *) expr)->arg);
! 		case T_ConstraintTestValue:
! 			return ((ConstraintTestValue *) expr)->typeMod;
  		default:
  			break;
  	}
--- 1116,1123 ----
  				return typmod;
  			}
  			break;
! 		case T_CoerceToDomain:
! 			return exprTypmod((Node *) ((CoerceToDomain *) expr)->arg);
  		default:
  			break;
  	}
Index: src/backend/utils/adt/ruleutils.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/ruleutils.c,v
retrieving revision 1.132
diff -c -r1.132 ruleutils.c
*** src/backend/utils/adt/ruleutils.c	2003/01/10 21:08:15	1.132
--- src/backend/utils/adt/ruleutils.c	2003/01/17 18:21:26
***************
*** 2249,2269 ****
  				}
  			}
  			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((Node *) ctest->arg, context, showimplicit);
! 			}
! 			break;
  
! 		case T_ConstraintTestValue:
! 			appendStringInfo(buf, "VALUE");
  			break;
  
  		default:
--- 2249,2269 ----
  				}
  			}
  			break;
! 		case T_ConstraintTestValue:
! 			appendStringInfo(buf, "VALUE");
! 			break;
! 		case T_CoerceToDomain:
  			{
! 				CoerceToDomain *cdomain = (CoerceToDomain *) node;
! 				Node   *arg = (Node *) cdomain->arg;
  
! 				arg = strip_type_coercion(arg, cdomain->domainOid);
  
! 				appendStringInfoChar(buf, '(');
! 				get_rule_expr(arg, context, showimplicit);
! 				appendStringInfo(buf, ")::%s",
! 						format_type_with_typemod(cdomain->domainOid, -1));
! 			}
  			break;
  
  		default:
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.89
diff -c -r1.89 lsyscache.c
*** src/backend/utils/cache/lsyscache.c	2003/01/15 19:35:44	1.89
--- src/backend/utils/cache/lsyscache.c	2003/01/17 18:21:39
***************
*** 891,896 ****
--- 891,922 ----
  }
  
  /*
+  * get_typtypmod
+  *
+  *		Given the type OID, return the mod of the type.
+  */
+ int32
+ get_typtypmod(Oid typid)
+ {
+ 	HeapTuple	tp;
+ 
+ 	tp = SearchSysCache(TYPEOID,
+ 						ObjectIdGetDatum(typid),
+ 						0, 0, 0);
+ 	if (HeapTupleIsValid(tp))
+ 	{
+ 		Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+ 		int32		result;
+ 
+ 		result = typtup->typtypmod;
+ 		ReleaseSysCache(tp);
+ 		return result;
+ 	}
+ 	else
+ 		return 0;
+ }
+ 
+ /*
   * get_typbyval
   *
   *		Given the type OID, determine whether the type is returned by value or
Index: src/include/nodes/execnodes.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/nodes/execnodes.h,v
retrieving revision 1.91
diff -c -r1.91 execnodes.h
*** src/include/nodes/execnodes.h	2003/01/12 04:03:34	1.91
--- src/include/nodes/execnodes.h	2003/01/17 18:22:18
***************
*** 537,552 ****
  } CaseWhenState;
  
  /* ----------------
!  *		ConstraintTestState node
   * ----------------
   */
! typedef struct ConstraintTestState
  {
  	ExprState	xprstate;
! 	ExprState  *arg;			/* input expression */
! 	ExprState  *check_expr;		/* for CHECK test, a boolean expression */
! } ConstraintTestState;
  
  
  /* ----------------------------------------------------------------
   *				 Executor State Trees
--- 537,555 ----
  } CaseWhenState;
  
  /* ----------------
!  *		DomainExprState node
   * ----------------
   */
! typedef struct CoerceToDomainState
  {
  	ExprState	xprstate;
! 	ExprState  *arg;			/* states of argument expressions */
  
+ 	/*
+ 	 * Cached constraint list that need to be applied for domain coercion
+ 	 */
+ 	List	   *constraints;
+ } CoerceToDomainState;
  
  /* ----------------------------------------------------------------
   *				 Executor State Trees
Index: src/include/nodes/nodes.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/nodes/nodes.h,v
retrieving revision 1.134
diff -c -r1.134 nodes.h
*** src/include/nodes/nodes.h	2002/12/16 16:22:46	1.134
--- src/include/nodes/nodes.h	2003/01/17 18:22:24
***************
*** 114,121 ****
  	T_CaseWhen,
  	T_NullTest,
  	T_BooleanTest,
- 	T_ConstraintTest,
  	T_ConstraintTestValue,
  	T_TargetEntry,
  	T_RangeTblRef,
  	T_JoinExpr,
--- 114,121 ----
  	T_CaseWhen,
  	T_NullTest,
  	T_BooleanTest,
  	T_ConstraintTestValue,
+ 	T_CoerceToDomain,
  	T_TargetEntry,
  	T_RangeTblRef,
  	T_JoinExpr,
***************
*** 137,142 ****
--- 137,143 ----
  	T_CaseExprState,
  	T_CaseWhenState,
  	T_ConstraintTestState,
+ 	T_CoerceToDomainState,
  
  	/*
  	 * TAGS FOR PLANNER NODES (relation.h)
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.77
diff -c -r1.77 primnodes.h
*** src/include/nodes/primnodes.h	2003/01/10 21:08:15	1.77
--- src/include/nodes/primnodes.h	2003/01/17 18:22:37
***************
*** 355,360 ****
--- 355,371 ----
  	List	   *args;			/* arguments to this expression */
  } BoolExpr;
  
+ /*
+  * CoerceToDomain
+  */
+ typedef struct CoerceToDomain
+ {
+ 	Expr	xpr;
+ 	Expr   *arg;				/* Expression to Coerce */
+ 	Oid		domainOid;			/* Destination type */
+ 	CoercionForm	cformat;	/* Coercion Format */
+ } CoerceToDomain;
+ 
  /* ----------------
   * SubLink
   *
***************
*** 578,607 ****
  } 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
- {
- 	Expr		xpr;
- 	Expr	   *arg;			/* input expression */
- 	ConstraintTestType testtype;	/* test type */
- 	char	   *name;			/* name of constraint (for error msgs) */
- 	char	   *domname; 		/* name of domain (for error messages) */
- 	Expr	   *check_expr;		/* for CHECK test, a boolean expression */
- } ConstraintTest;
- 
- /*
   * 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.
--- 589,594 ----
***************
*** 616,622 ****
  	Oid			typeId;			/* type for substituted value */
  	int32		typeMod;		/* typemod for substituted value */
  } ConstraintTestValue;
- 
  
  /*
   * TargetEntry -
--- 603,608 ----
Index: src/include/parser/parse_coerce.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/parser/parse_coerce.h,v
retrieving revision 1.48
diff -c -r1.48 parse_coerce.h
*** src/include/parser/parse_coerce.h	2002/10/24 22:09:00	1.48
--- src/include/parser/parse_coerce.h	2003/01/17 18:22:37
***************
*** 45,52 ****
  							CoercionContext ccontext);
  extern Node *coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
  						 CoercionContext ccontext, CoercionForm cformat);
! extern Node *coerce_type_constraints(Node *arg, Oid typeId,
! 									 CoercionForm cformat);
  
  extern Node *coerce_to_boolean(Node *node, const char *constructName);
  
--- 45,52 ----
  							CoercionContext ccontext);
  extern Node *coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
  						 CoercionContext ccontext, CoercionForm cformat);
! extern Node *coerce_todomain(Node *arg, Oid baseTypeId,
! 							 Oid domainOid, CoercionForm cformat);
  
  extern Node *coerce_to_boolean(Node *node, const char *constructName);
  
Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.66
diff -c -r1.66 lsyscache.h
*** src/include/utils/lsyscache.h	2003/01/15 19:35:47	1.66
--- src/include/utils/lsyscache.h	2003/01/17 18:22:43
***************
*** 49,54 ****
--- 49,55 ----
  extern char get_rel_relkind(Oid relid);
  extern bool get_typisdefined(Oid typid);
  extern int16 get_typlen(Oid typid);
+ extern int32 get_typtypmod(Oid typid);
  extern bool get_typbyval(Oid typid);
  extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
  extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
Index: src/test/regress/expected/domain.out
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/test/regress/expected/domain.out,v
retrieving revision 1.18
diff -c -r1.18 domain.out
*** src/test/regress/expected/domain.out	2003/01/04 00:46:08	1.18
--- src/test/regress/expected/domain.out	2003/01/17 18:23:11
***************
*** 265,270 ****
--- 265,305 ----
  insert into domcontest values (-5); --fails
  ERROR:  ExecEvalConstraintTest: Domain con constraint $1 failed
  insert into domcontest values (42);
+ -- Confirm ALTER DOMAIN with RULES.
+ create table domtab (col1 integer);
+ create domain dom as integer;
+ create view domview as select cast(col1 as dom) from domtab;
+ insert into domtab (col1) values (null);
+ insert into domtab (col1) values (5);
+ select * from domview;
+  col1 
+ ------
+  
+  5
+ (2 rows)
+ 
+ alter domain dom set not null;
+ select * from domview; -- fail
+ ERROR:  Domain dom does not allow NULL values
+ alter domain dom drop not null;
+ select * from domview;
+  col1 
+ ------
+  
+  5
+ (2 rows)
+ 
+ alter domain dom add constraint domchkgt6 check(value > 6);
+ select * from domview; --fail
+ ERROR:  ExecEvalConstraintTest: Domain dom constraint domchkgt6 failed
+ alter domain dom drop constraint domchkgt6 restrict;
+ select * from domview;
+  col1 
+ ------
+  
+  5
+ (2 rows)
+ 
  -- cleanup
  drop domain ddef1 restrict;
  drop domain ddef2 restrict;
Index: src/test/regress/sql/domain.sql
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/test/regress/sql/domain.sql,v
retrieving revision 1.13
diff -c -r1.13 domain.sql
*** src/test/regress/sql/domain.sql	2002/12/06 05:00:34	1.13
--- src/test/regress/sql/domain.sql	2003/01/17 18:23:26
***************
*** 224,229 ****
--- 224,249 ----
  insert into domcontest values (-5); --fails
  insert into domcontest values (42);
  
+ -- Confirm ALTER DOMAIN with RULES.
+ create table domtab (col1 integer);
+ create domain dom as integer;
+ create view domview as select cast(col1 as dom) from domtab;
+ insert into domtab (col1) values (null);
+ insert into domtab (col1) values (5);
+ select * from domview;
+ 
+ alter domain dom set not null;
+ select * from domview; -- fail
+ 
+ alter domain dom drop not null;
+ select * from domview;
+ 
+ alter domain dom add constraint domchkgt6 check(value > 6);
+ select * from domview; --fail
+ 
+ alter domain dom drop constraint domchkgt6 restrict;
+ select * from domview;
+ 
  -- cleanup
  drop domain ddef1 restrict;
  drop domain ddef2 restrict;
