Re: inconsistent results querying table partitioned by date

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: inconsistent results querying table partitioned by date
Дата
Msg-id 7099.1558047050@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: inconsistent results querying table partitioned by date  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: inconsistent results querying table partitioned by date  (Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>)
Список pgsql-bugs
I wrote:
> In other words, as I compose this I'm talking myself into the
> idea that invoking gen_partprune_steps three times (for planner,
> executor startup, and executor runtime, with corresponding
> filters on which clauses can be used) is the only safe near-term
> fix.  In the future we can look at cutting down the repetitive
> costs that entails.

Here's a really quick and dirty POC along that line.  It's unfinished
but I'm out of time for today.  I think the executor side of it is OK,
but the planner side is crufty: it lacks logic to decide whether
either the initial pruning or exec pruning steps are unnecessary.
And I didn't update most of the comments either.

I think the rule for "exec pruning is unnecessary" could probably
just be "we didn't find any exec params in the prune steps", since
that would imply that the initial steps include everything.
I don't see an equally cheap way to decide that initial pruning
is useless, though.  Thoughts?

Also, it seems like we could save at least some of the planning expense by
having the PARTCLAUSE_INITIAL pass report back whether it had discarded
any Param-bearing clauses or not.  If not, there's definitely no need to
run the PARTCLAUSE_EXEC pass.

            regards, tom lane

diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 70709e5..d663118 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -183,6 +183,11 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
                                      bool *isnull,
                                      int maxfieldlen);
 static List *adjust_partition_tlist(List *tlist, TupleConversionMap *map);
