I wrote:
> Attached is a patch series that allows us to create arrays of domain
> types.

Here's a rebased-up-to-HEAD version of this patch set.  The only
actual change is removal of a no-longer-needed hunk in pl_exec.c.

                        regards, tom lane

diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 9d75e86..d7db32e 100644
*** a/src/backend/optimizer/prep/preptlist.c
--- b/src/backend/optimizer/prep/preptlist.c
*************** expand_targetlist(List *tlist, int comma
*** 306,314 ****
  						new_expr = coerce_to_domain(new_expr,
  													InvalidOid, -1,
  													atttype,
  													COERCE_IMPLICIT_CAST,
  													-1,
- 													false,
  													false);
  					}
  					else
--- 306,314 ----
  						new_expr = coerce_to_domain(new_expr,
  													InvalidOid, -1,
  													atttype,
+ 													COERCION_IMPLICIT,
  													COERCE_IMPLICIT_CAST,
  													-1,
  													false);
  					}
  					else
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index e79ad26..5a241bd 100644
*** a/src/backend/parser/parse_coerce.c
--- b/src/backend/parser/parse_coerce.c
***************
*** 34,48 ****
  
  static Node *coerce_type_typmod(Node *node,
  				   Oid targetTypeId, int32 targetTypMod,
! 				   CoercionForm cformat, int location,
! 				   bool isExplicit, bool hideInputCoercion);
  static void hide_coercion_node(Node *node);
  static Node *build_coercion_expression(Node *node,
  						  CoercionPathType pathtype,
  						  Oid funcId,
  						  Oid targetTypeId, int32 targetTypMod,
! 						  CoercionForm cformat, int location,
! 						  bool isExplicit);
  static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
  						 Oid targetTypeId,
  						 CoercionContext ccontext,
--- 34,49 ----
  
  static Node *coerce_type_typmod(Node *node,
  				   Oid targetTypeId, int32 targetTypMod,
! 				   CoercionContext ccontext, CoercionForm cformat,
! 				   int location,
! 				   bool hideInputCoercion);
  static void hide_coercion_node(Node *node);
  static Node *build_coercion_expression(Node *node,
  						  CoercionPathType pathtype,
  						  Oid funcId,
  						  Oid targetTypeId, int32 targetTypMod,
! 						  CoercionContext ccontext, CoercionForm cformat,
! 						  int location);
  static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
  						 Oid targetTypeId,
  						 CoercionContext ccontext,
*************** coerce_to_target_type(ParseState *pstate
*** 110,117 ****
  	 */
  	result = coerce_type_typmod(result,
  								targettype, targettypmod,
! 								cformat, location,
! 								(cformat != COERCE_IMPLICIT_CAST),
  								(result != expr && !IsA(result, Const)));
  
  	if (expr != origexpr)
--- 111,117 ----
  	 */
  	result = coerce_type_typmod(result,
  								targettype, targettypmod,
! 								ccontext, cformat, location,
  								(result != expr && !IsA(result, Const)));
  
  	if (expr != origexpr)
*************** coerce_type(ParseState *pstate, Node *no
*** 355,361 ****
  			result = coerce_to_domain(result,
  									  baseTypeId, baseTypeMod,
  									  targetTypeId,
! 									  cformat, location, false, false);
  
  		ReleaseSysCache(baseType);
  
--- 355,362 ----
  			result = coerce_to_domain(result,
  									  baseTypeId, baseTypeMod,
  									  targetTypeId,
! 									  ccontext, cformat, location,
! 									  false);
  
  		ReleaseSysCache(baseType);
  
*************** coerce_type(ParseState *pstate, Node *no
*** 417,436 ****
  
  			result = build_coercion_expression(node, pathtype, funcId,
  											   baseTypeId, baseTypeMod,
! 											   cformat, location,
! 											   (cformat != COERCE_IMPLICIT_CAST));
  
  			/*
  			 * If domain, coerce to the domain type and relabel with domain
! 			 * type ID.  We can skip the internal length-coercion step if the
! 			 * selected coercion function was a type-and-length coercion.
  			 */
  			if (targetTypeId != baseTypeId)
  				result = coerce_to_domain(result, baseTypeId, baseTypeMod,
  										  targetTypeId,
! 										  cformat, location, true,
! 										  exprIsLengthCoercion(result,
! 															   NULL));
  		}
  		else
  		{
--- 418,434 ----
  
  			result = build_coercion_expression(node, pathtype, funcId,
  											   baseTypeId, baseTypeMod,
! 											   ccontext, cformat, location);
  
  			/*
  			 * If domain, coerce to the domain type and relabel with domain
! 			 * type ID, hiding the previous coercion node.
  			 */
  			if (targetTypeId != baseTypeId)
  				result = coerce_to_domain(result, baseTypeId, baseTypeMod,
  										  targetTypeId,
! 										  ccontext, cformat, location,
! 										  true);
  		}
  		else
  		{
*************** coerce_type(ParseState *pstate, Node *no
*** 444,450 ****
  			 * then we won't need a RelabelType node.
  			 */
  			result = coerce_to_domain(node, InvalidOid, -1, targetTypeId,
! 									  cformat, location, false, false);
  			if (result == node)
  			{
  				/*
--- 442,449 ----
  			 * then we won't need a RelabelType node.
  			 */
  			result = coerce_to_domain(node, InvalidOid, -1, targetTypeId,
! 									  ccontext, cformat, location,
! 									  false);
  			if (result == node)
  			{
  				/*
*************** can_coerce_type(int nargs, Oid *input_ty
*** 636,654 ****
   * '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
   * 'location': coercion request location
   * 'hideInputCoercion': if true, hide the input coercion under this one.
-  * '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, int32 baseTypeMod, Oid typeId,
! 				 CoercionForm cformat, int location,
! 				 bool hideInputCoercion,
! 				 bool lengthCoercionDone)
  {
  	CoerceToDomain *result;
  
--- 635,651 ----
   * '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
!  * 'ccontext': context indicator to control coercions
!  * 'cformat': coercion display format
   * 'location': coercion request location
   * 'hideInputCoercion': if true, hide the input coercion under this one.
   *
   * If the target type isn't a domain, the given 'arg' is returned as-is.
   */
  Node *
  coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
! 				 CoercionContext ccontext, CoercionForm cformat, int location,
! 				 bool hideInputCoercion)
  {
  	CoerceToDomain *result;
  
*************** coerce_to_domain(Node *arg, Oid baseType
*** 677,690 ****
  	 * would be safe to do anyway, without lots of knowledge about what the
  	 * base type thinks the typmod means.
  	 */
! 	if (!lengthCoercionDone)
! 	{
! 		if (baseTypeMod >= 0)
! 			arg = coerce_type_typmod(arg, baseTypeId, baseTypeMod,
! 									 COERCE_IMPLICIT_CAST, location,
! 									 (cformat != COERCE_IMPLICIT_CAST),
! 									 false);
! 	}
  
  	/*
  	 * Now build the domain coercion node.  This represents run-time checking
--- 674,682 ----
  	 * would be safe to do anyway, without lots of knowledge about what the
  	 * base type thinks the typmod means.
  	 */
! 	arg = coerce_type_typmod(arg, baseTypeId, baseTypeMod,
! 							 ccontext, COERCE_IMPLICIT_CAST, location,
! 							 false);
  
  	/*
  	 * Now build the domain coercion node.  This represents run-time checking
*************** coerce_to_domain(Node *arg, Oid baseType
*** 714,724 ****
   * The caller must have already ensured that the value is of the correct
   * type, typically by applying coerce_type.
   *
!  * cformat determines the display properties of the generated node (if any),
!  * while isExplicit may affect semantics.  If hideInputCoercion is true
!  * *and* we generate a node, the input node is forced to IMPLICIT display
!  * form, so that only the typmod coercion node will be visible when
!  * displaying the expression.
   *
   * NOTE: this does not need to work on domain types, because any typmod
   * coercion for a domain is considered to be part of the type coercion
--- 706,719 ----
   * The caller must have already ensured that the value is of the correct
   * type, typically by applying coerce_type.
   *
!  * ccontext may affect semantics, depending on whether the length coercion
!  * function pays attention to the isExplicit flag it's passed.
!  *
!  * cformat determines the display properties of the generated node (if any).
!  *
!  * If hideInputCoercion is true *and* we generate a node, the input node is
!  * forced to IMPLICIT display form, so that only the typmod coercion node will
!  * be visible when displaying the expression.
   *
   * NOTE: this does not need to work on domain types, because any typmod
   * coercion for a domain is considered to be part of the type coercion
*************** coerce_to_domain(Node *arg, Oid baseType
*** 726,733 ****
   */
  static Node *
  coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
! 				   CoercionForm cformat, int location,
! 				   bool isExplicit, bool hideInputCoercion)
  {
  	CoercionPathType pathtype;
  	Oid			funcId;
--- 721,729 ----
   */
  static Node *
  coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
! 				   CoercionContext ccontext, CoercionForm cformat,
! 				   int location,
! 				   bool hideInputCoercion)
  {
  	CoercionPathType pathtype;
  	Oid			funcId;
*************** coerce_type_typmod(Node *node, Oid targe
*** 749,756 ****
  
  		node = build_coercion_expression(node, pathtype, funcId,
  										 targetTypeId, targetTypMod,
! 										 cformat, location,
! 										 isExplicit);
  	}
  
  	return node;
--- 745,751 ----
  
  		node = build_coercion_expression(node, pathtype, funcId,
  										 targetTypeId, targetTypMod,
! 										 ccontext, cformat, location);
  	}
  
  	return node;
*************** build_coercion_expression(Node *node,
*** 799,806 ****
  						  CoercionPathType pathtype,
  						  Oid funcId,
  						  Oid targetTypeId, int32 targetTypMod,
! 						  CoercionForm cformat, int location,
! 						  bool isExplicit)
  {
  	int			nargs = 0;
  
--- 794,801 ----
  						  CoercionPathType pathtype,
  						  Oid funcId,
  						  Oid targetTypeId, int32 targetTypMod,
! 						  CoercionContext ccontext, CoercionForm cformat,
! 						  int location)
  {
  	int			nargs = 0;
  
*************** build_coercion_expression(Node *node,
*** 865,871 ****
  							 -1,
  							 InvalidOid,
  							 sizeof(bool),
! 							 BoolGetDatum(isExplicit),
  							 false,
  							 true);
  
--- 860,866 ----
  							 -1,
  							 InvalidOid,
  							 sizeof(bool),
! 							 BoolGetDatum(ccontext == COERCION_EXPLICIT),
  							 false,
  							 true);
  
*************** build_coercion_expression(Node *node,
*** 893,899 ****
  		 */
  		acoerce->resulttypmod = (nargs >= 2) ? targetTypMod : -1;
  		/* resultcollid will be set by parse_collate.c */
! 		acoerce->isExplicit = isExplicit;
  		acoerce->coerceformat = cformat;
  		acoerce->location = location;
  
--- 888,894 ----
  		 */
  		acoerce->resulttypmod = (nargs >= 2) ? targetTypMod : -1;
  		/* resultcollid will be set by parse_collate.c */
! 		acoerce->isExplicit = (ccontext == COERCION_EXPLICIT);
  		acoerce->coerceformat = cformat;
  		acoerce->location = location;
  
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index ef52dd5..7054d4f 100644
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
*************** rewriteTargetListIU(List *targetList,
*** 875,883 ****
  					new_expr = coerce_to_domain(new_expr,
  												InvalidOid, -1,
  												att_tup->atttypid,
  												COERCE_IMPLICIT_CAST,
  												-1,
- 												false,
  												false);
  				}
  			}
--- 875,883 ----
  					new_expr = coerce_to_domain(new_expr,
  												InvalidOid, -1,
  												att_tup->atttypid,
+ 												COERCION_IMPLICIT,
  												COERCE_IMPLICIT_CAST,
  												-1,
  												false);
  				}
  			}
*************** rewriteValuesRTE(RangeTblEntry *rte, Rel
*** 1271,1279 ****
  					new_expr = coerce_to_domain(new_expr,
  												InvalidOid, -1,
  												att_tup->atttypid,
  												COERCE_IMPLICIT_CAST,
  												-1,
- 												false,
  												false);
  				}
  				newList = lappend(newList, new_expr);
--- 1271,1279 ----
  					new_expr = coerce_to_domain(new_expr,
  												InvalidOid, -1,
  												att_tup->atttypid,
+ 												COERCION_IMPLICIT,
  												COERCE_IMPLICIT_CAST,
  												-1,
  												false);
  				}
  				newList = lappend(newList, new_expr);
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 5c17213..c5773ef 100644
*** a/src/backend/rewrite/rewriteManip.c
--- b/src/backend/rewrite/rewriteManip.c
*************** ReplaceVarsFromTargetList_callback(Var *
*** 1429,1437 ****
  															   var->varcollid),
  										InvalidOid, -1,
  										var->vartype,
  										COERCE_IMPLICIT_CAST,
  										-1,
- 										false,
  										false);
  		}
  		elog(ERROR, "could not find replacement targetlist entry for attno %d",
--- 1429,1437 ----
  															   var->varcollid),
  										InvalidOid, -1,
  										var->vartype,
+ 										COERCION_IMPLICIT,
  										COERCE_IMPLICIT_CAST,
  										-1,
  										false);
  		}
  		elog(ERROR, "could not find replacement targetlist entry for attno %d",
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 06f6529..e560f0c 100644
*** a/src/include/parser/parse_coerce.h
--- b/src/include/parser/parse_coerce.h
*************** extern Node *coerce_type(ParseState *pst
*** 48,56 ****
  			CoercionContext ccontext, CoercionForm cformat, int location);
  extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod,
  				 Oid typeId,
! 				 CoercionForm cformat, int location,
! 				 bool hideInputCoercion,
! 				 bool lengthCoercionDone);
  
  extern Node *coerce_to_boolean(ParseState *pstate, Node *node,
  				  const char *constructName);
