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;