diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml
index 4beb0ae53b8393ce7066f37ea687faa7aa0edd84..bfed9027399ea1c99b2fbc6a855adf7db02c219e 100644
--- a/doc/src/sgml/ref/create_domain.sgml
+++ b/doc/src/sgml/ref/create_domain.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_domain.sgml,v 1.27 2005/12/25 01:41:15 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_domain.sgml,v 1.28 2006/04/05 22:11:54 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -35,8 +35,10 @@ where <replaceable class="PARAMETER">constraint</replaceable> is:
   <title>Description</title>
 
   <para>
-   <command>CREATE DOMAIN</command> creates a new data domain.  The
-   user who defines a domain becomes its owner.
+   <command>CREATE DOMAIN</command> creates a new domain.  A domain is
+   essentially a data type with optional constraints (restrictions on
+   the allowed set of values).
+   The user who defines a domain becomes its owner.
   </para>
 
   <para>
@@ -48,24 +50,13 @@ where <replaceable class="PARAMETER">constraint</replaceable> is:
   </para>
 
   <para>
-   Domains are useful for abstracting common fields between tables
-   into a single location for maintenance.  For example, an email address
-   column may be used in several tables, all with the same properties.
-   Define a domain and use that rather than setting up each table's
-   constraints individually. 
+   Domains are useful for abstracting common constraints on fields into
+   a single location for maintenance.  For example, several tables might
+   contain email address columns, all requiring the same CHECK constraint
+   to verify the address syntax.
+   Define a domain rather than setting up each table's constraint
+   individually.
   </para>
-
-  <caution>
-  <para>
-   At present, declaring a function result value as a domain 
-   is pretty dangerous, because none of the procedural languages enforce domain constraints 
-   on their results.  You'll need to make sure that the function code itself
-   respects the constraints.  In <application>PL/pgSQL</>, one possible
-   workaround is to explicitly cast the result value to the domain type
-   when you return it.  <application>PL/pgSQL</> does not enforce domain
-   constraints for local variables within functions, either.
-  </para>
-  </caution>
  </refsect1>
 
  <refsect1>
@@ -156,7 +147,7 @@ where <replaceable class="PARAMETER">constraint</replaceable> is:
       <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</>
+      producing a Boolean result.  It should use the key word <literal>VALUE</>
       to refer to the value being tested.
      </para>
 
@@ -185,12 +176,12 @@ OR VALUE ~ '^\\d{5}-\\d{4}$'
 );
 
 CREATE TABLE us_snail_addy (
-  address_id SERIAL PRIMARY KEY
-, street1 TEXT NOT NULL
-, street2 TEXT
-, street3 TEXT
-, city TEXT NOT NULL
-, postal us_postal_code NOT NULL
+  address_id SERIAL PRIMARY KEY,
+  street1 TEXT NOT NULL,
+  street2 TEXT,
+  street3 TEXT,
+  city TEXT NOT NULL,
+  postal us_postal_code NOT NULL
 );
 </programlisting>
   </para>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index e3b8b44d8f9ddf5a784fd7815aaff5b0fc7fabab..68ec242ae5a2cb69d31df61a397a7c1e1088ca90 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.62 2006/04/04 19:35:32 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.63 2006/04/05 22:11:54 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -591,6 +591,7 @@ CREATE TABLE big_objs (
    <member><xref linkend="sql-createfunction" endterm="sql-createfunction-title"></member>
    <member><xref linkend="sql-droptype" endterm="sql-droptype-title"></member>
    <member><xref linkend="sql-altertype" endterm="sql-altertype-title"></member>
+   <member><xref linkend="sql-createdomain" endterm="sql-createdomain-title"></member>
   </simplelist>
  </refsect1>
 
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index ba5793b0e7eb3581c39032b5defb8ece5081bb0a..7eb46bb6ec54132428a8bc4059edf4eaf4e33911 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.95 2006/04/04 19:35:33 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.96 2006/04/05 22:11:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -177,7 +177,6 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
 	{
 		Oid			atttypid = attrs[i]->atttypid;
 		int32		atttypmod = attrs[i]->atttypmod;
-		Oid			basetype;
 
 		pq_sendstring(&buf, NameStr(attrs[i]->attname));
 		/* column ID info appears in protocol 3.0 and up */
@@ -203,12 +202,7 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
 			}
 		}
 		/* If column is a domain, send the base type and typmod instead */
-		basetype = getBaseType(atttypid);
-		if (basetype != atttypid)
-		{
-			atttypmod = get_typtypmod(atttypid);
-			atttypid = basetype;
-		}
+		atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
 		pq_sendint(&buf, (int) atttypid, sizeof(atttypid));
 		pq_sendint(&buf, attrs[i]->attlen, sizeof(attrs[i]->attlen));
 		/* typmod appears in protocol 2.0 and up */
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index af21d565f1781bbc392c9234324ca58ad6588a30..2b49094bea014f375df8fcf0cf6bb68c6ed065a9 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.262 2006/04/04 19:35:33 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.263 2006/04/05 22:11:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1545,9 +1545,7 @@ CopyFrom(CopyState cstate)
 	FmgrInfo	oid_in_function;
 	Oid		   *typioparams;
 	Oid			oid_typioparam;
-	ExprState **constraintexprs;
 	bool	   *force_notnull;
-	bool		hasConstraints = false;
 	int			attnum;
 	int			i;
 	Oid			in_func_oid;
@@ -1608,7 +1606,6 @@ CopyFrom(CopyState cstate)
 	typioparams = (Oid *) palloc(num_phys_attrs * sizeof(Oid));
 	defmap = (int *) palloc(num_phys_attrs * sizeof(int));
 	defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *));
