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 7496189..6337bfa 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 f2a52f6..98fa93e 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 45a04b0..9655f4a 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyArrayCoerceExpr(const ArrayCoerceEx
*** 1696,1706 ****
  	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);
  
--- 1696,1705 ----
  	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 8d92c03..9fdc887 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 379d92a..21cdf90 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outArrayCoerceExpr(StringInfo str, cons
*** 1392,1402 ****
  	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);
  }
--- 1392,1401 ----
  	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 86c811d..76ba570 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 b35acb7..3721a49 100644
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
*************** cost_qual_eval_walker(Node *node, cost_q
*** 3607,3617 ****
  	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))
  	{
--- 3607,3620 ----
  	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 8b4425d..e15f043 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 c406cea..8fe5444 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
*** 2142,2149 ****
   *	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
   *
--- 2175,2181 ----
   *	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, 
*** 2229,2239 ****
  	{
  		/*
  		 * 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
--- 2261,2268 ----
  	{
  		/*
  		 * 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, 
*** 2248,2254 ****
  			Oid			sourceElem;
  
  			if ((targetElem = get_element_type(targetTypeId)) != InvalidOid &&
! 				(sourceElem = get_base_element_type(sourceTypeId)) != InvalidOid)
  			{
  				CoercionPathType elempathtype;
  				Oid			elemfuncid;
--- 2277,2283 ----
  			Oid			sourceElem;
  
  			if ((targetElem = get_element_type(targetTypeId)) != InvalidOid &&
! 				(sourceElem = get_element_type(sourceTypeId)) != InvalidOid)
  			{
  				CoercionPathType elempathtype;
  				Oid			elemfuncid;
*************** find_coercion_pathway(Oid targetTypeId, 
*** 2257,2270 ****
  													 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;
  				}
  			}
  		}
--- 2286,2294 ----
  													 sourceElem,
  													 ccontext,
  													 &elemfuncid);
! 				if (elempathtype != COERCION_PATH_NONE)
  				{
! 					result = COERCION_PATH_ARRAYCOERCE;
  				}
  			}
  		}
*************** find_coercion_pathway(Oid targetTypeId, 
*** 2305,2311 ****
   * 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:
--- 2329,2337 ----
   * 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 34dadd6..8287b39 100644
*** a/src/backend/utils/adt/arrayfuncs.c
--- b/src/backend/utils/adt/arrayfuncs.c
*************** array_set(ArrayType *array, int nSubscri
*** 3091,3111 ****
  /*
   * 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.
   *
--- 3091,3108 ----
  /*
   * 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
*** 3115,3125 ****
   *
   * 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;
--- 3112,3125 ----
   *
   * 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
*** 3140,3152 ****
  	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);
--- 3140,3147 ----
  	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
*** 3157,3163 ****
  	if (nitems <= 0)
  	{
  		/* Return empty array */
! 		PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
  	}
  
  	/*
--- 3152,3158 ----
  	if (nitems <= 0)
  	{
  		/* Return empty array */
! 		return PointerGetDatum(construct_empty_array(retType));
  	}
  
  	/*
*************** array_map(FunctionCallInfo fcinfo, Oid r
*** 3202,3240 ****
  
  	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
  		{
--- 3197,3211 ----
  
  	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
*** 3253,3259 ****
  		}
  	}
  
! 	/* Allocate and initialize the result array */
  	if (hasnulls)
  	{
  		dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
--- 3224,3230 ----
  		}
  	}
  
! 	/* Allocate and fill the result array */
  	if (hasnulls)
  	{
  		dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
*************** array_map(FunctionCallInfo fcinfo, Oid r
*** 3272,3289 ****
  	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);
  }
  
  /*
--- 3243,3260 ----
  	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 e103f5e..1bb0da1 100644
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** strip_array_coercion(Node *node)
*** 1752,1761 ****
  {
  	for (;;)
  	{
! 		if (node && IsA(node, ArrayCoerceExpr) &&
! 			((ArrayCoerceExpr *) node)->elemfuncid == InvalidOid)
  		{
! 			node = (Node *) ((ArrayCoerceExpr *) node)->arg;
  		}
  		else if (node && IsA(node, RelabelType))
  		{
--- 1752,1770 ----
  {
  	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/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index c98492b..eb584ac 100644
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** exec_simple_check_node(Node *node)
*** 6577,6583 ****
  			return exec_simple_check_node((Node *) ((CoerceViaIO *) node)->arg);
  
  		case T_ArrayCoerceExpr:
! 			return exec_simple_check_node((Node *) ((ArrayCoerceExpr *) node)->arg);
  
  		case T_ConvertRowtypeExpr:
  			return exec_simple_check_node((Node *) ((ConvertRowtypeExpr *) node)->arg);
--- 6577,6592 ----
  			return exec_simple_check_node((Node *) ((CoerceViaIO *) node)->arg);
  
  		case T_ArrayCoerceExpr:
! 			{
! 				ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
! 
! 				if (!exec_simple_check_node((Node *) expr->arg))
! 					return FALSE;
! 				if (!exec_simple_check_node((Node *) expr->elemexpr))
! 					return FALSE;
! 
! 				return TRUE;
! 			}
  
  		case T_ConvertRowtypeExpr:
  			return exec_simple_check_node((Node *) ((ConvertRowtypeExpr *) node)->arg);
