diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2012-01-29 16:31:31 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2012-01-29 16:31:31 -0500 |
commit | 106123fa269de39e5215eb8808889a78c9a45fe7 (patch) | |
tree | 0b65eeb8aeb819d1e9cba6055db5e09714ec99a2 | |
parent | b3bd5a093f3758cf168dae88478f9fc5b37ff6b1 (diff) | |
download | postgresql-106123fa269de39e5215eb8808889a78c9a45fe7.tar.gz postgresql-106123fa269de39e5215eb8808889a78c9a45fe7.zip |
Fix pushing of index-expression qualifications through UNION ALL.
In commit 57664ed25e5dea117158a2e663c29e60b3546e1c, I made the planner
wrap non-simple-variable outputs of appendrel children (IOW, child SELECTs
of UNION ALL subqueries) inside PlaceHolderVars, in order to solve some
issues with EquivalenceClass processing. However, this means that any
upper-level WHERE clauses mentioning such outputs will now contain
PlaceHolderVars after they're pushed down into the appendrel child,
and that prevents indxpath.c from recognizing that they could be matched
to index expressions. To fix, add explicit stripping of PlaceHolderVars
from index operands, same as we have long done for RelabelType nodes.
Add a regression test covering both this and the plain-UNION case (which
is a totally different code path, but should also be able to do it).
Per bug #6416 from Matteo Beccati. Back-patch to 9.1, same as the
previous change.
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 9 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 5 | ||||
-rw-r--r-- | src/test/regress/expected/union.out | 48 | ||||
-rw-r--r-- | src/test/regress/sql/union.sql | 33 |
4 files changed, 94 insertions, 1 deletions
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 58b8797b738..e97c1636b1a 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -2286,6 +2286,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: * we can assume there is at most one RelabelType node; diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 7a81010e4cf..bf0c6208333 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -2623,8 +2623,11 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index) 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 2f8037f9eb0..4fff2297751 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 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 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 daa72c929c0..b5c2128f3f3 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; |