-	constraintexprs = (ExprState **) palloc0(num_phys_attrs * sizeof(ExprState *));
 	force_notnull = (bool *) palloc(num_phys_attrs * sizeof(bool));
 
 	for (attnum = 1; attnum <= num_phys_attrs; attnum++)
@@ -1646,35 +1643,6 @@ CopyFrom(CopyState cstate)
 				num_defaults++;
 			}
 		}
-
-		/* If it's a domain type, set up to check domain constraints */
-		if (get_typtype(attr[attnum - 1]->atttypid) == 'd')
-		{
-			Param	   *prm;
-			Node	   *node;
-
-			/*
-			 * Easiest way to do this is to use parse_coerce.c to set up an
-			 * expression that checks the constraints.	(At present, the
-			 * expression might contain a length-coercion-function call and/or
-			 * CoerceToDomain nodes.)  The bottom of the expression is a Param
-			 * node so that we can fill in the actual datum during the data
-			 * input loop.
-			 */
-			prm = makeNode(Param);
-			prm->paramkind = PARAM_EXEC;
-			prm->paramid = 0;
-			prm->paramtype = getBaseType(attr[attnum - 1]->atttypid);
-
-			node = coerce_to_domain((Node *) prm,
-									prm->paramtype,
-									attr[attnum - 1]->atttypid,
-									COERCE_IMPLICIT_CAST, false, false);
-
-			constraintexprs[attnum - 1] = ExecPrepareExpr((Expr *) node,
-														  estate);
-			hasConstraints = true;
-		}
 	}
 
 	/* Prepare to catch AFTER triggers. */
@@ -1743,11 +1711,6 @@ CopyFrom(CopyState cstate)
 	nfields = file_has_oids ? (attr_count + 1) : attr_count;
 	field_strings = (char **) palloc(nfields * sizeof(char *));
 
-	/* Make room for a PARAM_EXEC value for domain constraint checks */
-	if (hasConstraints)
-		econtext->ecxt_param_exec_vals = (ParamExecData *)
-			palloc0(sizeof(ParamExecData));
-
 	/* Initialize state variables */
 	cstate->fe_eof = false;
 	cstate->eol_type = EOL_UNKNOWN;
@@ -1942,33 +1905,6 @@ CopyFrom(CopyState cstate)
 				nulls[defmap[i]] = ' ';
 		}
 
-		/* Next apply any domain constraints */
-		if (hasConstraints)
-		{
-			ParamExecData *prmdata = &econtext->ecxt_param_exec_vals[0];
-
-			for (i = 0; i < num_phys_attrs; i++)
-			{
-				ExprState  *exprstate = constraintexprs[i];
-
-				if (exprstate == NULL)
-					continue;	/* no constraint for this attr */
-
-				/* Insert current row's value into the Param value */
-				prmdata->value = values[i];
-				prmdata->isnull = (nulls[i] == 'n');
-
-				/*
-				 * Execute the constraint expression.  Allow the expression to
-				 * replace the value (consider e.g. a timestamp precision
-				 * restriction).
-				 */
-				values[i] = ExecEvalExpr(exprstate, econtext,
-										 &isnull, NULL);
-				nulls[i] = isnull ? 'n' : ' ';
-			}
-		}
-
 		/* And now we can form the input tuple. */
 		tuple = heap_formtuple(tupDesc, values, nulls);
 
@@ -2043,7 +1979,6 @@ CopyFrom(CopyState cstate)
 	pfree(typioparams);
 	pfree(defmap);
 	pfree(defexprs);
-	pfree(constraintexprs);
 	pfree(force_notnull);
 
 	ExecDropSingleTupleTableSlot(slot);
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 83143496dbccbc0927595006be1e11dbb778d08f..21546d1c8b08c02eb5192914471591aef4d991de 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.89 2006/03/14 22:48:18 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.90 2006/04/05 22:11:55 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -536,6 +536,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	Oid			sendProcedure;
 	Oid			analyzeProcedure;
 	bool		byValue;
+	Oid			typelem;
 	char		delimiter;
 	char		alignment;
 	char		storage;
@@ -547,7 +548,6 @@ DefineDomain(CreateDomainStmt *stmt)
 	char	   *defaultValueBin = NULL;
 	bool		typNotNull = false;
 	bool		nullDefined = false;
-	Oid			basetypelem;
 	int32		typNDims = list_length(stmt->typename->arrayBounds);
 	HeapTuple	typeTup;
 	List	   *schema = stmt->constraints;
@@ -589,12 +589,12 @@ DefineDomain(CreateDomainStmt *stmt)
 	basetypeoid = HeapTupleGetOid(typeTup);
 
 	/*
-	 * Base type must be a plain base type.  Domains over pseudo types would
-	 * create a security hole.	Domains of domains might be made to work in
-	 * the future, but not today.  Ditto for domains over complex types.
+	 * Base type must be a plain base type or another domain.  Domains over
+	 * pseudotypes would create a security hole.  Domains over composite
+	 * types might be made to work in the future, but not today.
 	 */
 	typtype = baseType->typtype;
