diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd7..85d76b2 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -886,6 +886,56 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				break;
 			}
 
+		case T_MapExpr:
+			{
+				MapExpr	   *map = (MapExpr *) node;
+				ExprState  *elemstate;
+				Oid			resultelemtype;
+
+				ExecInitExprRec(map->arrexpr, state, resv, resnull);
+
+				resultelemtype = get_element_type(map->resulttype);
+				if (!OidIsValid(resultelemtype))
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("target type is not an array")));
+
+				/* Construct a sub-expression for the per-element expression */
+				elemstate = makeNode(ExprState);
+				elemstate->expr = map->elemexpr;
+				elemstate->parent = state->parent;
+				elemstate->ext_params = state->ext_params;
+				elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
+				elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
+
+				ExecInitExprRec(map->elemexpr, elemstate,
+								&elemstate->resvalue, &elemstate->resnull);
+
+				/* Append a DONE step and ready the subexpression */
+				scratch.opcode = EEOP_DONE;
+				ExprEvalPushStep(elemstate, &scratch);
+				ExecReadyExpr(elemstate);
+
+				scratch.opcode = EEOP_MAP;
+				scratch.d.map.elemexprstate = elemstate;
+				scratch.d.map.resultelemtype = resultelemtype;
+
+				if (elemstate)
+				{
+					/* Set up workspace for array_map */
+					scratch.d.map.amstate =
+						(ArrayMapState *) palloc0(sizeof(ArrayMapState));
+				}
+				else
+				{
+					/* Don't need workspace if there's no subexpression */
+					scratch.d.map.amstate = NULL;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+				break;
+			}
+
 		case T_OpExpr:
 			{
 				OpExpr	   *op = (OpExpr *) node;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 9d6e25a..b2cbc45 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -328,6 +328,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_FUNCEXPR_STRICT,
 		&&CASE_EEOP_FUNCEXPR_FUSAGE,
 		&&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEOP_MAP,
 		&&CASE_EEOP_BOOL_AND_STEP_FIRST,
 		&&CASE_EEOP_BOOL_AND_STEP,
 		&&CASE_EEOP_BOOL_AND_STEP_LAST,
@@ -699,6 +700,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			EEO_NEXT();
 		}
 
