On 2017/12/11 14:31, Amit Langote wrote: > On 2017/12/09 3:46, Robert Haas wrote: >> On Fri, Dec 8, 2017 at 5:40 AM, Amit Langote wrote: >>> I noticed that if you partition using a array type column, partition >>> pruning using constraint exclusion fails to work due to a minor problem. >> >> I guess the question is whether that's guaranteed to be safe. I spent >> a little bit of time thinking about it and I don't see a problem. The >> function is careful to check that the opclasses and collations of the >> OpExprs are compatible, and it is the behavior of the operator that is >> in question here, not the column type, so your change seems OK to me. >> But I hope somebody else will also study this, because this stuff is >> fairly subtle and I would not like to be the one who breaks it. > > Thanks for taking a look at it. > > I will try to say a little more on why it seems safe. RelabelType node > exists (if any) on top of a given expression node only to denote that the > operator for which the node is an input will interpret its result as of > the type RelableType.resulttype, instead of the node's original type. No > conversion of values actually occurs before making any decisions that this > function is in charge of making, because the mismatching types in question > are known to be binary coercible. Or more to the point, the operator that > will be used in the proof will give correct answers for the values without > having to do any conversion of values. IOW, it's okay if we simply drop > the RelabelType, because it doesn't alter in any way the result of the > proof that operator_predicate_proof() performs. > > That said, I've to come think in this particular case that the > partitioning code that generates the predicate expression should be a bit > smarter about the various types it manipulates such that RelabelType won't > be added in the first place. In contrast, make_op(), that generates an > OpExpr from the parser representation of a = '{1}' appearing in the > query's WHERE clause, won't add the RelabelType because the underlying > type machinery that it invokes is able to conclude that that's > unnecessary. The original patch may still be worth considering as a > solution though. > > I hope someone else chimes in as well. :)
Bug #15042 [1] seems to be caused by this same problem. There, a RelabelType node is being slapped (by the partitioning code) on a Var node of a partition key on enum. Attached updated patch. Thanks, Amit [1] https://www.postgresql.org/message-id/151747550943.1247.2111555422760537959%40wrigleys.postgresql.org
From c4e6a456309e97f0391b2bf8b417b8a71cfac778 Mon Sep 17 00:00:00 2001 From: amit <amitlangot...@gmail.com> Date: Fri, 8 Dec 2017 19:09:31 +0900 Subject: [PATCH v2] Teach operator_predicate_proof() to strip RelabelType --- src/backend/optimizer/util/predtest.c | 13 +++++--- src/test/regress/expected/partition_prune.out | 45 +++++++++++++++++++++++++++ src/test/regress/sql/partition_prune.sql | 18 +++++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c index 8106010329..64b0926641 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 348719bd62..cf25c8ec86 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -1088,4 +1088,49 @@ 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 arrpart; +-- enum type list partition key +create type colors as enum ('green', 'blue'); +create table enumpart (a colors) partition by list (a); +create table enumpart_green partition of enumpart for values in ('green'); +create table enumpart_blue partition of enumpart for values in ('blue'); +explain (costs off) select * from enumpart where a = 'blue'; + QUERY PLAN +-------------------------------------- + Append + -> Seq Scan on enumpart_blue + Filter: (a = 'blue'::colors) +(3 rows) + +drop table enumpart; +drop type colors; 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..abe1b56c80 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -152,4 +152,22 @@ 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 arrpart; + +-- enum type list partition key +create type colors as enum ('green', 'blue'); +create table enumpart (a colors) partition by list (a); +create table enumpart_green partition of enumpart for values in ('green'); +create table enumpart_blue partition of enumpart for values in ('blue'); +explain (costs off) select * from enumpart where a = 'blue'; +drop table enumpart; +drop type colors; + drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart; -- 2.11.0