--- 48,55 ----
  			CoercionContext ccontext, CoercionForm cformat, int location);
  extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod,
  				 Oid typeId,
! 				 CoercionContext ccontext, CoercionForm cformat, int location,
! 				 bool hideInputCoercion);
  
  extern Node *coerce_to_boolean(ParseState *pstate, Node *node,
  				  const char *constructName);
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index fa409d7..1776730 100644
*** a/contrib/pg_stat_statements/pg_stat_statements.c
--- b/contrib/pg_stat_statements/pg_stat_statements.c
*************** JumbleExpr(pgssJumbleState *jstate, Node
*** 2631,2636 ****
--- 2631,2637 ----
  
  				APP_JUMB(acexpr->resulttype);
  				JumbleExpr(jstate, (Node *) acexpr->arg);
+ 				JumbleExpr(jstate, (Node *) acexpr->elemexpr);
  			}
  			break;
  		case T_ConvertRowtypeExpr:
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 6fffc29..2668650 100644
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
*************** find_expr_references_walker(Node *node,
*** 1738,1748 ****
  	{
  		ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
  
! 		if (OidIsValid(acoerce->elemfuncid))
! 			add_object_address(OCLASS_PROC, acoerce->elemfuncid, 0,
! 							   context->addrs);
  		add_object_address(OCLASS_TYPE, acoerce->resulttype, 0,
  						   context->addrs);
  		/* fall through to examine arguments */
  	}
  	else if (IsA(node, ConvertRowtypeExpr))
--- 1738,1751 ----
  	{
  		ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
  
! 		/* as above, depend on type */
  		add_object_address(OCLASS_TYPE, acoerce->resulttype, 0,
  						   context->addrs);
+ 		/* the collation might not be referenced anywhere else, either */
+ 		if (OidIsValid(acoerce->resultcollid) &&
+ 			acoerce->resultcollid != DEFAULT_COLLATION_OID)
+ 			add_object_address(OCLASS_COLLATION, acoerce->resultcollid, 0,
+ 							   context->addrs);
  		/* fall through to examine arguments */
  	}
  	else if (IsA(node, ConvertRowtypeExpr))
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index be9d23b..e083961 100644
*** a/src/backend/executor/execExpr.c
--- b/src/backend/executor/execExpr.c
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1225,1230 ****
--- 1225,1231 ----
  			{
  				ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
  				Oid			resultelemtype;
+ 				ExprState  *elemstate;
  
  				/* evaluate argument into step's result area */
  				ExecInitExprRec(acoerce->arg, parent, state, resv, resnull);
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1234,1275 ****
  					ereport(ERROR,
  							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  							 errmsg("target type is not an array")));
- 				/* Arrays over domains aren't supported yet */
- 				Assert(getBaseType(resultelemtype) == resultelemtype);
  
! 				scratch.opcode = EEOP_ARRAYCOERCE;
! 				scratch.d.arraycoerce.coerceexpr = acoerce;
! 				scratch.d.arraycoerce.resultelemtype = resultelemtype;
  
! 				if (OidIsValid(acoerce->elemfuncid))
! 				{
! 					AclResult	aclresult;
  
! 					/* Check permission to call function */
! 					aclresult = pg_proc_aclcheck(acoerce->elemfuncid,
! 												 GetUserId(),
! 												 ACL_EXECUTE);
! 					if (aclresult != ACLCHECK_OK)
! 						aclcheck_error(aclresult, ACL_KIND_PROC,
! 									   get_func_name(acoerce->elemfuncid));
! 					InvokeFunctionExecuteHook(acoerce->elemfuncid);
  
! 					/* Set up the primary fmgr lookup information */
! 					scratch.d.arraycoerce.elemfunc =
! 						(FmgrInfo *) palloc0(sizeof(FmgrInfo));
! 					fmgr_info(acoerce->elemfuncid,
! 							  scratch.d.arraycoerce.elemfunc);
! 					fmgr_info_set_expr((Node *) acoerce,
! 									   scratch.d.arraycoerce.elemfunc);
  
  					/* Set up workspace for array_map */
  					scratch.d.arraycoerce.amstate =
  						(ArrayMapState *) palloc0(sizeof(ArrayMapState));
  				}
  				else
  				{
! 					/* Don't need workspace if there's no conversion func */
! 					scratch.d.arraycoerce.elemfunc = NULL;
  					scratch.d.arraycoerce.amstate = NULL;
  				}
  
--- 1235,1283 ----
  					ereport(ERROR,
  							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  							 errmsg("target type is not an array")));
  