+static void ExecInitPruningContext(PartitionPruneContext *context,
+                       List *pruning_steps,
+                       PartitionDesc partdesc,
+                       PartitionKey partkey,
+                       PlanState *planstate);
 static void find_matching_subplans_recurse(PartitionPruningData *prunedata,
                                PartitionedRelPruningData *pprune,
                                bool initial_prune,
@@ -1614,13 +1619,9 @@ ExecCreatePartitionPruneState(PlanState *planstate,
         {
             PartitionedRelPruneInfo *pinfo = lfirst_node(PartitionedRelPruneInfo, lc2);
             PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
-            PartitionPruneContext *context = &pprune->context;
             Relation    partrel;
             PartitionDesc partdesc;
             PartitionKey partkey;
-            int            partnatts;
-            int            n_steps;
-            ListCell   *lc3;

             /* present_parts is also subject to later modification */
             pprune->present_parts = bms_copy(pinfo->present_parts);
@@ -1700,66 +1701,36 @@ ExecCreatePartitionPruneState(PlanState *planstate,
                 Assert(pd_idx == pinfo->nparts);
             }

-            n_steps = list_length(pinfo->pruning_steps);
-
-            context->strategy = partkey->strategy;
-            context->partnatts = partnatts = partkey->partnatts;
-            context->nparts = pinfo->nparts;
-            context->boundinfo = partdesc->boundinfo;
-            context->partcollation = partkey->partcollation;
-            context->partsupfunc = partkey->partsupfunc;
-
-            /* We'll look up type-specific support functions as needed */
-            context->stepcmpfuncs = (FmgrInfo *)
-                palloc0(sizeof(FmgrInfo) * n_steps * partnatts);
-
-            context->ppccontext = CurrentMemoryContext;
-            context->planstate = planstate;
-
-            /* Initialize expression state for each expression we need */
-            context->exprstates = (ExprState **)
-                palloc0(sizeof(ExprState *) * n_steps * partnatts);
-            foreach(lc3, pinfo->pruning_steps)
+            /*
+             * Initialize pruning contexts as needed.
+             */
+            pprune->initial_pruning_steps = pinfo->initial_pruning_steps;
+            if (pinfo->initial_pruning_steps)
             {
-                PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc3);
-                ListCell   *lc4;
-                int            keyno;
-
-                /* not needed for other step kinds */
-                if (!IsA(step, PartitionPruneStepOp))
-                    continue;
-
-                Assert(list_length(step->exprs) <= partnatts);
-
-                keyno = 0;
-                foreach(lc4, step->exprs)
-                {
-                    Expr       *expr = (Expr *) lfirst(lc4);
-
-                    /* not needed for Consts */
-                    if (!IsA(expr, Const))
-                    {
-                        int            stateidx = PruneCxtStateIdx(partnatts,
-                                                                step->step.step_id,
-                                                                keyno);
-
-                        context->exprstates[stateidx] =
-                            ExecInitExpr(expr, context->planstate);
-                    }
-                    keyno++;
-                }
+                ExecInitPruningContext(&pprune->initial_context,
+                                       pinfo->initial_pruning_steps,
+                                       partdesc, partkey, planstate);
+                /* Record whether initial pruning is needed at any level */
+                prunestate->do_initial_prune = true;
+            }
+            else
+            {
+                /*
+                 * Hack: ExecFindInitialMatchingSubPlans requires this field
+                 * to be valid whether pruning or not.
+                 */
+                pprune->initial_context.nparts = partdesc->nparts;
             }

-            /* Array is not modified at runtime, so just point to plan's copy */
-            context->exprhasexecparam = pinfo->hasexecparam;
-
-            pprune->pruning_steps = pinfo->pruning_steps;
-            pprune->do_initial_prune = pinfo->do_initial_prune;
-            pprune->do_exec_prune = pinfo->do_exec_prune;
-
-            /* Record if pruning would be useful at any level */
-            prunestate->do_initial_prune |= pinfo->do_initial_prune;
-            prunestate->do_exec_prune |= pinfo->do_exec_prune;
+            pprune->exec_pruning_steps = pinfo->exec_pruning_steps;
+            if (pinfo->exec_pruning_steps)
+            {
+                ExecInitPruningContext(&pprune->exec_context,
+                                       pinfo->exec_pruning_steps,
+                                       partdesc, partkey, planstate);
+                /* Record whether exec pruning is needed at any level */
+                prunestate->do_exec_prune = true;
+            }

             /*
              * Accumulate the IDs of all PARAM_EXEC Params affecting the
@@ -1777,6 +1748,71 @@ ExecCreatePartitionPruneState(PlanState *planstate,
 }

 /*
+ * Initialize a PartitionPruneContext for the given list of pruning steps.
+ */
+static void
+ExecInitPruningContext(PartitionPruneContext *context,
+                       List *pruning_steps,
+                       PartitionDesc partdesc,
+                       PartitionKey partkey,
+                       PlanState *planstate)
+{
+    int            n_steps;
+    int            partnatts;
+    ListCell   *lc;
+
+    n_steps = list_length(pruning_steps);
+
+    context->strategy = partkey->strategy;
+    context->partnatts = partnatts = partkey->partnatts;
+    context->nparts = partdesc->nparts;
+    context->boundinfo = partdesc->boundinfo;
+    context->partcollation = partkey->partcollation;
+    context->partsupfunc = partkey->partsupfunc;
+
+    /* We'll look up type-specific support functions as needed */
+    context->stepcmpfuncs = (FmgrInfo *)
+        palloc0(sizeof(FmgrInfo) * n_steps * partnatts);
+
+    context->ppccontext = CurrentMemoryContext;
+    context->planstate = planstate;
+
+    /* Initialize expression state for each expression we need */
+    context->exprstates = (ExprState **)
+        palloc0(sizeof(ExprState *) * n_steps * partnatts);
+    foreach(lc, pruning_steps)
+    {
+        PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
+        ListCell   *lc2;
+        int            keyno;
+
+        /* not needed for other step kinds */
+        if (!IsA(step, PartitionPruneStepOp))
+            continue;
+
+        Assert(list_length(step->exprs) <= partnatts);
+
+        keyno = 0;
+        foreach(lc2, step->exprs)
+        {
+            Expr       *expr = (Expr *) lfirst(lc2);
+
+            /* not needed for Consts */
+            if (!IsA(expr, Const))
+            {
+                int            stateidx = PruneCxtStateIdx(partnatts,
+                                                        step->step.step_id,
+                                                        keyno);
+
+                context->exprstates[stateidx] =
+                    ExecInitExpr(expr, context->planstate);
+            }
+            keyno++;
+        }
+    }
+}
+
+/*
  * ExecFindInitialMatchingSubPlans
  *        Identify the set of subplans that cannot be eliminated by initial
  *        pruning, disregarding any pruning constraints involving PARAM_EXEC
@@ -1824,7 +1860,8 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubplans)
         find_matching_subplans_recurse(prunedata, pprune, true, &result);

         /* Expression eval may have used space in node's ps_ExprContext too */
-        ResetExprContext(pprune->context.planstate->ps_ExprContext);
+        if (pprune->initial_pruning_steps)
+            ResetExprContext(pprune->initial_context.planstate->ps_ExprContext);
     }

     /* Add in any subplans that partition pruning didn't account for */
@@ -1888,7 +1925,7 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubplans)
             for (j = prunedata->num_partrelprunedata - 1; j >= 0; j--)
             {
                 PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
-                int            nparts = pprune->context.nparts;
+                int            nparts = pprune->initial_context.nparts;
                 int            k;

                 /* We just rebuild present_parts from scratch */
@@ -1993,7 +2030,8 @@ ExecFindMatchingSubPlans(PartitionPruneState *prunestate)
         find_matching_subplans_recurse(prunedata, pprune, false, &result);

         /* Expression eval may have used space in node's ps_ExprContext too */
-        ResetExprContext(pprune->context.planstate->ps_ExprContext);
+        if (pprune->exec_pruning_steps)
+            ResetExprContext(pprune->exec_context.planstate->ps_ExprContext);
     }

     /* Add in any subplans that partition pruning didn't account for */