-	if (typtype != 'b')
+	if (typtype != 'b' && typtype != 'd')
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("\"%s\" is not a valid base type for a domain",
@@ -612,13 +612,16 @@ DefineDomain(CreateDomainStmt *stmt)
 	/* Storage Length */
 	internalLength = baseType->typlen;
 
+	/* Array element type (in case base type is an array) */
+	typelem = baseType->typelem;
+
 	/* Array element Delimiter */
 	delimiter = baseType->typdelim;
 
 	/* I/O Functions */
-	inputProcedure = baseType->typinput;
+	inputProcedure = F_DOMAIN_IN;
 	outputProcedure = baseType->typoutput;
-	receiveProcedure = baseType->typreceive;
+	receiveProcedure = F_DOMAIN_RECV;
 	sendProcedure = baseType->typsend;
 
 	/* Analysis function */
@@ -636,13 +639,6 @@ DefineDomain(CreateDomainStmt *stmt)
 	if (!isnull)
 		defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
 
-	/*
-	 * Pull out the typelem name of the parent OID.
-	 *
-	 * This is what enables us to make a domain of an array
-	 */
-	basetypelem = baseType->typelem;
-
 	/*
 	 * Run through constraints manually to avoid the additional processing
 	 * conducted by DefineRelation() and friends.
@@ -776,7 +772,7 @@ DefineDomain(CreateDomainStmt *stmt)
 				   receiveProcedure,	/* receive procedure */
 				   sendProcedure,		/* send procedure */
 				   analyzeProcedure,	/* analyze procedure */
-				   basetypelem, /* element type ID */
+				   typelem,		/* element type ID */
 				   basetypeoid, /* base type ID */
 				   defaultValue,	/* default type value (text) */
 				   defaultValueBin,		/* default type value (binary) */
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 436d6fdce0651dab28caffa6a5c1e55e862e22b4..5a37b86eb5bcb6f6f54c3edc6bc100411f1da1b2 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
- *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.80 2006/03/05 15:58:31 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.81 2006/04/05 22:11:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -249,7 +249,7 @@ expand_targetlist(List *tlist, int command_type,
 													  true,		/* isnull */
 													  att_tup->attbyval);
 						new_expr = coerce_to_domain(new_expr,
-													InvalidOid,
+													InvalidOid, -1,
 													atttype,
 													COERCE_IMPLICIT_CAST,
 													false,
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 5a343e768dda1a9492fb68f45a4c259e1ae88925..98393a54aa11d64564bbc0822a214c35d8e470a2 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.136 2006/04/04 19:35:34 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.137 2006/04/05 22:11:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -158,10 +158,24 @@ coerce_type(ParseState *pstate, Node *node,
 		 */
 		Const	   *con = (Const *) node;
 		Const	   *newcon = makeNode(Const);
-		Type		targetType = typeidType(targetTypeId);
-		char		targetTyptype = typeTypType(targetType);
+		Oid			baseTypeId;
+		int32		baseTypeMod;
+		Type		targetType;
 
-		newcon->consttype = targetTypeId;
+		/*
+		 * If the target type is a domain, we want to call its base type's
+		 * input routine, not domain_in().  This is to avoid premature
+		 * failure when the domain applies a typmod: existing input
+		 * routines follow implicit-coercion semantics for length checks,
+		 * which is not always what we want here.  The needed check will
+		 * be applied properly inside coerce_to_domain().
+		 */
+		baseTypeMod = -1;
+		baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod);
+
+		targetType = typeidType(baseTypeId);
+
+		newcon->consttype = baseTypeId;
 		newcon->constlen = typeLen(targetType);
 		newcon->constbyval = typeByVal(targetType);
 		newcon->constisnull = con->constisnull;
@@ -185,8 +199,10 @@ coerce_type(ParseState *pstate, Node *node,
 		result = (Node *) newcon;
 
 		/* If target is a domain, apply constraints. */
-		if (targetTyptype == 'd')
-			result = coerce_to_domain(result, InvalidOid, targetTypeId,
+		if (baseTypeId != targetTypeId)
+			result = coerce_to_domain(result,
+									  baseTypeId, baseTypeMod,
+									  targetTypeId,
 									  cformat, false, false);
 
 		ReleaseSysCache(targetType);
@@ -239,9 +255,7 @@ coerce_type(ParseState *pstate, Node *node,
 
 		param->paramtype = targetTypeId;
 
-		/* Apply domain constraints, if necessary */
-		return coerce_to_domain((Node *) param, InvalidOid, targetTypeId,
-								cformat, false, false);
+		return (Node *) param;
 	}
 	if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
 							  &funcId))
@@ -255,13 +269,11 @@ coerce_type(ParseState *pstate, Node *node,
 			 * and we need to extract the correct typmod to use from the
 			 * domain's typtypmod.
 			 */
-			Oid			baseTypeId = getBaseType(targetTypeId);
+			Oid			baseTypeId;
 			int32		baseTypeMod;
 
-			if (targetTypeId != baseTypeId)
-				baseTypeMod = get_typtypmod(targetTypeId);
-			else
-				baseTypeMod = targetTypeMod;
+			baseTypeMod = targetTypeMod;
+			baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod);
 
 			result = build_coercion_expression(node, funcId,
 											   baseTypeId, baseTypeMod,
@@ -274,7 +286,8 @@ coerce_type(ParseState *pstate, Node *node,
 			 * selected coercion function was a type-and-length coercion.
 			 */
 			if (targetTypeId != baseTypeId)
-				result = coerce_to_domain(result, baseTypeId, targetTypeId,
+				result = coerce_to_domain(result, baseTypeId, baseTypeMod,
+										  targetTypeId,
 										  cformat, true,
 										  exprIsLengthCoercion(result,
 															   NULL));
@@ -290,7 +303,7 @@ coerce_type(ParseState *pstate, Node *node,
 			 * that must be accounted for.	If the destination is a domain
 			 * then we won't need a RelabelType node.
 			 */
-			result = coerce_to_domain(node, InvalidOid, targetTypeId,
+			result = coerce_to_domain(node, InvalidOid, -1, targetTypeId,
 									  cformat, false, false);
 			if (result == node)
 			{
@@ -439,15 +452,18 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
  * 'arg': input expression
  * 'baseTypeId': base type of domain, if known (pass InvalidOid if caller
  *		has not bothered to look this up)
+ * 'baseTypeMod': base type typmod of domain, if known (pass -1 if caller
+ *		has not bothered to look this up)
  * 'typeId': target type to coerce to
  * 'cformat': coercion format
  * 'hideInputCoercion': if true, hide the input coercion under this one.
- * 'lengthCoercionDone': if true, caller already accounted for length.
+ * 'lengthCoercionDone': if true, caller already accounted for length,
+ *		ie the input is already of baseTypMod as well as baseTypeId.
  *
  * If the target type isn't a domain, the given 'arg' is returned as-is.
  */
 Node *
-coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId,
+coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
 				 CoercionForm cformat, bool hideInputCoercion,
 				 bool lengthCoercionDone)
 {
@@ -455,7 +471,7 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId,
 
 	/* Get the base type if it hasn't been supplied */
 	if (baseTypeId == InvalidOid)
-		baseTypeId = getBaseType(typeId);
+		baseTypeId = getBaseTypeAndTypmod(typeId, &baseTypeMod);
 
 	/* If it isn't a domain, return the node as it was passed in */
 	if (baseTypeId == typeId)
@@ -480,10 +496,8 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId,
 	 */
 	if (!lengthCoercionDone)
 	{
-		int32		typmod = get_typtypmod(typeId);
-
-		if (typmod >= 0)
-			arg = coerce_type_typmod(arg, baseTypeId, typmod,
+		if (baseTypeMod >= 0)
+			arg = coerce_type_typmod(arg, baseTypeId, baseTypeMod,
 									 COERCE_IMPLICIT_CAST,
 									 (cformat != COERCE_IMPLICIT_CAST),
 									 false);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index acebefca3f0f278bed804dd4fe61a1746201b0d2..6d1ace66f1afe07b666f24c5dee3c886e742882d 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.161 2006/03/05 15:58:36 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.162 2006/04/05 22:11:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -599,7 +599,7 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
 												  att_tup->attbyval);
 					/* this is to catch a NOT NULL domain constraint */
 					new_expr = coerce_to_domain(new_expr,
-												InvalidOid,
+												InvalidOid, -1,
 												att_tup->atttypid,
 												COERCE_IMPLICIT_CAST,
 												false,
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index d7cd8de6774e5d19e920742a42db134d8e0ae1ad..175df7a695a1ffda9e048410a655b016b760a4d9 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.97 2006/03/05 15:58:36 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.98 2006/04/05 22:11:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -860,7 +860,7 @@ resolve_one_var(Var *var, ResolveNew_context *context)
 			/* Otherwise replace unmatched var with a null */
 			/* need coerce_to_domain in case of NOT NULL domain constraint */
 			return coerce_to_domain((Node *) makeNullConst(var->vartype),
-									InvalidOid,
+									InvalidOid, -1,
 									var->vartype,
 									COERCE_IMPLICIT_CAST,
 									false,
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 294fd9de49b757e745bfb4343bc14982b0d931ab..5a1996c3439461cece6b6f06df8c21c85a5c9c38 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -1,7 +1,7 @@
 #
 # Makefile for utils/adt
 #
-# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.59 2005/08/12 03:24:08 momjian Exp $
+# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.60 2006/04/05 22:11:55 tgl Exp $
 #
 
 subdir = src/backend/utils/adt
@@ -16,7 +16,8 @@ endif
 endif
 
 OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
-	cash.o char.o date.o datetime.o datum.o float.o format_type.o \
+	cash.o char.o date.o datetime.o datum.o domains.o \
+	float.o format_type.o \
 	geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
 	misc.o nabstime.o name.o not_in.o numeric.o numutils.o \
 	oid.o oracle_compat.o pseudotypes.o rowtypes.o \
diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c
new file mode 100644
index 0000000000000000000000000000000000000000..051145f237643b43d82a51607e76ba8147aaa404
--- /dev/null
+++ b/src/backend/utils/adt/domains.c
@@ -0,0 +1,297 @@
+/*-------------------------------------------------------------------------
+ *
+ * domains.c
+ *	  I/O functions for domain types.
+ *
+ * The output functions for a domain type are just the same ones provided
+ * by its underlying base type.  The input functions, however, must be
+ * prepared to apply any constraints defined by the type.  So, we create
+ * special input functions that invoke the base type's input function
+ * and then check the constraints.
+ *
+ * The overhead required for constraint checking can be high, since examining
+ * the catalogs to discover the constraints for a given domain is not cheap.
+ * We have two mechanisms for minimizing this cost:
+ *	1.	In a nest of domains, we flatten the checking of all the levels
+ *		into just one operation.
+ *	2.	We cache the list of constraint items in the FmgrInfo struct
+ *		passed by the caller.
+ *
+ * We also have to create an EState to evaluate CHECK expressions in.
+ * Creating and destroying an EState is somewhat expensive, and so it's
+ * tempting to cache the EState too.  However, that would mean that the
+ * EState never gets an explicit FreeExecutorState call, which is a bad idea
+ * because it risks leaking non-memory resources.
+ *
+ *
+ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/domains.c,v 1.1 2006/04/05 22:11:55 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "commands/typecmds.h"
+#include "executor/executor.h"
+#include "lib/stringinfo.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+
+
+/*
+ * structure to cache state across multiple calls
+ */
+typedef struct DomainIOData
+{
+	Oid			domain_type;
+	/* Data needed to call base type's input function */
+	Oid			typiofunc;
+	Oid			typioparam;
+	int32		typtypmod;
+	FmgrInfo	proc;
+	/* List of constraint items to check */
+	List	   *constraint_list;
+} DomainIOData;
+
+
+/*
+ * domain_state_setup - initialize the cache for a new domain type.
+ */
+static void
+domain_state_setup(DomainIOData *my_extra, Oid domainType, bool binary,
+				   MemoryContext mcxt)
+{
+	Oid			baseType;
+	MemoryContext oldcontext;
+
+	/* Mark cache invalid */
+	my_extra->domain_type = InvalidOid;
+
+	/* Find out the base type */
+	my_extra->typtypmod = -1;
+	baseType = getBaseTypeAndTypmod(domainType, &my_extra->typtypmod);
+	if (baseType == domainType)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("type %s is not a domain",
+						format_type_be(domainType))));
+
+	/* Look up underlying I/O function */
+	if (binary)
+		getTypeBinaryInputInfo(baseType,
+							   &my_extra->typiofunc,
+							   &my_extra->typioparam);
+	else
+		getTypeInputInfo(baseType,
+						 &my_extra->typiofunc,
+						 &my_extra->typioparam);
+	fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, mcxt);
+
+	/* Look up constraints for domain */
+	oldcontext = MemoryContextSwitchTo(mcxt);
+	my_extra->constraint_list = GetDomainConstraints(domainType);
+	MemoryContextSwitchTo(oldcontext);
+
+	/* Mark cache valid */
+	my_extra->domain_type = domainType;
+}
+
+/*
+ * domain_check_input - apply the cached checks.
+ *
+ * This is extremely similar to ExecEvalCoerceToDomain in execQual.c.
+ */
+static void
+domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
+{
+	EState	   *estate = NULL;
+	ListCell   *l;
+
+	foreach(l, my_extra->constraint_list)
+	{
+		DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+
+		switch (con->constrainttype)
+		{
+			case DOM_CONSTRAINT_NOTNULL:
+				if (isnull)
+					ereport(ERROR,
+							(errcode(ERRCODE_NOT_NULL_VIOLATION),
+							 errmsg("domain %s does not allow null values",
+									format_type_be(my_extra->domain_type))));
+				break;
+			case DOM_CONSTRAINT_CHECK:
+				{
+					ExprContext *econtext;
+					Datum		conResult;
+					bool		conIsNull;
+					Datum		save_datum;
+					bool		save_isNull;
+
+					if (estate == NULL)
+						estate = CreateExecutorState();
+					econtext = GetPerTupleExprContext(estate);
+
+					/*
+					 * Set up value to be returned by CoerceToDomainValue
+					 * nodes. We must save and restore prior setting of
+					 * econtext's domainValue fields, in case this node is
+					 * itself within a check expression for another domain.
+					 */
+					save_datum = econtext->domainValue_datum;
+					save_isNull = econtext->domainValue_isNull;
+
+					econtext->domainValue_datum = value;
+					econtext->domainValue_isNull = isnull;
+
+					conResult = ExecEvalExprSwitchContext(con->check_expr,
+														  econtext,
+														  &conIsNull, NULL);
+
+					if (!conIsNull &&
+						!DatumGetBool(conResult))
+						ereport(ERROR,
+								(errcode(ERRCODE_CHECK_VIOLATION),
+								 errmsg("value for domain %s violates check constraint \"%s\"",
+										format_type_be(my_extra->domain_type),
+										con->name)));
+					econtext->domainValue_datum = save_datum;
+					econtext->domainValue_isNull = save_isNull;
+
+					break;
+				}
+			default:
+				elog(ERROR, "unrecognized constraint type: %d",
+					 (int) con->constrainttype);
+				break;
+		}
+	}
+
+	if (estate)
+		FreeExecutorState(estate);
+}
+
+
+/*
+ * domain_in		- input routine for any domain type.
+ */
+Datum
+domain_in(PG_FUNCTION_ARGS)
+{
+	char	   *string;
+	Oid			domainType;
+	DomainIOData *my_extra;
+	Datum		value;
+
+	/*
+	 * Since domain_in is not strict, we have to check for null inputs.
+	 * The typioparam argument should never be null in normal system usage,
+	 * but it could be null in a manual invocation --- if so, just return null.
+	 */
+	if (PG_ARGISNULL(0))
+		string = NULL;
+	else
+		string = PG_GETARG_CSTRING(0);
+	if (PG_ARGISNULL(1))
+		PG_RETURN_NULL();
+	domainType = PG_GETARG_OID(1);
+
+	/*
+	 * We arrange to look up the needed info just once per series of
+	 * calls, assuming the domain type doesn't change underneath us.
+	 */
+	my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
+	if (my_extra == NULL)
+	{
+		my_extra = (DomainIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													   sizeof(DomainIOData));
+		domain_state_setup(my_extra, domainType, false,
+						   fcinfo->flinfo->fn_mcxt);
+		fcinfo->flinfo->fn_extra = (void *) my_extra;
+	}
+	else if (my_extra->domain_type != domainType)
+		domain_state_setup(my_extra, domainType, false,
+						   fcinfo->flinfo->fn_mcxt);
+
+	/*
+	 * Invoke the base type's typinput procedure to convert the data.
+	 */
+	value = InputFunctionCall(&my_extra->proc,
+							  string,
+							  my_extra->typioparam,
+							  my_extra->typtypmod);
+
+	/*
+	 * Do the necessary checks to ensure it's a valid domain value.
+	 */
+	domain_check_input(value, (string == NULL), my_extra);
+
+	if (string == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_DATUM(value);
+}
+
+/*
+ * domain_recv		- binary input routine for any domain type.
+ */
+Datum
+domain_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf;
+	Oid			domainType;
+	DomainIOData *my_extra;
+	Datum		value;
+
+	/*
+	 * Since domain_recv is not strict, we have to check for null inputs.
+	 * The typioparam argument should never be null in normal system usage,
+	 * but it could be null in a manual invocation --- if so, just return null.
+	 */
+	if (PG_ARGISNULL(0))
+		buf = NULL;
+	else
+		buf = (StringInfo) PG_GETARG_POINTER(0);
+	if (PG_ARGISNULL(1))
+		PG_RETURN_NULL();
+	domainType = PG_GETARG_OID(1);
+
+	/*
+	 * We arrange to look up the needed info just once per series of
+	 * calls, assuming the domain type doesn't change underneath us.
+	 */
+	my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
+	if (my_extra == NULL)
+	{
+		my_extra = (DomainIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													   sizeof(DomainIOData));
+		domain_state_setup(my_extra, domainType, true,
+						   fcinfo->flinfo->fn_mcxt);
+		fcinfo->flinfo->fn_extra = (void *) my_extra;
+	}
+	else if (my_extra->domain_type != domainType)
+		domain_state_setup(my_extra, domainType, true,
+						   fcinfo->flinfo->fn_mcxt);
+
+	/*
+	 * Invoke the base type's typreceive procedure to convert the data.
+	 */
+	value = ReceiveFunctionCall(&my_extra->proc,
+								buf,
+								my_extra->typioparam,
+								my_extra->typtypmod);
+
+	/*
+	 * Do the necessary checks to ensure it's a valid domain value.
+	 */
+	domain_check_input(value, (buf == NULL), my_extra);
+
+	if (buf == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_DATUM(value);
+}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index fb24cc623660bcb7fd162af4262b252f35abcc24..a52366c5347b5334863ccee35d5f1cd44c6c091a 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
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.133 2006/04/04 19:35:36 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.134 2006/04/05 22:11:55 tgl Exp $
  *
  * NOTES
  *	  Eventually, the index information should go through here, too.
@@ -1469,33 +1469,6 @@ get_typstorage(Oid typid)
 		return 'p';
 }
 
-/*
- * get_typtypmod
- *
- *		Given the type OID, return the typtypmod field (domain's typmod
- *		for base type)
- */
-int32
-get_typtypmod(Oid typid)
-{
-	HeapTuple	tp;
-
-	tp = SearchSysCache(TYPEOID,
-						ObjectIdGetDatum(typid),
-						0, 0, 0);
-	if (HeapTupleIsValid(tp))
-	{
-		Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
-		int32		result;
-
-		result = typtup->typtypmod;
-		ReleaseSysCache(tp);
-		return result;
-	}
-	else
-		return -1;
-}
-
 /*
  * get_typdefault
  *	  Given a type OID, return the type's default value, if any.
@@ -1583,6 +1556,23 @@ get_typdefault(Oid typid)
  */
 Oid
 getBaseType(Oid typid)
+{
+	int32		typmod = -1;
+
+	return getBaseTypeAndTypmod(typid, &typmod);
+}
+
+/*
+ * getBaseTypeAndTypmod
+ *		If the given type is a domain, return its base type and typmod;
+ *		otherwise return the type's own OID, and leave *typmod unchanged.
+ *
+ * Note that the "applied typmod" should be -1 for every domain level
+ * above the bottommost; therefore, if the passed-in typid is indeed
+ * a domain, *typmod should be -1.
+ */
+Oid
+getBaseTypeAndTypmod(Oid typid, int32 *typmod)
 {
 	/*
 	 * We loop to find the bottom base type in a stack of domains.
@@ -1605,7 +1595,10 @@ getBaseType(Oid typid)
 			break;
 		}
 
+		Assert(*typmod == -1);
 		typid = typTup->typbasetype;
+		*typmod = typTup->typtypmod;
+
 		ReleaseSysCache(tup);
 	}
 
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 2ae52c723fce01494ecff641dde23f09b03c33f2..6b59dd55fe1d3753d816625b486a9919d4106cd2 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.321 2006/03/16 00:31:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.322 2006/04/05 22:11:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200603151
+#define CATALOG_VERSION_NO	200604051
 
 #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d8fda0b8c3aa4ea33de52264bc96eb9aaa1724c4..9542d632f2518ea9c6c5db4b483cb8a8c92fe8e5 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.404 2006/03/10 20:15:26 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.405 2006/04/05 22:11:55 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -3363,6 +3363,10 @@ DATA(insert OID = 2398 (  shell_in			PGNSP PGUID 12 f f t f i 1 2282 "2275" _nul
 DESCR("I/O");
 DATA(insert OID = 2399 (  shell_out			PGNSP PGUID 12 f f t f i 1 2275 "2282" _null_ _null_ _null_ shell_out - _null_ ));
 DESCR("I/O");
+DATA(insert OID = 2597 (  domain_in			PGNSP PGUID 12 f f f f v 3 2276 "2275 26 23" _null_ _null_ _null_ domain_in - _null_ ));
+DESCR("I/O");
+DATA(insert OID = 2598 (  domain_recv		PGNSP PGUID 12 f f f f v 3 2276 "2281 26 23" _null_ _null_ _null_ domain_recv - _null_ ));
+DESCR("I/O");
 
 /* cryptographic */
 DATA(insert OID =  2311 (  md5	   PGNSP PGUID 12 f f t f i 1 25 "25" _null_ _null_ _null_	md5_text - _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 00b7bf3dc3c576a659c09c212fcc05011cd3c57d..b9aeec1e3cbe7a9753a074979fb70121c8f38060 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.170 2006/03/05 15:58:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.171 2006/04/05 22:11:57 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -156,7 +156,7 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP
 	bool		typnotnull;
 
 	/*
-	 * Domains use typbasetype to show the base (or complex) type that the
+	 * Domains use typbasetype to show the base (or domain) type that the
 	 * domain is based on.	Zero if the type is not a domain.
 	 */
 	Oid			typbasetype;
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 871c82892d58857db0fb0119326dd33835c2a28a..70b7d0748977225140058253649f1ac5847db592 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.61 2006/03/05 15:58:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.62 2006/04/05 22:11:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,7 +49,8 @@ extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
 extern Node *coerce_type(ParseState *pstate, Node *node,
 			Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
 			CoercionContext ccontext, CoercionForm cformat);
-extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId,
+extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod,
+				 Oid typeId,
 				 CoercionForm cformat, bool hideInputCoercion,
 				 bool lengthCoercionDone);
 
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 3c4841697409729ef2fd640c3123e02091d2d2f4..47553c5cf291dcc4c169b48c4d37982267dd0a0b 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.277 2006/03/10 20:15:27 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.278 2006/04/05 22:11:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -99,6 +99,10 @@ extern Datum i4tochar(PG_FUNCTION_ARGS);
 extern Datum text_char(PG_FUNCTION_ARGS);
 extern Datum char_text(PG_FUNCTION_ARGS);
 
+/* domains.c */
+extern Datum domain_in(PG_FUNCTION_ARGS);
+extern Datum domain_recv(PG_FUNCTION_ARGS);
+
 /* int.c */
 extern Datum int2in(PG_FUNCTION_ARGS);
 extern Datum int2out(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 6e0d0de2d6fa125d60bbe41067ac5c0baaa41498..9a33fc360212af964b5642a2e1e82d0f34064a30 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.103 2006/03/05 15:59:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.104 2006/04/05 22:11:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -86,7 +86,6 @@ extern void get_type_io_data(Oid typid,
 				 Oid *typioparam,
 				 Oid *func);
 extern char get_typstorage(Oid typid);
-extern int32 get_typtypmod(Oid typid);
 extern Node *get_typdefault(Oid typid);
 extern char get_typtype(Oid typid);
 extern Oid	get_typ_typrelid(Oid typid);
@@ -97,6 +96,7 @@ extern void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena);
 extern void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam);
 extern void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena);
 extern Oid	getBaseType(Oid typid);
+extern Oid	getBaseTypeAndTypmod(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 c89be9535f17721efaff82c94192b5db749ef893..86e2ca54bf9297031886fc0f121a2f5c33ea1990 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -1,10 +1,17 @@
+--
+-- Test domains.
+--
 -- Test Comment / Drop
 create domain domaindroptest int4;
 comment on domain domaindroptest is 'About to drop this..';
--- currently this will be disallowed
-create domain basetypetest domaindroptest;
-ERROR:  "domaindroptest" is not a valid base type for a domain
+create domain dependenttypetest domaindroptest;
+-- fail because of dependent type
 drop domain domaindroptest;
+NOTICE:  type dependenttypetest depends on type domaindroptest
+ERROR:  cannot drop type domaindroptest because other objects depend on it
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+drop domain domaindroptest cascade;
+NOTICE:  drop cascades to type dependenttypetest
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 ERROR:  type "domaindroptest" does not exist
@@ -40,7 +47,7 @@ INSERT INTO basictest values ('88', 'haha', 'short', '123.1212');    -- Truncate
 -- Test copy
 COPY basictest (testvarchar) FROM stdin; -- fail
 ERROR:  value too long for type character varying(5)
-CONTEXT:  COPY basictest, line 1: "notsoshorttext"
+CONTEXT:  COPY basictest, line 1, column testvarchar: "notsoshorttext"
 COPY basictest (testvarchar) FROM stdin;
 select * from basictest;
  testint4 | testtext | testvarchar | testnumeric 
@@ -126,8 +133,11 @@ ERROR:  null value in column "col3" violates not-null constraint
 INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
 -- Test copy
 COPY nulltest FROM stdin; --fail
+ERROR:  null value in column "col3" violates not-null constraint
+CONTEXT:  COPY nulltest, line 1: "a	b	\N	d	d"
+COPY nulltest FROM stdin; --fail
 ERROR:  domain dcheck does not allow null values
-CONTEXT:  COPY nulltest, line 1: "a	b	\N	d	\N"
+CONTEXT:  COPY nulltest, line 1, column col5: NULL input
 -- Last row is bad
 COPY nulltest FROM stdin;
 ERROR:  new row for relation "nulltest" violates check constraint "nulltest_col5_check"
@@ -300,6 +310,46 @@ drop domain ddef3 restrict;
 drop domain ddef4 restrict;
 drop domain ddef5 restrict;
 drop sequence ddef4_seq;
+-- Test domains over domains
+create domain vchar4 varchar(4);
+create domain dinter vchar4 check (substring(VALUE, 1, 1) = 'x');
+create domain dtop dinter check (substring(VALUE, 2, 1) = '1');
+select 'x123'::dtop;
+ dtop 
+------
+ x123
+(1 row)
+
+select 'x1234'::dtop; -- explicit coercion should truncate
+ dtop 
+------
+ x123
+(1 row)
+
+select 'y1234'::dtop; -- fail
+ERROR:  value for domain dtop violates check constraint "dinter_check"
+select 'y123'::dtop; -- fail
+ERROR:  value for domain dtop violates check constraint "dinter_check"
+select 'yz23'::dtop; -- fail
+ERROR:  value for domain dtop violates check constraint "dinter_check"
+select 'xz23'::dtop; -- fail
+ERROR:  value for domain dtop violates check constraint "dtop_check"
+create temp table dtest(f1 dtop);
+insert into dtest values('x123');
+insert into dtest values('x1234'); -- fail, implicit coercion
+ERROR:  value too long for type character varying(4)
+insert into dtest values('y1234'); -- fail, implicit coercion
+ERROR:  value too long for type character varying(4)
+insert into dtest values('y123'); -- fail
+ERROR:  value for domain dtop violates check constraint "dinter_check"
+insert into dtest values('yz23'); -- fail
+ERROR:  value for domain dtop violates check constraint "dinter_check"
+insert into dtest values('xz23'); -- fail
+ERROR:  value for domain dtop violates check constraint "dtop_check"
+drop table dtest;
+drop domain vchar4 cascade;
+NOTICE:  drop cascades to type dinter
+NOTICE:  drop cascades to type dtop
 -- Make sure that constraints of newly-added domain columns are
 -- enforced correctly, even if there's no default value for the new
 -- column. Per bug #1433
@@ -328,3 +378,27 @@ execute s1(0); -- should fail
 ERROR:  value for domain pos_int violates check constraint "pos_int_check"
 execute s1(NULL); -- should fail
 ERROR:  domain pos_int does not allow null values
+-- Check that domain constraints on plpgsql function parameters, results,
+-- and local variables are enforced correctly.
+create function doubledecrement(p1 pos_int) returns pos_int as $$
+declare v pos_int;
+begin
+    v := p1 - 1;
+    return v - 1;
+end$$ language plpgsql;
+select doubledecrement(null); -- fail before call
+ERROR:  domain pos_int does not allow null values
+select doubledecrement(0); -- fail before call
+ERROR:  value for domain pos_int violates check constraint "pos_int_check"
+select doubledecrement(1); -- fail at assignment to v
+ERROR:  value for domain pos_int violates check constraint "pos_int_check"
+CONTEXT:  PL/pgSQL function "doubledecrement" line 3 at assignment
+select doubledecrement(2); -- fail at return
+ERROR:  value for domain pos_int violates check constraint "pos_int_check"
+CONTEXT:  PL/pgSQL function "doubledecrement" while casting return value to function's return type
+select doubledecrement(3); -- good
+ doubledecrement 
+-----------------
+               1
+(1 row)
+
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index f6010e636cb8dcdd6a9fc3be92bca4c38e105a70..21940e0e618cc8cb882dd212a3ac3e81084a4fd4 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -1,14 +1,18 @@
-
+--
+-- Test domains.
+--
 
 -- Test Comment / Drop
 create domain domaindroptest int4;
 comment on domain domaindroptest is 'About to drop this..';
 
--- currently this will be disallowed
-create domain basetypetest domaindroptest;
+create domain dependenttypetest domaindroptest;
 
+-- fail because of dependent type
 drop domain domaindroptest;
 
+drop domain domaindroptest cascade;
+
 -- this should fail because already gone
 drop domain domaindroptest cascade;
 
@@ -101,7 +105,11 @@ INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
 
 -- Test copy
 COPY nulltest FROM stdin; --fail
-a	b	\N	d	\N
+a	b	\N	d	d
+\.
+
+COPY nulltest FROM stdin; --fail
+a	b	c	d	\N
 \.
 
 -- Last row is bad
@@ -245,6 +253,30 @@ drop domain ddef4 restrict;
 drop domain ddef5 restrict;
 drop sequence ddef4_seq;
 
+-- Test domains over domains
+create domain vchar4 varchar(4);
+create domain dinter vchar4 check (substring(VALUE, 1, 1) = 'x');
+create domain dtop dinter check (substring(VALUE, 2, 1) = '1');
+
+select 'x123'::dtop;
+select 'x1234'::dtop; -- explicit coercion should truncate
+select 'y1234'::dtop; -- fail
+select 'y123'::dtop; -- fail
+select 'yz23'::dtop; -- fail
+select 'xz23'::dtop; -- fail
+
+create temp table dtest(f1 dtop);
+
+insert into dtest values('x123');
+insert into dtest values('x1234'); -- fail, implicit coercion
+insert into dtest values('y1234'); -- fail, implicit coercion
+insert into dtest values('y123'); -- fail
+insert into dtest values('yz23'); -- fail
+insert into dtest values('xz23'); -- fail
+
+drop table dtest;
+drop domain vchar4 cascade;
+
 -- Make sure that constraints of newly-added domain columns are
 -- enforced correctly, even if there's no default value for the new
 -- column. Per bug #1433
@@ -271,3 +303,19 @@ prepare s1 as select $1::pos_int = 10 as "is_ten";
 execute s1(10);
 execute s1(0); -- should fail
 execute s1(NULL); -- should fail
+
+-- Check that domain constraints on plpgsql function parameters, results,
+-- and local variables are enforced correctly.
+
+create function doubledecrement(p1 pos_int) returns pos_int as $$
+declare v pos_int;
+begin
+    v := p1 - 1;
+    return v - 1;
+end$$ language plpgsql;
+
+select doubledecrement(null); -- fail before call
+select doubledecrement(0); -- fail before call
+select doubledecrement(1); -- fail at assignment to v
+select doubledecrement(2); -- fail at return
+select doubledecrement(3); -- good