+		EEO_CASE(EEOP_MAP)
+		{
+			ExecEvalMapExpr(state, op, econtext);
+
+			EEO_NEXT();
+		}
+
 		/*
 		 * If any of its clauses is FALSE, an AND's result is FALSE regardless
 		 * of the states of the rest of the clauses, so we can stop evaluating
@@ -2230,6 +2238,33 @@ ExecEvalFuncExprStrictFusage(ExprState *state, ExprEvalStep *op,
 }
 
 /*
+ * Evaluate a MapExpr expression.
+ *
+ * Source array is in step's result variable.
+ */
+void
+ExecEvalMapExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	Datum		arraydatum;
+
+	/* NULL array -> NULL result */
+	if (*op->resnull)
+		return;
+
+	arraydatum = *op->resvalue;
+	Assert(op->d.map.elemexprstate != NULL);
+
+	/*
+	 * Use array_map to apply the sub-expression to each array element.
+	 */
+	*op->resvalue = array_map(arraydatum,
+							  op->d.map.elemexprstate,
+							  econtext,
+							  op->d.map.resultelemtype,
+							  op->d.map.amstate);
+}
+
+/*
  * Evaluate a PARAM_EXEC parameter.
  *
  * PARAM_EXEC params (internal executor parameters) are stored in the
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7c045a7..c877c29 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2621,6 +2621,32 @@ _copyFuncCall(const FuncCall *from)
 	return newnode;
 }
 
+static A_MapExpr *
+_copyAMapExpr(const A_MapExpr *from)
+{
+	A_MapExpr   *newnode = makeNode(A_MapExpr);
+
+	COPY_NODE_FIELD(elemexpr);
+	COPY_STRING_FIELD(placeholder);
+	COPY_NODE_FIELD(arrexpr);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
+static MapExpr *
+_copyMapExpr(const MapExpr *from)
+{
+	MapExpr   *newnode = makeNode(MapExpr);
+
+	COPY_NODE_FIELD(elemexpr);
+	COPY_NODE_FIELD(arrexpr);
+	COPY_SCALAR_FIELD(resulttype);
+	COPY_SCALAR_FIELD(resultcollid);
+
+	return newnode;
+}
+
 static A_Star *
 _copyAStar(const A_Star *from)
 {
@@ -5496,6 +5522,12 @@ copyObjectImpl(const void *from)
 		case T_FuncCall:
 			retval = _copyFuncCall(from);
 			break;
+		case T_A_MapExpr:
+			retval = _copyAMapExpr(from);
+			break;
+		case T_MapExpr:
+			retval = _copyMapExpr(from);
+			break;
 		case T_A_Star:
 			retval = _copyAStar(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6a971d0..18e2555 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2350,6 +2350,28 @@ _equalFuncCall(const FuncCall *a, const FuncCall *b)
 }
 
 static bool
+_equalAMapExpr(const A_MapExpr *a, const A_MapExpr *b)
+{
+	COMPARE_NODE_FIELD(elemexpr);
+	COMPARE_STRING_FIELD(placeholder);
+	COMPARE_NODE_FIELD(arrexpr);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
+static bool
+_equalMapExpr(const MapExpr *a, const MapExpr *b)
+{
+	COMPARE_NODE_FIELD(elemexpr);
+	COMPARE_NODE_FIELD(arrexpr);
+	COMPARE_SCALAR_FIELD(resulttype);
+	COMPARE_SCALAR_FIELD(resultcollid);
+
+	return true;
+}
+
+static bool
 _equalAStar(const A_Star *a, const A_Star *b)
 {
 	return true;
@@ -3573,6 +3595,12 @@ equal(const void *a, const void *b)
 		case T_FuncCall:
 			retval = _equalFuncCall(a, b);
 			break;
+		case T_A_MapExpr:
+			retval = _equalAMapExpr(a, b);
+			break;
+		case T_MapExpr:
+			retval = _equalMapExpr(a, b);
+			break;
 		case T_A_Star:
 			retval = _equalAStar(a, b);
 			break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index a10014f..95a3844 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -80,6 +80,8 @@ exprType(const Node *expr)
 		case T_FuncExpr:
 			type = ((const FuncExpr *) expr)->funcresulttype;
 			break;
+		case T_MapExpr:
+			return ((MapExpr *) expr)->resulttype;
 		case T_NamedArgExpr:
 			type = exprType((Node *) ((const NamedArgExpr *) expr)->arg);
 			break;
@@ -750,6 +752,9 @@ exprCollation(const Node *expr)
 		case T_FuncExpr:
 			coll = ((const FuncExpr *) expr)->funccollid;
 			break;
+		case T_MapExpr:
+			coll = ((const MapExpr *) expr)->resultcollid;
+			break;
 		case T_NamedArgExpr:
 			coll = exprCollation((Node *) ((const NamedArgExpr *) expr)->arg);
 			break;
@@ -994,6 +999,9 @@ exprSetCollation(Node *expr, Oid collation)
 		case T_FuncExpr:
 			((FuncExpr *) expr)->funccollid = collation;
 			break;
+		case T_MapExpr:
+			((MapExpr *) expr)->resultcollid = collation;
+			break;
 		case T_NamedArgExpr:
 			Assert(collation == exprCollation((Node *) ((NamedArgExpr *) expr)->arg));
 			break;
@@ -1132,6 +1140,10 @@ exprSetInputCollation(Node *expr, Oid inputcollation)
 		case T_FuncExpr:
 			((FuncExpr *) expr)->inputcollid = inputcollation;
 			break;
+		case T_MapExpr:
+			exprSetInputCollation((Node *) ((MapExpr *) expr)->elemexpr,
+								  inputcollation);
+			break;
 		case T_OpExpr:
 			((OpExpr *) expr)->inputcollid = inputcollation;
 			break;
@@ -1937,6 +1949,16 @@ expression_tree_walker(Node *node,
 					return true;
 			}
 			break;
+		case T_MapExpr:
+			{
+				MapExpr	   *map = (MapExpr *) node;
+
+				if (walker((Node *) map->arrexpr, context))
+					return true;
+				if (walker((Node *) map->elemexpr, context))
+					return true;
+			}
+			break;
 		case T_NamedArgExpr:
 			return walker(((NamedArgExpr *) node)->arg, context);
 		case T_OpExpr:
@@ -2479,6 +2501,9 @@ expression_tree_mutator(Node *node,
 		case T_NextValueExpr:
 		case T_RangeTblRef:
 		case T_SortGroupClause:
+		case T_ColumnRef:
+		case T_ParamRef:
+		case T_A_Const:
 			return (Node *) copyObject(node);
 		case T_WithCheckOption:
 			{
@@ -2566,6 +2591,15 @@ expression_tree_mutator(Node *node,
 				return (Node *) newnode;
 			}
 			break;
+		case T_MapExpr:
+			{
+				MapExpr	   *expr = (MapExpr *) node;
+				MapExpr	   *newnode;
+
+				FLATCOPY(newnode, expr, MapExpr);
+				MUTATE(newnode->arrexpr, expr->arrexpr, Expr *);
+				return (Node *) newnode;
+			}
 		case T_NamedArgExpr:
 			{
 				NamedArgExpr *nexpr = (NamedArgExpr *) node;
@@ -3060,6 +3094,36 @@ expression_tree_mutator(Node *node,
 				return (Node *) newnode;
 			}
 			break;
+		case T_A_Expr:
+			{
+				A_Expr	   *expr = (A_Expr *) node;
+				A_Expr	   *newnode;
+
+				FLATCOPY(newnode, expr, A_Expr);
+				MUTATE(newnode->lexpr, expr->lexpr, Node *);
+				MUTATE(newnode->rexpr, expr->rexpr, Node *);
+				return (Node *) newnode;
+			}
+			break;
+		case T_FuncCall:
+			{
+				FuncCall   *fc = (FuncCall *) node;
+				FuncCall   *newnode;
+
+				FLATCOPY(newnode, fc, FuncCall);
+				MUTATE(newnode->args, fc->args, List *);
+				return (Node *) newnode;
+			}
+		case T_TypeCast:
+			{
+				TypeCast   *tc = (TypeCast *) node;
+				TypeCast   *newnode;
+
+				FLATCOPY(newnode, tc, TypeCast);
+				MUTATE(newnode->arg, tc->arg, Node *);
+				return (Node *) newnode;
+			}
+
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 1da9d7e..98b6740 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2793,6 +2793,28 @@ _outFuncCall(StringInfo str, const FuncCall *node)
 }
 
 static void
+_outAMapExpr(StringInfo str, const A_MapExpr *node)
+{
+	WRITE_NODE_TYPE("A_MAPEXPR");
+
+	WRITE_NODE_FIELD(elemexpr);
+	WRITE_STRING_FIELD(placeholder);
+	WRITE_NODE_FIELD(arrexpr);
+	WRITE_LOCATION_FIELD(location);
+}
+
+static void
+_outMapExpr(StringInfo str, const MapExpr *node)
+{
+	WRITE_NODE_TYPE("MAPEXPR");
+
+	WRITE_NODE_FIELD(elemexpr);
+	WRITE_NODE_FIELD(arrexpr);
+	WRITE_OID_FIELD(resulttype);
+	WRITE_OID_FIELD(resultcollid);
+}
+
+static void
 _outDefElem(StringInfo str, const DefElem *node)
 {
 	WRITE_NODE_TYPE("DEFELEM");
@@ -4278,6 +4300,12 @@ outNode(StringInfo str, const void *obj)
 			case T_FuncCall:
 				_outFuncCall(str, obj);
 				break;
+			case T_A_MapExpr:
+				_outAMapExpr(str, obj);
+				break;
+			case T_MapExpr:
+				_outMapExpr(str, obj);
+				break;
 			case T_DefElem:
 				_outDefElem(str, obj);
 				break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index babb62d..37a6e1c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -563,6 +563,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <node>	func_application func_expr_common_subexpr
 %type <node>	func_expr func_expr_windowless
+%type <node>	map_expr
 %type <node>	common_table_expr
 %type <with>	with_clause opt_with_clause
 %type <list>	cte_list
@@ -652,7 +653,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
 	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
 
-	MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
+	MAP MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
 
 	NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NONE
 	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
@@ -13461,6 +13462,8 @@ c_expr:		columnref								{ $$ = $1; }
 				}
 			| case_expr
 				{ $$ = $1; }
+			| map_expr
+				{ $$ = $1; }
 			| func_expr
 				{ $$ = $1; }
 			| select_with_parens			%prec UMINUS
@@ -13556,6 +13559,17 @@ c_expr:		columnref								{ $$ = $1; }
 			  }
 		;
 
+map_expr:	MAP '(' a_expr FOR IDENT IN_P a_expr ')'
+				{
+					A_MapExpr *m = makeNode(A_MapExpr);
+					m->elemexpr = $3;
+					m->placeholder = $5;
+					m->arrexpr = $7;
+					m->location = @1;
+					$$ = (Node *)m;
+				}
+		;
+
 func_application: func_name '(' ')'
 				{
 					$$ = (Node *) makeFuncCall($1, NIL, @1);
@@ -15423,6 +15437,7 @@ reserved_keyword:
 			| LIMIT
 			| LOCALTIME
 			| LOCALTIMESTAMP
+			| MAP
 			| NOT
 			| NULL_P
 			| OFFSET
diff --git a/src/backend/parser/parse_collate.c b/src/backend/parser/parse_collate.c
index 6d34245..0021b24 100644
--- a/src/backend/parser/parse_collate.c
+++ b/src/backend/parser/parse_collate.c
@@ -667,6 +667,15 @@ assign_collations_walker(Node *node, assign_collations_context *context)
 															&loccontext);
 						}
 						break;
+					case T_MapExpr:
+						{
+							MapExpr	   *map = (MapExpr *) node;
+
+							(void) expression_tree_walker((Node *) map->elemexpr,
+														  assign_collations_walker,
+														  (void *) &loccontext);
+						}
+						break;
 					default:
 
 						/*
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 385e54a..18e7310 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -101,6 +101,9 @@ static Node *transformAExprIn(ParseState *pstate, A_Expr *a);
 static Node *transformAExprBetween(ParseState *pstate, A_Expr *a);
 static Node *transformBoolExpr(ParseState *pstate, BoolExpr *a);
 static Node *transformFuncCall(ParseState *pstate, FuncCall *fn);
+static Node *convert_placeholder_mutator(Node *node, 
+							convert_placeholder_context *context);
+static Node *transformMapExpr(ParseState *pstate, A_MapExpr *a_mapexpr);
 static Node *transformMultiAssignRef(ParseState *pstate, MultiAssignRef *maref);
 static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c);
 static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
@@ -135,6 +138,14 @@ static void emit_precedence_warnings(ParseState *pstate,
 						 Node *lchild, Node *rchild,
 						 int location);
 
+/* Context for convert_placeholder_mutator */
+typedef struct
+{
+	char   *placeholder;
+	Oid		typid;
+	Oid		typmod;
+	Oid		collid;
+} convert_placeholder_context;
 
 /*
  * transformExpr -
@@ -258,6 +269,10 @@ transformExprRecurse(ParseState *pstate, Node *expr)
 				break;
 			}
 
+		case T_A_MapExpr:
+			result = transformMapExpr(pstate, (A_MapExpr *) expr);
+			break;
+
 		case T_BoolExpr:
 			result = transformBoolExpr(pstate, (BoolExpr *) expr);
 			break;
@@ -1486,6 +1501,99 @@ transformFuncCall(ParseState *pstate, FuncCall *fn)
 }
 
 static Node *
+convert_placeholder(Node *node, char *placeholder,
+					Oid typid, Oid typmod, Oid collid)
+{
+	convert_placeholder_context context;
+
+	context.placeholder = placeholder;
+	context.typid = typid;
+	context.typmod = typmod;
+	context.collid = collid;
+
+	return convert_placeholder_mutator(node, &context);
+}
+
+static Node *
+convert_placeholder_mutator(Node *node,
+							convert_placeholder_context *context)
+{
+	if (node == NULL)
+		return NULL;
+	if (IsA(node, ColumnRef))
+	{
+		ColumnRef  *cref = (ColumnRef *) node;
+		char	   *refname;
+
+		if (list_length(cref->fields) != 1)
+			return node;
+
+		refname = strVal(linitial(cref->fields));
+
+		if (strcmp(refname, context->placeholder) == 0)
+		{
+			CaseTestExpr *placeholder;
+
+			/* Create placeholder for per-element expression */
+			placeholder = makeNode(CaseTestExpr);
+			placeholder->typeId = context->typid;
+			placeholder->typeMod = context->typmod;
+			placeholder->collation = context->collid;
+
+			return (Node *) placeholder;
+		}
+	}
+
+	return expression_tree_mutator(node,
+								   convert_placeholder_mutator,
+								   (void *) context);
+}
+
+static Node *
+transformMapExpr(ParseState *pstate, A_MapExpr *a_mapexpr)
+{
+	MapExpr	   *map;
+	CaseTestExpr *placeholder;
+	Node	   *arrexpr;
+	Oid			funcId;
+	Oid			sourceElemType;
+	Oid			targetElemType;
+	Node	   *elemexpr;
+	Oid			collation;
+
+	/* Transfotm array expression */
+	arrexpr = transformExprRecurse(pstate, a_mapexpr->arrexpr);
+
+	/* Determine element type and collation */
+	sourceElemType = get_element_type(exprType(arrexpr));
+	if (sourceElemType == InvalidOid)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("array expression is expected"),
+				 parser_errposition(pstate, exprLocation(a_mapexpr->arrexpr))));
+	collation = IsA(arrexpr, ArrayExpr) ?
+		get_typcollation(sourceElemType) :
+		exprCollation(arrexpr);
+
+	/* Replace placeholder ColumnRef by CaseTestExpr */
+	elemexpr = convert_placeholder(a_mapexpr->elemexpr,
+									a_mapexpr->placeholder,
+									sourceElemType,
+									exprTypmod(arrexpr),
+									collation);
+	/* Transform per-element expression */
+	elemexpr = transformExprRecurse(pstate, elemexpr);
+
+	/* Build resulting MapExpr */
+	map = makeNode(MapExpr);
+	map->arrexpr = (Expr *) arrexpr;
+	map->elemexpr = (Expr *) elemexpr;
+	map->resulttype = get_array_type(exprType(elemexpr));
+
+	return (Node *) map;
+}
+
+static Node *
 transformMultiAssignRef(ParseState *pstate, MultiAssignRef *maref)
 {
 	SubLink    *sublink;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index f7b1f77..ddad247 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -92,6 +92,11 @@ typedef enum ExprEvalOp
 	EEOP_FUNCEXPR_STRICT_FUSAGE,
 
 	/*
+	 * Evaluate map expression
+	 */
+	EEOP_MAP,
+
+	/*
 	 * Evaluate boolean AND expression, one step per subexpression. FIRST/LAST
 	 * subexpressions are special-cased for performance.  Since AND always has
 	 * at least two subexpressions, FIRST and LAST never apply to the same
@@ -318,6 +323,13 @@ typedef struct ExprEvalStep
 			int			nargs;	/* number of arguments */
 		}			func;
 