@@ -2029,15 +2067,15 @@ find_matching_subplans_recurse(PartitionPruningData *prunedata,
     check_stack_depth();

     /* Only prune if pruning would be useful at this level. */
-    if (initial_prune ? pprune->do_initial_prune : pprune->do_exec_prune)
+    if (initial_prune && pprune->initial_pruning_steps)
     {
-        PartitionPruneContext *context = &pprune->context;
-
-        /* Set whether we can evaluate PARAM_EXEC Params or not */
-        context->evalexecparams = !initial_prune;
-
-        partset = get_matching_partitions(context,
-                                          pprune->pruning_steps);
+        partset = get_matching_partitions(&pprune->initial_context,
+                                          pprune->initial_pruning_steps);
+    }
+    else if (!initial_prune && pprune->exec_pruning_steps)
+    {
+        partset = get_matching_partitions(&pprune->exec_context,
+                                          pprune->exec_pruning_steps);
     }
     else
     {
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 780d7ab..78deade 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1198,16 +1198,13 @@ _copyPartitionedRelPruneInfo(const PartitionedRelPruneInfo *from)
     PartitionedRelPruneInfo *newnode = makeNode(PartitionedRelPruneInfo);

     COPY_SCALAR_FIELD(rtindex);
-    COPY_NODE_FIELD(pruning_steps);
     COPY_BITMAPSET_FIELD(present_parts);
     COPY_SCALAR_FIELD(nparts);
-    COPY_SCALAR_FIELD(nexprs);
     COPY_POINTER_FIELD(subplan_map, from->nparts * sizeof(int));
     COPY_POINTER_FIELD(subpart_map, from->nparts * sizeof(int));
     COPY_POINTER_FIELD(relid_map, from->nparts * sizeof(Oid));
-    COPY_POINTER_FIELD(hasexecparam, from->nexprs * sizeof(bool));
-    COPY_SCALAR_FIELD(do_initial_prune);
-    COPY_SCALAR_FIELD(do_exec_prune);
+    COPY_NODE_FIELD(initial_pruning_steps);
+    COPY_NODE_FIELD(exec_pruning_steps);
     COPY_BITMAPSET_FIELD(execparamids);

     return newnode;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 387e4b9..237598e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -948,16 +948,13 @@ _outPartitionedRelPruneInfo(StringInfo str, const PartitionedRelPruneInfo *node)
     WRITE_NODE_TYPE("PARTITIONEDRELPRUNEINFO");

     WRITE_UINT_FIELD(rtindex);
-    WRITE_NODE_FIELD(pruning_steps);
     WRITE_BITMAPSET_FIELD(present_parts);
     WRITE_INT_FIELD(nparts);
-    WRITE_INT_FIELD(nexprs);
     WRITE_INT_ARRAY(subplan_map, node->nparts);
     WRITE_INT_ARRAY(subpart_map, node->nparts);
     WRITE_OID_ARRAY(relid_map, node->nparts);
-    WRITE_BOOL_ARRAY(hasexecparam, node->nexprs);
-    WRITE_BOOL_FIELD(do_initial_prune);
-    WRITE_BOOL_FIELD(do_exec_prune);
+    WRITE_NODE_FIELD(initial_pruning_steps);
+    WRITE_NODE_FIELD(exec_pruning_steps);
     WRITE_BITMAPSET_FIELD(execparamids);
 }

diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3b96492..6c2626e 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -2388,16 +2388,13 @@ _readPartitionedRelPruneInfo(void)
     READ_LOCALS(PartitionedRelPruneInfo);

     READ_UINT_FIELD(rtindex);
-    READ_NODE_FIELD(pruning_steps);
     READ_BITMAPSET_FIELD(present_parts);
     READ_INT_FIELD(nparts);
-    READ_INT_FIELD(nexprs);
     READ_INT_ARRAY(subplan_map, local_node->nparts);
     READ_INT_ARRAY(subpart_map, local_node->nparts);
     READ_OID_ARRAY(relid_map, local_node->nparts);
-    READ_BOOL_ARRAY(hasexecparam, local_node->nexprs);
-    READ_BOOL_FIELD(do_initial_prune);
-    READ_BOOL_FIELD(do_exec_prune);
+    READ_NODE_FIELD(initial_pruning_steps);
+    READ_NODE_FIELD(exec_pruning_steps);
     READ_BITMAPSET_FIELD(execparamids);

     READ_DONE();
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index ff3caf1..a892ca1 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -85,6 +85,17 @@ typedef enum PartClauseMatchStatus
 } PartClauseMatchStatus;

 /*
+ * PartClauseTarget
+ *        Identifies which qual clauses we can use for generating pruning steps
+ */
+typedef enum PartClauseTarget
+{
+    PARTCLAUSE_PLANNER,            /* want to prune during planning */
+    PARTCLAUSE_INITIAL,            /* want to prune during executor startup */
+    PARTCLAUSE_EXEC                /* want to prune for each plan node scan */
+} PartClauseTarget;
+
+/*
  * GeneratePruningStepsContext
  *        Information about the current state of generation of "pruning steps"
  *        for a given set of clauses
@@ -95,8 +106,7 @@ typedef enum PartClauseMatchStatus
 typedef struct GeneratePruningStepsContext
 {
     /* Input data: */
