diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 920593781b2a2cdef83496b2ae90f1870b21bbc5..d51e84a80a2ef918a96967d7ec7c52323fa207ef 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -2580,6 +2580,15 @@ match_index_to_operand(Node *operand,
 {
 	int			indkey;
 
+	/*
+	 * Ignore any PlaceHolderVar nodes above the operand.  This is needed so
+	 * that we can successfully use expression-index constraints pushed down
+	 * through appendrels (UNION ALL).  It's safe because a PlaceHolderVar
+	 * appearing in a relation-scan-level expression is certainly a no-op.
+	 */
+	while (operand && IsA(operand, PlaceHolderVar))
+		operand = (Node *) ((PlaceHolderVar *) operand)->phexpr;
+
 	/*
 	 * Ignore any RelabelType node above the operand.	This is needed to be
 	 * able to apply indexscanning in binary-compatible-operator cases. Note:
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index aea42b1dd461a3ef5d94bee79b980bdadfd57551..9ac0c9919027aa7f1066246425819533f61a493f 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2673,8 +2673,11 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
 	ListCell   *indexpr_item;
 
 	/*
-	 * Remove any binary-compatible relabeling of the indexkey
+	 * Remove any PlaceHolderVars or binary-compatible relabeling of the
+	 * indexkey (this must match logic in match_index_to_operand()).
 	 */
+	while (IsA(node, PlaceHolderVar))
+		node = (Node *) ((PlaceHolderVar *) node)->phexpr;
 	if (IsA(node, RelabelType))
 		node = (Node *) ((RelabelType *) node)->arg;
 
diff --git a/src/test/regress/expected/union.out b/src/test/regress/expected/union.out
index 2f8037f9eb04dae009f90e58bde11736d877cad2..2913f3d8f378796b711f78404dcc90620f5eadf2 100644
--- a/src/test/regress/expected/union.out
+++ b/src/test/regress/expected/union.out
@@ -455,3 +455,51 @@ SELECT '3.4'::numeric UNION SELECT 'foo';
 ERROR:  invalid input syntax for type numeric: "foo"
 LINE 1: SELECT '3.4'::numeric UNION SELECT 'foo';
                                            ^
+--
+-- Test that expression-index constraints can be pushed down through
+-- UNION or UNION ALL
+--
+CREATE TEMP TABLE t1 (a text, b text);
+CREATE INDEX t1_ab_idx on t1 ((a || b));
+CREATE TEMP TABLE t2 (ab text primary key);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "t2_pkey" for table "t2"
+INSERT INTO t1 VALUES ('a', 'b'), ('x', 'y');
+INSERT INTO t2 VALUES ('ab'), ('xy');
+set enable_seqscan = off;
+set enable_indexscan = on;
+set enable_bitmapscan = off;
+explain (costs off)
+ SELECT * FROM
+ (SELECT a || b AS ab FROM t1
+  UNION ALL
+  SELECT * FROM t2) t
+ WHERE ab = 'ab';
+                    QUERY PLAN                     
+---------------------------------------------------
+ Result
+   ->  Append
+         ->  Index Scan using t1_ab_idx on t1
+               Index Cond: ((a || b) = 'ab'::text)
+         ->  Index Only Scan using t2_pkey on t2
+               Index Cond: (ab = 'ab'::text)
+(6 rows)
+
+explain (costs off)
+ SELECT * FROM
+ (SELECT a || b AS ab FROM t1
+  UNION
+  SELECT * FROM t2) t
+ WHERE ab = 'ab';
+                    QUERY PLAN                     
+---------------------------------------------------
+ HashAggregate
+   ->  Append
+         ->  Index Scan using t1_ab_idx on t1
+               Index Cond: ((a || b) = 'ab'::text)
+         ->  Index Only Scan using t2_pkey on t2
+               Index Cond: (ab = 'ab'::text)
+(6 rows)
+
+reset enable_seqscan;
+reset enable_indexscan;
+reset enable_bitmapscan;
diff --git a/src/test/regress/sql/union.sql b/src/test/regress/sql/union.sql
index daa72c929c034de52be43e198674991ef28ca5f9..b5c2128f3f3826f49cd1a7287067dbc7ff275ed4 100644
--- a/src/test/regress/sql/union.sql
+++ b/src/test/regress/sql/union.sql
@@ -166,3 +166,36 @@ ORDER BY 1;
 
 -- This should fail, but it should produce an error cursor
 SELECT '3.4'::numeric UNION SELECT 'foo';
+
+--
+-- Test that expression-index constraints can be pushed down through
+-- UNION or UNION ALL
+--
+
+CREATE TEMP TABLE t1 (a text, b text);
+CREATE INDEX t1_ab_idx on t1 ((a || b));
+CREATE TEMP TABLE t2 (ab text primary key);
+INSERT INTO t1 VALUES ('a', 'b'), ('x', 'y');
+INSERT INTO t2 VALUES ('ab'), ('xy');
+
+set enable_seqscan = off;
+set enable_indexscan = on;
+set enable_bitmapscan = off;
+
+explain (costs off)
+ SELECT * FROM
+ (SELECT a || b AS ab FROM t1
+  UNION ALL
+  SELECT * FROM t2) t
+ WHERE ab = 'ab';
+
+explain (costs off)
+ SELECT * FROM
+ (SELECT a || b AS ab FROM t1
+  UNION
+  SELECT * FROM t2) t
+ WHERE ab = 'ab';
+
+reset enable_seqscan;
+reset enable_indexscan;
+reset enable_bitmapscan;