From 8ac87a9a8b1cc5a82946afa3c942d6353b57f847 Mon Sep 17 00:00:00 2001 From: amit Date: Mon, 13 May 2019 13:55:24 +0900 Subject: [PATCH v3] Fix planner to not prune partitions with non-immutable operators --- src/backend/partitioning/partprune.c | 43 ++++++++++++++++++++++----- src/test/regress/expected/partition_prune.out | 17 +++++++++++ src/test/regress/sql/partition_prune.sql | 10 +++++++ 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 7c894fc6ad..94954c7a6d 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" @@ -552,25 +553,45 @@ Relids prune_append_rel_partitions(RelOptInfo *rel) { Relids result; - List *clauses = rel->baserestrictinfo; + List *safe_restrictions = NIL; + ListCell *lc; List *pruning_steps; bool contradictory; PartitionPruneContext context; Bitmapset *partindexes; int i; - Assert(clauses != NIL); + Assert(rel->baserestrictinfo != NIL); Assert(rel->part_scheme != NULL); /* If there are no partitions, return the empty set */ if (rel->nparts == 0) return NULL; + /* 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); + } + + /* No clauses to perform pruning with; return all partitions. */ + if (safe_restrictions == NIL) + { + result = NULL; + for (i = 0; i < rel->nparts; i++) + result = bms_add_member(result, rel->part_rels[i]->relid); + return result; + } + /* * 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; @@ -2896,8 +2917,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); @@ -2908,16 +2930,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)) { @@ -2940,6 +2964,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 889d4e14e2..004b3be003 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -3505,3 +3505,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 b78b08dfc1..8c3b641a19 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -928,3 +928,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