-    bool        forplanner;        /* true when generating steps to be used
-                                 * during query planning */
+    PartClauseTarget target;    /* context we're generating steps for */
     /* Working state and result data: */
     int            next_step_id;
     List       *steps;            /* output, list of PartitionPruneSteps */
@@ -122,7 +132,7 @@ static List *make_partitionedrel_pruneinfo(PlannerInfo *root,
                               List *partitioned_rels, List *prunequal,
                               Bitmapset **matchedsubplans);
 static List *gen_partprune_steps(RelOptInfo *rel, List *clauses,
-                    bool forplanner, bool *contradictory);
+                    PartClauseTarget target, bool *contradictory);
 static List *gen_partprune_steps_internal(GeneratePruningStepsContext *context,
                              RelOptInfo *rel, List *clauses,
                              bool *contradictory);
@@ -169,8 +179,7 @@ static PruneStepResult *get_matching_range_bounds(PartitionPruneContext *context
                           FmgrInfo *partsupfunc, Bitmapset *nullkeys);
 static Bitmapset *pull_exec_paramids(Expr *expr);
 static bool pull_exec_paramids_walker(Node *node, Bitmapset **context);
-static bool analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps,
-                      int partnatts);
+static Bitmapset *get_partkey_exec_paramids(List *steps);
 static PruneStepResult *perform_pruning_base_step(PartitionPruneContext *context,
                           PartitionPruneStepOp *opstep);
 static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *context,
@@ -347,9 +356,9 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
         Index        rti = lfirst_int(lc);
         RelOptInfo *subpart = find_base_rel(root, rti);
         PartitionedRelPruneInfo *pinfo;
-        int            partnatts = subpart->part_scheme->partnatts;
         List       *partprunequal;
-        List       *pruning_steps;
+        List       *initial_pruning_steps;
+        List       *exec_pruning_steps;
         bool        contradictory;

         /*
@@ -415,13 +424,13 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
         }

         /*
-         * Convert pruning qual to pruning steps.  Since these steps will be
-         * used in the executor, we can pass 'forplanner' as false to allow
-         * steps to be generated that are unsafe for evaluation during
-         * planning, e.g. evaluation of stable functions.
+         * Convert pruning qual to pruning steps.  We need to do this twice,
+         * once to obtain executor startup pruning steps, and once for
+         * executor per-scan pruning steps.
          */
-        pruning_steps = gen_partprune_steps(subpart, partprunequal, false,
-                                            &contradictory);
+        initial_pruning_steps = gen_partprune_steps(subpart, partprunequal,
+                                                    PARTCLAUSE_INITIAL,
+                                                    &contradictory);

         if (contradictory)
         {
@@ -437,20 +446,28 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
             return NIL;
         }

+        exec_pruning_steps = gen_partprune_steps(subpart, partprunequal,
+                                                 PARTCLAUSE_EXEC,
+                                                 &contradictory);
+
+        if (contradictory)
+        {
+            /* As above, abort run-time pruning if anything fishy happens */
+            return NIL;
+        }
+
+        if (initial_pruning_steps || exec_pruning_steps)
+            doruntimeprune = true;
+
         /* Begin constructing the PartitionedRelPruneInfo for this rel */
         pinfo = makeNode(PartitionedRelPruneInfo);
         pinfo->rtindex = rti;