! 				/*
! 				 * Construct a sub-expression for the per-element expression;
! 				 * but don't ready it until after we check it for triviality.
! 				 * We assume it hasn't any Var references, but does have a
! 				 * CaseTestExpr representing the source array element values.
! 				 */
! 				elemstate = makeNode(ExprState);
! 				elemstate->expr = acoerce->elemexpr;
! 				elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
! 				elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
  
! 				ExecInitExprRec(acoerce->elemexpr, parent, elemstate,
! 								&elemstate->resvalue, &elemstate->resnull);
  
! 				if (elemstate->steps_len == 1 &&
! 					elemstate->steps[0].opcode == EEOP_CASE_TESTVAL)
! 				{
! 					/* Trivial, so we need no per-element work at runtime */
! 					elemstate = NULL;
! 				}
! 				else
! 				{
! 					/* Not trivial, so append a DONE step */
! 					scratch.opcode = EEOP_DONE;
! 					ExprEvalPushStep(elemstate, &scratch);
! 					/* and ready the subexpression */
! 					ExecReadyExpr(elemstate);
! 				}
  
! 				scratch.opcode = EEOP_ARRAYCOERCE;
! 				scratch.d.arraycoerce.elemexprstate = elemstate;
! 				scratch.d.arraycoerce.resultelemtype = resultelemtype;
  
+ 				if (elemstate)
+ 				{
  					/* Set up workspace for array_map */
  					scratch.d.arraycoerce.amstate =
  						(ArrayMapState *) palloc0(sizeof(ArrayMapState));
  				}
  				else
  				{
! 					/* Don't need workspace if there's no subexpression */
  					scratch.d.arraycoerce.amstate = NULL;
  				}
  
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index bd8a15d..f9244d8 100644
*** a/src/backend/executor/execExprInterp.c
--- b/src/backend/executor/execExprInterp.c
*************** ExecInterpExpr(ExprState *state, ExprCon
*** 1252,1258 ****
  		EEO_CASE(EEOP_ARRAYCOERCE)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalArrayCoerce(state, op);
  
  			EEO_NEXT();
  		}
--- 1252,1258 ----
  		EEO_CASE(EEOP_ARRAYCOERCE)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalArrayCoerce(state, op, econtext);
  
  			EEO_NEXT();
  		}
