Hi.

I noticed that if you partition using a array type column, partition
pruning using constraint exclusion fails to work due to a minor problem.

Example:

create table p (a int[]) partition by list (a);
create table p1 partition of p for values in ('{1}');
create table p1 partition of p for values in ('{2, 3}', '{4, 5}');

explain select a from p where a = '{1}';
                        QUERY PLAN
|---------------------------------------------------------
 Append  (cost=0.00..54.00 rows=14 width=32)
   ->  Seq Scan on p1  (cost=0.00..27.00 rows=7 width=32)
         Filter: (a = '{1}'::integer[])
   ->  Seq Scan on p2  (cost=0.00..27.00 rows=7 width=32)
         Filter: (a = '{1}'::integer[])

explain select a from p where a = '{2, 3}';
                        QUERY PLAN
|---------------------------------------------------------
 Append  (cost=0.00..54.00 rows=14 width=32)
   ->  Seq Scan on p1  (cost=0.00..27.00 rows=7 width=32)
         Filter: (a = '{2,3}'::integer[])
   ->  Seq Scan on p2  (cost=0.00..27.00 rows=7 width=32)
         Filter: (a = '{2,3}'::integer[])
(5 rows)


In the case of array type partition key, make_partition_op_expr() will
have to put a RelabelType node on top of the partition key Var, after
having selected an = operator from the array_ops family.  The RelabelType
causes operator_predicate_proof() to fail to consider predicate leftop and
clause leftop as equal, because only one of them ends up having the
RelabelType attached to it.

As a simple measure, the attached patch teaches operator_predicate_proof()
to strip RelabelType nodes from all the nodes it compares using equal().
I also added a relevant test in partition_prune.sql.

Thoughts?

Thanks,
Amit
From 2a25202f2f0a415c6884c28fd5bc76243507d5c4 Mon Sep 17 00:00:00 2001
From: amit <amitlangot...@gmail.com>
Date: Fri, 8 Dec 2017 19:09:31 +0900
Subject: [PATCH] Teach operator_predicate_proof() to strip RelabelType

---
 src/backend/optimizer/util/predtest.c         | 13 ++++++++----
 src/test/regress/expected/partition_prune.out | 29 +++++++++++++++++++++++++++
 src/test/regress/sql/partition_prune.sql      |  8 ++++++++
 3 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/src/backend/optimizer/util/predtest.c 
b/src/backend/optimizer/util/predtest.c
index 134460cc13..d0f6278984 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -1407,6 +1407,11 @@ static const StrategyNumber BT_refute_table[6][6] = {
        {none, none, BTEQ, none, none, none}    /* NE */
 };
 
+/* Strip expr of the surrounding RelabelType, if any. */
+#define strip_relabel(expr) \
+               ((Node *) (IsA((expr), RelabelType) \
+                                       ? ((RelabelType *) (expr))->arg \
+                                       : (expr)))
 
 /*
  * operator_predicate_proof
@@ -1503,10 +1508,10 @@ operator_predicate_proof(Expr *predicate, Node *clause, 
bool refute_it)
        /*
         * We have to match up at least one pair of input expressions.
         */
-       pred_leftop = (Node *) linitial(pred_opexpr->args);
-       pred_rightop = (Node *) lsecond(pred_opexpr->args);
-       clause_leftop = (Node *) linitial(clause_opexpr->args);
-       clause_rightop = (Node *) lsecond(clause_opexpr->args);
+       pred_leftop = strip_relabel(linitial(pred_opexpr->args));
+       pred_rightop = strip_relabel(lsecond(pred_opexpr->args));
+       clause_leftop = strip_relabel(linitial(clause_opexpr->args));
+       clause_rightop = strip_relabel(lsecond(clause_opexpr->args));
 
        if (equal(pred_leftop, clause_leftop))
        {
diff --git a/src/test/regress/expected/partition_prune.out 
b/src/test/regress/expected/partition_prune.out
index aabb0240a9..7c78f11d55 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -1092,4 +1092,33 @@ explain (costs off) select * from boolpart where a is 
not unknown;
          Filter: (a IS NOT UNKNOWN)
 (7 rows)
 
+-- array type list partition key
+create table arrpart (a int[]) partition by list (a);
+create table arrpart1 partition of arrpart for values in ('{1}');
+create table arrpart2 partition of arrpart for values in ('{2, 3}', '{4, 5}');
+explain (costs off) select * from arrpart where a = '{1}';
+               QUERY PLAN               
+----------------------------------------
+ Append
+   ->  Seq Scan on arrpart1
+         Filter: (a = '{1}'::integer[])
+(3 rows)
+
+explain (costs off) select * from arrpart where a = '{1, 2}';
+        QUERY PLAN        
+--------------------------
+ Result
+   One-Time Filter: false
+(2 rows)
+
+explain (costs off) select * from arrpart where a in ('{4, 5}', '{1}');
+                              QUERY PLAN                              
+----------------------------------------------------------------------
+ Append
+   ->  Seq Scan on arrpart1
+         Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[]))
+   ->  Seq Scan on arrpart2
+         Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[]))
+(5 rows)
+
 drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart;
diff --git a/src/test/regress/sql/partition_prune.sql 
b/src/test/regress/sql/partition_prune.sql
index 514f8e5ce1..c353387bb9 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -152,4 +152,12 @@ explain (costs off) select * from boolpart where a is not 
true and a is not fals
 explain (costs off) select * from boolpart where a is unknown;
 explain (costs off) select * from boolpart where a is not unknown;
 
+-- array type list partition key
+create table arrpart (a int[]) partition by list (a);
+create table arrpart1 partition of arrpart for values in ('{1}');
+create table arrpart2 partition of arrpart for values in ('{2, 3}', '{4, 5}');
+explain (costs off) select * from arrpart where a = '{1}';
+explain (costs off) select * from arrpart where a = '{1, 2}';
+explain (costs off) select * from arrpart where a in ('{4, 5}', '{1}');
+
 drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart;
-- 
2.11.0

Reply via email to