+		struct
+		{
+			ExprState  *elemexprstate;	/* null if no per-element work */
+			Oid			resultelemtype; /* element type of result array */
+			struct ArrayMapState *amstate;	/* workspace for array_map */
+		}			map;
+
 		/* for EEOP_BOOL_*_STEP */
 		struct
 		{
@@ -695,6 +707,8 @@ extern void ExecEvalFuncExprFusage(ExprState *state, ExprEvalStep *op,
 					   ExprContext *econtext);
 extern void ExecEvalFuncExprStrictFusage(ExprState *state, ExprEvalStep *op,
 							 ExprContext *econtext);
+extern void ExecEvalMapExpr(ExprState *state, ExprEvalStep *op,
+				  ExprContext *econtext);
 extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op,
 				  ExprContext *econtext);
 extern void ExecEvalParamExecParams(Bitmapset *params, EState *estate);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index adb159a..d34abc2 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -428,6 +428,8 @@ typedef enum NodeTag
 	T_ParamRef,
 	T_A_Const,
 	T_FuncCall,
+	T_A_MapExpr,
+	T_MapExpr,
 	T_A_Star,
 	T_A_Indices,
 	T_A_Indirection,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 6390f7e..be4b2d1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -418,6 +418,18 @@ typedef struct A_ArrayExpr
 } A_ArrayExpr;
 
 /*
+ * A_MapExpr - array map expression
+ */
+typedef struct A_MapExpr
+{
+	NodeTag		type;
+	Node	   *elemexpr;
+	char	   *placeholder;
+	Node	   *arrexpr;
+	int			location;		/* token location, or -1 if unknown */
+} A_MapExpr;
+
+/*
  * ResTarget -
  *	  result target (used in target list of pre-transformed parse trees)
  *
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index f90aa7b..f108898 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -460,6 +460,18 @@ typedef struct FuncExpr
 } FuncExpr;
 
 /*
+ * MapExpr - array map expression
+ */
+typedef struct MapExpr
+{
+	NodeTag		type;
+	Expr	   *elemexpr;		/* expression representing per-element work */
+	Expr	   *arrexpr;		/* source expression of array type */
+	Oid			resulttype;		/* OID of target array type */
+	Oid			resultcollid;	/* OID of collation, or InvalidOid if none */
+} MapExpr;
+
+/*
  * NamedArgExpr - a named argument of a function
  *
  * This node type can only appear in the args list of a FuncCall or FuncExpr
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 23db401..d6c5254 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -243,6 +243,7 @@ PG_KEYWORD("location", LOCATION, UNRESERVED_KEYWORD)
 PG_KEYWORD("lock", LOCK_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("locked", LOCKED, UNRESERVED_KEYWORD)
 PG_KEYWORD("logged", LOGGED, UNRESERVED_KEYWORD)
+PG_KEYWORD("map", MAP, RESERVED_KEYWORD)
 PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
 PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
 PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