-        pinfo->pruning_steps = pruning_steps;
+        pinfo->initial_pruning_steps = initial_pruning_steps;
+        pinfo->exec_pruning_steps = exec_pruning_steps;
+        pinfo->execparamids = get_partkey_exec_paramids(exec_pruning_steps);
         /* Remaining fields will be filled in the next loop */

         pinfolist = lappend(pinfolist, pinfo);
-
-        /*
-         * Determine which pruning types should be enabled at this level. This
-         * also records paramids relevant to pruning steps in 'pinfo'.
-         */
-        doruntimeprune |= analyze_partkey_exprs(pinfo, pruning_steps,
-                                                partnatts);
     }

     if (!doruntimeprune)
@@ -535,6 +552,8 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
  *        Process 'clauses' (a rel's baserestrictinfo list of clauses) and return
  *        a list of "partition pruning steps".
  *
+ * XXX: FIX COMMENTS
+ *
  * 'forplanner' must be true when generating steps to be evaluated during
  * query planning, false when generating steps to be used at run-time.
  *
@@ -555,12 +574,12 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
  * else it's set false.
  */
 static List *
-gen_partprune_steps(RelOptInfo *rel, List *clauses, bool forplanner,
+gen_partprune_steps(RelOptInfo *rel, List *clauses, PartClauseTarget target,
                     bool *contradictory)
 {
     GeneratePruningStepsContext context;

-    context.forplanner = forplanner;
+    context.target = target;
     context.next_step_id = 0;
     context.steps = NIL;

@@ -635,7 +654,8 @@ prune_append_rel_partitions(RelOptInfo *rel)
      * pruning code that we only want pruning steps that can be evaluated
      * during planning.
      */
-    pruning_steps = gen_partprune_steps(rel, clauses, true,
+    pruning_steps = gen_partprune_steps(rel, clauses,
+                                        PARTCLAUSE_PLANNER,
                                         &contradictory);
     if (contradictory)
         return NULL;
@@ -655,8 +675,6 @@ prune_append_rel_partitions(RelOptInfo *rel)
     /* These are not valid when being called from the planner */
     context.planstate = NULL;
     context.exprstates = NULL;
-    context.exprhasexecparam = NULL;
-    context.evalexecparams = false;

     /* Actual pruning happens here. */
     return get_matching_partitions(&context, pruning_steps);
@@ -1656,7 +1674,7 @@ match_clause_to_partition_key(RelOptInfo *rel,
          * will produce a usable result, since that'd mean there are Vars on
          * both sides.)
          */
-        if (context->forplanner)
+        if (context->target == PARTCLAUSE_PLANNER)
         {
             /*
              * When pruning in the planner, we only support pruning using
@@ -1675,6 +1693,8 @@ match_clause_to_partition_key(RelOptInfo *rel,
         }
         else
         {
+            Bitmapset  *paramids;
+
             /*
              * Otherwise, non-consts are allowed, but we can't prune using an
              * expression that contains Vars.
@@ -1683,6 +1703,14 @@ match_clause_to_partition_key(RelOptInfo *rel,
                 return PARTCLAUSE_UNSUPPORTED;

             /*
+             * See if there are exec Params, too.  If so, we can only use this
+             * expression during per-scan pruning.
+             */
+            paramids = pull_exec_paramids(expr);
+            if (!bms_is_empty(paramids) && context->target != PARTCLAUSE_EXEC)
+                return PARTCLAUSE_UNSUPPORTED;
+
+            /*
              * And we must reject anything containing a volatile function.
              * Stable functions are OK though.  (We need not check this for
              * the comparison operator itself: anything that belongs to a
@@ -1837,7 +1865,7 @@ match_clause_to_partition_key(RelOptInfo *rel,
          * see if it can sanely be used for partition pruning (this is
          * identical to the logic for a plain OpExpr).
          */
-        if (context->forplanner)
+        if (context->target == PARTCLAUSE_PLANNER)
         {
             /*
              * When pruning in the planner, we only support pruning using
@@ -1856,6 +1884,8 @@ match_clause_to_partition_key(RelOptInfo *rel,
         }
         else
         {
+            Bitmapset  *paramids;
+
             /*
              * Otherwise, non-consts are allowed, but we can't prune using an
              * expression that contains Vars.
@@ -1864,6 +1894,14 @@ match_clause_to_partition_key(RelOptInfo *rel,
                 return PARTCLAUSE_UNSUPPORTED;

             /*
+             * See if there are exec Params, too.  If so, we can only use this
+             * expression during per-scan pruning.
+             */
+            paramids = pull_exec_paramids(expr);
+            if (!bms_is_empty(paramids) && context->target != PARTCLAUSE_EXEC)
+                return PARTCLAUSE_UNSUPPORTED;
+
+            /*
              * And we must reject anything containing a volatile function.
              * Stable functions are OK though.  (We need not check this for
              * the comparison operator itself: anything that belongs to a
@@ -2985,89 +3023,38 @@ pull_exec_paramids_walker(Node *node, Bitmapset **context)
 }

 /*
- * analyze_partkey_exprs
- *        Loop through all pruning steps and identify which ones require
- *        executor startup-time or executor run-time pruning.
- *
- * Returns true if any executor partition pruning should be attempted at this
- * level.  Also fills fields of *pinfo to record how to process each step.
+ * get_partkey_exec_paramids
+ *        Loop through given pruning steps and find out which exec Params
+ *        are used.
  *
- * Note: when this is called, not much of *pinfo is valid; but that's OK
- * since we only use it as an output area.
+ * Returns a Bitmapset of Param IDs.
  */
-static bool
-analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps,
-                      int partnatts)
+static Bitmapset *
+get_partkey_exec_paramids(List *steps)
 {
-    bool        doruntimeprune = false;
+    Bitmapset  *execparamids = NULL;
     ListCell   *lc;

-    /*
-     * Steps require run-time pruning if they contain EXEC_PARAM Params.
-     * Otherwise, if their expressions aren't simple Consts or they involve
-     * non-immutable comparison operators, they require startup-time pruning.
-     * (Otherwise, the pruning would have been done at plan time.)
-     *
-     * Notice that what we actually check for mutability is the comparison
-     * functions, not the original operators.  This relies on the support
-     * functions of the btree or hash opfamily being marked consistently with
-     * the operators.
-     */
-    pinfo->nexprs = list_length(steps) * partnatts;
-    pinfo->hasexecparam = (bool *) palloc0(sizeof(bool) * pinfo->nexprs);
-    pinfo->do_initial_prune = false;
-    pinfo->do_exec_prune = false;
-    pinfo->execparamids = NULL;
-
     foreach(lc, steps)
     {
         PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
         ListCell   *lc2;
-        ListCell   *lc3;
-        int            keyno;

         if (!IsA(step, PartitionPruneStepOp))
             continue;

-        keyno = 0;
-        Assert(list_length(step->exprs) == list_length(step->cmpfns));
-        forboth(lc2, step->exprs, lc3, step->cmpfns)
+        foreach(lc2, step->exprs)
         {
             Expr       *expr = lfirst(lc2);
-            Oid            fnoid = lfirst_oid(lc3);

+            /* We can be quick for plain Consts */
             if (!IsA(expr, Const))
-            {
-                Bitmapset  *execparamids = pull_exec_paramids(expr);
-                bool        hasexecparams;
-                int            stateidx = PruneCxtStateIdx(partnatts,
-                                                        step->step.step_id,
-                                                        keyno);
-
-                Assert(stateidx < pinfo->nexprs);
-                hasexecparams = !bms_is_empty(execparamids);
-                pinfo->hasexecparam[stateidx] = hasexecparams;
-                pinfo->execparamids = bms_join(pinfo->execparamids,
-                                               execparamids);
-
-                if (hasexecparams)
-                    pinfo->do_exec_prune = true;
-                else
-                    pinfo->do_initial_prune = true;
-
-                doruntimeprune = true;
-            }
-            else if (func_volatile(fnoid) != PROVOLATILE_IMMUTABLE)
-            {
-                /* No exec params here, but must do initial pruning */
-                pinfo->do_initial_prune = true;
-                doruntimeprune = true;
-            }
-            keyno++;
+                execparamids = bms_join(execparamids,
+                                        pull_exec_paramids(expr));
         }
     }

-    return doruntimeprune;
+    return execparamids;
 }

 /*
@@ -3392,6 +3379,8 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
  * partkey_datum_from_expr
  *        Evaluate expression for potential partition pruning
  *
+ * XXX this can't fail anymore, so change to return void
+ *
  * Evaluate 'expr', whose ExprState is stateidx of the context exprstate
  * array; set *value and *isnull to the resulting Datum and nullflag.
  * Return true if evaluation was possible, otherwise false.
@@ -3417,30 +3406,19 @@ partkey_datum_from_expr(PartitionPruneContext *context,
     }
     else
     {
+        ExprState  *exprstate;
+        ExprContext *ectx;
+
         /*
          * We should never see a non-Const in a step unless we're running in
          * the executor.
          */
         Assert(context->planstate != NULL);

-        /*
-         * When called from the executor we'll have a valid planstate so we
-         * may be able to evaluate an expression which could not be folded to
-         * a Const during planning.  Since run-time pruning can occur both
-         * during initialization of the executor or while it's running, we
-         * must be careful here to evaluate expressions containing PARAM_EXEC
-         * Params only when told it's OK.
-         */
-        if (context->evalexecparams || !context->exprhasexecparam[stateidx])
-        {
-            ExprState  *exprstate;
-            ExprContext *ectx;
-
-            exprstate = context->exprstates[stateidx];
-            ectx = context->planstate->ps_ExprContext;
-            *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
-            return true;
-        }
+        exprstate = context->exprstates[stateidx];
+        ectx = context->planstate->ps_ExprContext;
+        *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
+        return true;
     }

     return false;
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index b363aba..465a975 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -62,24 +62,24 @@ typedef struct PartitionRoutingInfo
  * subpart_map                    Subpart index by partition index, or -1.
  * present_parts                A Bitmapset of the partition indexes that we
  *                                have subplans or subparts for.
- * context                        Contains the context details required to call
- *                                the partition pruning code.
- * pruning_steps                List of PartitionPruneSteps used to
- *                                perform the actual pruning.
- * do_initial_prune                true if pruning should be performed during
- *                                executor startup (for this partitioning level).
- * do_exec_prune                true if pruning should be performed during
- *                                executor run (for this partitioning level).
+ * initial_pruning_steps        List of PartitionPruneSteps used to
+ *                                perform executor startup pruning.
+ * exec_pruning_steps            List of PartitionPruneSteps used to
+ *                                perform per-scan pruning.
+ * initial_context                If initial_pruning_steps isn't NIL, contains
+ *                                the details needed to execute those steps.
+ * exec_context                    If exec_pruning_steps isn't NIL, contains
+ *                                the details needed to execute those steps.
  */
 typedef struct PartitionedRelPruningData
 {
     int           *subplan_map;
     int           *subpart_map;
     Bitmapset  *present_parts;
-    PartitionPruneContext context;
-    List       *pruning_steps;
-    bool        do_initial_prune;
-    bool        do_exec_prune;
+    List       *initial_pruning_steps;
+    List       *exec_pruning_steps;
+    PartitionPruneContext initial_context;
+    PartitionPruneContext exec_context;
 } PartitionedRelPruningData;

 /*
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 1cce762..1241245 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -1109,21 +1109,23 @@ typedef struct PartitionedRelPruneInfo
 {
     NodeTag        type;
     Index        rtindex;        /* RT index of partition rel for this level */
-    List       *pruning_steps;    /* List of PartitionPruneStep, see below */
     Bitmapset  *present_parts;    /* Indexes of all partitions which subplans or
-                                 * subparts are present for. */
-    int            nparts;            /* Length of subplan_map[] and subpart_map[] */
-    int            nexprs;            /* Length of hasexecparam[] */
+                                 * subparts are present for */
+    int            nparts;            /* Length of the following arrays: */
     int           *subplan_map;    /* subplan index by partition index, or -1 */
     int           *subpart_map;    /* subpart index by partition index, or -1 */
     Oid           *relid_map;        /* relation OID by partition index, or 0 */
-    bool       *hasexecparam;    /* true if corresponding pruning_step contains
-                                 * any PARAM_EXEC Params. */
-    bool        do_initial_prune;    /* true if pruning should be performed
-                                     * during executor startup. */
-    bool        do_exec_prune;    /* true if pruning should be performed during
-                                 * executor run. */
-    Bitmapset  *execparamids;    /* All PARAM_EXEC Param IDs in pruning_steps */
+
+    /*
+     * initial_pruning_steps shows how to prune during executor startup (i.e.,
+     * without use of any PARAM_EXEC Params); it is NIL if no startup pruning
+     * is required.  exec_pruning_steps shows how to prune with PARAM_EXEC
+     * Params; it is NIL if no per-scan pruning is required.
+     */
+    List       *initial_pruning_steps;    /* List of PartitionPruneStep */
+    List       *exec_pruning_steps; /* List of PartitionPruneStep */
+    Bitmapset  *execparamids;    /* All PARAM_EXEC Param IDs in
+                                 * exec_pruning_steps */
 } PartitionedRelPruneInfo;

 /*
diff --git a/src/include/partitioning/partprune.h b/src/include/partitioning/partprune.h
index 2f75717..b906ae1 100644
--- a/src/include/partitioning/partprune.h
+++ b/src/include/partitioning/partprune.h
@@ -44,10 +44,6 @@ struct RelOptInfo;
  * exprstates        Array of ExprStates, indexed as per PruneCtxStateIdx; one
  *                    for each partition key in each pruning step.  Allocated if
  *                    planstate is non-NULL, otherwise NULL.
- * exprhasexecparam    Array of bools, each true if corresponding 'exprstate'
- *                    expression contains any PARAM_EXEC Params.  (Can be NULL
- *                    if planstate is NULL.)
- * evalexecparams    True if it's safe to evaluate PARAM_EXEC Params.
  */
 typedef struct PartitionPruneContext
 {
@@ -61,8 +57,6 @@ typedef struct PartitionPruneContext
     MemoryContext ppccontext;
     PlanState  *planstate;
     ExprState **exprstates;
-    bool       *exprhasexecparam;
-    bool        evalexecparams;
 } PartitionPruneContext;

 /*
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 723fc70..841bd8b 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -3149,6 +3149,45 @@ select * from mc3p where a < 3 and abs(b) = 1;
          Filter: ((a < 3) AND (abs(b) = 1))
 (7 rows)

+--
+-- Check that pruning with composite range partitioning works correctly when
+-- a combination of runtime parameters is specified, not all of whose values
+-- are available at the same time
+--
+set plan_cache_mode = force_generic_plan;
+prepare ps1 as
+  select * from mc3p where a = $1 and abs(b) < (select 3);
+explain (analyze, costs off, summary off, timing off)
+execute ps1(1);
+                   QUERY PLAN
+-------------------------------------------------
+ Append (actual rows=1 loops=1)
+   InitPlan 1 (returns $0)
+     ->  Result (actual rows=1 loops=1)
+   Subplans Removed: 2
+   ->  Seq Scan on mc3p1 (actual rows=1 loops=1)
+         Filter: ((a = $1) AND (abs(b) < $0))
+(6 rows)
+
+deallocate ps1;
+prepare ps2 as
+  select * from mc3p where a <= $1 and abs(b) < (select 3);
+explain (analyze, costs off, summary off, timing off)
+execute ps2(1);
+                   QUERY PLAN
+-------------------------------------------------
+ Append (actual rows=2 loops=1)
+   InitPlan 1 (returns $0)
+     ->  Result (actual rows=1 loops=1)
+   Subplans Removed: 1
+   ->  Seq Scan on mc3p0 (actual rows=1 loops=1)
+         Filter: ((a <= $1) AND (abs(b) < $0))
+   ->  Seq Scan on mc3p1 (actual rows=1 loops=1)
+         Filter: ((a <= $1) AND (abs(b) < $0))
+(8 rows)
+
+deallocate ps2;
+reset plan_cache_mode;
 drop table mc3p;
 -- Ensure runtime pruning works with initplans params with boolean types
 create table boolvalues (value bool not null);
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index 2373bd8..071e28d 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -809,6 +809,24 @@ insert into mc3p values (0, 1, 1), (1, 1, 1), (2, 1, 1);
 explain (analyze, costs off, summary off, timing off)
 select * from mc3p where a < 3 and abs(b) = 1;

+--
+-- Check that pruning with composite range partitioning works correctly when
+-- a combination of runtime parameters is specified, not all of whose values
+-- are available at the same time
+--
+set plan_cache_mode = force_generic_plan;
+prepare ps1 as
+  select * from mc3p where a = $1 and abs(b) < (select 3);
+explain (analyze, costs off, summary off, timing off)
+execute ps1(1);
+deallocate ps1;
+prepare ps2 as
+  select * from mc3p where a <= $1 and abs(b) < (select 3);
+explain (analyze, costs off, summary off, timing off)
+execute ps2(1);
+deallocate ps2;
+reset plan_cache_mode;
+
 drop table mc3p;

 -- Ensure runtime pruning works with initplans params with boolean types

В списке pgsql-bugs по дате отправления:

Предыдущее
От: Tom Lane
Дата:
Сообщение: Re: inconsistent results querying table partitioned by date
Следующее
От: Michael Paquier
Дата:
Сообщение: Re: BUG #15810: Using custom directory on external HDD givespermission error