*************** ExecEvalArrayExpr(ExprState *state, Expr
*** 2328,2338 ****
   * Source array is in step's result variable.
   */
  void
! ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
  {
- 	ArrayCoerceExpr *acoerce = op->d.arraycoerce.coerceexpr;
  	Datum		arraydatum;
- 	FunctionCallInfoData locfcinfo;
  
  	/* NULL array -> NULL result */
  	if (*op->resnull)
--- 2328,2336 ----
   * Source array is in step's result variable.
   */
  void
! ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
  	Datum		arraydatum;
  
  	/* NULL array -> NULL result */
  	if (*op->resnull)
*************** ExecEvalArrayCoerce(ExprState *state, Ex
*** 2344,2350 ****
  	 * If it's binary-compatible, modify the element type in the array header,
  	 * but otherwise leave the array as we received it.
  	 */
! 	if (!OidIsValid(acoerce->elemfuncid))
  	{
  		/* Detoast input array if necessary, and copy in any case */
  		ArrayType  *array = DatumGetArrayTypePCopy(arraydatum);
--- 2342,2348 ----
  	 * If it's binary-compatible, modify the element type in the array header,
  	 * but otherwise leave the array as we received it.
  	 */
! 	if (op->d.arraycoerce.elemexprstate == NULL)
  	{
  		/* Detoast input array if necessary, and copy in any case */
  		ArrayType  *array = DatumGetArrayTypePCopy(arraydatum);
*************** ExecEvalArrayCoerce(ExprState *state, Ex
*** 2355,2377 ****
  	}
  
  	/*
! 	 * Use array_map to apply the function to each array element.
! 	 *
! 	 * We pass on the desttypmod and isExplicit flags whether or not the
! 	 * function wants them.
! 	 *
! 	 * Note: coercion functions are assumed to not use collation.
  	 */
! 	InitFunctionCallInfoData(locfcinfo, op->d.arraycoerce.elemfunc, 3,
! 							 InvalidOid, NULL, NULL);
! 	locfcinfo.arg[0] = arraydatum;
! 	locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
! 	locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
! 	locfcinfo.argnull[0] = false;
! 	locfcinfo.argnull[1] = false;
! 	locfcinfo.argnull[2] = false;
! 
! 	*op->resvalue = array_map(&locfcinfo, op->d.arraycoerce.resultelemtype,
  							  op->d.arraycoerce.amstate);
  }
  
--- 2353,2364 ----
  	}
  
  	/*
! 	 * Use array_map to apply the sub-expression to each array element.
  	 */
! 	*op->resvalue = array_map(arraydatum,
! 							  op->d.arraycoerce.elemexprstate,
! 							  econtext,
! 							  op->d.arraycoerce.resultelemtype,
  							  op->d.arraycoerce.amstate);
  }
  
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f1bed14..b274af2 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyArrayCoerceExpr(const ArrayCoerceEx
*** 1698,1708 ****
  	ArrayCoerceExpr *newnode = makeNode(ArrayCoerceExpr);
  
  	COPY_NODE_FIELD(arg);
! 	COPY_SCALAR_FIELD(elemfuncid);
  	COPY_SCALAR_FIELD(resulttype);
  	COPY_SCALAR_FIELD(resulttypmod);
  	COPY_SCALAR_FIELD(resultcollid);
- 	COPY_SCALAR_FIELD(isExplicit);
  	COPY_SCALAR_FIELD(coerceformat);
  	COPY_LOCATION_FIELD(location);
  
--- 1698,1707 ----
  	ArrayCoerceExpr *newnode = makeNode(ArrayCoerceExpr);
  
  	COPY_NODE_FIELD(arg);
! 	COPY_NODE_FIELD(elemexpr);
  	COPY_SCALAR_FIELD(resulttype);
  	COPY_SCALAR_FIELD(resulttypmod);
  	COPY_SCALAR_FIELD(resultcollid);
  	COPY_SCALAR_FIELD(coerceformat);
  	COPY_LOCATION_FIELD(location);
  
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8b56b91..5c839f4 100644
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** static bool
*** 513,523 ****
  _equalArrayCoerceExpr(const ArrayCoerceExpr *a, const ArrayCoerceExpr *b)
  {
  	COMPARE_NODE_FIELD(arg);
! 	COMPARE_SCALAR_FIELD(elemfuncid);
  	COMPARE_SCALAR_FIELD(resulttype);
  	COMPARE_SCALAR_FIELD(resulttypmod);
  	COMPARE_SCALAR_FIELD(resultcollid);
- 	COMPARE_SCALAR_FIELD(isExplicit);
  	COMPARE_COERCIONFORM_FIELD(coerceformat);
  	COMPARE_LOCATION_FIELD(location);
  
--- 513,522 ----
  _equalArrayCoerceExpr(const ArrayCoerceExpr *a, const ArrayCoerceExpr *b)
  {
  	COMPARE_NODE_FIELD(arg);
! 	COMPARE_NODE_FIELD(elemexpr);
  	COMPARE_SCALAR_FIELD(resulttype);
  	COMPARE_SCALAR_FIELD(resulttypmod);
  	COMPARE_SCALAR_FIELD(resultcollid);
  	COMPARE_COERCIONFORM_FIELD(coerceformat);
  	COMPARE_LOCATION_FIELD(location);
  
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index e3eb0c5..8e6f27e 100644
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
*************** check_functions_in_node(Node *node, chec
*** 1717,1731 ****
  					return true;
  			}
  			break;
- 		case T_ArrayCoerceExpr:
- 			{
- 				ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
- 
- 				if (OidIsValid(expr->elemfuncid) &&
- 					checker(expr->elemfuncid, context))
- 					return true;
- 			}
- 			break;
  		case T_RowCompareExpr:
  			{
  				RowCompareExpr *rcexpr = (RowCompareExpr *) node;
--- 1717,1722 ----
*************** expression_tree_walker(Node *node,
*** 2023,2029 ****
  		case T_CoerceViaIO:
  			return walker(((CoerceViaIO *) node)->arg, context);
  		case T_ArrayCoerceExpr:
! 			return walker(((ArrayCoerceExpr *) node)->arg, context);
  		case T_ConvertRowtypeExpr:
  			return walker(((ConvertRowtypeExpr *) node)->arg, context);
  		case T_CollateExpr:
--- 2014,2028 ----
  		case T_CoerceViaIO:
  			return walker(((CoerceViaIO *) node)->arg, context);
  		case T_ArrayCoerceExpr:
! 			{
! 				ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
! 
! 				if (walker(acoerce->arg, context))
! 					return true;
! 				if (walker(acoerce->elemexpr, context))
! 					return true;
! 			}
! 			break;
  		case T_ConvertRowtypeExpr:
  			return walker(((ConvertRowtypeExpr *) node)->arg, context);
  		case T_CollateExpr:
*************** expression_tree_mutator(Node *node,
*** 2705,2710 ****
--- 2704,2710 ----
  
  				FLATCOPY(newnode, acoerce, ArrayCoerceExpr);
  				MUTATE(newnode->arg, acoerce->arg, Expr *);
+ 				MUTATE(newnode->elemexpr, acoerce->elemexpr, Expr *);
  				return (Node *) newnode;
  			}
  			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b83d919..2532edc 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outArrayCoerceExpr(StringInfo str, cons
*** 1394,1404 ****
  	WRITE_NODE_TYPE("ARRAYCOERCEEXPR");
  
  	WRITE_NODE_FIELD(arg);
! 	WRITE_OID_FIELD(elemfuncid);
  	WRITE_OID_FIELD(resulttype);
  	WRITE_INT_FIELD(resulttypmod);
  	WRITE_OID_FIELD(resultcollid);
- 	WRITE_BOOL_FIELD(isExplicit);
  	WRITE_ENUM_FIELD(coerceformat, CoercionForm);
  	WRITE_LOCATION_FIELD(location);
  }
--- 1394,1403 ----
  	WRITE_NODE_TYPE("ARRAYCOERCEEXPR");
  
  	WRITE_NODE_FIELD(arg);
! 	WRITE_NODE_FIELD(elemexpr);
  	WRITE_OID_FIELD(resulttype);
  	WRITE_INT_FIELD(resulttypmod);
  	WRITE_OID_FIELD(resultcollid);
  	WRITE_ENUM_FIELD(coerceformat, CoercionForm);
  	WRITE_LOCATION_FIELD(location);
  }
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index fbf8330..07ba691 100644
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
*************** _readArrayCoerceExpr(void)
*** 892,902 ****
  	READ_LOCALS(ArrayCoerceExpr);
  
  	READ_NODE_FIELD(arg);
! 	READ_OID_FIELD(elemfuncid);
  	READ_OID_FIELD(resulttype);
  	READ_INT_FIELD(resulttypmod);
  	READ_OID_FIELD(resultcollid);
- 	READ_BOOL_FIELD(isExplicit);
  	READ_ENUM_FIELD(coerceformat, CoercionForm);
  	READ_LOCATION_FIELD(location);
  
--- 892,901 ----
  	READ_LOCALS(ArrayCoerceExpr);
  
  	READ_NODE_FIELD(arg);
! 	READ_NODE_FIELD(elemexpr);
  	READ_OID_FIELD(resulttype);
  	READ_INT_FIELD(resulttypmod);
  	READ_OID_FIELD(resultcollid);
  	READ_ENUM_FIELD(coerceformat, CoercionForm);
  	READ_LOCATION_FIELD(location);
  
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 051a854..8fd49d6 100644
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
*************** cost_qual_eval_walker(Node *node, cost_q
*** 3632,3642 ****
  	else if (IsA(node, ArrayCoerceExpr))
  	{
  		ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
! 		Node	   *arraynode = (Node *) acoerce->arg;
  
! 		if (OidIsValid(acoerce->elemfuncid))
! 			context->total.per_tuple += get_func_cost(acoerce->elemfuncid) *
! 				cpu_operator_cost * estimate_array_length(arraynode);
  	}
  	else if (IsA(node, RowCompareExpr))
  	{
--- 3632,3645 ----
  	else if (IsA(node, ArrayCoerceExpr))
  	{
  		ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
! 		QualCost	perelemcost;
  
! 		cost_qual_eval_node(&perelemcost, (Node *) acoerce->elemexpr,
! 							context->root);
! 		context->total.startup += perelemcost.startup;
! 		if (perelemcost.per_tuple > 0)
! 			context->total.per_tuple += perelemcost.per_tuple *
! 				estimate_array_length((Node *) acoerce->arg);
  	}
  	else if (IsA(node, RowCompareExpr))
  	{
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index b0c9e94..dee4414 100644
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
*************** fix_expr_common(PlannerInfo *root, Node 
*** 1395,1406 ****
  		record_plan_function_dependency(root,
  										((ScalarArrayOpExpr *) node)->opfuncid);
  	}
- 	else if (IsA(node, ArrayCoerceExpr))
- 	{
- 		if (OidIsValid(((ArrayCoerceExpr *) node)->elemfuncid))
- 			record_plan_function_dependency(root,
- 											((ArrayCoerceExpr *) node)->elemfuncid);
- 	}
  	else if (IsA(node, Const))
  	{
  		Const	   *con = (Const *) node;
--- 1395,1400 ----
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 93add27..7961362 100644
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** contain_nonstrict_functions_walker(Node 
*** 1361,1366 ****
--- 1361,1377 ----
  		return true;
  	if (IsA(node, FieldStore))
  		return true;
+ 	if (IsA(node, ArrayCoerceExpr))
+ 	{
+ 		/*
+ 		 * ArrayCoerceExpr is strict at the array level, regardless of what
+ 		 * the per-element expression is; so we should ignore elemexpr and
+ 		 * recurse only into the arg.
+ 		 */
+ 		return expression_tree_walker((Node *) ((ArrayCoerceExpr *) node)->arg,
+ 									  contain_nonstrict_functions_walker,
+ 									  context);
+ 	}
  	if (IsA(node, CaseExpr))
  		return true;
  	if (IsA(node, ArrayExpr))
*************** contain_nonstrict_functions_walker(Node 
*** 1380,1393 ****
  	if (IsA(node, BooleanTest))
  		return true;
  
! 	/*
! 	 * Check other function-containing nodes; but ArrayCoerceExpr is strict at
! 	 * the array level, regardless of elemfunc.
! 	 */
! 	if (!IsA(node, ArrayCoerceExpr) &&
! 		check_functions_in_node(node, contain_nonstrict_functions_checker,
  								context))
  		return true;
  	return expression_tree_walker(node, contain_nonstrict_functions_walker,
  								  context);
  }
--- 1391,1401 ----
  	if (IsA(node, BooleanTest))
  		return true;
  
! 	/* Check other function-containing nodes */
! 	if (check_functions_in_node(node, contain_nonstrict_functions_checker,
  								context))
  		return true;
+ 
  	return expression_tree_walker(node, contain_nonstrict_functions_walker,
  								  context);
  }
*************** find_nonnullable_rels_walker(Node *node,
*** 1757,1763 ****
  	}
  	else if (IsA(node, ArrayCoerceExpr))
  	{
! 		/* ArrayCoerceExpr is strict at the array level */
  		ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
  
  		result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);
--- 1765,1771 ----
  	}
  	else if (IsA(node, ArrayCoerceExpr))
  	{
! 		/* ArrayCoerceExpr is strict at the array level; ignore elemexpr */
  		ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
  
  		result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);
*************** find_nonnullable_vars_walker(Node *node,
*** 1965,1971 ****
  	}
  	else if (IsA(node, ArrayCoerceExpr))
  	{
! 		/* ArrayCoerceExpr is strict at the array level */
  		ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
  
  		result = find_nonnullable_vars_walker((Node *) expr->arg, top_level);
--- 1973,1979 ----
  	}
  	else if (IsA(node, ArrayCoerceExpr))
  	{
! 		/* ArrayCoerceExpr is strict at the array level; ignore elemexpr */
  		ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
  
  		result = find_nonnullable_vars_walker((Node *) expr->arg, top_level);
*************** eval_const_expressions_mutator(Node *nod
*** 3005,3036 ****
  			{
  				ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
  				Expr	   *arg;
  				ArrayCoerceExpr *newexpr;
  
  				/*
! 				 * Reduce constants in the ArrayCoerceExpr's argument, then
! 				 * build a new ArrayCoerceExpr.
  				 */
  				arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg,
  															  context);
  
  				newexpr = makeNode(ArrayCoerceExpr);
  				newexpr->arg = arg;
! 				newexpr->elemfuncid = expr->elemfuncid;
  				newexpr->resulttype = expr->resulttype;
  				newexpr->resulttypmod = expr->resulttypmod;
  				newexpr->resultcollid = expr->resultcollid;
- 				newexpr->isExplicit = expr->isExplicit;
  				newexpr->coerceformat = expr->coerceformat;
  				newexpr->location = expr->location;
  
  				/*
! 				 * If constant argument and it's a binary-coercible or
! 				 * immutable conversion, we can simplify it to a constant.
  				 */
  				if (arg && IsA(arg, Const) &&
! 					(!OidIsValid(newexpr->elemfuncid) ||
! 					 func_volatile(newexpr->elemfuncid) == PROVOLATILE_IMMUTABLE))
  					return (Node *) evaluate_expr((Expr *) newexpr,
  												  newexpr->resulttype,
  												  newexpr->resulttypmod,
--- 3013,3050 ----
  			{
  				ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
  				Expr	   *arg;
+ 				Expr	   *elemexpr;
  				ArrayCoerceExpr *newexpr;
  
  				/*
! 				 * Reduce constants in the ArrayCoerceExpr's argument and
! 				 * per-element expressions, then build a new ArrayCoerceExpr.
  				 */
  				arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg,
  															  context);
+ 				elemexpr = (Expr *) eval_const_expressions_mutator((Node *) expr->elemexpr,
+ 																   context);
  
  				newexpr = makeNode(ArrayCoerceExpr);
  				newexpr->arg = arg;
! 				newexpr->elemexpr = elemexpr;
  				newexpr->resulttype = expr->resulttype;
  				newexpr->resulttypmod = expr->resulttypmod;
  				newexpr->resultcollid = expr->resultcollid;
  				newexpr->coerceformat = expr->coerceformat;
  				newexpr->location = expr->location;
  
  				/*
! 				 * If constant argument and per-element expression is
! 				 * immutable, we can simplify the whole thing to a constant.
! 				 * Exception: although contain_mutable_functions considers
! 				 * CoerceToDomain immutable for historical reasons, let's not
! 				 * do so here; this ensures coercion to an array-over-domain
! 				 * does not apply the domain's constraints until runtime.
  				 */
  				if (arg && IsA(arg, Const) &&
! 					elemexpr && !IsA(elemexpr, CoerceToDomain) &&
! 					!contain_mutable_functions((Node *) elemexpr))
  					return (Node *) evaluate_expr((Expr *) newexpr,
  												  newexpr->resulttype,
  												  newexpr->resulttypmod,
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 5a241bd..0355c02 100644
*** a/src/backend/parser/parse_coerce.c
--- b/src/backend/parser/parse_coerce.c
*************** build_coercion_expression(Node *node,
*** 876,894 ****
  	{
  		/* We need to build an ArrayCoerceExpr */
  		ArrayCoerceExpr *acoerce = makeNode(ArrayCoerceExpr);
  
  		acoerce->arg = (Expr *) node;
! 		acoerce->elemfuncid = funcId;
  		acoerce->resulttype = targetTypeId;
  
  		/*
! 		 * Label the output as having a particular typmod only if we are
! 		 * really invoking a length-coercion function, ie one with more than
! 		 * one argument.
  		 */
! 		acoerce->resulttypmod = (nargs >= 2) ? targetTypMod : -1;
  		/* resultcollid will be set by parse_collate.c */
- 		acoerce->isExplicit = (ccontext == COERCION_EXPLICIT);
  		acoerce->coerceformat = cformat;
  		acoerce->location = location;
  
--- 876,927 ----
  	{
  		/* We need to build an ArrayCoerceExpr */
  		ArrayCoerceExpr *acoerce = makeNode(ArrayCoerceExpr);
+ 		CaseTestExpr *ctest = makeNode(CaseTestExpr);
+ 		Oid			sourceBaseTypeId;
+ 		int32		sourceBaseTypeMod;
+ 		Oid			targetElementType;
+ 		Node	   *elemexpr;
+ 
+ 		/*
+ 		 * Look through any domain over the source array type.  Note we don't
+ 		 * expect that the target type is a domain; it must be a plain array.
+ 		 * (To get to a domain target type, we'll do coerce_to_domain later.)
+ 		 */
+ 		sourceBaseTypeMod = exprTypmod(node);
+ 		sourceBaseTypeId = getBaseTypeAndTypmod(exprType(node),
+ 												&sourceBaseTypeMod);
+ 
+ 		/* Set up CaseTestExpr representing one element of source array */
+ 		ctest->typeId = get_element_type(sourceBaseTypeId);
+ 		Assert(OidIsValid(ctest->typeId));
+ 		ctest->typeMod = sourceBaseTypeMod;
+ 		ctest->collation = InvalidOid;	/* Assume coercions don't care */
+ 
+ 		/* And coerce it to the target element type */
+ 		targetElementType = get_element_type(targetTypeId);
+ 		Assert(OidIsValid(targetElementType));
+ 
+ 		elemexpr = coerce_to_target_type(NULL,
+ 										 (Node *) ctest,
+ 										 ctest->typeId,
+ 										 targetElementType,
+ 										 targetTypMod,
+ 										 ccontext,
+ 										 cformat,
+ 										 location);
+ 		if (elemexpr == NULL)	/* shouldn't happen */
+ 			elog(ERROR, "failed to coerce array element type as expected");
  
  		acoerce->arg = (Expr *) node;
! 		acoerce->elemexpr = (Expr *) elemexpr;
  		acoerce->resulttype = targetTypeId;
  
  		/*
! 		 * Label the output as having a particular element typmod only if we
! 		 * ended up with a per-element expression that is labeled that way.
  		 */
! 		acoerce->resulttypmod = exprTypmod(elemexpr);
  		/* resultcollid will be set by parse_collate.c */
  		acoerce->coerceformat = cformat;
  		acoerce->location = location;
  
*************** IsBinaryCoercible(Oid srctype, Oid targe
*** 2143,2150 ****
   *	COERCION_PATH_RELABELTYPE: binary-compatible cast, no function needed
   *				*funcid is set to InvalidOid
   *	COERCION_PATH_ARRAYCOERCE: need an ArrayCoerceExpr node
!  *				*funcid is set to the element cast function, or InvalidOid
!  *				if the array elements are binary-compatible
   *	COERCION_PATH_COERCEVIAIO: need a CoerceViaIO node
   *				*funcid is set to InvalidOid
   *
--- 2176,2182 ----
   *	COERCION_PATH_RELABELTYPE: binary-compatible cast, no function needed
   *				*funcid is set to InvalidOid
   *	COERCION_PATH_ARRAYCOERCE: need an ArrayCoerceExpr node
!  *				*funcid is set to InvalidOid
   *	COERCION_PATH_COERCEVIAIO: need a CoerceViaIO node
   *				*funcid is set to InvalidOid
   *
*************** find_coercion_pathway(Oid targetTypeId, 
*** 2230,2240 ****
  	{
  		/*
  		 * If there's no pg_cast entry, perhaps we are dealing with a pair of
! 		 * array types.  If so, and if the element types have a suitable cast,
! 		 * report that we can coerce with an ArrayCoerceExpr.
! 		 *
! 		 * Note that the source type can be a domain over array, but not the
! 		 * target, because ArrayCoerceExpr won't check domain constraints.
  		 *
  		 * Hack: disallow coercions to oidvector and int2vector, which
  		 * otherwise tend to capture coercions that should go to "real" array
--- 2262,2269 ----
  	{
  		/*
  		 * If there's no pg_cast entry, perhaps we are dealing with a pair of
! 		 * array types.  If so, and if their element types have a conversion
! 		 * pathway, report that we can coerce with an ArrayCoerceExpr.
  		 *
  		 * Hack: disallow coercions to oidvector and int2vector, which
  		 * otherwise tend to capture coercions that should go to "real" array
*************** find_coercion_pathway(Oid targetTypeId, 
*** 2249,2255 ****
  			Oid			sourceElem;
  
  			if ((targetElem = get_element_type(targetTypeId)) != InvalidOid &&
! 				(sourceElem = get_base_element_type(sourceTypeId)) != InvalidOid)
  			{
  				CoercionPathType elempathtype;
  				Oid			elemfuncid;
--- 2278,2284 ----
  			Oid			sourceElem;
  
  			if ((targetElem = get_element_type(targetTypeId)) != InvalidOid &&
! 				(sourceElem = get_element_type(sourceTypeId)) != InvalidOid)
  			{
  				CoercionPathType elempathtype;
  				Oid			elemfuncid;
*************** find_coercion_pathway(Oid targetTypeId, 
*** 2258,2271 ****
  													 sourceElem,
  													 ccontext,
  													 &elemfuncid);
! 				if (elempathtype != COERCION_PATH_NONE &&
! 					elempathtype != COERCION_PATH_ARRAYCOERCE)
  				{
! 					*funcid = elemfuncid;
! 					if (elempathtype == COERCION_PATH_COERCEVIAIO)
! 						result = COERCION_PATH_COERCEVIAIO;
! 					else
! 						result = COERCION_PATH_ARRAYCOERCE;
  				}
  			}
  		}
--- 2287,2295 ----
  													 sourceElem,
  													 ccontext,
  													 &elemfuncid);
! 				if (elempathtype != COERCION_PATH_NONE)
  				{
! 					result = COERCION_PATH_ARRAYCOERCE;
  				}
  			}
  		}
*************** find_coercion_pathway(Oid targetTypeId, 
*** 2306,2312 ****
   * If the given type is a varlena array type, we do not look for a coercion
   * function associated directly with the array type, but instead look for
   * one associated with the element type.  An ArrayCoerceExpr node must be
!  * used to apply such a function.
   *
   * We use the same result enum as find_coercion_pathway, but the only possible
   * result codes are:
--- 2330,2338 ----
   * If the given type is a varlena array type, we do not look for a coercion
   * function associated directly with the array type, but instead look for
   * one associated with the element type.  An ArrayCoerceExpr node must be
!  * used to apply such a function.  (Note: currently, it's pointless to
!  * return the funcid in this case, because it'll just get looked up again
!  * in the recursive construction of the ArrayCoerceExpr's elemexpr.)
   *
   * We use the same result enum as find_coercion_pathway, but the only possible
   * result codes are:
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 2a4de41..4caff0b 100644
*** a/src/backend/utils/adt/arrayfuncs.c
--- b/src/backend/utils/adt/arrayfuncs.c
*************** array_set(ArrayType *array, int nSubscri
*** 3092,3112 ****
  /*
   * array_map()
   *
!  * Map an array through an arbitrary function.  Return a new array with
!  * same dimensions and each source element transformed by fn().  Each
!  * source element is passed as the first argument to fn(); additional
!  * arguments to be passed to fn() can be specified by the caller.
!  * The output array can have a different element type than the input.
   *
   * Parameters are:
!  * * fcinfo: a function-call data structure pre-constructed by the caller
!  *	 to be ready to call the desired function, with everything except the
!  *	 first argument position filled in.  In particular, flinfo identifies
!  *	 the function fn(), and if nargs > 1 then argument positions after the
!  *	 first must be preset to the additional values to be passed.  The
!  *	 first argument position initially holds the input array value.
   * * retType: OID of element type of output array.  This must be the same as,
!  *	 or binary-compatible with, the result type of fn().
   * * amstate: workspace for array_map.  Must be zeroed by caller before
   *	 first call, and not touched after that.
   *
--- 3092,3109 ----
  /*
   * array_map()
   *
!  * Map an array through an arbitrary expression.  Return a new array with
!  * the same dimensions and each source element transformed by the given,
!  * already-compiled expression.  Each source element is placed in the
!  * innermost_caseval/innermost_casenull fields of the ExprState.
   *
   * Parameters are:
!  * * arrayd: Datum representing array argument.
!  * * exprstate: ExprState representing the per-element transformation.
!  * * econtext: context for expression evaluation.
   * * retType: OID of element type of output array.  This must be the same as,
!  *	 or binary-compatible with, the result type of the expression.  It might
!  *	 be different from the input array's element type.
   * * amstate: workspace for array_map.  Must be zeroed by caller before
   *	 first call, and not touched after that.
   *
*************** array_set(ArrayType *array, int nSubscri
*** 3116,3126 ****
   *
   * NB: caller must assure that input array is not NULL.  NULL elements in
   * the array are OK however.
   */
  Datum
! array_map(FunctionCallInfo fcinfo, Oid retType, ArrayMapState *amstate)
  {
! 	AnyArrayType *v;
  	ArrayType  *result;
  	Datum	   *values;
  	bool	   *nulls;
--- 3113,3126 ----
   *
   * NB: caller must assure that input array is not NULL.  NULL elements in
   * the array are OK however.
+  * NB: caller should be running in econtext's per-tuple memory context.
   */
  Datum
! array_map(Datum arrayd,
! 		  ExprState *exprstate, ExprContext *econtext,
! 		  Oid retType, ArrayMapState *amstate)
  {
! 	AnyArrayType *v = DatumGetAnyArray(arrayd);
  	ArrayType  *result;
  	Datum	   *values;
  	bool	   *nulls;
*************** array_map(FunctionCallInfo fcinfo, Oid r
*** 3141,3153 ****
  	array_iter	iter;
  	ArrayMetaState *inp_extra;
  	ArrayMetaState *ret_extra;
! 
! 	/* Get input array */
! 	if (fcinfo->nargs < 1)
! 		elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
! 	if (PG_ARGISNULL(0))
! 		elog(ERROR, "null input array");
! 	v = PG_GETARG_ANY_ARRAY(0);
  
  	inpType = AARR_ELEMTYPE(v);
  	ndim = AARR_NDIM(v);
--- 3141,3148 ----
  	array_iter	iter;
  	ArrayMetaState *inp_extra;
  	ArrayMetaState *ret_extra;
! 	Datum	   *transform_source = exprstate->innermost_caseval;
! 	bool	   *transform_source_isnull = exprstate->innermost_casenull;
  
  	inpType = AARR_ELEMTYPE(v);
  	ndim = AARR_NDIM(v);
*************** array_map(FunctionCallInfo fcinfo, Oid r
*** 3158,3164 ****
  	if (nitems <= 0)
  	{
  		/* Return empty array */
! 		PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
  	}
  
  	/*
--- 3153,3159 ----
  	if (nitems <= 0)
  	{
  		/* Return empty array */
! 		return PointerGetDatum(construct_empty_array(retType));
  	}
  
  	/*
*************** array_map(FunctionCallInfo fcinfo, Oid r
*** 3203,3241 ****
  
  	for (i = 0; i < nitems; i++)
  	{
- 		bool		callit = true;
- 
  		/* Get source element, checking for NULL */
! 		fcinfo->arg[0] = array_iter_next(&iter, &fcinfo->argnull[0], i,
! 										 inp_typlen, inp_typbyval, inp_typalign);
! 
! 		/*
! 		 * Apply the given function to source elt and extra args.
! 		 */
! 		if (fcinfo->flinfo->fn_strict)
! 		{
! 			int			j;
! 
! 			for (j = 0; j < fcinfo->nargs; j++)
! 			{
! 				if (fcinfo->argnull[j])
! 				{
! 					callit = false;
! 					break;
! 				}
! 			}
! 		}
  
! 		if (callit)
! 		{
! 			fcinfo->isnull = false;
! 			values[i] = FunctionCallInvoke(fcinfo);
! 		}
! 		else
! 			fcinfo->isnull = true;
  
! 		nulls[i] = fcinfo->isnull;
! 		if (fcinfo->isnull)
  			hasnulls = true;
  		else
  		{
--- 3198,3212 ----
  
  	for (i = 0; i < nitems; i++)
  	{
  		/* Get source element, checking for NULL */
! 		*transform_source =
! 			array_iter_next(&iter, transform_source_isnull, i,
! 							inp_typlen, inp_typbyval, inp_typalign);
  
! 		/* Apply the given expression to source element */
! 		values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
  
! 		if (nulls[i])
  			hasnulls = true;
  		else
  		{
*************** array_map(FunctionCallInfo fcinfo, Oid r
*** 3254,3260 ****
  		}
  	}
  
! 	/* Allocate and initialize the result array */
  	if (hasnulls)
  	{
  		dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
--- 3225,3231 ----
  		}
  	}
  
! 	/* Allocate and fill the result array */
  	if (hasnulls)
  	{
  		dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
*************** array_map(FunctionCallInfo fcinfo, Oid r
*** 3273,3290 ****
  	memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
  	memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
  
- 	/*
- 	 * Note: do not risk trying to pfree the results of the called function
- 	 */
  	CopyArrayEls(result,
  				 values, nulls, nitems,
  				 typlen, typbyval, typalign,
  				 false);
  
  	pfree(values);
  	pfree(nulls);
  
! 	PG_RETURN_ARRAYTYPE_P(result);
  }
  
  /*
--- 3244,3261 ----
  	memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
  	memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
  
  	CopyArrayEls(result,
  				 values, nulls, nitems,
  				 typlen, typbyval, typalign,
  				 false);
  
+ 	/*
+ 	 * Note: do not risk trying to pfree the results of the called expression
+ 	 */
  	pfree(values);
  	pfree(nulls);
  
! 	return PointerGetDatum(result);
  }
  
  /*
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 81b0bc3..7b3fdc9 100644
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** strip_array_coercion(Node *node)
*** 1753,1762 ****
  {
  	for (;;)
  	{
! 		if (node && IsA(node, ArrayCoerceExpr) &&
! 			((ArrayCoerceExpr *) node)->elemfuncid == InvalidOid)
  		{
! 			node = (Node *) ((ArrayCoerceExpr *) node)->arg;
  		}
  		else if (node && IsA(node, RelabelType))
  		{
--- 1753,1771 ----
  {
  	for (;;)
  	{
! 		if (node && IsA(node, ArrayCoerceExpr))
  		{
! 			ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
! 
! 			/*
! 			 * If the per-element expression is just a RelabelType on top of
! 			 * CaseTestExpr, then we know it's a binary-compatible relabeling.
! 			 */
! 			if (IsA(acoerce->elemexpr, RelabelType) &&
! 				IsA(((RelabelType *) acoerce->elemexpr)->arg, CaseTestExpr))
! 				node = (Node *) acoerce->arg;
! 			else
! 				break;
  		}
  		else if (node && IsA(node, RelabelType))
  		{
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index a7b0782..9197335 100644
*** a/src/backend/utils/fmgr/fmgr.c
--- b/src/backend/utils/fmgr/fmgr.c
*************** get_call_expr_argtype(Node *expr, int ar
*** 1941,1948 ****
  		args = ((DistinctExpr *) expr)->args;
  	else if (IsA(expr, ScalarArrayOpExpr))
  		args = ((ScalarArrayOpExpr *) expr)->args;
- 	else if (IsA(expr, ArrayCoerceExpr))
- 		args = list_make1(((ArrayCoerceExpr *) expr)->arg);
  	else if (IsA(expr, NullIfExpr))
  		args = ((NullIfExpr *) expr)->args;
  	else if (IsA(expr, WindowFunc))
--- 1941,1946 ----
*************** get_call_expr_argtype(Node *expr, int ar
*** 1956,1971 ****
  	argtype = exprType((Node *) list_nth(args, argnum));
  
  	/*
! 	 * special hack for ScalarArrayOpExpr and ArrayCoerceExpr: what the
! 	 * underlying function will actually get passed is the element type of the
! 	 * array.
  	 */
  	if (IsA(expr, ScalarArrayOpExpr) &&
  		argnum == 1)
  		argtype = get_base_element_type(argtype);
- 	else if (IsA(expr, ArrayCoerceExpr) &&
- 			 argnum == 0)
- 		argtype = get_base_element_type(argtype);
  
  	return argtype;
  }
--- 1954,1965 ----
  	argtype = exprType((Node *) list_nth(args, argnum));
  
  	/*
! 	 * special hack for ScalarArrayOpExpr: what the underlying function will
! 	 * actually get passed is the element type of the array.
  	 */
  	if (IsA(expr, ScalarArrayOpExpr) &&
  		argnum == 1)
  		argtype = get_base_element_type(argtype);
  
  	return argtype;
  }
*************** get_call_expr_arg_stable(Node *expr, int
*** 2012,2019 ****
  		args = ((DistinctExpr *) expr)->args;
  	else if (IsA(expr, ScalarArrayOpExpr))
  		args = ((ScalarArrayOpExpr *) expr)->args;
- 	else if (IsA(expr, ArrayCoerceExpr))
- 		args = list_make1(((ArrayCoerceExpr *) expr)->arg);
  	else if (IsA(expr, NullIfExpr))
  		args = ((NullIfExpr *) expr)->args;
  	else if (IsA(expr, WindowFunc))
--- 2006,2011 ----
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 8ee0496..78d2247 100644
*** a/src/include/executor/execExpr.h
--- b/src/include/executor/execExpr.h
*************** typedef struct ExprEvalStep
*** 385,394 ****
  		/* for EEOP_ARRAYCOERCE */
  		struct
  		{
! 			ArrayCoerceExpr *coerceexpr;
  			Oid			resultelemtype; /* element type of result array */
- 			FmgrInfo   *elemfunc;	/* lookup info for element coercion
- 									 * function */
  			struct ArrayMapState *amstate;	/* workspace for array_map */
  		}			arraycoerce;
  
--- 385,392 ----
  		/* for EEOP_ARRAYCOERCE */
  		struct
  		{
! 			ExprState  *elemexprstate;	/* null if no per-element work */
  			Oid			resultelemtype; /* element type of result array */
  			struct ArrayMapState *amstate;	/* workspace for array_map */
  		}			arraycoerce;
  
*************** extern void ExecEvalRowNull(ExprState *s
*** 621,627 ****
  extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op,
  				   ExprContext *econtext);
  extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op);
  extern void ExecEvalRow(ExprState *state, ExprEvalStep *op);
  extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op);
  extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op,
--- 619,626 ----
  extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op,
  				   ExprContext *econtext);
  extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op,
! 					ExprContext *econtext);
  extern void ExecEvalRow(ExprState *state, ExprEvalStep *op);
  extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op);
  extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 8c536a8..ccb5123 100644
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
*************** typedef struct CoerceViaIO
*** 820,830 ****
   * ArrayCoerceExpr
   *
   * ArrayCoerceExpr represents a type coercion from one array type to another,
!  * which is implemented by applying the indicated element-type coercion
!  * function to each element of the source array.  If elemfuncid is InvalidOid
!  * then the element types are binary-compatible, but the coercion still
!  * requires some effort (we have to fix the element type ID stored in the
!  * array header).
   * ----------------
   */
  
--- 820,831 ----
   * ArrayCoerceExpr
   *
   * ArrayCoerceExpr represents a type coercion from one array type to another,
!  * which is implemented by applying the per-element coercion expression
!  * "elemexpr" to each element of the source array.  Within elemexpr, the
!  * source element is represented by a CaseTestExpr node.  Note that even if
!  * elemexpr is a no-op (that is, just CaseTestExpr + RelabelType), the
!  * coercion still requires some effort: we have to fix the element type OID
!  * stored in the array header.
   * ----------------
   */
  
*************** typedef struct ArrayCoerceExpr
*** 832,842 ****
  {
  	Expr		xpr;
  	Expr	   *arg;			/* input expression (yields an array) */
! 	Oid			elemfuncid;		/* OID of element coercion function, or 0 */
  	Oid			resulttype;		/* output type of coercion (an array type) */
  	int32		resulttypmod;	/* output typmod (also element typmod) */
  	Oid			resultcollid;	/* OID of collation, or InvalidOid if none */
- 	bool		isExplicit;		/* conversion semantics flag to pass to func */
  	CoercionForm coerceformat;	/* how to display this node */
  	int			location;		/* token location, or -1 if unknown */
  } ArrayCoerceExpr;
--- 833,842 ----
  {
  	Expr		xpr;
  	Expr	   *arg;			/* input expression (yields an array) */
! 	Expr	   *elemexpr;		/* expression representing per-element work */
  	Oid			resulttype;		/* output type of coercion (an array type) */
  	int32		resulttypmod;	/* output typmod (also element typmod) */
  	Oid			resultcollid;	/* OID of collation, or InvalidOid if none */
  	CoercionForm coerceformat;	/* how to display this node */
  	int			location;		/* token location, or -1 if unknown */
  } ArrayCoerceExpr;
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 61a67a2..32833f0 100644
*** a/src/include/utils/array.h
--- b/src/include/utils/array.h
***************
*** 64,69 ****
--- 64,73 ----
  #include "fmgr.h"
  #include "utils/expandeddatum.h"
  
+ /* avoid including execnodes.h here */
+ struct ExprState;
+ struct ExprContext;
+ 
  
  /*
   * Arrays are varlena objects, so must meet the varlena convention that
*************** extern ArrayType *array_set(ArrayType *a
*** 360,367 ****
  		  Datum dataValue, bool isNull,
  		  int arraytyplen, int elmlen, bool elmbyval, char elmalign);
  
! extern Datum array_map(FunctionCallInfo fcinfo, Oid retType,
! 		  ArrayMapState *amstate);
  
  extern void array_bitmap_copy(bits8 *destbitmap, int destoffset,
  				  const bits8 *srcbitmap, int srcoffset,
--- 364,372 ----
  		  Datum dataValue, bool isNull,
  		  int arraytyplen, int elmlen, bool elmbyval, char elmalign);
  
! extern Datum array_map(Datum arrayd,
! 		  struct ExprState *exprstate, struct ExprContext *econtext,
! 		  Oid retType, ArrayMapState *amstate);
  
  extern void array_bitmap_copy(bits8 *destbitmap, int destoffset,
  				  const bits8 *srcbitmap, int srcoffset,
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7ed16ae..12d2cd6 100644
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
*************** ObjectAddress
*** 729,734 ****
--- 729,735 ----
  DefineDomain(CreateDomainStmt *stmt)
  {
  	char	   *domainName;
+ 	char	   *domainArrayName;
  	Oid			domainNamespace;
  	AclResult	aclresult;
  	int16		internalLength;
*************** DefineDomain(CreateDomainStmt *stmt)
*** 757,762 ****
--- 758,764 ----
  	Oid			basetypeoid;
  	Oid			old_type_oid;
  	Oid			domaincoll;
+ 	Oid			domainArrayOid;
  	Form_pg_type baseType;
  	int32		basetypeMod;
  	Oid			baseColl;
*************** DefineDomain(CreateDomainStmt *stmt)
*** 1027,1032 ****
--- 1029,1037 ----
  		}
  	}
  
+ 	/* Allocate OID for array type */
+ 	domainArrayOid = AssignTypeArrayOid();
+ 
  	/*
  	 * Have TypeCreate do all the real work.
  	 */
*************** DefineDomain(CreateDomainStmt *stmt)
*** 1051,1057 ****
  				   analyzeProcedure,	/* analyze procedure */
  				   InvalidOid,	/* no array element type */
  				   false,		/* this isn't an array */
! 				   InvalidOid,	/* no arrays for domains (yet) */
  				   basetypeoid, /* base type ID */
  				   defaultValue,	/* default type value (text) */
  				   defaultValueBin, /* default type value (binary) */
--- 1056,1062 ----
  				   analyzeProcedure,	/* analyze procedure */
  				   InvalidOid,	/* no array element type */
  				   false,		/* this isn't an array */
! 				   domainArrayOid,	/* array type we are about to create */
  				   basetypeoid, /* base type ID */
  				   defaultValue,	/* default type value (text) */
  				   defaultValueBin, /* default type value (binary) */
*************** DefineDomain(CreateDomainStmt *stmt)
*** 1064,1069 ****
--- 1069,1116 ----
  				   domaincoll); /* type's collation */
  
  	/*
+ 	 * Create the array type that goes with it.
+ 	 */
+ 	domainArrayName = makeArrayTypeName(domainName, domainNamespace);
+ 
+ 	/* alignment must be 'i' or 'd' for arrays */
+ 	alignment = (alignment == 'd') ? 'd' : 'i';
+ 
+ 	TypeCreate(domainArrayOid,	/* force assignment of this type OID */
+ 			   domainArrayName, /* type name */
+ 			   domainNamespace, /* namespace */
+ 			   InvalidOid,		/* relation oid (n/a here) */
+ 			   0,				/* relation kind (ditto) */
+ 			   GetUserId(),		/* owner's ID */
+ 			   -1,				/* internal size (always varlena) */
+ 			   TYPTYPE_BASE,	/* type-type (base type) */
+ 			   TYPCATEGORY_ARRAY,	/* type-category (array) */
+ 			   false,			/* array types are never preferred */
+ 			   delimiter,		/* array element delimiter */
+ 			   F_ARRAY_IN,		/* input procedure */
+ 			   F_ARRAY_OUT,		/* output procedure */
+ 			   F_ARRAY_RECV,	/* receive procedure */
+ 			   F_ARRAY_SEND,	/* send procedure */
+ 			   InvalidOid,		/* typmodin procedure - none */
+ 			   InvalidOid,		/* typmodout procedure - none */
+ 			   F_ARRAY_TYPANALYZE,	/* analyze procedure */
+ 			   address.objectId,	/* element type ID */
+ 			   true,			/* yes this is an array type */
+ 			   InvalidOid,		/* no further array type */
+ 			   InvalidOid,		/* base type ID */
+ 			   NULL,			/* never a default type value */
+ 			   NULL,			/* binary default isn't sent either */
+ 			   false,			/* never passed by value */
+ 			   alignment,		/* see above */
+ 			   'x',				/* ARRAY is always toastable */
+ 			   -1,				/* typMod (Domains only) */
+ 			   0,				/* Array dimensions of typbasetype */
+ 			   false,			/* Type NOT NULL */
+ 			   domaincoll);		/* type's collation */
+ 
+ 	pfree(domainArrayName);
+ 
+ 	/*
  	 * Process constraints which refer to the domain ID returned by TypeCreate
  	 */
  	foreach(listptr, schema)
*************** DefineEnum(CreateEnumStmt *stmt)
*** 1139,1144 ****
--- 1186,1192 ----
  					 errmsg("type \"%s\" already exists", enumName)));
  	}
  
+ 	/* Allocate OID for array type */
  	enumArrayOid = AssignTypeArrayOid();
  
  	/* Create the pg_type entry */
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 3acc696..1e62c57 100644
*** a/src/test/regress/expected/domain.out
--- b/src/test/regress/expected/domain.out
*************** Rules:
*** 310,315 ****
--- 310,410 ----
  drop table dcomptable;
  drop type comptype cascade;
  NOTICE:  drop cascades to type dcomptypea
+ -- Test arrays over domains
+ create domain posint as int check (value > 0);
+ create table pitable (f1 posint[]);
+ insert into pitable values(array[42]);
+ insert into pitable values(array[-1]);  -- fail
+ ERROR:  value for domain posint violates check constraint "posint_check"
+ insert into pitable values('{0}');  -- fail
+ ERROR:  value for domain posint violates check constraint "posint_check"
+ LINE 1: insert into pitable values('{0}');
+                                    ^
+ update pitable set f1[1] = f1[1] + 1;
+ update pitable set f1[1] = 0;  -- fail
+ ERROR:  value for domain posint violates check constraint "posint_check"
+ select * from pitable;
+   f1  
+ ------
+  {43}
+ (1 row)
+ 
+ drop table pitable;
+ create domain vc4 as varchar(4);
+ create table vc4table (f1 vc4[]);
+ insert into vc4table values(array['too long']);  -- fail
+ ERROR:  value too long for type character varying(4)
+ insert into vc4table values(array['too long']::vc4[]);  -- cast truncates
+ select * from vc4table;
+     f1    
+ ----------
+  {"too "}
+ (1 row)
+ 
+ drop table vc4table;
+ drop type vc4;
+ -- You can sort of fake arrays-of-arrays by putting a domain in between
+ create domain dposinta as posint[];
+ create table dposintatable (f1 dposinta[]);
+ insert into dposintatable values(array[array[42]]);  -- fail
+ ERROR:  column "f1" is of type dposinta[] but expression is of type integer[]
+ LINE 1: insert into dposintatable values(array[array[42]]);
+                                          ^
+ HINT:  You will need to rewrite or cast the expression.
+ insert into dposintatable values(array[array[42]::posint[]]); -- still fail
+ ERROR:  column "f1" is of type dposinta[] but expression is of type posint[]
+ LINE 1: insert into dposintatable values(array[array[42]::posint[]])...
+                                          ^
+ HINT:  You will need to rewrite or cast the expression.
+ insert into dposintatable values(array[array[42]::dposinta]); -- but this works
+ select f1, f1[1], (f1[1])[1] from dposintatable;
+     f1    |  f1  | f1 
+ ----------+------+----
+  {"{42}"} | {42} | 42
+ (1 row)
+ 
+ select pg_typeof(f1) from dposintatable;
+  pg_typeof  
+ ------------
+  dposinta[]
+ (1 row)
+ 
+ select pg_typeof(f1[1]) from dposintatable;
+  pg_typeof 
+ -----------
+  dposinta
+ (1 row)
+ 
+ select pg_typeof(f1[1][1]) from dposintatable;
+  pg_typeof 
+ -----------
+  dposinta
+ (1 row)
+ 
+ select pg_typeof((f1[1])[1]) from dposintatable;
+  pg_typeof 
+ -----------
+  posint
+ (1 row)
+ 
+ update dposintatable set f1[2] = array[99];
+ select f1, f1[1], (f1[2])[1] from dposintatable;
+        f1        |  f1  | f1 
+ -----------------+------+----
+  {"{42}","{99}"} | {42} | 99
+ (1 row)
+ 
+ -- it'd be nice if you could do something like this, but for now you can't:
+ update dposintatable set f1[2][1] = array[97];
+ ERROR:  wrong number of array subscripts
+ -- maybe someday we can make this syntax work:
+ update dposintatable set (f1[2])[1] = array[98];
+ ERROR:  syntax error at or near "["
+ LINE 1: update dposintatable set (f1[2])[1] = array[98];
+                                         ^
+ drop table dposintatable;
+ drop domain posint cascade;
+ NOTICE:  drop cascades to type dposinta
  -- Test not-null restrictions
  create domain dnotnull varchar(15) NOT NULL;
  create domain dnull    varchar(15);
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index 0fd383e..8fb3e20 100644
*** a/src/test/regress/sql/domain.sql
--- b/src/test/regress/sql/domain.sql
*************** drop table dcomptable;
*** 166,171 ****
--- 166,214 ----
  drop type comptype cascade;
  
  
+ -- Test arrays over domains
+ 
+ create domain posint as int check (value > 0);
+ 
+ create table pitable (f1 posint[]);
+ insert into pitable values(array[42]);
+ insert into pitable values(array[-1]);  -- fail
+ insert into pitable values('{0}');  -- fail
+ update pitable set f1[1] = f1[1] + 1;
+ update pitable set f1[1] = 0;  -- fail
+ select * from pitable;
+ drop table pitable;
+ 
+ create domain vc4 as varchar(4);
+ create table vc4table (f1 vc4[]);
+ insert into vc4table values(array['too long']);  -- fail
+ insert into vc4table values(array['too long']::vc4[]);  -- cast truncates
+ select * from vc4table;
+ drop table vc4table;
+ drop type vc4;
+ 
+ -- You can sort of fake arrays-of-arrays by putting a domain in between
+ create domain dposinta as posint[];
+ create table dposintatable (f1 dposinta[]);
+ insert into dposintatable values(array[array[42]]);  -- fail
+ insert into dposintatable values(array[array[42]::posint[]]); -- still fail
+ insert into dposintatable values(array[array[42]::dposinta]); -- but this works
+ select f1, f1[1], (f1[1])[1] from dposintatable;
+ select pg_typeof(f1) from dposintatable;
+ select pg_typeof(f1[1]) from dposintatable;
+ select pg_typeof(f1[1][1]) from dposintatable;
+ select pg_typeof((f1[1])[1]) from dposintatable;
+ update dposintatable set f1[2] = array[99];
+ select f1, f1[1], (f1[2])[1] from dposintatable;
+ -- it'd be nice if you could do something like this, but for now you can't:
+ update dposintatable set f1[2][1] = array[97];
+ -- maybe someday we can make this syntax work:
+ update dposintatable set (f1[2])[1] = array[98];
+ 
+ drop table dposintatable;
+ drop domain posint cascade;
+ 
+ 
  -- Test not-null restrictions
  
  create domain dnotnull varchar(15) NOT NULL;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to