From 555e65d5fc74aa7be75d7873c2180cd93ec915ac Mon Sep 17 00:00:00 2001 From: amit Date: Mon, 13 May 2019 13:02:47 +0900 Subject: [PATCH v3 3/3] Fix planner to not prune partitions with non-immutable operators --- src/backend/partitioning/partprune.c | 37 ++++++++++++++++++++++----- src/test/regress/expected/partition_prune.out | 17 ++++++++++++ src/test/regress/sql/partition_prune.sql | 10 ++++++++ 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 92806ab9e1..e19c7b6917 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -39,6 +39,7 @@ #include "access/nbtree.h" #include "catalog/pg_operator.h" #include "catalog/pg_opfamily.h" +#include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "executor/executor.h" #include "miscadmin.h" @@ -583,7 +584,8 @@ gen_partprune_steps(RelOptInfo *rel, List *clauses, bool *contradictory) Bitmapset * prune_append_rel_partitions(RelOptInfo *rel) { - List *clauses = rel->baserestrictinfo; + List *safe_restrictions = NIL; + ListCell *lc; List *pruning_steps; bool contradictory; PartitionPruneContext context; @@ -598,14 +600,27 @@ prune_append_rel_partitions(RelOptInfo *rel) * If pruning is disabled or if there are no clauses to prune with, return * all partitions. */ - if (!enable_partition_pruning || clauses == NIL) + if (!enable_partition_pruning) + return bms_add_range(NULL, 0, rel->nparts - 1); + + /* We dare not perform pruning with non-immutable functions. */ + foreach(lc, rel->baserestrictinfo) + { + RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); + + if (!contain_mutable_functions((Node *) rinfo->clause)) + safe_restrictions = lappend(safe_restrictions, rinfo); + } + + if (safe_restrictions == NIL) return bms_add_range(NULL, 0, rel->nparts - 1); /* * Process clauses. If the clauses are found to be contradictory, we can * return the empty set. */ - pruning_steps = gen_partprune_steps(rel, clauses, &contradictory); + pruning_steps = gen_partprune_steps(rel, safe_restrictions, + &contradictory); if (contradictory) return NULL; @@ -2926,8 +2941,9 @@ analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps, /* * Steps require run-time pruning if they contain EXEC_PARAM Params. - * Otherwise, if their expressions aren't simple Consts, they require - * startup-time pruning. + * Otherwise, if their expressions aren't simple Consts or if the + * comparison functions are non-immutable, they require startup-time + * pruning. */ pinfo->nexprs = list_length(steps) * partnatts; pinfo->hasexecparam = (bool *) palloc0(sizeof(bool) * pinfo->nexprs); @@ -2938,16 +2954,18 @@ analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps, foreach(lc, steps) { PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc); - ListCell *lc2; + ListCell *lc2, + *lc3; int keyno; if (!IsA(step, PartitionPruneStepOp)) continue; keyno = 0; - foreach(lc2, step->exprs) + forboth(lc2, step->exprs, lc3, step->cmpfns) { Expr *expr = lfirst(lc2); + Oid fnoid = lfirst_oid(lc3); if (!IsA(expr, Const)) { @@ -2970,6 +2988,11 @@ analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps, doruntimeprune = true; } + else if (func_volatile(fnoid) != PROVOLATILE_IMMUTABLE) + { + doruntimeprune = true; + pinfo->do_initial_prune = true; + } keyno++; } } diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 3edbbc6dbb..ba6dd4734c 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -3727,3 +3727,20 @@ explain (costs off) update listp1 set a = 1 where a = 2; reset constraint_exclusion; reset enable_partition_pruning; drop table listp; +-- +-- check that stable query clauses are only used in run-time pruning +-- +create table stable_qual_pruning(a timestamp) partition by range (a); +create table stable_qual_pruning1 partition of stable_qual_pruning for values from ('2019-01-01') to ('2019-02-01'); +create table stable_qual_pruning2 partition of stable_qual_pruning for values from ('2019-02-01') to ('2019-03-01'); +-- timestamp < timestamptz comparison is only stable, not immutable +explain (analyze, costs off, summary off, timing off) select * from stable_qual_pruning where a < '2019-02-01'::timestamptz; + QUERY PLAN +-------------------------------------------------------------------------------- + Append (actual rows=0 loops=1) + Subplans Removed: 1 + -> Seq Scan on stable_qual_pruning1 (actual rows=0 loops=1) + Filter: (a < 'Fri Feb 01 00:00:00 2019 PST'::timestamp with time zone) +(4 rows) + +drop table stable_qual_pruning; diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index 66489dff63..fd3778aa18 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -1019,3 +1019,13 @@ reset constraint_exclusion; reset enable_partition_pruning; drop table listp; + +-- +-- check that stable query clauses are only used in run-time pruning +-- +create table stable_qual_pruning(a timestamp) partition by range (a); +create table stable_qual_pruning1 partition of stable_qual_pruning for values from ('2019-01-01') to ('2019-02-01'); +create table stable_qual_pruning2 partition of stable_qual_pruning for values from ('2019-02-01') to ('2019-03-01'); +-- timestamp < timestamptz comparison is only stable, not immutable +explain (analyze, costs off, summary off, timing off) select * from stable_qual_pruning where a < '2019-02-01'::timestamptz; +drop table stable_qual_pruning; -- 2.11.0