Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/partitioning/partprune.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * partprune.c
4
 *    Support for partition pruning during query planning and execution
5
 *
6
 * This module implements partition pruning using the information contained in
7
 * a table's partition descriptor, query clauses, and run-time parameters.
8
 *
9
 * During planning, clauses that can be matched to the table's partition key
10
 * are turned into a set of "pruning steps", which are then executed to
11
 * identify a set of partitions (as indexes in the RelOptInfo->part_rels
12
 * array) that satisfy the constraints in the step.  Partitions not in the set
13
 * are said to have been pruned.
14
 *
15
 * A base pruning step may involve expressions whose values are only known
16
 * during execution, such as Params, in which case pruning cannot occur
17
 * entirely during planning.  In that case, such steps are included alongside
18
 * the plan, so that they can be used by the executor for further pruning.
19
 *
20
 * There are two kinds of pruning steps.  A "base" pruning step represents
21
 * tests on partition key column(s), typically comparisons to expressions.
22
 * A "combine" pruning step represents a Boolean connector (AND/OR), and
23
 * combines the outputs of some previous steps using the appropriate
24
 * combination method.
25
 *
26
 * See gen_partprune_steps_internal() for more details on step generation.
27
 *
28
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
29
 * Portions Copyright (c) 1994, Regents of the University of California
30
 *
31
 * IDENTIFICATION
32
 *      src/backend/partitioning/partprune.c
33
 *
34
 *-------------------------------------------------------------------------
35
*/
36
#include "postgres.h"
37
38
#include "access/hash.h"
39
#include "access/nbtree.h"
40
#include "catalog/pg_operator.h"
41
#include "catalog/pg_opfamily.h"
42
#include "catalog/pg_proc.h"
43
#include "catalog/pg_type.h"
44
#include "executor/executor.h"
45
#include "miscadmin.h"
46
#include "nodes/makefuncs.h"
47
#include "nodes/nodeFuncs.h"
48
#include "optimizer/appendinfo.h"
49
#include "optimizer/clauses.h"
50
#include "optimizer/cost.h"
51
#include "optimizer/optimizer.h"
52
#include "optimizer/pathnode.h"
53
#include "optimizer/placeholder.h"
54
#include "parser/parsetree.h"
55
#include "partitioning/partbounds.h"
56
#include "partitioning/partprune.h"
57
#include "utils/array.h"
58
#include "utils/lsyscache.h"
59
60
61
/*
62
 * Information about a clause matched with a partition key.
63
 */
64
typedef struct PartClauseInfo
65
{
66
  int     keyno;      /* Partition key number (0 to partnatts - 1) */
67
  Oid     opno;     /* operator used to compare partkey to expr */
68
  bool    op_is_ne;   /* is clause's original operator <> ? */
69
  Expr     *expr;     /* expr the partition key is compared to */
70
  Oid     cmpfn;      /* Oid of function to compare 'expr' to the
71
                 * partition key */
72
  int     op_strategy;  /* btree strategy identifying the operator */
73
} PartClauseInfo;
74
75
/*
76
 * PartClauseMatchStatus
77
 *    Describes the result of match_clause_to_partition_key()
78
 */
79
typedef enum PartClauseMatchStatus
80
{
81
  PARTCLAUSE_NOMATCH,
82
  PARTCLAUSE_MATCH_CLAUSE,
83
  PARTCLAUSE_MATCH_NULLNESS,
84
  PARTCLAUSE_MATCH_STEPS,
85
  PARTCLAUSE_MATCH_CONTRADICT,
86
  PARTCLAUSE_UNSUPPORTED,
87
} PartClauseMatchStatus;
88
89
/*
90
 * PartClauseTarget
91
 *    Identifies which qual clauses we can use for generating pruning steps
92
 */
93
typedef enum PartClauseTarget
94
{
95
  PARTTARGET_PLANNER,     /* want to prune during planning */
96
  PARTTARGET_INITIAL,     /* want to prune during executor startup */
97
  PARTTARGET_EXEC,      /* want to prune during each plan node scan */
98
} PartClauseTarget;
99
100
/*
101
 * GeneratePruningStepsContext
102
 *    Information about the current state of generation of "pruning steps"
103
 *    for a given set of clauses
104
 *
105
 * gen_partprune_steps() initializes and returns an instance of this struct.
106
 *
107
 * Note that has_mutable_op, has_mutable_arg, and has_exec_param are set if
108
 * we found any potentially-useful-for-pruning clause having those properties,
109
 * whether or not we actually used the clause in the steps list.  This
110
 * definition allows us to skip the PARTTARGET_EXEC pass in some cases.
111
 */
112
typedef struct GeneratePruningStepsContext
113
{
114
  /* Copies of input arguments for gen_partprune_steps: */
115
  RelOptInfo *rel;      /* the partitioned relation */
116
  PartClauseTarget target;  /* use-case we're generating steps for */
117
  /* Result data: */
118
  List     *steps;      /* list of PartitionPruneSteps */
119
  bool    has_mutable_op; /* clauses include any stable operators */
120
  bool    has_mutable_arg;  /* clauses include any mutable comparison
121
                   * values, *other than* exec params */
122
  bool    has_exec_param; /* clauses include any PARAM_EXEC params */
123
  bool    contradictory;  /* clauses were proven self-contradictory */
124
  /* Working state: */
125
  int     next_step_id;
126
} GeneratePruningStepsContext;
127
128
/* The result of performing one PartitionPruneStep */
129
typedef struct PruneStepResult
130
{
131
  /*
132
   * The offsets of bounds (in a table's boundinfo) whose partition is
133
   * selected by the pruning step.
134
   */
135
  Bitmapset  *bound_offsets;
136
137
  bool    scan_default; /* Scan the default partition? */
138
  bool    scan_null;    /* Scan the partition for NULL values? */
139
} PruneStepResult;
140
141
142
static List *add_part_relids(List *allpartrelids, Bitmapset *partrelids);
143
static List *make_partitionedrel_pruneinfo(PlannerInfo *root,
144
                       RelOptInfo *parentrel,
145
                       List *prunequal,
146
                       Bitmapset *partrelids,
147
                       int *relid_subplan_map,
148
                       Bitmapset **matchedsubplans);
149
static void gen_partprune_steps(RelOptInfo *rel, List *clauses,
150
                PartClauseTarget target,
151
                GeneratePruningStepsContext *context);
152
static List *gen_partprune_steps_internal(GeneratePruningStepsContext *context,
153
                      List *clauses);
154
static PartitionPruneStep *gen_prune_step_op(GeneratePruningStepsContext *context,
155
                       StrategyNumber opstrategy, bool op_is_ne,
156
                       List *exprs, List *cmpfns, Bitmapset *nullkeys);
157
static PartitionPruneStep *gen_prune_step_combine(GeneratePruningStepsContext *context,
158
                          List *source_stepids,
159
                          PartitionPruneCombineOp combineOp);
160
static List *gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
161
                     List **keyclauses, Bitmapset *nullkeys);
162
static PartClauseMatchStatus match_clause_to_partition_key(GeneratePruningStepsContext *context,
163
                               Expr *clause, const Expr *partkey, int partkeyidx,
164
                               bool *clause_is_not_null,
165
                               PartClauseInfo **pc, List **clause_steps);
166
static List *get_steps_using_prefix(GeneratePruningStepsContext *context,
167
                  StrategyNumber step_opstrategy,
168
                  bool step_op_is_ne,
169
                  Expr *step_lastexpr,
170
                  Oid step_lastcmpfn,
171
                  Bitmapset *step_nullkeys,
172
                  List *prefix);
173
static List *get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
174
                      StrategyNumber step_opstrategy,
175
                      bool step_op_is_ne,
176
                      Expr *step_lastexpr,
177
                      Oid step_lastcmpfn,
178
                      Bitmapset *step_nullkeys,
179
                      List *prefix,
180
                      ListCell *start,
181
                      List *step_exprs,
182
                      List *step_cmpfns);
183
static PruneStepResult *get_matching_hash_bounds(PartitionPruneContext *context,
184
                         StrategyNumber opstrategy, const Datum *values, int nvalues,
185
                         FmgrInfo *partsupfunc, Bitmapset *nullkeys);
186
static PruneStepResult *get_matching_list_bounds(PartitionPruneContext *context,
187
                         StrategyNumber opstrategy, Datum value, int nvalues,
188
                         FmgrInfo *partsupfunc, Bitmapset *nullkeys);
189
static PruneStepResult *get_matching_range_bounds(PartitionPruneContext *context,
190
                          StrategyNumber opstrategy, const Datum *values, int nvalues,
191
                          FmgrInfo *partsupfunc, Bitmapset *nullkeys);
192
static Bitmapset *pull_exec_paramids(Expr *expr);
193
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context);
194
static Bitmapset *get_partkey_exec_paramids(List *steps);
195
static PruneStepResult *perform_pruning_base_step(PartitionPruneContext *context,
196
                          PartitionPruneStepOp *opstep);
197
static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *context,
198
                           PartitionPruneStepCombine *cstep,
199
                           PruneStepResult **step_results);
200
static PartClauseMatchStatus match_boolean_partition_clause(Oid partopfamily,
201
                              Expr *clause,
202
                              const Expr *partkey,
203
                              Expr **outconst,
204
                              bool *notclause);
205
static void partkey_datum_from_expr(PartitionPruneContext *context,
206
                  Expr *expr, int stateidx,
207
                  Datum *value, bool *isnull);
208
209
210
/*
211
 * make_partition_pruneinfo
212
 *    Checks if the given set of quals can be used to build pruning steps
213
 *    that the executor can use to prune away unneeded partitions.  If
214
 *    suitable quals are found then a PartitionPruneInfo is built and tagged
215
 *    onto the PlannerInfo's partPruneInfos list.
216
 *
217
 * The return value is the 0-based index of the item added to the
218
 * partPruneInfos list or -1 if nothing was added.
219
 *
220
 * 'parentrel' is the RelOptInfo for an appendrel, and 'subpaths' is the list
221
 * of scan paths for its child rels.
222
 * 'prunequal' is a list of potential pruning quals (i.e., restriction
223
 * clauses that are applicable to the appendrel).
224
 */
225
int
226
make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
227
             List *subpaths,
228
             List *prunequal)
229
0
{
230
0
  PartitionPruneInfo *pruneinfo;
231
0
  Bitmapset  *allmatchedsubplans = NULL;
232
0
  List     *allpartrelids;
233
0
  List     *prunerelinfos;
234
0
  int      *relid_subplan_map;
235
0
  ListCell   *lc;
236
0
  int     i;
237
238
  /*
239
   * Scan the subpaths to see which ones are scans of partition child
240
   * relations, and identify their parent partitioned rels.  (Note: we must
241
   * restrict the parent partitioned rels to be parentrel or children of
242
   * parentrel, otherwise we couldn't translate prunequal to match.)
243
   *
244
   * Also construct a temporary array to map from partition-child-relation
245
   * relid to the index in 'subpaths' of the scan plan for that partition.
246
   * (Use of "subplan" rather than "subpath" is a bit of a misnomer, but
247
   * we'll let it stand.)  For convenience, we use 1-based indexes here, so
248
   * that zero can represent an un-filled array entry.
249
   */
250
0
  allpartrelids = NIL;
251
0
  relid_subplan_map = palloc0_array(int, root->simple_rel_array_size);
252
253
0
  i = 1;
254
0
  foreach(lc, subpaths)
255
0
  {
256
0
    Path     *path = (Path *) lfirst(lc);
257
0
    RelOptInfo *pathrel = path->parent;
258
259
    /* We don't consider partitioned joins here */
260
0
    if (pathrel->reloptkind == RELOPT_OTHER_MEMBER_REL)
261
0
    {
262
0
      RelOptInfo *prel = pathrel;
263
0
      Bitmapset  *partrelids = NULL;
264
265
      /*
266
       * Traverse up to the pathrel's topmost partitioned parent,
267
       * collecting parent relids as we go; but stop if we reach
268
       * parentrel.  (Normally, a pathrel's topmost partitioned parent
269
       * is either parentrel or a UNION ALL appendrel child of
270
       * parentrel.  But when handling partitionwise joins of
271
       * multi-level partitioning trees, we can see an append path whose
272
       * parentrel is an intermediate partitioned table.)
273
       */
274
0
      do
275
0
      {
276
0
        AppendRelInfo *appinfo;
277
278
0
        Assert(prel->relid < root->simple_rel_array_size);
279
0
        appinfo = root->append_rel_array[prel->relid];
280
0
        prel = find_base_rel(root, appinfo->parent_relid);
281
0
        if (!IS_PARTITIONED_REL(prel))
282
0
          break;   /* reached a non-partitioned parent */
283
        /* accept this level as an interesting parent */
284
0
        partrelids = bms_add_member(partrelids, prel->relid);
285
0
        if (prel == parentrel)
286
0
          break;   /* don't traverse above parentrel */
287
0
      } while (prel->reloptkind == RELOPT_OTHER_MEMBER_REL);
288
289
0
      if (partrelids)
290
0
      {
291
        /*
292
         * Found some relevant parent partitions, which may or may not
293
         * overlap with partition trees we already found.  Add new
294
         * information to the allpartrelids list.
295
         */
296
0
        allpartrelids = add_part_relids(allpartrelids, partrelids);
297
        /* Also record the subplan in relid_subplan_map[] */
298
        /* No duplicates please */
299
0
        Assert(relid_subplan_map[pathrel->relid] == 0);
300
0
        relid_subplan_map[pathrel->relid] = i;
301
0
      }
302
0
    }
303
0
    i++;
304
0
  }
305
306
  /*
307
   * We now build a PartitionedRelPruneInfo for each topmost partitioned rel
308
   * (omitting any that turn out not to have useful pruning quals).
309
   */
310
0
  prunerelinfos = NIL;
311
0
  foreach(lc, allpartrelids)
312
0
  {
313
0
    Bitmapset  *partrelids = (Bitmapset *) lfirst(lc);
314
0
    List     *pinfolist;
315
0
    Bitmapset  *matchedsubplans = NULL;
316
317
0
    pinfolist = make_partitionedrel_pruneinfo(root, parentrel,
318
0
                          prunequal,
319
0
                          partrelids,
320
0
                          relid_subplan_map,
321
0
                          &matchedsubplans);
322
323
    /* When pruning is possible, record the matched subplans */
324
0
    if (pinfolist != NIL)
325
0
    {
326
0
      prunerelinfos = lappend(prunerelinfos, pinfolist);
327
0
      allmatchedsubplans = bms_join(matchedsubplans,
328
0
                      allmatchedsubplans);
329
0
    }
330
0
  }
331
332
0
  pfree(relid_subplan_map);
333
334
  /*
335
   * If none of the partition hierarchies had any useful run-time pruning
336
   * quals, then we can just not bother with run-time pruning.
337
   */
338
0
  if (prunerelinfos == NIL)
339
0
    return -1;
340
341
  /* Else build the result data structure */
342
0
  pruneinfo = makeNode(PartitionPruneInfo);
343
0
  pruneinfo->relids = bms_copy(parentrel->relids);
344
0
  pruneinfo->prune_infos = prunerelinfos;
345
346
  /*
347
   * Some subplans may not belong to any of the identified partitioned rels.
348
   * This can happen for UNION ALL queries which include a non-partitioned
349
   * table, or when some of the hierarchies aren't run-time prunable.  Build
350
   * a bitmapset of the indexes of all such subplans, so that the executor
351
   * can identify which subplans should never be pruned.
352
   */
353
0
  if (bms_num_members(allmatchedsubplans) < list_length(subpaths))
354
0
  {
355
0
    Bitmapset  *other_subplans;
356
357
    /* Create the complement of allmatchedsubplans */
358
0
    other_subplans = bms_add_range(NULL, 0, list_length(subpaths) - 1);
359
0
    other_subplans = bms_del_members(other_subplans, allmatchedsubplans);
360
361
0
    pruneinfo->other_subplans = other_subplans;
362
0
  }
363
0
  else
364
0
    pruneinfo->other_subplans = NULL;
365
366
0
  root->partPruneInfos = lappend(root->partPruneInfos, pruneinfo);
367
368
0
  return list_length(root->partPruneInfos) - 1;
369
0
}
370
371
/*
372
 * add_part_relids
373
 *    Add new info to a list of Bitmapsets of partitioned relids.
374
 *
375
 * Within 'allpartrelids', there is one Bitmapset for each topmost parent
376
 * partitioned rel.  Each Bitmapset contains the RT indexes of the topmost
377
 * parent as well as its relevant non-leaf child partitions.  Since (by
378
 * construction of the rangetable list) parent partitions must have lower
379
 * RT indexes than their children, we can distinguish the topmost parent
380
 * as being the lowest set bit in the Bitmapset.
381
 *
382
 * 'partrelids' contains the RT indexes of a parent partitioned rel, and
383
 * possibly some non-leaf children, that are newly identified as parents of
384
 * some subpath rel passed to make_partition_pruneinfo().  These are added
385
 * to an appropriate member of 'allpartrelids'.
386
 *
387
 * Note that the list contains only RT indexes of partitioned tables that
388
 * are parents of some scan-level relation appearing in the 'subpaths' that
389
 * make_partition_pruneinfo() is dealing with.  Also, "topmost" parents are
390
 * not allowed to be higher than the 'parentrel' associated with the append
391
 * path.  In this way, we avoid expending cycles on partitioned rels that
392
 * can't contribute useful pruning information for the problem at hand.
393
 * (It is possible for 'parentrel' to be a child partitioned table, and it
394
 * is also possible for scan-level relations to be child partitioned tables
395
 * rather than leaf partitions.  Hence we must construct this relation set
396
 * with reference to the particular append path we're dealing with, rather
397
 * than looking at the full partitioning structure represented in the
398
 * RelOptInfos.)
399
 */
400
static List *
401
add_part_relids(List *allpartrelids, Bitmapset *partrelids)
402
0
{
403
0
  Index   targetpart;
404
0
  ListCell   *lc;
405
406
  /* We can easily get the lowest set bit this way: */
407
0
  targetpart = bms_next_member(partrelids, -1);
408
0
  Assert(targetpart > 0);
409
410
  /* Look for a matching topmost parent */
411
0
  foreach(lc, allpartrelids)
412
0
  {
413
0
    Bitmapset  *currpartrelids = (Bitmapset *) lfirst(lc);
414
0
    Index   currtarget = bms_next_member(currpartrelids, -1);
415
416
0
    if (targetpart == currtarget)
417
0
    {
418
      /* Found a match, so add any new RT indexes to this hierarchy */
419
0
      currpartrelids = bms_add_members(currpartrelids, partrelids);
420
0
      lfirst(lc) = currpartrelids;
421
0
      return allpartrelids;
422
0
    }
423
0
  }
424
  /* No match, so add the new partition hierarchy to the list */
425
0
  return lappend(allpartrelids, partrelids);
426
0
}
427
428
/*
429
 * make_partitionedrel_pruneinfo
430
 *    Build a List of PartitionedRelPruneInfos, one for each interesting
431
 *    partitioned rel in a partitioning hierarchy.  These can be used in the
432
 *    executor to allow additional partition pruning to take place.
433
 *
434
 * parentrel: rel associated with the appendpath being considered
435
 * prunequal: potential pruning quals, represented for parentrel
436
 * partrelids: Set of RT indexes identifying relevant partitioned tables
437
 *   within a single partitioning hierarchy
438
 * relid_subplan_map[]: maps child relation relids to subplan indexes
439
 * matchedsubplans: on success, receives the set of subplan indexes which
440
 *   were matched to this partition hierarchy
441
 *
442
 * If we cannot find any useful run-time pruning steps, return NIL.
443
 * However, on success, each rel identified in partrelids will have
444
 * an element in the result list, even if some of them are useless.
445
 */
446
static List *
447
make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
448
                List *prunequal,
449
                Bitmapset *partrelids,
450
                int *relid_subplan_map,
451
                Bitmapset **matchedsubplans)
452
0
{
453
0
  RelOptInfo *targetpart = NULL;
454
0
  List     *pinfolist = NIL;
455
0
  bool    doruntimeprune = false;
456
0
  int      *relid_subpart_map;
457
0
  Bitmapset  *subplansfound = NULL;
458
0
  ListCell   *lc;
459
0
  int     rti;
460
0
  int     i;
461
462
  /*
463
   * Examine each partitioned rel, constructing a temporary array to map
464
   * from planner relids to index of the partitioned rel, and building a
465
   * PartitionedRelPruneInfo for each partitioned rel.
466
   *
467
   * In this phase we discover whether runtime pruning is needed at all; if
468
   * not, we can avoid doing further work.
469
   */
470
0
  relid_subpart_map = palloc0_array(int, root->simple_rel_array_size);
471
472
0
  i = 1;
473
0
  rti = -1;
474
0
  while ((rti = bms_next_member(partrelids, rti)) > 0)
475
0
  {
476
0
    RelOptInfo *subpart = find_base_rel(root, rti);
477
0
    PartitionedRelPruneInfo *pinfo;
478
0
    List     *partprunequal;
479
0
    List     *initial_pruning_steps;
480
0
    List     *exec_pruning_steps;
481
0
    Bitmapset  *execparamids;
482
0
    GeneratePruningStepsContext context;
483
484
    /*
485
     * Fill the mapping array.
486
     *
487
     * relid_subpart_map maps relid of a non-leaf partition to the index
488
     * in the returned PartitionedRelPruneInfo list of the info for that
489
     * partition.  We use 1-based indexes here, so that zero can represent
490
     * an un-filled array entry.
491
     */
492
0
    Assert(rti < root->simple_rel_array_size);
493
0
    relid_subpart_map[rti] = i++;
494
495
    /*
496
     * Translate pruning qual, if necessary, for this partition.
497
     *
498
     * The first item in the list is the target partitioned relation.
499
     */
500
0
    if (!targetpart)
501
0
    {
502
0
      targetpart = subpart;
503
504
      /*
505
       * The prunequal is presented to us as a qual for 'parentrel'.
506
       * Frequently this rel is the same as targetpart, so we can skip
507
       * an adjust_appendrel_attrs step.  But it might not be, and then
508
       * we have to translate.  We update the prunequal parameter here,
509
       * because in later iterations of the loop for child partitions,
510
       * we want to translate from parent to child variables.
511
       */
512
0
      if (!bms_equal(parentrel->relids, subpart->relids))
513
0
      {
514
0
        int     nappinfos;
515
0
        AppendRelInfo **appinfos = find_appinfos_by_relids(root,
516
0
                                   subpart->relids,
517
0
                                   &nappinfos);
518
519
0
        prunequal = (List *) adjust_appendrel_attrs(root, (Node *)
520
0
                              prunequal,
521
0
                              nappinfos,
522
0
                              appinfos);
523
524
0
        pfree(appinfos);
525
0
      }
526
527
0
      partprunequal = prunequal;
528
0
    }
529
0
    else
530
0
    {
531
      /*
532
       * For sub-partitioned tables the columns may not be in the same
533
       * order as the parent, so we must translate the prunequal to make
534
       * it compatible with this relation.
535
       */
536
0
      partprunequal = (List *)
537
0
        adjust_appendrel_attrs_multilevel(root,
538
0
                          (Node *) prunequal,
539
0
                          subpart,
540
0
                          targetpart);
541
0
    }
542
543
    /*
544
     * Convert pruning qual to pruning steps.  We may need to do this
545
     * twice, once to obtain executor startup pruning steps, and once for
546
     * executor per-scan pruning steps.  This first pass creates startup
547
     * pruning steps and detects whether there's any possibly-useful quals
548
     * that would require per-scan pruning.
549
     */
550
0
    gen_partprune_steps(subpart, partprunequal, PARTTARGET_INITIAL,
551
0
              &context);
552
553
0
    if (context.contradictory)
554
0
    {
555
      /*
556
       * This shouldn't happen as the planner should have detected this
557
       * earlier. However, we do use additional quals from parameterized
558
       * paths here. These do only compare Params to the partition key,
559
       * so this shouldn't cause the discovery of any new qual
560
       * contradictions that were not previously discovered as the Param
561
       * values are unknown during planning.  Anyway, we'd better do
562
       * something sane here, so let's just disable run-time pruning.
563
       */
564
0
      return NIL;
565
0
    }
566
567
    /*
568
     * If no mutable operators or expressions appear in usable pruning
569
     * clauses, then there's no point in running startup pruning, because
570
     * plan-time pruning should have pruned everything prunable.
571
     */
572
0
    if (context.has_mutable_op || context.has_mutable_arg)
573
0
      initial_pruning_steps = context.steps;
574
0
    else
575
0
      initial_pruning_steps = NIL;
576
577
    /*
578
     * If no exec Params appear in potentially-usable pruning clauses,
579
     * then there's no point in even thinking about per-scan pruning.
580
     */
581
0
    if (context.has_exec_param)
582
0
    {
583
      /* ... OK, we'd better think about it */
584
0
      gen_partprune_steps(subpart, partprunequal, PARTTARGET_EXEC,
585
0
                &context);
586
587
0
      if (context.contradictory)
588
0
      {
589
        /* As above, skip run-time pruning if anything fishy happens */
590
0
        return NIL;
591
0
      }
592
593
0
      exec_pruning_steps = context.steps;
594
595
      /*
596
       * Detect which exec Params actually got used; the fact that some
597
       * were in available clauses doesn't mean we actually used them.
598
       * Skip per-scan pruning if there are none.
599
       */
600
0
      execparamids = get_partkey_exec_paramids(exec_pruning_steps);
601
602
0
      if (bms_is_empty(execparamids))
603
0
        exec_pruning_steps = NIL;
604
0
    }
605
0
    else
606
0
    {
607
      /* No exec Params anywhere, so forget about scan-time pruning */
608
0
      exec_pruning_steps = NIL;
609
0
      execparamids = NULL;
610
0
    }
611
612
0
    if (initial_pruning_steps || exec_pruning_steps)
613
0
      doruntimeprune = true;
614
615
    /* Begin constructing the PartitionedRelPruneInfo for this rel */
616
0
    pinfo = makeNode(PartitionedRelPruneInfo);
617
0
    pinfo->rtindex = rti;
618
0
    pinfo->initial_pruning_steps = initial_pruning_steps;
619
0
    pinfo->exec_pruning_steps = exec_pruning_steps;
620
0
    pinfo->execparamids = execparamids;
621
    /* Remaining fields will be filled in the next loop */
622
623
0
    pinfolist = lappend(pinfolist, pinfo);
624
0
  }
625
626
0
  if (!doruntimeprune)
627
0
  {
628
    /* No run-time pruning required. */
629
0
    pfree(relid_subpart_map);
630
0
    return NIL;
631
0
  }
632
633
  /*
634
   * Run-time pruning will be required, so initialize other information.
635
   * That includes two maps -- one needed to convert partition indexes of
636
   * leaf partitions to the indexes of their subplans in the subplan list,
637
   * another needed to convert partition indexes of sub-partitioned
638
   * partitions to the indexes of their PartitionedRelPruneInfo in the
639
   * PartitionedRelPruneInfo list.
640
   */
641
0
  foreach(lc, pinfolist)
642
0
  {
643
0
    PartitionedRelPruneInfo *pinfo = lfirst(lc);
644
0
    RelOptInfo *subpart = find_base_rel(root, pinfo->rtindex);
645
0
    Bitmapset  *present_parts;
646
0
    int     nparts = subpart->nparts;
647
0
    int      *subplan_map;
648
0
    int      *subpart_map;
649
0
    Oid      *relid_map;
650
0
    int      *leafpart_rti_map;
651
652
    /*
653
     * Construct the subplan and subpart maps for this partitioning level.
654
     * Here we convert to zero-based indexes, with -1 for empty entries.
655
     * Also construct a Bitmapset of all partitions that are present (that
656
     * is, not pruned already).
657
     */
658
0
    subplan_map = palloc_array(int, nparts);
659
0
    memset(subplan_map, -1, nparts * sizeof(int));
660
0
    subpart_map = palloc_array(int, nparts);
661
0
    memset(subpart_map, -1, nparts * sizeof(int));
662
0
    relid_map = palloc0_array(Oid, nparts);
663
0
    leafpart_rti_map = palloc0_array(int, nparts);
664
0
    present_parts = NULL;
665
666
0
    i = -1;
667
0
    while ((i = bms_next_member(subpart->live_parts, i)) >= 0)
668
0
    {
669
0
      RelOptInfo *partrel = subpart->part_rels[i];
670
0
      int     subplanidx;
671
0
      int     subpartidx;
672
673
0
      Assert(partrel != NULL);
674
675
0
      subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;
676
0
      subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
677
0
      relid_map[i] = planner_rt_fetch(partrel->relid, root)->relid;
678
679
      /*
680
       * Track the RT indexes of "leaf" partitions so they can be
681
       * included in the PlannerGlobal.prunableRelids set, indicating
682
       * relations that may be pruned during executor startup.
683
       *
684
       * Only leaf partitions with a valid subplan that are prunable
685
       * using initial pruning are added to prunableRelids. So
686
       * partitions without a subplan due to constraint exclusion will
687
       * remain in PlannedStmt.unprunableRelids.
688
       */
689
0
      if (subplanidx >= 0)
690
0
      {
691
0
        present_parts = bms_add_member(present_parts, i);
692
693
        /*
694
         * Non-leaf partitions may appear here when they use an
695
         * unflattened Append or MergeAppend. These should not be
696
         * included in prunableRelids.
697
         */
698
0
        if (partrel->nparts == -1)
699
0
          leafpart_rti_map[i] = (int) partrel->relid;
700
701
        /* Record finding this subplan  */
702
0
        subplansfound = bms_add_member(subplansfound, subplanidx);
703
0
      }
704
0
      else if (subpartidx >= 0)
705
0
        present_parts = bms_add_member(present_parts, i);
706
0
    }
707
708
    /*
709
     * Ensure there were no stray PartitionedRelPruneInfo generated for
710
     * partitioned tables that we have no sub-paths or
711
     * sub-PartitionedRelPruneInfo for.
712
     */
713
0
    Assert(!bms_is_empty(present_parts));
714
715
    /* Record the maps and other information. */
716
0
    pinfo->present_parts = present_parts;
717
0
    pinfo->nparts = nparts;
718
0
    pinfo->subplan_map = subplan_map;
719
0
    pinfo->subpart_map = subpart_map;
720
0
    pinfo->relid_map = relid_map;
721
0
    pinfo->leafpart_rti_map = leafpart_rti_map;
722
0
  }
723
724
0
  pfree(relid_subpart_map);
725
726
0
  *matchedsubplans = subplansfound;
727
728
0
  return pinfolist;
729
0
}
730
731
/*
732
 * gen_partprune_steps
733
 *    Process 'clauses' (typically a rel's baserestrictinfo list of clauses)
734
 *    and create a list of "partition pruning steps".
735
 *
736
 * 'target' tells whether to generate pruning steps for planning (use
737
 * immutable clauses only), or for executor startup (use any allowable
738
 * clause except ones containing PARAM_EXEC Params), or for executor
739
 * per-scan pruning (use any allowable clause).
740
 *
741
 * 'context' is an output argument that receives the steps list as well as
742
 * some subsidiary flags; see the GeneratePruningStepsContext typedef.
743
 */
744
static void
745
gen_partprune_steps(RelOptInfo *rel, List *clauses, PartClauseTarget target,
746
          GeneratePruningStepsContext *context)
747
0
{
748
  /* Initialize all output values to zero/false/NULL */
749
0
  memset(context, 0, sizeof(GeneratePruningStepsContext));
750
0
  context->rel = rel;
751
0
  context->target = target;
752
753
  /*
754
   * If this partitioned table is in turn a partition, and it shares any
755
   * partition keys with its parent, then it's possible that the hierarchy
756
   * allows the parent a narrower range of values than some of its
757
   * partitions (particularly the default one).  This is normally not
758
   * useful, but it can be to prune the default partition.
759
   */
760
0
  if (partition_bound_has_default(rel->boundinfo) && rel->partition_qual)
761
0
  {
762
    /* Make a copy to avoid modifying the passed-in List */
763
0
    clauses = list_concat_copy(clauses, rel->partition_qual);
764
0
  }
765
766
  /* Down into the rabbit-hole. */
767
0
  (void) gen_partprune_steps_internal(context, clauses);
768
0
}
769
770
/*
771
 * prune_append_rel_partitions
772
 *    Process rel's baserestrictinfo and make use of quals which can be
773
 *    evaluated during query planning in order to determine the minimum set
774
 *    of partitions which must be scanned to satisfy these quals.  Returns
775
 *    the matching partitions in the form of a Bitmapset containing the
776
 *    partitions' indexes in the rel's part_rels array.
777
 *
778
 * Callers must ensure that 'rel' is a partitioned table.
779
 */
780
Bitmapset *
781
prune_append_rel_partitions(RelOptInfo *rel)
782
0
{
783
0
  List     *clauses = rel->baserestrictinfo;
784
0
  List     *pruning_steps;
785
0
  GeneratePruningStepsContext gcontext;
786
0
  PartitionPruneContext context;
787
788
0
  Assert(rel->part_scheme != NULL);
789
790
  /* If there are no partitions, return the empty set */
791
0
  if (rel->nparts == 0)
792
0
    return NULL;
793
794
  /*
795
   * If pruning is disabled or if there are no clauses to prune with, return
796
   * all partitions.
797
   */
798
0
  if (!enable_partition_pruning || clauses == NIL)
799
0
    return bms_add_range(NULL, 0, rel->nparts - 1);
800
801
  /*
802
   * Process clauses to extract pruning steps that are usable at plan time.
803
   * If the clauses are found to be contradictory, we can return the empty
804
   * set.
805
   */
806
0
  gen_partprune_steps(rel, clauses, PARTTARGET_PLANNER,
807
0
            &gcontext);
808
0
  if (gcontext.contradictory)
809
0
    return NULL;
810
0
  pruning_steps = gcontext.steps;
811
812
  /* If there's nothing usable, return all partitions */
813
0
  if (pruning_steps == NIL)
814
0
    return bms_add_range(NULL, 0, rel->nparts - 1);
815
816
  /* Set up PartitionPruneContext */
817
0
  context.strategy = rel->part_scheme->strategy;
818
0
  context.partnatts = rel->part_scheme->partnatts;
819
0
  context.nparts = rel->nparts;
820
0
  context.boundinfo = rel->boundinfo;
821
0
  context.partcollation = rel->part_scheme->partcollation;
822
0
  context.partsupfunc = rel->part_scheme->partsupfunc;
823
0
  context.stepcmpfuncs = palloc0_array(FmgrInfo,
824
0
                     context.partnatts * list_length(pruning_steps));
825
0
  context.ppccontext = CurrentMemoryContext;
826
827
  /* These are not valid when being called from the planner */
828
0
  context.planstate = NULL;
829
0
  context.exprcontext = NULL;
830
0
  context.exprstates = NULL;
831
832
  /* Actual pruning happens here. */
833
0
  return get_matching_partitions(&context, pruning_steps);
834
0
}
835
836
/*
837
 * get_matching_partitions
838
 *    Determine partitions that survive partition pruning
839
 *
840
 * Note: context->exprcontext must be valid when the pruning_steps were
841
 * generated with a target other than PARTTARGET_PLANNER.
842
 *
843
 * Returns a Bitmapset of the RelOptInfo->part_rels indexes of the surviving
844
 * partitions.
845
 */
846
Bitmapset *
847
get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
848
0
{
849
0
  Bitmapset  *result;
850
0
  int     num_steps = list_length(pruning_steps),
851
0
        i;
852
0
  PruneStepResult **results,
853
0
         *final_result;
854
0
  ListCell   *lc;
855
0
  bool    scan_default;
856
857
  /* If there are no pruning steps then all partitions match. */
858
0
  if (num_steps == 0)
859
0
  {
860
0
    Assert(context->nparts > 0);
861
0
    return bms_add_range(NULL, 0, context->nparts - 1);
862
0
  }
863
864
  /*
865
   * Allocate space for individual pruning steps to store its result.  Each
866
   * slot will hold a PruneStepResult after performing a given pruning step.
867
   * Later steps may use the result of one or more earlier steps.  The
868
   * result of applying all pruning steps is the value contained in the slot
869
   * of the last pruning step.
870
   */
871
0
  results = palloc0_array(PruneStepResult *, num_steps);
872
0
  foreach(lc, pruning_steps)
873
0
  {
874
0
    PartitionPruneStep *step = lfirst(lc);
875
876
0
    switch (nodeTag(step))
877
0
    {
878
0
      case T_PartitionPruneStepOp:
879
0
        results[step->step_id] =
880
0
          perform_pruning_base_step(context,
881
0
                        (PartitionPruneStepOp *) step);
882
0
        break;
883
884
0
      case T_PartitionPruneStepCombine:
885
0
        results[step->step_id] =
886
0
          perform_pruning_combine_step(context,
887
0
                         (PartitionPruneStepCombine *) step,
888
0
                         results);
889
0
        break;
890
891
0
      default:
892
0
        elog(ERROR, "invalid pruning step type: %d",
893
0
           (int) nodeTag(step));
894
0
    }
895
0
  }
896
897
  /*
898
   * At this point we know the offsets of all the datums whose corresponding
899
   * partitions need to be in the result, including special null-accepting
900
   * and default partitions.  Collect the actual partition indexes now.
901
   */
902
0
  final_result = results[num_steps - 1];
903
0
  Assert(final_result != NULL);
904
0
  i = -1;
905
0
  result = NULL;
906
0
  scan_default = final_result->scan_default;
907
0
  while ((i = bms_next_member(final_result->bound_offsets, i)) >= 0)
908
0
  {
909
0
    int     partindex;
910
911
0
    Assert(i < context->boundinfo->nindexes);
912
0
    partindex = context->boundinfo->indexes[i];
913
914
0
    if (partindex < 0)
915
0
    {
916
      /*
917
       * In range partitioning cases, if a partition index is -1 it
918
       * means that the bound at the offset is the upper bound for a
919
       * range not covered by any partition (other than a possible
920
       * default partition).  In hash partitioning, the same means no
921
       * partition has been defined for the corresponding remainder
922
       * value.
923
       *
924
       * In either case, the value is still part of the queried range of
925
       * values, so mark to scan the default partition if one exists.
926
       */
927
0
      scan_default |= partition_bound_has_default(context->boundinfo);
928
0
      continue;
929
0
    }
930
931
0
    result = bms_add_member(result, partindex);
932
0
  }
933
934
  /* Add the null and/or default partition if needed and present. */
935
0
  if (final_result->scan_null)
936
0
  {
937
0
    Assert(context->strategy == PARTITION_STRATEGY_LIST);
938
0
    Assert(partition_bound_accepts_nulls(context->boundinfo));
939
0
    result = bms_add_member(result, context->boundinfo->null_index);
940
0
  }
941
0
  if (scan_default)
942
0
  {
943
0
    Assert(context->strategy == PARTITION_STRATEGY_LIST ||
944
0
         context->strategy == PARTITION_STRATEGY_RANGE);
945
0
    Assert(partition_bound_has_default(context->boundinfo));
946
0
    result = bms_add_member(result, context->boundinfo->default_index);
947
0
  }
948
949
0
  return result;
950
0
}
951
952
/*
953
 * gen_partprune_steps_internal
954
 *    Processes 'clauses' to generate a List of partition pruning steps.  We
955
 *    return NIL when no steps were generated.
956
 *
957
 * These partition pruning steps come in 2 forms; operator steps and combine
958
 * steps.
959
 *
960
 * Operator steps (PartitionPruneStepOp) contain details of clauses that we
961
 * determined that we can use for partition pruning.  These contain details of
962
 * the expression which is being compared to the partition key and the
963
 * comparison function.
964
 *
965
 * Combine steps (PartitionPruneStepCombine) instruct the partition pruning
966
 * code how it should produce a single set of partitions from multiple input
967
 * operator and other combine steps.  A PARTPRUNE_COMBINE_INTERSECT type
968
 * combine step will merge its input steps to produce a result which only
969
 * contains the partitions which are present in all of the input operator
970
 * steps.  A PARTPRUNE_COMBINE_UNION combine step will produce a result that
971
 * has all of the partitions from each of the input operator steps.
972
 *
973
 * For BoolExpr clauses, each argument is processed recursively. Steps
974
 * generated from processing an OR BoolExpr will be combined using
975
 * PARTPRUNE_COMBINE_UNION.  AND BoolExprs get combined using
976
 * PARTPRUNE_COMBINE_INTERSECT.
977
 *
978
 * Otherwise, the list of clauses we receive we assume to be mutually ANDed.
979
 * We generate all of the pruning steps we can based on these clauses and then
980
 * at the end, if we have more than 1 step, we combine each step with a
981
 * PARTPRUNE_COMBINE_INTERSECT combine step.  Single steps are returned as-is.
982
 *
983
 * If we find clauses that are mutually contradictory, or contradictory with
984
 * the partitioning constraint, or a pseudoconstant clause that contains
985
 * false, we set context->contradictory to true and return NIL (that is, no
986
 * pruning steps).  Caller should consider all partitions as pruned in that
987
 * case.
988
 */
989
static List *
990
gen_partprune_steps_internal(GeneratePruningStepsContext *context,
991
               List *clauses)
992
0
{
993
0
  PartitionScheme part_scheme = context->rel->part_scheme;
994
0
  List     *keyclauses[PARTITION_MAX_KEYS];
995
0
  Bitmapset  *nullkeys = NULL,
996
0
         *notnullkeys = NULL;
997
0
  bool    generate_opsteps = false;
998
0
  List     *result = NIL;
999
0
  ListCell   *lc;
1000
1001
  /*
1002
   * If this partitioned relation has a default partition and is itself a
1003
   * partition (as evidenced by partition_qual being not NIL), we first
1004
   * check if the clauses contradict the partition constraint.  If they do,
1005
   * there's no need to generate any steps as it'd already be proven that no
1006
   * partitions need to be scanned.
1007
   *
1008
   * This is a measure of last resort only to be used because the default
1009
   * partition cannot be pruned using the steps generated from clauses that
1010
   * contradict the parent's partition constraint; regular pruning, which is
1011
   * cheaper, is sufficient when no default partition exists.
1012
   */
1013
0
  if (partition_bound_has_default(context->rel->boundinfo) &&
1014
0
    predicate_refuted_by(context->rel->partition_qual, clauses, false))
1015
0
  {
1016
0
    context->contradictory = true;
1017
0
    return NIL;
1018
0
  }
1019
1020
0
  memset(keyclauses, 0, sizeof(keyclauses));
1021
0
  foreach(lc, clauses)
1022
0
  {
1023
0
    Expr     *clause = (Expr *) lfirst(lc);
1024
0
    int     i;
1025
1026
    /* Look through RestrictInfo, if any */
1027
0
    if (IsA(clause, RestrictInfo))
1028
0
      clause = ((RestrictInfo *) clause)->clause;
1029
1030
    /* Constant-false-or-null is contradictory */
1031
0
    if (IsA(clause, Const) &&
1032
0
      (((Const *) clause)->constisnull ||
1033
0
       !DatumGetBool(((Const *) clause)->constvalue)))
1034
0
    {
1035
0
      context->contradictory = true;
1036
0
      return NIL;
1037
0
    }
1038
1039
    /* Get the BoolExpr's out of the way. */
1040
0
    if (IsA(clause, BoolExpr))
1041
0
    {
1042
      /*
1043
       * Generate steps for arguments.
1044
       *
1045
       * While steps generated for the arguments themselves will be
1046
       * added to context->steps during recursion and will be evaluated
1047
       * independently, collect their step IDs to be stored in the
1048
       * combine step we'll be creating.
1049
       */
1050
0
      if (is_orclause(clause))
1051
0
      {
1052
0
        List     *arg_stepids = NIL;
1053
0
        bool    all_args_contradictory = true;
1054
0
        ListCell   *lc1;
1055
1056
        /*
1057
         * We can share the outer context area with the recursive
1058
         * call, but contradictory had better not be true yet.
1059
         */
1060
0
        Assert(!context->contradictory);
1061
1062
        /*
1063
         * Get pruning step for each arg.  If we get contradictory for
1064
         * all args, it means the OR expression is false as a whole.
1065
         */
1066
0
        foreach(lc1, ((BoolExpr *) clause)->args)
1067
0
        {
1068
0
          Expr     *arg = lfirst(lc1);
1069
0
          bool    arg_contradictory;
1070
0
          List     *argsteps;
1071
1072
0
          argsteps = gen_partprune_steps_internal(context,
1073
0
                              list_make1(arg));
1074
0
          arg_contradictory = context->contradictory;
1075
          /* Keep context->contradictory clear till we're done */
1076
0
          context->contradictory = false;
1077
1078
0
          if (arg_contradictory)
1079
0
          {
1080
            /* Just ignore self-contradictory arguments. */
1081
0
            continue;
1082
0
          }
1083
0
          else
1084
0
            all_args_contradictory = false;
1085
1086
0
          if (argsteps != NIL)
1087
0
          {
1088
            /*
1089
             * gen_partprune_steps_internal() always adds a single
1090
             * combine step when it generates multiple steps, so
1091
             * here we can just pay attention to the last one in
1092
             * the list.  If it just generated one, then the last
1093
             * one in the list is still the one we want.
1094
             */
1095
0
            PartitionPruneStep *last = llast(argsteps);
1096
1097
0
            arg_stepids = lappend_int(arg_stepids, last->step_id);
1098
0
          }
1099
0
          else
1100
0
          {
1101
0
            PartitionPruneStep *orstep;
1102
1103
            /*
1104
             * The arg didn't contain a clause matching this
1105
             * partition key.  We cannot prune using such an arg.
1106
             * To indicate that to the pruning code, we must
1107
             * construct a dummy PartitionPruneStepCombine whose
1108
             * source_stepids is set to an empty List.
1109
             */
1110
0
            orstep = gen_prune_step_combine(context, NIL,
1111
0
                            PARTPRUNE_COMBINE_UNION);
1112
0
            arg_stepids = lappend_int(arg_stepids, orstep->step_id);
1113
0
          }
1114
0
        }
1115
1116
        /* If all the OR arms are contradictory, we can stop */
1117
0
        if (all_args_contradictory)
1118
0
        {
1119
0
          context->contradictory = true;
1120
0
          return NIL;
1121
0
        }
1122
1123
0
        if (arg_stepids != NIL)
1124
0
        {
1125
0
          PartitionPruneStep *step;
1126
1127
0
          step = gen_prune_step_combine(context, arg_stepids,
1128
0
                          PARTPRUNE_COMBINE_UNION);
1129
0
          result = lappend(result, step);
1130
0
        }
1131
0
        continue;
1132
0
      }
1133
0
      else if (is_andclause(clause))
1134
0
      {
1135
0
        List     *args = ((BoolExpr *) clause)->args;
1136
0
        List     *argsteps;
1137
1138
        /*
1139
         * args may itself contain clauses of arbitrary type, so just
1140
         * recurse and later combine the component partitions sets
1141
         * using a combine step.
1142
         */
1143
0
        argsteps = gen_partprune_steps_internal(context, args);
1144
1145
        /* If any AND arm is contradictory, we can stop immediately */
1146
0
        if (context->contradictory)
1147
0
          return NIL;
1148
1149
        /*
1150
         * gen_partprune_steps_internal() always adds a single combine
1151
         * step when it generates multiple steps, so here we can just
1152
         * pay attention to the last one in the list.  If it just
1153
         * generated one, then the last one in the list is still the
1154
         * one we want.
1155
         */
1156
0
        if (argsteps != NIL)
1157
0
          result = lappend(result, llast(argsteps));
1158
1159
0
        continue;
1160
0
      }
1161
1162
      /*
1163
       * Fall-through for a NOT clause, which if it's a Boolean clause,
1164
       * will be handled in match_clause_to_partition_key(). We
1165
       * currently don't perform any pruning for more complex NOT
1166
       * clauses.
1167
       */
1168
0
    }
1169
1170
    /*
1171
     * See if we can match this clause to any of the partition keys.
1172
     */
1173
0
    for (i = 0; i < part_scheme->partnatts; i++)
1174
0
    {
1175
0
      Expr     *partkey = linitial(context->rel->partexprs[i]);
1176
0
      bool    clause_is_not_null = false;
1177
0
      PartClauseInfo *pc = NULL;
1178
0
      List     *clause_steps = NIL;
1179
1180
0
      switch (match_clause_to_partition_key(context,
1181
0
                          clause, partkey, i,
1182
0
                          &clause_is_not_null,
1183
0
                          &pc, &clause_steps))
1184
0
      {
1185
0
        case PARTCLAUSE_MATCH_CLAUSE:
1186
0
          Assert(pc != NULL);
1187
1188
          /*
1189
           * Since we only allow strict operators, check for any
1190
           * contradicting IS NULL.
1191
           */
1192
0
          if (bms_is_member(i, nullkeys))
1193
0
          {
1194
0
            context->contradictory = true;
1195
0
            return NIL;
1196
0
          }
1197
0
          generate_opsteps = true;
1198
0
          keyclauses[i] = lappend(keyclauses[i], pc);
1199
0
          break;
1200
1201
0
        case PARTCLAUSE_MATCH_NULLNESS:
1202
0
          if (!clause_is_not_null)
1203
0
          {
1204
            /*
1205
             * check for conflicting IS NOT NULL as well as
1206
             * contradicting strict clauses
1207
             */
1208
0
            if (bms_is_member(i, notnullkeys) ||
1209
0
              keyclauses[i] != NIL)
1210
0
            {
1211
0
              context->contradictory = true;
1212
0
              return NIL;
1213
0
            }
1214
0
            nullkeys = bms_add_member(nullkeys, i);
1215
0
          }
1216
0
          else
1217
0
          {
1218
            /* check for conflicting IS NULL */
1219
0
            if (bms_is_member(i, nullkeys))
1220
0
            {
1221
0
              context->contradictory = true;
1222
0
              return NIL;
1223
0
            }
1224
0
            notnullkeys = bms_add_member(notnullkeys, i);
1225
0
          }
1226
0
          break;
1227
1228
0
        case PARTCLAUSE_MATCH_STEPS:
1229
0
          Assert(clause_steps != NIL);
1230
0
          result = list_concat(result, clause_steps);
1231
0
          break;
1232
1233
0
        case PARTCLAUSE_MATCH_CONTRADICT:
1234
          /* We've nothing more to do if a contradiction was found. */
1235
0
          context->contradictory = true;
1236
0
          return NIL;
1237
1238
0
        case PARTCLAUSE_NOMATCH:
1239
1240
          /*
1241
           * Clause didn't match this key, but it might match the
1242
           * next one.
1243
           */
1244
0
          continue;
1245
1246
0
        case PARTCLAUSE_UNSUPPORTED:
1247
          /* This clause cannot be used for pruning. */
1248
0
          break;
1249
0
      }
1250
1251
      /* done; go check the next clause. */
1252
0
      break;
1253
0
    }
1254
0
  }
1255
1256
  /*-----------
1257
   * Now generate some (more) pruning steps.  We have three strategies:
1258
   *
1259
   * 1) Generate pruning steps based on IS NULL clauses:
1260
   *   a) For list partitioning, null partition keys can only be found in
1261
   *      the designated null-accepting partition, so if there are IS NULL
1262
   *      clauses containing partition keys we should generate a pruning
1263
   *      step that gets rid of all partitions but that one.  We can
1264
   *      disregard any OpExpr we may have found.
1265
   *   b) For range partitioning, only the default partition can contain
1266
   *      NULL values, so the same rationale applies.
1267
   *   c) For hash partitioning, we only apply this strategy if we have
1268
   *      IS NULL clauses for all the keys.  Strategy 2 below will take
1269
   *      care of the case where some keys have OpExprs and others have
1270
   *      IS NULL clauses.
1271
   *
1272
   * 2) If not, generate steps based on OpExprs we have (if any).
1273
   *
1274
   * 3) If this doesn't work either, we may be able to generate steps to
1275
   *    prune just the null-accepting partition (if one exists), if we have
1276
   *    IS NOT NULL clauses for all partition keys.
1277
   */
1278
0
  if (!bms_is_empty(nullkeys) &&
1279
0
    (part_scheme->strategy == PARTITION_STRATEGY_LIST ||
1280
0
     part_scheme->strategy == PARTITION_STRATEGY_RANGE ||
1281
0
     (part_scheme->strategy == PARTITION_STRATEGY_HASH &&
1282
0
      bms_num_members(nullkeys) == part_scheme->partnatts)))
1283
0
  {
1284
0
    PartitionPruneStep *step;
1285
1286
    /* Strategy 1 */
1287
0
    step = gen_prune_step_op(context, InvalidStrategy,
1288
0
                 false, NIL, NIL, nullkeys);
1289
0
    result = lappend(result, step);
1290
0
  }
1291
0
  else if (generate_opsteps)
1292
0
  {
1293
0
    List     *opsteps;
1294
1295
    /* Strategy 2 */
1296
0
    opsteps = gen_prune_steps_from_opexps(context, keyclauses, nullkeys);
1297
0
    result = list_concat(result, opsteps);
1298
0
  }
1299
0
  else if (bms_num_members(notnullkeys) == part_scheme->partnatts)
1300
0
  {
1301
0
    PartitionPruneStep *step;
1302
1303
    /* Strategy 3 */
1304
0
    step = gen_prune_step_op(context, InvalidStrategy,
1305
0
                 false, NIL, NIL, NULL);
1306
0
    result = lappend(result, step);
1307
0
  }
1308
1309
  /*
1310
   * Finally, if there are multiple steps, since the 'clauses' are mutually
1311
   * ANDed, add an INTERSECT step to combine the partition sets resulting
1312
   * from them and append it to the result list.
1313
   */
1314
0
  if (list_length(result) > 1)
1315
0
  {
1316
0
    List     *step_ids = NIL;
1317
0
    PartitionPruneStep *final;
1318
1319
0
    foreach(lc, result)
1320
0
    {
1321
0
      PartitionPruneStep *step = lfirst(lc);
1322
1323
0
      step_ids = lappend_int(step_ids, step->step_id);
1324
0
    }
1325
1326
0
    final = gen_prune_step_combine(context, step_ids,
1327
0
                     PARTPRUNE_COMBINE_INTERSECT);
1328
0
    result = lappend(result, final);
1329
0
  }
1330
1331
0
  return result;
1332
0
}
1333
1334
/*
1335
 * gen_prune_step_op
1336
 *    Generate a pruning step for a specific operator
1337
 *
1338
 * The step is assigned a unique step identifier and added to context's 'steps'
1339
 * list.
1340
 */
1341
static PartitionPruneStep *
1342
gen_prune_step_op(GeneratePruningStepsContext *context,
1343
          StrategyNumber opstrategy, bool op_is_ne,
1344
          List *exprs, List *cmpfns,
1345
          Bitmapset *nullkeys)
1346
0
{
1347
0
  PartitionPruneStepOp *opstep = makeNode(PartitionPruneStepOp);
1348
1349
0
  opstep->step.step_id = context->next_step_id++;
1350
1351
  /*
1352
   * For clauses that contain an <> operator, set opstrategy to
1353
   * InvalidStrategy to signal get_matching_list_bounds to do the right
1354
   * thing.
1355
   */
1356
0
  opstep->opstrategy = op_is_ne ? InvalidStrategy : opstrategy;
1357
0
  Assert(list_length(exprs) == list_length(cmpfns));
1358
0
  opstep->exprs = exprs;
1359
0
  opstep->cmpfns = cmpfns;
1360
0
  opstep->nullkeys = nullkeys;
1361
1362
0
  context->steps = lappend(context->steps, opstep);
1363
1364
0
  return (PartitionPruneStep *) opstep;
1365
0
}
1366
1367
/*
1368
 * gen_prune_step_combine
1369
 *    Generate a pruning step for a combination of several other steps
1370
 *
1371
 * The step is assigned a unique step identifier and added to context's
1372
 * 'steps' list.
1373
 */
1374
static PartitionPruneStep *
1375
gen_prune_step_combine(GeneratePruningStepsContext *context,
1376
             List *source_stepids,
1377
             PartitionPruneCombineOp combineOp)
1378
0
{
1379
0
  PartitionPruneStepCombine *cstep = makeNode(PartitionPruneStepCombine);
1380
1381
0
  cstep->step.step_id = context->next_step_id++;
1382
0
  cstep->combineOp = combineOp;
1383
0
  cstep->source_stepids = source_stepids;
1384
1385
0
  context->steps = lappend(context->steps, cstep);
1386
1387
0
  return (PartitionPruneStep *) cstep;
1388
0
}
1389
1390
/*
1391
 * gen_prune_steps_from_opexps
1392
 *    Generate and return a list of PartitionPruneStepOp that are based on
1393
 *    OpExpr and BooleanTest clauses that have been matched to the partition
1394
 *    key.
1395
 *
1396
 * 'keyclauses' is an array of List pointers, indexed by the partition key's
1397
 * index.  Each List element in the array can contain clauses that match to
1398
 * the corresponding partition key column.  Partition key columns without any
1399
 * matched clauses will have an empty List.
1400
 *
1401
 * Some partitioning strategies allow pruning to still occur when we only have
1402
 * clauses for a prefix of the partition key columns, for example, RANGE
1403
 * partitioning.  Other strategies, such as HASH partitioning, require clauses
1404
 * for all partition key columns.
1405
 *
1406
 * When we return multiple pruning steps here, it's up to the caller to add a
1407
 * relevant "combine" step to combine the returned steps.  This is not done
1408
 * here as callers may wish to include additional pruning steps before
1409
 * combining them all.
1410
 */
1411
static List *
1412
gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
1413
              List **keyclauses, Bitmapset *nullkeys)
1414
0
{
1415
0
  PartitionScheme part_scheme = context->rel->part_scheme;
1416
0
  List     *opsteps = NIL;
1417
0
  List     *btree_clauses[BTMaxStrategyNumber + 1],
1418
0
         *hash_clauses[HTMaxStrategyNumber + 1];
1419
0
  int     i;
1420
0
  ListCell   *lc;
1421
1422
0
  memset(btree_clauses, 0, sizeof(btree_clauses));
1423
0
  memset(hash_clauses, 0, sizeof(hash_clauses));
1424
0
  for (i = 0; i < part_scheme->partnatts; i++)
1425
0
  {
1426
0
    List     *clauselist = keyclauses[i];
1427
0
    bool    consider_next_key = true;
1428
1429
    /*
1430
     * For range partitioning, if we have no clauses for the current key,
1431
     * we can't consider any later keys either, so we can stop here.
1432
     */
1433
0
    if (part_scheme->strategy == PARTITION_STRATEGY_RANGE &&
1434
0
      clauselist == NIL)
1435
0
      break;
1436
1437
    /*
1438
     * For hash partitioning, if a column doesn't have the necessary
1439
     * equality clause, there should be an IS NULL clause, otherwise
1440
     * pruning is not possible.
1441
     */
1442
0
    if (part_scheme->strategy == PARTITION_STRATEGY_HASH &&
1443
0
      clauselist == NIL && !bms_is_member(i, nullkeys))
1444
0
      return NIL;
1445
1446
0
    foreach(lc, clauselist)
1447
0
    {
1448
0
      PartClauseInfo *pc = (PartClauseInfo *) lfirst(lc);
1449
0
      Oid     lefttype,
1450
0
            righttype;
1451
1452
      /* Look up the operator's btree/hash strategy number. */
1453
0
      if (pc->op_strategy == InvalidStrategy)
1454
0
        get_op_opfamily_properties(pc->opno,
1455
0
                       part_scheme->partopfamily[i],
1456
0
                       false,
1457
0
                       &pc->op_strategy,
1458
0
                       &lefttype,
1459
0
                       &righttype);
1460
1461
0
      switch (part_scheme->strategy)
1462
0
      {
1463
0
        case PARTITION_STRATEGY_LIST:
1464
0
        case PARTITION_STRATEGY_RANGE:
1465
0
          btree_clauses[pc->op_strategy] =
1466
0
            lappend(btree_clauses[pc->op_strategy], pc);
1467
1468
          /*
1469
           * We can't consider subsequent partition keys if the
1470
           * clause for the current key contains a non-inclusive
1471
           * operator.
1472
           */
1473
0
          if (pc->op_strategy == BTLessStrategyNumber ||
1474
0
            pc->op_strategy == BTGreaterStrategyNumber)
1475
0
            consider_next_key = false;
1476
0
          break;
1477
1478
0
        case PARTITION_STRATEGY_HASH:
1479
0
          if (pc->op_strategy != HTEqualStrategyNumber)
1480
0
            elog(ERROR, "invalid clause for hash partitioning");
1481
0
          hash_clauses[pc->op_strategy] =
1482
0
            lappend(hash_clauses[pc->op_strategy], pc);
1483
0
          break;
1484
1485
0
        default:
1486
0
          elog(ERROR, "invalid partition strategy: %c",
1487
0
             part_scheme->strategy);
1488
0
          break;
1489
0
      }
1490
0
    }
1491
1492
    /*
1493
     * If we've decided that clauses for subsequent partition keys
1494
     * wouldn't be useful for pruning, don't search any further.
1495
     */
1496
0
    if (!consider_next_key)
1497
0
      break;
1498
0
  }
1499
1500
  /*
1501
   * Now, we have divided clauses according to their operator strategies.
1502
   * Check for each strategy if we can generate pruning step(s) by
1503
   * collecting a list of expressions whose values will constitute a vector
1504
   * that can be used as a lookup key by a partition bound searching
1505
   * function.
1506
   */
1507
0
  switch (part_scheme->strategy)
1508
0
  {
1509
0
    case PARTITION_STRATEGY_LIST:
1510
0
    case PARTITION_STRATEGY_RANGE:
1511
0
      {
1512
0
        List     *eq_clauses = btree_clauses[BTEqualStrategyNumber];
1513
0
        List     *le_clauses = btree_clauses[BTLessEqualStrategyNumber];
1514
0
        List     *ge_clauses = btree_clauses[BTGreaterEqualStrategyNumber];
1515
0
        int     strat;
1516
1517
        /*
1518
         * For each clause under consideration for a given strategy,
1519
         * we collect expressions from clauses for earlier keys, whose
1520
         * operator strategy is inclusive, into a list called
1521
         * 'prefix'. By appending the clause's own expression to the
1522
         * 'prefix', we'll generate one step using the so generated
1523
         * vector and assign the current strategy to it.  Actually,
1524
         * 'prefix' might contain multiple clauses for the same key,
1525
         * in which case, we must generate steps for various
1526
         * combinations of expressions of different keys, which
1527
         * get_steps_using_prefix takes care of for us.
1528
         */
1529
0
        for (strat = 1; strat <= BTMaxStrategyNumber; strat++)
1530
0
        {
1531
0
          foreach(lc, btree_clauses[strat])
1532
0
          {
1533
0
            PartClauseInfo *pc = lfirst(lc);
1534
0
            ListCell   *eq_start;
1535
0
            ListCell   *le_start;
1536
0
            ListCell   *ge_start;
1537
0
            ListCell   *lc1;
1538
0
            List     *prefix = NIL;
1539
0
            List     *pc_steps;
1540
0
            bool    prefix_valid = true;
1541
0
            bool    pk_has_clauses;
1542
0
            int     keyno;
1543
1544
            /*
1545
             * If this is a clause for the first partition key,
1546
             * there are no preceding expressions; generate a
1547
             * pruning step without a prefix.
1548
             *
1549
             * Note that we pass NULL for step_nullkeys, because
1550
             * we don't search list/range partition bounds where
1551
             * some keys are NULL.
1552
             */
1553
0
            if (pc->keyno == 0)
1554
0
            {
1555
0
              Assert(pc->op_strategy == strat);
1556
0
              pc_steps = get_steps_using_prefix(context, strat,
1557
0
                                pc->op_is_ne,
1558
0
                                pc->expr,
1559
0
                                pc->cmpfn,
1560
0
                                NULL,
1561
0
                                NIL);
1562
0
              opsteps = list_concat(opsteps, pc_steps);
1563
0
              continue;
1564
0
            }
1565
1566
0
            eq_start = list_head(eq_clauses);
1567
0
            le_start = list_head(le_clauses);
1568
0
            ge_start = list_head(ge_clauses);
1569
1570
            /*
1571
             * We arrange clauses into prefix in ascending order
1572
             * of their partition key numbers.
1573
             */
1574
0
            for (keyno = 0; keyno < pc->keyno; keyno++)
1575
0
            {
1576
0
              pk_has_clauses = false;
1577
1578
              /*
1579
               * Expressions from = clauses can always be in the
1580
               * prefix, provided they're from an earlier key.
1581
               */
1582
0
              for_each_cell(lc1, eq_clauses, eq_start)
1583
0
              {
1584
0
                PartClauseInfo *eqpc = lfirst(lc1);
1585
1586
0
                if (eqpc->keyno == keyno)
1587
0
                {
1588
0
                  prefix = lappend(prefix, eqpc);
1589
0
                  pk_has_clauses = true;
1590
0
                }
1591
0
                else
1592
0
                {
1593
0
                  Assert(eqpc->keyno > keyno);
1594
0
                  break;
1595
0
                }
1596
0
              }
1597
0
              eq_start = lc1;
1598
1599
              /*
1600
               * If we're generating steps for </<= strategy, we
1601
               * can add other <= clauses to the prefix,
1602
               * provided they're from an earlier key.
1603
               */
1604
0
              if (strat == BTLessStrategyNumber ||
1605
0
                strat == BTLessEqualStrategyNumber)
1606
0
              {
1607
0
                for_each_cell(lc1, le_clauses, le_start)
1608
0
                {
1609
0
                  PartClauseInfo *lepc = lfirst(lc1);
1610
1611
0
                  if (lepc->keyno == keyno)
1612
0
                  {
1613
0
                    prefix = lappend(prefix, lepc);
1614
0
                    pk_has_clauses = true;
1615
0
                  }
1616
0
                  else
1617
0
                  {
1618
0
                    Assert(lepc->keyno > keyno);
1619
0
                    break;
1620
0
                  }
1621
0
                }
1622
0
                le_start = lc1;
1623
0
              }
1624
1625
              /*
1626
               * If we're generating steps for >/>= strategy, we
1627
               * can add other >= clauses to the prefix,
1628
               * provided they're from an earlier key.
1629
               */
1630
0
              if (strat == BTGreaterStrategyNumber ||
1631
0
                strat == BTGreaterEqualStrategyNumber)
1632
0
              {
1633
0
                for_each_cell(lc1, ge_clauses, ge_start)
1634
0
                {
1635
0
                  PartClauseInfo *gepc = lfirst(lc1);
1636
1637
0
                  if (gepc->keyno == keyno)
1638
0
                  {
1639
0
                    prefix = lappend(prefix, gepc);
1640
0
                    pk_has_clauses = true;
1641
0
                  }
1642
0
                  else
1643
0
                  {
1644
0
                    Assert(gepc->keyno > keyno);
1645
0
                    break;
1646
0
                  }
1647
0
                }
1648
0
                ge_start = lc1;
1649
0
              }
1650
1651
              /*
1652
               * If this key has no clauses, prefix is not valid
1653
               * anymore.
1654
               */
1655
0
              if (!pk_has_clauses)
1656
0
              {
1657
0
                prefix_valid = false;
1658
0
                break;
1659
0
              }
1660
0
            }
1661
1662
            /*
1663
             * If prefix_valid, generate PartitionPruneStepOps.
1664
             * Otherwise, we would not find clauses for a valid
1665
             * subset of the partition keys anymore for the
1666
             * strategy; give up on generating partition pruning
1667
             * steps further for the strategy.
1668
             *
1669
             * As mentioned above, if 'prefix' contains multiple
1670
             * expressions for the same key, the following will
1671
             * generate multiple steps, one for each combination
1672
             * of the expressions for different keys.
1673
             *
1674
             * Note that we pass NULL for step_nullkeys, because
1675
             * we don't search list/range partition bounds where
1676
             * some keys are NULL.
1677
             */
1678
0
            if (prefix_valid)
1679
0
            {
1680
0
              Assert(pc->op_strategy == strat);
1681
0
              pc_steps = get_steps_using_prefix(context, strat,
1682
0
                                pc->op_is_ne,
1683
0
                                pc->expr,
1684
0
                                pc->cmpfn,
1685
0
                                NULL,
1686
0
                                prefix);
1687
0
              opsteps = list_concat(opsteps, pc_steps);
1688
0
            }
1689
0
            else
1690
0
              break;
1691
0
          }
1692
0
        }
1693
0
        break;
1694
0
      }
1695
1696
0
    case PARTITION_STRATEGY_HASH:
1697
0
      {
1698
0
        List     *eq_clauses = hash_clauses[HTEqualStrategyNumber];
1699
1700
        /* For hash partitioning, we have just the = strategy. */
1701
0
        if (eq_clauses != NIL)
1702
0
        {
1703
0
          PartClauseInfo *pc;
1704
0
          List     *pc_steps;
1705
0
          List     *prefix = NIL;
1706
0
          int     last_keyno;
1707
0
          ListCell   *lc1;
1708
1709
          /*
1710
           * Locate the clause for the greatest column.  This may
1711
           * not belong to the last partition key, but it is the
1712
           * clause belonging to the last partition key we found a
1713
           * clause for above.
1714
           */
1715
0
          pc = llast(eq_clauses);
1716
1717
          /*
1718
           * There might be multiple clauses which matched to that
1719
           * partition key; find the first such clause.  While at
1720
           * it, add all the clauses before that one to 'prefix'.
1721
           */
1722
0
          last_keyno = pc->keyno;
1723
0
          foreach(lc, eq_clauses)
1724
0
          {
1725
0
            pc = lfirst(lc);
1726
0
            if (pc->keyno == last_keyno)
1727
0
              break;
1728
0
            prefix = lappend(prefix, pc);
1729
0
          }
1730
1731
          /*
1732
           * For each clause for the "last" column, after appending
1733
           * the clause's own expression to the 'prefix', we'll
1734
           * generate one step using the so generated vector and
1735
           * assign = as its strategy.  Actually, 'prefix' might
1736
           * contain multiple clauses for the same key, in which
1737
           * case, we must generate steps for various combinations
1738
           * of expressions of different keys, which
1739
           * get_steps_using_prefix will take care of for us.
1740
           */
1741
0
          for_each_cell(lc1, eq_clauses, lc)
1742
0
          {
1743
0
            pc = lfirst(lc1);
1744
1745
            /*
1746
             * Note that we pass nullkeys for step_nullkeys,
1747
             * because we need to tell hash partition bound search
1748
             * function which of the keys we found IS NULL clauses
1749
             * for.
1750
             */
1751
0
            Assert(pc->op_strategy == HTEqualStrategyNumber);
1752
0
            pc_steps =
1753
0
              get_steps_using_prefix(context,
1754
0
                           HTEqualStrategyNumber,
1755
0
                           false,
1756
0
                           pc->expr,
1757
0
                           pc->cmpfn,
1758
0
                           nullkeys,
1759
0
                           prefix);
1760
0
            opsteps = list_concat(opsteps, pc_steps);
1761
0
          }
1762
0
        }
1763
0
        break;
1764
0
      }
1765
1766
0
    default:
1767
0
      elog(ERROR, "invalid partition strategy: %c",
1768
0
         part_scheme->strategy);
1769
0
      break;
1770
0
  }
1771
1772
0
  return opsteps;
1773
0
}
1774
1775
/*
1776
 * If the partition key has a collation, then the clause must have the same
1777
 * input collation.  If the partition key is non-collatable, we assume the
1778
 * collation doesn't matter, because while collation wasn't considered when
1779
 * performing partitioning, the clause still may have a collation assigned
1780
 * due to the other input being of a collatable type.
1781
 *
1782
 * See also IndexCollMatchesExprColl.
1783
 */
1784
#define PartCollMatchesExprColl(partcoll, exprcoll) \
1785
0
  ((partcoll) == InvalidOid || (partcoll) == (exprcoll))
1786
1787
/*
1788
 * match_clause_to_partition_key
1789
 *    Attempt to match the given 'clause' with the specified partition key.
1790
 *
1791
 * Return value is:
1792
 * * PARTCLAUSE_NOMATCH if the clause doesn't match this partition key (but
1793
 *   caller should keep trying, because it might match a subsequent key).
1794
 *   Output arguments: none set.
1795
 *
1796
 * * PARTCLAUSE_MATCH_CLAUSE if there is a match.
1797
 *   Output arguments: *pc is set to a PartClauseInfo constructed for the
1798
 *   matched clause.
1799
 *
1800
 * * PARTCLAUSE_MATCH_NULLNESS if there is a match, and the matched clause was
1801
 *   either a "a IS NULL" or "a IS NOT NULL" clause.
1802
 *   Output arguments: *clause_is_not_null is set to false in the former case
1803
 *   true otherwise.
1804
 *
1805
 * * PARTCLAUSE_MATCH_STEPS if there is a match.
1806
 *   Output arguments: *clause_steps is set to the list of recursively
1807
 *   generated steps for the clause.
1808
 *
1809
 * * PARTCLAUSE_MATCH_CONTRADICT if the clause is self-contradictory, ie
1810
 *   it provably returns FALSE or NULL.
1811
 *   Output arguments: none set.
1812
 *
1813
 * * PARTCLAUSE_UNSUPPORTED if the clause doesn't match this partition key
1814
 *   and couldn't possibly match any other one either, due to its form or
1815
 *   properties (such as containing a volatile function).
1816
 *   Output arguments: none set.
1817
 *
1818
 * Note that when pulling up a subquery, the clause operands may get wrapped
1819
 * in PlaceHolderVars to enforce separate identity or as a result of outer
1820
 * joins.  We must strip such no-op PlaceHolderVars before comparing operands
1821
 * to the partition key, otherwise the equal() checks will fail to recognize
1822
 * valid matches.  This is safe because the clauses here are always
1823
 * relation-scan-level expressions, where a PlaceHolderVar with empty
1824
 * phnullingrels is effectively a no-op.  Stripping may also bring separate
1825
 * RelabelType nodes into adjacency, so we must loop when peeling those.
1826
 */
1827
static PartClauseMatchStatus
1828
match_clause_to_partition_key(GeneratePruningStepsContext *context,
1829
                Expr *clause, const Expr *partkey, int partkeyidx,
1830
                bool *clause_is_not_null, PartClauseInfo **pc,
1831
                List **clause_steps)
1832
0
{
1833
0
  PartClauseMatchStatus boolmatchstatus;
1834
0
  PartitionScheme part_scheme = context->rel->part_scheme;
1835
0
  Oid     partopfamily = part_scheme->partopfamily[partkeyidx],
1836
0
        partcoll = part_scheme->partcollation[partkeyidx];
1837
0
  Expr     *expr;
1838
0
  bool    notclause;
1839
1840
  /*
1841
   * Recognize specially shaped clauses that match a Boolean partition key.
1842
   */
1843
0
  boolmatchstatus = match_boolean_partition_clause(partopfamily, clause,
1844
0
                           partkey, &expr,
1845
0
                           &notclause);
1846
1847
0
  if (boolmatchstatus == PARTCLAUSE_MATCH_CLAUSE)
1848
0
  {
1849
0
    PartClauseInfo *partclause;
1850
1851
    /*
1852
     * For bool tests in the form of partkey IS NOT true and IS NOT false,
1853
     * we invert these clauses.  Effectively, "partkey IS NOT true"
1854
     * becomes "partkey IS false OR partkey IS NULL".  We do this by
1855
     * building an OR BoolExpr and forming a clause just like that and
1856
     * punt it off to gen_partprune_steps_internal() to generate pruning
1857
     * steps.
1858
     */
1859
0
    if (notclause)
1860
0
    {
1861
0
      List     *new_clauses;
1862
0
      List     *or_clause;
1863
0
      BooleanTest *new_booltest = (BooleanTest *) copyObject(clause);
1864
0
      NullTest   *nulltest;
1865
1866
      /* We expect 'notclause' to only be set to true for BooleanTests */
1867
0
      Assert(IsA(clause, BooleanTest));
1868
1869
      /* reverse the bool test */
1870
0
      if (new_booltest->booltesttype == IS_NOT_TRUE)
1871
0
        new_booltest->booltesttype = IS_FALSE;
1872
0
      else if (new_booltest->booltesttype == IS_NOT_FALSE)
1873
0
        new_booltest->booltesttype = IS_TRUE;
1874
0
      else
1875
0
      {
1876
        /*
1877
         * We only expect match_boolean_partition_clause to return
1878
         * PARTCLAUSE_MATCH_CLAUSE for IS_NOT_TRUE and IS_NOT_FALSE.
1879
         */
1880
0
        Assert(false);
1881
0
      }
1882
1883
0
      nulltest = makeNode(NullTest);
1884
0
      nulltest->arg = copyObject(partkey);
1885
0
      nulltest->nulltesttype = IS_NULL;
1886
0
      nulltest->argisrow = false;
1887
0
      nulltest->location = -1;
1888
1889
0
      new_clauses = list_make2(new_booltest, nulltest);
1890
0
      or_clause = list_make1(makeBoolExpr(OR_EXPR, new_clauses, -1));
1891
1892
      /* Finally, generate steps */
1893
0
      *clause_steps = gen_partprune_steps_internal(context, or_clause);
1894
1895
0
      if (context->contradictory)
1896
0
        return PARTCLAUSE_MATCH_CONTRADICT; /* shouldn't happen */
1897
0
      else if (*clause_steps == NIL)
1898
0
        return PARTCLAUSE_UNSUPPORTED; /* step generation failed */
1899
0
      return PARTCLAUSE_MATCH_STEPS;
1900
0
    }
1901
1902
0
    partclause = palloc_object(PartClauseInfo);
1903
0
    partclause->keyno = partkeyidx;
1904
    /* Do pruning with the Boolean equality operator. */
1905
0
    partclause->opno = BooleanEqualOperator;
1906
0
    partclause->op_is_ne = false;
1907
0
    partclause->expr = expr;
1908
    /* We know that expr is of Boolean type. */
1909
0
    partclause->cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
1910
0
    partclause->op_strategy = InvalidStrategy;
1911
1912
0
    *pc = partclause;
1913
1914
0
    return PARTCLAUSE_MATCH_CLAUSE;
1915
0
  }
1916
0
  else if (boolmatchstatus == PARTCLAUSE_MATCH_NULLNESS)
1917
0
  {
1918
    /*
1919
     * Handle IS UNKNOWN and IS NOT UNKNOWN.  These just logically
1920
     * translate to IS NULL and IS NOT NULL.
1921
     */
1922
0
    *clause_is_not_null = notclause;
1923
0
    return PARTCLAUSE_MATCH_NULLNESS;
1924
0
  }
1925
0
  else if (IsA(clause, OpExpr) &&
1926
0
       list_length(((OpExpr *) clause)->args) == 2)
1927
0
  {
1928
0
    OpExpr     *opclause = (OpExpr *) clause;
1929
0
    Expr     *leftop,
1930
0
           *rightop;
1931
0
    Oid     opno,
1932
0
          op_lefttype,
1933
0
          op_righttype,
1934
0
          negator = InvalidOid;
1935
0
    Oid     cmpfn;
1936
0
    int     op_strategy;
1937
0
    bool    is_opne_listp = false;
1938
0
    PartClauseInfo *partclause;
1939
1940
0
    leftop = (Expr *) get_leftop(clause);
1941
0
    leftop = (Expr *) strip_noop_phvs((Node *) leftop);
1942
0
    while (IsA(leftop, RelabelType))
1943
0
      leftop = ((RelabelType *) leftop)->arg;
1944
0
    rightop = (Expr *) get_rightop(clause);
1945
0
    rightop = (Expr *) strip_noop_phvs((Node *) rightop);
1946
0
    while (IsA(rightop, RelabelType))
1947
0
      rightop = ((RelabelType *) rightop)->arg;
1948
0
    opno = opclause->opno;
1949
1950
    /* check if the clause matches this partition key */
1951
0
    if (equal(leftop, partkey))
1952
0
      expr = rightop;
1953
0
    else if (equal(rightop, partkey))
1954
0
    {
1955
      /*
1956
       * It's only useful if we can commute the operator to put the
1957
       * partkey on the left.  If we can't, the clause can be deemed
1958
       * UNSUPPORTED.  Even if its leftop matches some later partkey, we
1959
       * now know it has Vars on the right, so it's no use.
1960
       */
1961
0
      opno = get_commutator(opno);
1962
0
      if (!OidIsValid(opno))
1963
0
        return PARTCLAUSE_UNSUPPORTED;
1964
0
      expr = leftop;
1965
0
    }
1966
0
    else
1967
      /* clause does not match this partition key, but perhaps next. */
1968
0
      return PARTCLAUSE_NOMATCH;
1969
1970
    /*
1971
     * Partition key match also requires collation match.  There may be
1972
     * multiple partkeys with the same expression but different
1973
     * collations, so failure is NOMATCH.
1974
     */
1975
0
    if (!PartCollMatchesExprColl(partcoll, opclause->inputcollid))
1976
0
      return PARTCLAUSE_NOMATCH;
1977
1978
    /*
1979
     * See if the operator is relevant to the partitioning opfamily.
1980
     *
1981
     * Normally we only care about operators that are listed as being part
1982
     * of the partitioning operator family.  But there is one exception:
1983
     * the not-equals operators are not listed in any operator family
1984
     * whatsoever, but their negators (equality) are.  We can use one of
1985
     * those if we find it, but only for list partitioning.
1986
     *
1987
     * Note: we report NOMATCH on failure if the negator isn't the
1988
     * equality operator for the partkey's opfamily as other partkeys may
1989
     * have the same expression but different opfamily.  That's unlikely,
1990
     * but not much more so than duplicate expressions with different
1991
     * collations.
1992
     */
1993
0
    if (op_in_opfamily(opno, partopfamily))
1994
0
    {
1995
0
      get_op_opfamily_properties(opno, partopfamily, false,
1996
0
                     &op_strategy, &op_lefttype,
1997
0
                     &op_righttype);
1998
0
    }
1999
0
    else
2000
0
    {
2001
      /* not supported for anything apart from LIST partitioned tables */
2002
0
      if (part_scheme->strategy != PARTITION_STRATEGY_LIST)
2003
0
        return PARTCLAUSE_UNSUPPORTED;
2004
2005
      /* See if the negator is equality */
2006
0
      negator = get_negator(opno);
2007
0
      if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
2008
0
      {
2009
0
        get_op_opfamily_properties(negator, partopfamily, false,
2010
0
                       &op_strategy, &op_lefttype,
2011
0
                       &op_righttype);
2012
0
        if (op_strategy == BTEqualStrategyNumber)
2013
0
          is_opne_listp = true; /* bingo */
2014
0
      }
2015
2016
      /* Nope, it's not <> either. */
2017
0
      if (!is_opne_listp)
2018
0
        return PARTCLAUSE_NOMATCH;
2019
0
    }
2020
2021
    /*
2022
     * Only allow strict operators.  This will guarantee nulls are
2023
     * filtered.  (This test is likely useless, since btree and hash
2024
     * comparison operators are generally strict.)
2025
     */
2026
0
    if (!op_strict(opno))
2027
0
      return PARTCLAUSE_UNSUPPORTED;
2028
2029
    /*
2030
     * OK, we have a match to the partition key and a suitable operator.
2031
     * Examine the other argument to see if it's usable for pruning.
2032
     *
2033
     * In most of these cases, we can return UNSUPPORTED because the same
2034
     * failure would occur no matter which partkey it's matched to.  (In
2035
     * particular, now that we've successfully matched one side of the
2036
     * opclause to a partkey, there is no chance that matching the other
2037
     * side to another partkey will produce a usable result, since that'd
2038
     * mean there are Vars on both sides.)
2039
     *
2040
     * Also, if we reject an argument for a target-dependent reason, set
2041
     * appropriate fields of *context to report that.  We postpone these
2042
     * tests until after matching the partkey and the operator, so as to
2043
     * reduce the odds of setting the context fields for clauses that do
2044
     * not end up contributing to pruning steps.
2045
     *
2046
     * First, check for non-Const argument.  (We assume that any immutable
2047
     * subexpression will have been folded to a Const already.)
2048
     */
2049
0
    if (!IsA(expr, Const))
2050
0
    {
2051
0
      Bitmapset  *paramids;
2052
2053
      /*
2054
       * When pruning in the planner, we only support pruning using
2055
       * comparisons to constants.  We cannot prune on the basis of
2056
       * anything that's not immutable.  (Note that has_mutable_arg and
2057
       * has_exec_param do not get set for this target value.)
2058
       */
2059
0
      if (context->target == PARTTARGET_PLANNER)
2060
0
        return PARTCLAUSE_UNSUPPORTED;
2061
2062
      /*
2063
       * We can never prune using an expression that contains Vars.
2064
       */
2065
0
      if (contain_var_clause((Node *) expr))
2066
0
        return PARTCLAUSE_UNSUPPORTED;
2067
2068
      /*
2069
       * And we must reject anything containing a volatile function.
2070
       * Stable functions are OK though.
2071
       */
2072
0
      if (contain_volatile_functions((Node *) expr))
2073
0
        return PARTCLAUSE_UNSUPPORTED;
2074
2075
      /*
2076
       * See if there are any exec Params.  If so, we can only use this
2077
       * expression during per-scan pruning.  The same is true if it
2078
       * contains a SubPlan, as evaluating that requires the parent
2079
       * node's PlanState, which startup-time pruning hasn't got.  (A
2080
       * SubPlan's testexpr normally contains exec Params, so we'd have
2081
       * caught it above, but the testexpr can be folded to a Const.)
2082
       */
2083
0
      paramids = pull_exec_paramids(expr);
2084
0
      if (!bms_is_empty(paramids))
2085
0
      {
2086
0
        context->has_exec_param = true;
2087
0
        if (context->target != PARTTARGET_EXEC)
2088
0
          return PARTCLAUSE_UNSUPPORTED;
2089
0
      }
2090
0
      else if (contain_subplans((Node *) expr))
2091
0
      {
2092
0
        if (context->target != PARTTARGET_EXEC)
2093
0
          return PARTCLAUSE_UNSUPPORTED;
2094
0
      }
2095
0
      else
2096
0
      {
2097
        /* It's potentially usable, but mutable */
2098
0
        context->has_mutable_arg = true;
2099
0
      }
2100
0
    }
2101
2102
    /*
2103
     * Check whether the comparison operator itself is immutable.  (We
2104
     * assume anything that's in a btree or hash opclass is at least
2105
     * stable, but we need to check for immutability.)
2106
     */
2107
0
    if (op_volatile(opno) != PROVOLATILE_IMMUTABLE)
2108
0
    {
2109
0
      context->has_mutable_op = true;
2110
2111
      /*
2112
       * When pruning in the planner, we cannot prune with mutable
2113
       * operators.
2114
       */
2115
0
      if (context->target == PARTTARGET_PLANNER)
2116
0
        return PARTCLAUSE_UNSUPPORTED;
2117
0
    }
2118
2119
    /*
2120
     * Now find the procedure to use, based on the types.  If the clause's
2121
     * other argument is of the same type as the partitioning opclass's
2122
     * declared input type, we can use the procedure cached in
2123
     * PartitionKey.  If not, search for a cross-type one in the same
2124
     * opfamily; if one doesn't exist, report no match.
2125
     */
2126
0
    if (op_righttype == part_scheme->partopcintype[partkeyidx])
2127
0
      cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
2128
0
    else
2129
0
    {
2130
0
      switch (part_scheme->strategy)
2131
0
      {
2132
          /*
2133
           * For range and list partitioning, we need the ordering
2134
           * procedure with lefttype being the partition key's type,
2135
           * and righttype the clause's operator's right type.
2136
           */
2137
0
        case PARTITION_STRATEGY_LIST:
2138
0
        case PARTITION_STRATEGY_RANGE:
2139
0
          cmpfn =
2140
0
            get_opfamily_proc(part_scheme->partopfamily[partkeyidx],
2141
0
                      part_scheme->partopcintype[partkeyidx],
2142
0
                      op_righttype, BTORDER_PROC);
2143
0
          break;
2144
2145
          /*
2146
           * For hash partitioning, we need the hashing procedure
2147
           * for the clause's type.
2148
           */
2149
0
        case PARTITION_STRATEGY_HASH:
2150
0
          cmpfn =
2151
0
            get_opfamily_proc(part_scheme->partopfamily[partkeyidx],
2152
0
                      op_righttype, op_righttype,
2153
0
                      HASHEXTENDED_PROC);
2154
0
          break;
2155
2156
0
        default:
2157
0
          elog(ERROR, "invalid partition strategy: %c",
2158
0
             part_scheme->strategy);
2159
0
          cmpfn = InvalidOid; /* keep compiler quiet */
2160
0
          break;
2161
0
      }
2162
2163
0
      if (!OidIsValid(cmpfn))
2164
0
        return PARTCLAUSE_NOMATCH;
2165
0
    }
2166
2167
    /*
2168
     * Build the clause, passing the negator if applicable.
2169
     */
2170
0
    partclause = palloc_object(PartClauseInfo);
2171
0
    partclause->keyno = partkeyidx;
2172
0
    if (is_opne_listp)
2173
0
    {
2174
0
      Assert(OidIsValid(negator));
2175
0
      partclause->opno = negator;
2176
0
      partclause->op_is_ne = true;
2177
0
      partclause->op_strategy = InvalidStrategy;
2178
0
    }
2179
0
    else
2180
0
    {
2181
0
      partclause->opno = opno;
2182
0
      partclause->op_is_ne = false;
2183
0
      partclause->op_strategy = op_strategy;
2184
0
    }
2185
0
    partclause->expr = expr;
2186
0
    partclause->cmpfn = cmpfn;
2187
2188
0
    *pc = partclause;
2189
2190
0
    return PARTCLAUSE_MATCH_CLAUSE;
2191
0
  }
2192
0
  else if (IsA(clause, ScalarArrayOpExpr))
2193
0
  {
2194
0
    ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
2195
0
    Oid     saop_op = saop->opno;
2196
0
    Oid     saop_coll = saop->inputcollid;
2197
0
    Expr     *leftop = (Expr *) linitial(saop->args),
2198
0
           *rightop = (Expr *) lsecond(saop->args);
2199
0
    List     *elem_exprs,
2200
0
           *elem_clauses;
2201
0
    ListCell   *lc1;
2202
2203
0
    leftop = (Expr *) strip_noop_phvs((Node *) leftop);
2204
0
    while (IsA(leftop, RelabelType))
2205
0
      leftop = ((RelabelType *) leftop)->arg;
2206
2207
    /* check if the LHS matches this partition key */
2208
0
    if (!equal(leftop, partkey) ||
2209
0
      !PartCollMatchesExprColl(partcoll, saop->inputcollid))
2210
0
      return PARTCLAUSE_NOMATCH;
2211
2212
    /*
2213
     * See if the operator is relevant to the partitioning opfamily.
2214
     *
2215
     * In case of NOT IN (..), we get a '<>', which we handle if list
2216
     * partitioning is in use and we're able to confirm that it's negator
2217
     * is a btree equality operator belonging to the partitioning operator
2218
     * family.  As above, report NOMATCH for non-matching operator.
2219
     */
2220
0
    if (!op_in_opfamily(saop_op, partopfamily))
2221
0
    {
2222
0
      Oid     negator;
2223
2224
0
      if (part_scheme->strategy != PARTITION_STRATEGY_LIST)
2225
0
        return PARTCLAUSE_NOMATCH;
2226
2227
0
      negator = get_negator(saop_op);
2228
0
      if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
2229
0
      {
2230
0
        int     strategy;
2231
0
        Oid     lefttype,
2232
0
              righttype;
2233
2234
0
        get_op_opfamily_properties(negator, partopfamily,
2235
0
                       false, &strategy,
2236
0
                       &lefttype, &righttype);
2237
0
        if (strategy != BTEqualStrategyNumber)
2238
0
          return PARTCLAUSE_NOMATCH;
2239
0
      }
2240
0
      else
2241
0
        return PARTCLAUSE_NOMATCH; /* no useful negator */
2242
0
    }
2243
2244
    /*
2245
     * Only allow strict operators.  This will guarantee nulls are
2246
     * filtered.  (This test is likely useless, since btree and hash
2247
     * comparison operators are generally strict.)
2248
     */
2249
0
    if (!op_strict(saop_op))
2250
0
      return PARTCLAUSE_UNSUPPORTED;
2251
2252
    /*
2253
     * OK, we have a match to the partition key and a suitable operator.
2254
     * Examine the array argument to see if it's usable for pruning.  This
2255
     * is identical to the logic for a plain OpExpr.
2256
     */
2257
0
    if (!IsA(rightop, Const))
2258
0
    {
2259
0
      Bitmapset  *paramids;
2260
2261
      /*
2262
       * When pruning in the planner, we only support pruning using
2263
       * comparisons to constants.  We cannot prune on the basis of
2264
       * anything that's not immutable.  (Note that has_mutable_arg and
2265
       * has_exec_param do not get set for this target value.)
2266
       */
2267
0
      if (context->target == PARTTARGET_PLANNER)
2268
0
        return PARTCLAUSE_UNSUPPORTED;
2269
2270
      /*
2271
       * We can never prune using an expression that contains Vars.
2272
       */
2273
0
      if (contain_var_clause((Node *) rightop))
2274
0
        return PARTCLAUSE_UNSUPPORTED;
2275
2276
      /*
2277
       * And we must reject anything containing a volatile function.
2278
       * Stable functions are OK though.
2279
       */
2280
0
      if (contain_volatile_functions((Node *) rightop))
2281
0
        return PARTCLAUSE_UNSUPPORTED;
2282
2283
      /*
2284
       * See if there are any exec Params.  If so, we can only use this
2285
       * expression during per-scan pruning.
2286
       */
2287
0
      paramids = pull_exec_paramids(rightop);
2288
0
      if (!bms_is_empty(paramids))
2289
0
      {
2290
0
        context->has_exec_param = true;
2291
0
        if (context->target != PARTTARGET_EXEC)
2292
0
          return PARTCLAUSE_UNSUPPORTED;
2293
0
      }
2294
0
      else
2295
0
      {
2296
        /* It's potentially usable, but mutable */
2297
0
        context->has_mutable_arg = true;
2298
0
      }
2299
0
    }
2300
2301
    /*
2302
     * Check whether the comparison operator itself is immutable.  (We
2303
     * assume anything that's in a btree or hash opclass is at least
2304
     * stable, but we need to check for immutability.)
2305
     */
2306
0
    if (op_volatile(saop_op) != PROVOLATILE_IMMUTABLE)
2307
0
    {
2308
0
      context->has_mutable_op = true;
2309
2310
      /*
2311
       * When pruning in the planner, we cannot prune with mutable
2312
       * operators.
2313
       */
2314
0
      if (context->target == PARTTARGET_PLANNER)
2315
0
        return PARTCLAUSE_UNSUPPORTED;
2316
0
    }
2317
2318
    /*
2319
     * Examine the contents of the array argument.
2320
     */
2321
0
    elem_exprs = NIL;
2322
0
    if (IsA(rightop, Const))
2323
0
    {
2324
      /*
2325
       * For a constant array, convert the elements to a list of Const
2326
       * nodes, one for each array element (excepting nulls).
2327
       */
2328
0
      Const    *arr = (Const *) rightop;
2329
0
      ArrayType  *arrval;
2330
0
      int16   elemlen;
2331
0
      bool    elembyval;
2332
0
      char    elemalign;
2333
0
      Datum    *elem_values;
2334
0
      bool     *elem_nulls;
2335
0
      int     num_elems,
2336
0
            i;
2337
2338
      /* If the array itself is null, the saop returns null */
2339
0
      if (arr->constisnull)
2340
0
        return PARTCLAUSE_MATCH_CONTRADICT;
2341
2342
0
      arrval = DatumGetArrayTypeP(arr->constvalue);
2343
0
      get_typlenbyvalalign(ARR_ELEMTYPE(arrval),
2344
0
                 &elemlen, &elembyval, &elemalign);
2345
0
      deconstruct_array(arrval,
2346
0
                ARR_ELEMTYPE(arrval),
2347
0
                elemlen, elembyval, elemalign,
2348
0
                &elem_values, &elem_nulls,
2349
0
                &num_elems);
2350
0
      for (i = 0; i < num_elems; i++)
2351
0
      {
2352
0
        Const    *elem_expr;
2353
2354
        /*
2355
         * A null array element must lead to a null comparison result,
2356
         * since saop_op is known strict.  We can ignore it in the
2357
         * useOr case, but otherwise it implies self-contradiction.
2358
         */
2359
0
        if (elem_nulls[i])
2360
0
        {
2361
0
          if (saop->useOr)
2362
0
            continue;
2363
0
          return PARTCLAUSE_MATCH_CONTRADICT;
2364
0
        }
2365
2366
0
        elem_expr = makeConst(ARR_ELEMTYPE(arrval), -1,
2367
0
                    arr->constcollid, elemlen,
2368
0
                    elem_values[i], false, elembyval);
2369
0
        elem_exprs = lappend(elem_exprs, elem_expr);
2370
0
      }
2371
0
    }
2372
0
    else if (IsA(rightop, ArrayExpr))
2373
0
    {
2374
0
      ArrayExpr  *arrexpr = castNode(ArrayExpr, rightop);
2375
2376
      /*
2377
       * For a nested ArrayExpr, we don't know how to get the actual
2378
       * scalar values out into a flat list, so we give up doing
2379
       * anything with this ScalarArrayOpExpr.
2380
       */
2381
0
      if (arrexpr->multidims)
2382
0
        return PARTCLAUSE_UNSUPPORTED;
2383
2384
      /*
2385
       * Otherwise, we can just use the list of element values.
2386
       */
2387
0
      elem_exprs = arrexpr->elements;
2388
0
    }
2389
0
    else
2390
0
    {
2391
      /* Give up on any other clause types. */
2392
0
      return PARTCLAUSE_UNSUPPORTED;
2393
0
    }
2394
2395
    /*
2396
     * Now generate a list of clauses, one for each array element, of the
2397
     * form leftop saop_op elem_expr
2398
     */
2399
0
    elem_clauses = NIL;
2400
0
    foreach(lc1, elem_exprs)
2401
0
    {
2402
0
      Expr     *elem_clause;
2403
2404
0
      elem_clause = make_opclause(saop_op, BOOLOID, false,
2405
0
                    leftop, lfirst(lc1),
2406
0
                    InvalidOid, saop_coll);
2407
0
      elem_clauses = lappend(elem_clauses, elem_clause);
2408
0
    }
2409
2410
    /*
2411
     * If we have an ANY clause and multiple elements, now turn the list
2412
     * of clauses into an OR expression.
2413
     */
2414
0
    if (saop->useOr && list_length(elem_clauses) > 1)
2415
0
      elem_clauses = list_make1(makeBoolExpr(OR_EXPR, elem_clauses, -1));
2416
2417
    /* Finally, generate steps */
2418
0
    *clause_steps = gen_partprune_steps_internal(context, elem_clauses);
2419
0
    if (context->contradictory)
2420
0
      return PARTCLAUSE_MATCH_CONTRADICT;
2421
0
    else if (*clause_steps == NIL)
2422
0
      return PARTCLAUSE_UNSUPPORTED; /* step generation failed */
2423
0
    return PARTCLAUSE_MATCH_STEPS;
2424
0
  }
2425
0
  else if (IsA(clause, NullTest))
2426
0
  {
2427
0
    NullTest   *nulltest = (NullTest *) clause;
2428
0
    Expr     *arg = nulltest->arg;
2429
2430
0
    arg = (Expr *) strip_noop_phvs((Node *) arg);
2431
0
    while (IsA(arg, RelabelType))
2432
0
      arg = ((RelabelType *) arg)->arg;
2433
2434
    /* Does arg match with this partition key column? */
2435
0
    if (!equal(arg, partkey))
2436
0
      return PARTCLAUSE_NOMATCH;
2437
2438
0
    *clause_is_not_null = (nulltest->nulltesttype == IS_NOT_NULL);
2439
2440
0
    return PARTCLAUSE_MATCH_NULLNESS;
2441
0
  }
2442
2443
  /*
2444
   * If we get here then the return value depends on the result of the
2445
   * match_boolean_partition_clause call above.  If the call returned
2446
   * PARTCLAUSE_UNSUPPORTED then we're either not dealing with a bool qual
2447
   * or the bool qual is not suitable for pruning.  Since the qual didn't
2448
   * match up to any of the other qual types supported here, then trying to
2449
   * match it against any other partition key is a waste of time, so just
2450
   * return PARTCLAUSE_UNSUPPORTED.  If the qual just couldn't be matched to
2451
   * this partition key, then it may match another, so return
2452
   * PARTCLAUSE_NOMATCH.  The only other value that
2453
   * match_boolean_partition_clause can return is PARTCLAUSE_MATCH_CLAUSE,
2454
   * and since that value was already dealt with above, then we can just
2455
   * return boolmatchstatus.
2456
   */
2457
0
  return boolmatchstatus;
2458
0
}
2459
2460
/*
2461
 * get_steps_using_prefix
2462
 *    Generate a list of PartitionPruneStepOps based on the given input.
2463
 *
2464
 * 'step_lastexpr' and 'step_lastcmpfn' are the Expr and comparison function
2465
 * belonging to the final partition key that we have a clause for.  'prefix'
2466
 * is a list of PartClauseInfos for partition key numbers prior to the given
2467
 * 'step_lastexpr' and 'step_lastcmpfn'.  'prefix' may contain multiple
2468
 * PartClauseInfos belonging to a single partition key.  We will generate a
2469
 * PartitionPruneStepOp for each combination of the given PartClauseInfos
2470
 * using, at most, one PartClauseInfo per partition key.
2471
 *
2472
 * For LIST and RANGE partitioned tables, callers must ensure that
2473
 * step_nullkeys is NULL, and that prefix contains at least one clause for
2474
 * each of the partition keys prior to the key that 'step_lastexpr' and
2475
 * 'step_lastcmpfn' belong to.
2476
 *
2477
 * For HASH partitioned tables, callers must ensure that 'prefix' contains at
2478
 * least one clause for each of the partition keys apart from the final key
2479
 * (the expr and comparison function for the final key are in 'step_lastexpr'
2480
 * and 'step_lastcmpfn').  A bit set in step_nullkeys can substitute clauses
2481
 * in the 'prefix' list for any given key.  If a bit is set in 'step_nullkeys'
2482
 * for a given key, then there must be no PartClauseInfo for that key in the
2483
 * 'prefix' list.
2484
 *
2485
 * For each of the above cases, callers must ensure that PartClauseInfos in
2486
 * 'prefix' are sorted in ascending order of keyno.
2487
 */
2488
static List *
2489
get_steps_using_prefix(GeneratePruningStepsContext *context,
2490
             StrategyNumber step_opstrategy,
2491
             bool step_op_is_ne,
2492
             Expr *step_lastexpr,
2493
             Oid step_lastcmpfn,
2494
             Bitmapset *step_nullkeys,
2495
             List *prefix)
2496
0
{
2497
  /* step_nullkeys must be empty for RANGE and LIST partitioned tables */
2498
0
  Assert(step_nullkeys == NULL ||
2499
0
       context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH);
2500
2501
  /*
2502
   * No recursive processing is required when 'prefix' is an empty list.
2503
   * This occurs when there is only 1 partition key column.
2504
   */
2505
0
  if (prefix == NIL)
2506
0
  {
2507
0
    PartitionPruneStep *step;
2508
2509
0
    step = gen_prune_step_op(context,
2510
0
                 step_opstrategy,
2511
0
                 step_op_is_ne,
2512
0
                 list_make1(step_lastexpr),
2513
0
                 list_make1_oid(step_lastcmpfn),
2514
0
                 step_nullkeys);
2515
0
    return list_make1(step);
2516
0
  }
2517
2518
  /* Recurse to generate steps for every combination of clauses. */
2519
0
  return get_steps_using_prefix_recurse(context,
2520
0
                      step_opstrategy,
2521
0
                      step_op_is_ne,
2522
0
                      step_lastexpr,
2523
0
                      step_lastcmpfn,
2524
0
                      step_nullkeys,
2525
0
                      prefix,
2526
0
                      list_head(prefix),
2527
0
                      NIL, NIL);
2528
0
}
2529
2530
/*
2531
 * get_steps_using_prefix_recurse
2532
 *    Generate and return a list of PartitionPruneStepOps using the 'prefix'
2533
 *    list of PartClauseInfos starting at the 'start' cell.
2534
 *
2535
 * When 'prefix' contains multiple PartClauseInfos for a single partition key
2536
 * we create a PartitionPruneStepOp for each combination of duplicated
2537
 * PartClauseInfos.  The returned list will contain a PartitionPruneStepOp
2538
 * for each unique combination of input PartClauseInfos containing at most one
2539
 * PartClauseInfo per partition key.
2540
 *
2541
 * 'prefix' is the input list of PartClauseInfos sorted by keyno.
2542
 * 'start' marks the cell that searching the 'prefix' list should start from.
2543
 * 'step_exprs' and 'step_cmpfns' each contains the expressions and cmpfns
2544
 * we've generated so far from the clauses for the previous part keys.
2545
 */
2546
static List *
2547
get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
2548
                 StrategyNumber step_opstrategy,
2549
                 bool step_op_is_ne,
2550
                 Expr *step_lastexpr,
2551
                 Oid step_lastcmpfn,
2552
                 Bitmapset *step_nullkeys,
2553
                 List *prefix,
2554
                 ListCell *start,
2555
                 List *step_exprs,
2556
                 List *step_cmpfns)
2557
0
{
2558
0
  List     *result = NIL;
2559
0
  ListCell   *lc;
2560
0
  int     cur_keyno;
2561
0
  int     final_keyno;
2562
2563
  /* Actually, recursion would be limited by PARTITION_MAX_KEYS. */
2564
0
  check_stack_depth();
2565
2566
0
  CHECK_FOR_INTERRUPTS();
2567
2568
0
  Assert(start != NULL);
2569
0
  cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno;
2570
0
  final_keyno = ((PartClauseInfo *) llast(prefix))->keyno;
2571
2572
  /* Check if we need to recurse. */
2573
0
  if (cur_keyno < final_keyno)
2574
0
  {
2575
0
    PartClauseInfo *pc;
2576
0
    ListCell   *next_start;
2577
2578
    /*
2579
     * Find the first PartClauseInfo belonging to the next partition key,
2580
     * the next recursive call must start iteration of the prefix list
2581
     * from that point.
2582
     */
2583
0
    for_each_cell(lc, prefix, start)
2584
0
    {
2585
0
      pc = lfirst(lc);
2586
2587
0
      if (pc->keyno > cur_keyno)
2588
0
        break;
2589
0
    }
2590
2591
    /* record where to start iterating in the next recursive call */
2592
0
    next_start = lc;
2593
2594
    /*
2595
     * For each PartClauseInfo with keyno set to cur_keyno, add its expr
2596
     * and cmpfn to step_exprs and step_cmpfns, respectively, and recurse
2597
     * using 'next_start' as the starting point in the 'prefix' list.
2598
     */
2599
0
    for_each_cell(lc, prefix, start)
2600
0
    {
2601
0
      List     *moresteps;
2602
0
      List     *step_exprs1,
2603
0
             *step_cmpfns1;
2604
2605
0
      pc = lfirst(lc);
2606
0
      if (pc->keyno == cur_keyno)
2607
0
      {
2608
        /* Leave the original step_exprs unmodified. */
2609
0
        step_exprs1 = list_copy(step_exprs);
2610
0
        step_exprs1 = lappend(step_exprs1, pc->expr);
2611
2612
        /* Leave the original step_cmpfns unmodified. */
2613
0
        step_cmpfns1 = list_copy(step_cmpfns);
2614
0
        step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2615
0
      }
2616
0
      else
2617
0
      {
2618
        /* check the 'prefix' list is sorted correctly */
2619
0
        Assert(pc->keyno > cur_keyno);
2620
0
        break;
2621
0
      }
2622
2623
0
      moresteps = get_steps_using_prefix_recurse(context,
2624
0
                             step_opstrategy,
2625
0
                             step_op_is_ne,
2626
0
                             step_lastexpr,
2627
0
                             step_lastcmpfn,
2628
0
                             step_nullkeys,
2629
0
                             prefix,
2630
0
                             next_start,
2631
0
                             step_exprs1,
2632
0
                             step_cmpfns1);
2633
0
      result = list_concat(result, moresteps);
2634
2635
0
      list_free(step_exprs1);
2636
0
      list_free(step_cmpfns1);
2637
0
    }
2638
0
  }
2639
0
  else
2640
0
  {
2641
    /*
2642
     * End the current recursion cycle and start generating steps, one for
2643
     * each clause with cur_keyno, which is all clauses from here onward
2644
     * till the end of the list.  Note that for hash partitioning,
2645
     * step_nullkeys is allowed to be non-empty, in which case step_exprs
2646
     * would only contain expressions for the partition keys that are not
2647
     * specified in step_nullkeys.
2648
     */
2649
0
    Assert(list_length(step_exprs) == cur_keyno ||
2650
0
         !bms_is_empty(step_nullkeys));
2651
2652
    /*
2653
     * Note also that for hash partitioning, each partition key should
2654
     * have either equality clauses or an IS NULL clause, so if a
2655
     * partition key doesn't have an expression, it would be specified in
2656
     * step_nullkeys.
2657
     */
2658
0
    Assert(context->rel->part_scheme->strategy
2659
0
         != PARTITION_STRATEGY_HASH ||
2660
0
         list_length(step_exprs) + 2 + bms_num_members(step_nullkeys) ==
2661
0
         context->rel->part_scheme->partnatts);
2662
0
    for_each_cell(lc, prefix, start)
2663
0
    {
2664
0
      PartClauseInfo *pc = lfirst(lc);
2665
0
      PartitionPruneStep *step;
2666
0
      List     *step_exprs1,
2667
0
             *step_cmpfns1;
2668
2669
0
      Assert(pc->keyno == cur_keyno);
2670
2671
      /* Leave the original step_exprs unmodified. */
2672
0
      step_exprs1 = list_copy(step_exprs);
2673
0
      step_exprs1 = lappend(step_exprs1, pc->expr);
2674
0
      step_exprs1 = lappend(step_exprs1, step_lastexpr);
2675
2676
      /* Leave the original step_cmpfns unmodified. */
2677
0
      step_cmpfns1 = list_copy(step_cmpfns);
2678
0
      step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2679
0
      step_cmpfns1 = lappend_oid(step_cmpfns1, step_lastcmpfn);
2680
2681
0
      step = gen_prune_step_op(context,
2682
0
                   step_opstrategy, step_op_is_ne,
2683
0
                   step_exprs1, step_cmpfns1,
2684
0
                   step_nullkeys);
2685
0
      result = lappend(result, step);
2686
0
    }
2687
0
  }
2688
2689
0
  return result;
2690
0
}
2691
2692
/*
2693
 * get_matching_hash_bounds
2694
 *    Determine offset of the hash bound matching the specified values,
2695
 *    considering that all the non-null values come from clauses containing
2696
 *    a compatible hash equality operator and any keys that are null come
2697
 *    from an IS NULL clause.
2698
 *
2699
 * Generally this function will return a single matching bound offset,
2700
 * although if a partition has not been setup for a given modulus then we may
2701
 * return no matches.  If the number of clauses found don't cover the entire
2702
 * partition key, then we'll need to return all offsets.
2703
 *
2704
 * 'opstrategy' if non-zero must be HTEqualStrategyNumber.
2705
 *
2706
 * 'values' contains Datums indexed by the partition key to use for pruning.
2707
 *
2708
 * 'nvalues', the number of Datums in the 'values' array.
2709
 *
2710
 * 'partsupfunc' contains partition hashing functions that can produce correct
2711
 * hash for the type of the values contained in 'values'.
2712
 *
2713
 * 'nullkeys' is the set of partition keys that are null.
2714
 */
2715
static PruneStepResult *
2716
get_matching_hash_bounds(PartitionPruneContext *context,
2717
             StrategyNumber opstrategy, const Datum *values, int nvalues,
2718
             FmgrInfo *partsupfunc, Bitmapset *nullkeys)
2719
0
{
2720
0
  PruneStepResult *result = palloc0_object(PruneStepResult);
2721
0
  PartitionBoundInfo boundinfo = context->boundinfo;
2722
0
  int      *partindices = boundinfo->indexes;
2723
0
  int     partnatts = context->partnatts;
2724
0
  bool    isnull[PARTITION_MAX_KEYS];
2725
0
  int     i;
2726
0
  uint64    rowHash;
2727
0
  int     greatest_modulus;
2728
0
  Oid      *partcollation = context->partcollation;
2729
2730
0
  Assert(context->strategy == PARTITION_STRATEGY_HASH);
2731
2732
  /*
2733
   * For hash partitioning we can only perform pruning based on equality
2734
   * clauses to the partition key or IS NULL clauses.  We also can only
2735
   * prune if we got values for all keys.
2736
   */
2737
0
  if (nvalues + bms_num_members(nullkeys) == partnatts)
2738
0
  {
2739
    /*
2740
     * If there are any values, they must have come from clauses
2741
     * containing an equality operator compatible with hash partitioning.
2742
     */
2743
0
    Assert(opstrategy == HTEqualStrategyNumber || nvalues == 0);
2744
2745
0
    for (i = 0; i < partnatts; i++)
2746
0
      isnull[i] = bms_is_member(i, nullkeys);
2747
2748
0
    rowHash = compute_partition_hash_value(partnatts, partsupfunc, partcollation,
2749
0
                         values, isnull);
2750
2751
0
    greatest_modulus = boundinfo->nindexes;
2752
0
    if (partindices[rowHash % greatest_modulus] >= 0)
2753
0
      result->bound_offsets =
2754
0
        bms_make_singleton(rowHash % greatest_modulus);
2755
0
  }
2756
0
  else
2757
0
  {
2758
    /* Report all valid offsets into the boundinfo->indexes array. */
2759
0
    result->bound_offsets = bms_add_range(NULL, 0,
2760
0
                        boundinfo->nindexes - 1);
2761
0
  }
2762
2763
  /*
2764
   * There is neither a special hash null partition or the default hash
2765
   * partition.
2766
   */
2767
0
  result->scan_null = result->scan_default = false;
2768
2769
0
  return result;
2770
0
}
2771
2772
/*
2773
 * get_matching_list_bounds
2774
 *    Determine the offsets of list bounds matching the specified value,
2775
 *    according to the semantics of the given operator strategy
2776
 *
2777
 * scan_default will be set in the returned struct, if the default partition
2778
 * needs to be scanned, provided one exists at all.  scan_null will be set if
2779
 * the special null-accepting partition needs to be scanned.
2780
 *
2781
 * 'opstrategy' if non-zero must be a btree strategy number.
2782
 *
2783
 * 'value' contains the value to use for pruning.
2784
 *
2785
 * 'nvalues', if non-zero, should be exactly 1, because of list partitioning.
2786
 *
2787
 * 'partsupfunc' contains the list partitioning comparison function to be used
2788
 * to perform partition_list_bsearch
2789
 *
2790
 * 'nullkeys' is the set of partition keys that are null.
2791
 */
2792
static PruneStepResult *
2793
get_matching_list_bounds(PartitionPruneContext *context,
2794
             StrategyNumber opstrategy, Datum value, int nvalues,
2795
             FmgrInfo *partsupfunc, Bitmapset *nullkeys)
2796
0
{
2797
0
  PruneStepResult *result = palloc0_object(PruneStepResult);
2798
0
  PartitionBoundInfo boundinfo = context->boundinfo;
2799
0
  int     off,
2800
0
        minoff,
2801
0
        maxoff;
2802
0
  bool    is_equal;
2803
0
  bool    inclusive = false;
2804
0
  Oid      *partcollation = context->partcollation;
2805
2806
0
  Assert(context->strategy == PARTITION_STRATEGY_LIST);
2807
0
  Assert(context->partnatts == 1);
2808
2809
0
  result->scan_null = result->scan_default = false;
2810
2811
0
  if (!bms_is_empty(nullkeys))
2812
0
  {
2813
    /*
2814
     * Nulls may exist in only one partition - the partition whose
2815
     * accepted set of values includes null or the default partition if
2816
     * the former doesn't exist.
2817
     */
2818
0
    if (partition_bound_accepts_nulls(boundinfo))
2819
0
      result->scan_null = true;
2820
0
    else
2821
0
      result->scan_default = partition_bound_has_default(boundinfo);
2822
0
    return result;
2823
0
  }
2824
2825
  /*
2826
   * If there are no datums to compare keys with, but there are partitions,
2827
   * just return the default partition if one exists.
2828
   */
2829
0
  if (boundinfo->ndatums == 0)
2830
0
  {
2831
0
    result->scan_default = partition_bound_has_default(boundinfo);
2832
0
    return result;
2833
0
  }
2834
2835
0
  minoff = 0;
2836
0
  maxoff = boundinfo->ndatums - 1;
2837
2838
  /*
2839
   * If there are no values to compare with the datums in boundinfo, it
2840
   * means the caller asked for partitions for all non-null datums.  Add
2841
   * indexes of *all* partitions, including the default if any.
2842
   */
2843
0
  if (nvalues == 0)
2844
0
  {
2845
0
    Assert(boundinfo->ndatums > 0);
2846
0
    result->bound_offsets = bms_add_range(NULL, 0,
2847
0
                        boundinfo->ndatums - 1);
2848
0
    result->scan_default = partition_bound_has_default(boundinfo);
2849
0
    return result;
2850
0
  }
2851
2852
  /* Special case handling of values coming from a <> operator clause. */
2853
0
  if (opstrategy == InvalidStrategy)
2854
0
  {
2855
    /*
2856
     * First match to all bounds.  We'll remove any matching datums below.
2857
     */
2858
0
    Assert(boundinfo->ndatums > 0);
2859
0
    result->bound_offsets = bms_add_range(NULL, 0,
2860
0
                        boundinfo->ndatums - 1);
2861
2862
0
    off = partition_list_bsearch(partsupfunc, partcollation, boundinfo,
2863
0
                   value, &is_equal);
2864
0
    if (off >= 0 && is_equal)
2865
0
    {
2866
2867
      /* We have a match. Remove from the result. */
2868
0
      Assert(boundinfo->indexes[off] >= 0);
2869
0
      result->bound_offsets = bms_del_member(result->bound_offsets,
2870
0
                           off);
2871
0
    }
2872
2873
    /* Always include the default partition if any. */
2874
0
    result->scan_default = partition_bound_has_default(boundinfo);
2875
2876
0
    return result;
2877
0
  }
2878
2879
  /*
2880
   * With range queries, always include the default list partition, because
2881
   * list partitions divide the key space in a discontinuous manner, not all
2882
   * values in the given range will have a partition assigned.  This may not
2883
   * technically be true for some data types (e.g. integer types), however,
2884
   * we currently lack any sort of infrastructure to provide us with proofs
2885
   * that would allow us to do anything smarter here.
2886
   */
2887
0
  if (opstrategy != BTEqualStrategyNumber)
2888
0
    result->scan_default = partition_bound_has_default(boundinfo);
2889
2890
0
  switch (opstrategy)
2891
0
  {
2892
0
    case BTEqualStrategyNumber:
2893
0
      off = partition_list_bsearch(partsupfunc,
2894
0
                     partcollation,
2895
0
                     boundinfo, value,
2896
0
                     &is_equal);
2897
0
      if (off >= 0 && is_equal)
2898
0
      {
2899
0
        Assert(boundinfo->indexes[off] >= 0);
2900
0
        result->bound_offsets = bms_make_singleton(off);
2901
0
      }
2902
0
      else
2903
0
        result->scan_default = partition_bound_has_default(boundinfo);
2904
0
      return result;
2905
2906
0
    case BTGreaterEqualStrategyNumber:
2907
0
      inclusive = true;
2908
0
      pg_fallthrough;
2909
0
    case BTGreaterStrategyNumber:
2910
0
      off = partition_list_bsearch(partsupfunc,
2911
0
                     partcollation,
2912
0
                     boundinfo, value,
2913
0
                     &is_equal);
2914
0
      if (off >= 0)
2915
0
      {
2916
        /* We don't want the matched datum to be in the result. */
2917
0
        if (!is_equal || !inclusive)
2918
0
          off++;
2919
0
      }
2920
0
      else
2921
0
      {
2922
        /*
2923
         * This case means all partition bounds are greater, which in
2924
         * turn means that all partitions satisfy this key.
2925
         */
2926
0
        off = 0;
2927
0
      }
2928
2929
      /*
2930
       * off is greater than the numbers of datums we have partitions
2931
       * for.  The only possible partition that could contain a match is
2932
       * the default partition, but we must've set context->scan_default
2933
       * above anyway if one exists.
2934
       */
2935
0
      if (off > boundinfo->ndatums - 1)
2936
0
        return result;
2937
2938
0
      minoff = off;
2939
0
      break;
2940
2941
0
    case BTLessEqualStrategyNumber:
2942
0
      inclusive = true;
2943
0
      pg_fallthrough;
2944
0
    case BTLessStrategyNumber:
2945
0
      off = partition_list_bsearch(partsupfunc,
2946
0
                     partcollation,
2947
0
                     boundinfo, value,
2948
0
                     &is_equal);
2949
0
      if (off >= 0 && is_equal && !inclusive)
2950
0
        off--;
2951
2952
      /*
2953
       * off is smaller than the datums of all non-default partitions.
2954
       * The only possible partition that could contain a match is the
2955
       * default partition, but we must've set context->scan_default
2956
       * above anyway if one exists.
2957
       */
2958
0
      if (off < 0)
2959
0
        return result;
2960
2961
0
      maxoff = off;
2962
0
      break;
2963
2964
0
    default:
2965
0
      elog(ERROR, "invalid strategy number %d", opstrategy);
2966
0
      break;
2967
0
  }
2968
2969
0
  Assert(minoff >= 0 && maxoff >= 0);
2970
0
  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2971
0
  return result;
2972
0
}
2973
2974
2975
/*
2976
 * get_matching_range_bounds
2977
 *    Determine the offsets of range bounds matching the specified values,
2978
 *    according to the semantics of the given operator strategy
2979
 *
2980
 * Each datum whose offset is in result is to be treated as the upper bound of
2981
 * the partition that will contain the desired values.
2982
 *
2983
 * scan_default is set in the returned struct if a default partition exists
2984
 * and we're absolutely certain that it needs to be scanned.  We do *not* set
2985
 * it just because values match portions of the key space uncovered by
2986
 * partitions other than default (space which we normally assume to belong to
2987
 * the default partition): the final set of bounds obtained after combining
2988
 * multiple pruning steps might exclude it, so we infer its inclusion
2989
 * elsewhere.
2990
 *
2991
 * 'opstrategy' must be a btree strategy number.
2992
 *
2993
 * 'values' contains Datums indexed by the partition key to use for pruning.
2994
 *
2995
 * 'nvalues', number of Datums in 'values' array. Must be <= context->partnatts.
2996
 *
2997
 * 'partsupfunc' contains the range partitioning comparison functions to be
2998
 * used to perform partition_range_datum_bsearch or partition_rbound_datum_cmp
2999
 * using.
3000
 *
3001
 * 'nullkeys' is the set of partition keys that are null.
3002
 */
3003
static PruneStepResult *
3004
get_matching_range_bounds(PartitionPruneContext *context,
3005
              StrategyNumber opstrategy, const Datum *values, int nvalues,
3006
              FmgrInfo *partsupfunc, Bitmapset *nullkeys)
3007
0
{
3008
0
  PruneStepResult *result = palloc0_object(PruneStepResult);
3009
0
  PartitionBoundInfo boundinfo = context->boundinfo;
3010
0
  Oid      *partcollation = context->partcollation;
3011
0
  int     partnatts = context->partnatts;
3012
0
  int      *partindices = boundinfo->indexes;
3013
0
  int     off,
3014
0
        minoff,
3015
0
        maxoff;
3016
0
  bool    is_equal;
3017
0
  bool    inclusive = false;
3018
3019
0
  Assert(context->strategy == PARTITION_STRATEGY_RANGE);
3020
0
  Assert(nvalues <= partnatts);
3021
3022
0
  result->scan_null = result->scan_default = false;
3023
3024
  /*
3025
   * If there are no datums to compare keys with, or if we got an IS NULL
3026
   * clause just return the default partition, if it exists.
3027
   */
3028
0
  if (boundinfo->ndatums == 0 || !bms_is_empty(nullkeys))
3029
0
  {
3030
0
    result->scan_default = partition_bound_has_default(boundinfo);
3031
0
    return result;
3032
0
  }
3033
3034
0
  minoff = 0;
3035
0
  maxoff = boundinfo->ndatums;
3036
3037
  /*
3038
   * If there are no values to compare with the datums in boundinfo, it
3039
   * means the caller asked for partitions for all non-null datums.  Add
3040
   * indexes of *all* partitions, including the default partition if one
3041
   * exists.
3042
   */
3043
0
  if (nvalues == 0)
3044
0
  {
3045
0
    result->scan_default = partition_bound_has_default(boundinfo);
3046
0
    Assert(partindices[minoff] >= -1 &&
3047
0
         partindices[maxoff] >= -1);
3048
0
    result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3049
3050
0
    return result;
3051
0
  }
3052
3053
  /*
3054
   * If the query does not constrain all key columns, we'll need to scan the
3055
   * default partition, if any.
3056
   */
3057
0
  if (nvalues < partnatts)
3058
0
    result->scan_default = partition_bound_has_default(boundinfo);
3059
3060
0
  switch (opstrategy)
3061
0
  {
3062
0
    case BTEqualStrategyNumber:
3063
      /* Look for the smallest bound that is = lookup value. */
3064
0
      off = partition_range_datum_bsearch(partsupfunc,
3065
0
                        partcollation,
3066
0
                        boundinfo,
3067
0
                        nvalues, values,
3068
0
                        &is_equal);
3069
3070
0
      if (off >= 0 && is_equal)
3071
0
      {
3072
0
        if (nvalues == partnatts)
3073
0
        {
3074
          /* There can only be zero or one matching partition. */
3075
0
          result->bound_offsets = bms_make_singleton(off + 1);
3076
0
          return result;
3077
0
        }
3078
0
        else
3079
0
        {
3080
0
          int     saved_off = off;
3081
3082
          /*
3083
           * Since the lookup value contains only a prefix of keys,
3084
           * we must find other bounds that may also match the
3085
           * prefix.  partition_range_datum_bsearch() returns the
3086
           * offset of one of them, find others by checking adjacent
3087
           * bounds.
3088
           */
3089
3090
          /*
3091
           * First find greatest bound that's smaller than the
3092
           * lookup value.
3093
           */
3094
0
          while (off >= 1)
3095
0
          {
3096
0
            int32   cmpval;
3097
3098
0
            cmpval =
3099
0
              partition_rbound_datum_cmp(partsupfunc,
3100
0
                             partcollation,
3101
0
                             boundinfo->datums[off - 1],
3102
0
                             boundinfo->kind[off - 1],
3103
0
                             values, nvalues);
3104
0
            if (cmpval != 0)
3105
0
              break;
3106
0
            off--;
3107
0
          }
3108
3109
0
          Assert(0 ==
3110
0
               partition_rbound_datum_cmp(partsupfunc,
3111
0
                            partcollation,
3112
0
                            boundinfo->datums[off],
3113
0
                            boundinfo->kind[off],
3114
0
                            values, nvalues));
3115
3116
          /*
3117
           * We can treat 'off' as the offset of the smallest bound
3118
           * to be included in the result, if we know it is the
3119
           * upper bound of the partition in which the lookup value
3120
           * could possibly exist.  One case it couldn't is if the
3121
           * bound, or precisely the matched portion of its prefix,
3122
           * is not inclusive.
3123
           */
3124
0
          if (boundinfo->kind[off][nvalues] ==
3125
0
            PARTITION_RANGE_DATUM_MINVALUE)
3126
0
            off++;
3127
3128
0
          minoff = off;
3129
3130
          /*
3131
           * Now find smallest bound that's greater than the lookup
3132
           * value.
3133
           */
3134
0
          off = saved_off;
3135
0
          while (off < boundinfo->ndatums - 1)
3136
0
          {
3137
0
            int32   cmpval;
3138
3139
0
            cmpval = partition_rbound_datum_cmp(partsupfunc,
3140
0
                              partcollation,
3141
0
                              boundinfo->datums[off + 1],
3142
0
                              boundinfo->kind[off + 1],
3143
0
                              values, nvalues);
3144
0
            if (cmpval != 0)
3145
0
              break;
3146
0
            off++;
3147
0
          }
3148
3149
0
          Assert(0 ==
3150
0
               partition_rbound_datum_cmp(partsupfunc,
3151
0
                            partcollation,
3152
0
                            boundinfo->datums[off],
3153
0
                            boundinfo->kind[off],
3154
0
                            values, nvalues));
3155
3156
          /*
3157
           * off + 1, then would be the offset of the greatest bound
3158
           * to be included in the result.  The exception is the
3159
           * mirror image of the MINVALUE case above: if the matched
3160
           * bound is exactly (prefix, MAXVALUE), no row carrying
3161
           * this prefix can sort above it, so the key space beyond
3162
           * it need not be considered.
3163
           */
3164
0
          if (boundinfo->kind[off][nvalues] ==
3165
0
            PARTITION_RANGE_DATUM_MAXVALUE)
3166
0
            maxoff = off;
3167
0
          else
3168
0
            maxoff = off + 1;
3169
0
        }
3170
3171
0
        Assert(minoff >= 0 && maxoff >= 0);
3172
0
        result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3173
0
      }
3174
0
      else
3175
0
      {
3176
        /*
3177
         * The lookup value falls in the range between some bounds in
3178
         * boundinfo.  'off' would be the offset of the greatest bound
3179
         * that is <= lookup value, so add off + 1 to the result
3180
         * instead as the offset of the upper bound of the only
3181
         * partition that may contain the lookup value.  If 'off' is
3182
         * -1 indicating that all bounds are greater, then we simply
3183
         * end up adding the first bound's offset, that is, 0.
3184
         */
3185
0
        result->bound_offsets = bms_make_singleton(off + 1);
3186
0
      }
3187
3188
0
      return result;
3189
3190
0
    case BTGreaterEqualStrategyNumber:
3191
0
      inclusive = true;
3192
0
      pg_fallthrough;
3193
0
    case BTGreaterStrategyNumber:
3194
3195
      /*
3196
       * Look for the smallest bound that is > or >= lookup value and
3197
       * set minoff to its offset.
3198
       */
3199
0
      off = partition_range_datum_bsearch(partsupfunc,
3200
0
                        partcollation,
3201
0
                        boundinfo,
3202
0
                        nvalues, values,
3203
0
                        &is_equal);
3204
0
      if (off < 0)
3205
0
      {
3206
        /*
3207
         * All bounds are greater than the lookup value, so include
3208
         * all of them in the result.
3209
         */
3210
0
        minoff = 0;
3211
0
      }
3212
0
      else
3213
0
      {
3214
0
        if (is_equal && nvalues < partnatts)
3215
0
        {
3216
          /*
3217
           * Since the lookup value contains only a prefix of keys,
3218
           * we must find other bounds that may also match the
3219
           * prefix.  partition_range_datum_bsearch() returns the
3220
           * offset of one of them, find others by checking adjacent
3221
           * bounds.
3222
           *
3223
           * Based on whether the lookup values are inclusive or
3224
           * not, we must either include the indexes of all such
3225
           * bounds in the result (that is, set minoff to the index
3226
           * of smallest such bound) or find the smallest one that's
3227
           * greater than the lookup values and set minoff to that.
3228
           */
3229
0
          while (true)
3230
0
          {
3231
0
            int32   cmpval;
3232
0
            int     nextoff;
3233
3234
0
            nextoff = inclusive ? off - 1 : off + 1;
3235
3236
0
            if (nextoff < 0 || nextoff >= boundinfo->ndatums)
3237
0
              break;
3238
0
            cmpval =
3239
0
              partition_rbound_datum_cmp(partsupfunc,
3240
0
                             partcollation,
3241
0
                             boundinfo->datums[nextoff],
3242
0
                             boundinfo->kind[nextoff],
3243
0
                             values, nvalues);
3244
0
            if (cmpval != 0)
3245
0
              break;
3246
3247
0
            off = nextoff;
3248
0
          }
3249
3250
0
          Assert(0 ==
3251
0
               partition_rbound_datum_cmp(partsupfunc,
3252
0
                            partcollation,
3253
0
                            boundinfo->datums[off],
3254
0
                            boundinfo->kind[off],
3255
0
                            values, nvalues));
3256
3257
0
          minoff = inclusive ? off : off + 1;
3258
0
        }
3259
0
        else
3260
0
        {
3261
3262
          /*
3263
           * lookup value falls in the range between some bounds in
3264
           * boundinfo.  off would be the offset of the greatest
3265
           * bound that is <= lookup value, so add off + 1 to the
3266
           * result instead as the offset of the upper bound of the
3267
           * smallest partition that may contain the lookup value.
3268
           */
3269
0
          minoff = off + 1;
3270
0
        }
3271
0
      }
3272
0
      break;
3273
3274
0
    case BTLessEqualStrategyNumber:
3275
0
      inclusive = true;
3276
0
      pg_fallthrough;
3277
0
    case BTLessStrategyNumber:
3278
3279
      /*
3280
       * Look for the greatest bound that is < or <= lookup value and
3281
       * set maxoff to its offset.
3282
       */
3283
0
      off = partition_range_datum_bsearch(partsupfunc,
3284
0
                        partcollation,
3285
0
                        boundinfo,
3286
0
                        nvalues, values,
3287
0
                        &is_equal);
3288
0
      if (off >= 0)
3289
0
      {
3290
        /*
3291
         * As above, check adjacent bounds to see if the bound is
3292
         * equal to the lookup value.
3293
         */
3294
0
        if (is_equal && nvalues < partnatts)
3295
0
        {
3296
0
          while (true)
3297
0
          {
3298
0
            int32   cmpval;
3299
0
            int     nextoff;
3300
3301
0
            nextoff = inclusive ? off + 1 : off - 1;
3302
3303
0
            if (nextoff < 0 || nextoff >= boundinfo->ndatums)
3304
0
              break;
3305
3306
0
            cmpval = partition_rbound_datum_cmp(partsupfunc,
3307
0
                              partcollation,
3308
0
                              boundinfo->datums[nextoff],
3309
0
                              boundinfo->kind[nextoff],
3310
0
                              values, nvalues);
3311
0
            if (cmpval != 0)
3312
0
              break;
3313
3314
0
            off = nextoff;
3315
0
          }
3316
3317
0
          Assert(0 ==
3318
0
               partition_rbound_datum_cmp(partsupfunc,
3319
0
                            partcollation,
3320
0
                            boundinfo->datums[off],
3321
0
                            boundinfo->kind[off],
3322
0
                            values, nvalues));
3323
3324
0
          maxoff = inclusive ? off + 1 : off;
3325
0
        }
3326
3327
        /*
3328
         * The lookup value falls in the range between some bounds in
3329
         * boundinfo.  'off' would be the offset of the greatest bound
3330
         * that is <= lookup value, so add off + 1 to the result
3331
         * instead as the offset of the upper bound of the greatest
3332
         * partition that may contain lookup value.  If the lookup
3333
         * value had exactly matched the bound, but it isn't
3334
         * inclusive, no need add the adjacent partition.
3335
         */
3336
0
        else if (!is_equal || inclusive)
3337
0
          maxoff = off + 1;
3338
0
        else
3339
0
          maxoff = off;
3340
0
      }
3341
0
      else
3342
0
      {
3343
        /*
3344
         * 'off' is -1 indicating that all bounds are greater, so just
3345
         * set the first bound's offset as maxoff.
3346
         */
3347
0
        maxoff = off + 1;
3348
0
      }
3349
0
      break;
3350
3351
0
    default:
3352
0
      elog(ERROR, "invalid strategy number %d", opstrategy);
3353
0
      break;
3354
0
  }
3355
3356
0
  Assert(minoff >= 0 && minoff <= boundinfo->ndatums);
3357
0
  Assert(maxoff >= 0 && maxoff <= boundinfo->ndatums);
3358
3359
3360
  /*
3361
   * Check for cases that we're scanning the DEFAULT partition when no rows
3362
   * can exist there for the given value.  This can happen when the first or
3363
   * final bound for partitions we're scanning have a MINVALUE or MAXVALUE
3364
   * clause respectively.  We needn't scan the DEFAULT partition for values
3365
   * beyond the bound since the MINVALUE / MAXVALUE bound handles up to
3366
   * negative or positive infinity.  The BTEqualStrategyNumber code above
3367
   * handled doing this for subsequent partition keys, so all that's left to
3368
   * handle here is the same for the first partition key.  We needn't
3369
   * perform this for all steps as the intersected results from the
3370
   * recursive processing of pruning steps for leading keys means we only
3371
   * scan the partitions common to all steps in the intersected set of
3372
   * steps.
3373
   */
3374
0
  if (nvalues == 1)
3375
0
  {
3376
    /*
3377
     * Check if we're scanning the default and if a MINVALUE bound covers
3378
     * the key space for the lower end.
3379
     */
3380
0
    if (minoff < boundinfo->ndatums && partindices[minoff] < 0 &&
3381
0
      boundinfo->kind[minoff][0] == PARTITION_RANGE_DATUM_MINVALUE)
3382
0
    {
3383
0
      minoff++;
3384
0
      Assert(boundinfo->indexes[minoff] >= 0);
3385
0
    }
3386
3387
    /*
3388
     * Likewise, do the same check for the upper range partition if the
3389
     * prior bound is a MAXVALUE kind.
3390
     */
3391
0
    if (maxoff >= 1 && partindices[maxoff] < 0 &&
3392
0
      boundinfo->kind[maxoff - 1][0] == PARTITION_RANGE_DATUM_MAXVALUE)
3393
0
    {
3394
0
      maxoff--;
3395
0
      Assert(boundinfo->indexes[maxoff] >= 0);
3396
0
    }
3397
0
  }
3398
0
  Assert(minoff >= 0 && maxoff >= 0);
3399
0
  if (minoff <= maxoff)
3400
0
    result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3401
3402
0
  return result;
3403
0
}
3404
3405
/*
3406
 * pull_exec_paramids
3407
 *    Returns a Bitmapset containing the paramids of all Params with
3408
 *    paramkind = PARAM_EXEC in 'expr'.
3409
 */
3410
static Bitmapset *
3411
pull_exec_paramids(Expr *expr)
3412
0
{
3413
0
  Bitmapset  *result = NULL;
3414
3415
0
  (void) pull_exec_paramids_walker((Node *) expr, &result);
3416
3417
0
  return result;
3418
0
}
3419
3420
static bool
3421
pull_exec_paramids_walker(Node *node, Bitmapset **context)
3422
0
{
3423
0
  if (node == NULL)
3424
0
    return false;
3425
0
  if (IsA(node, Param))
3426
0
  {
3427
0
    Param    *param = (Param *) node;
3428
3429
0
    if (param->paramkind == PARAM_EXEC)
3430
0
      *context = bms_add_member(*context, param->paramid);
3431
0
    return false;
3432
0
  }
3433
0
  return expression_tree_walker(node, pull_exec_paramids_walker, context);
3434
0
}
3435
3436
/*
3437
 * get_partkey_exec_paramids
3438
 *    Loop through given pruning steps and find out which exec Params
3439
 *    are used.
3440
 *
3441
 * Returns a Bitmapset of Param IDs.
3442
 */
3443
static Bitmapset *
3444
get_partkey_exec_paramids(List *steps)
3445
0
{
3446
0
  Bitmapset  *execparamids = NULL;
3447
0
  ListCell   *lc;
3448
3449
0
  foreach(lc, steps)
3450
0
  {
3451
0
    PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
3452
0
    ListCell   *lc2;
3453
3454
0
    if (!IsA(step, PartitionPruneStepOp))
3455
0
      continue;
3456
3457
0
    foreach(lc2, step->exprs)
3458
0
    {
3459
0
      Expr     *expr = lfirst(lc2);
3460
3461
      /* We can be quick for plain Consts */
3462
0
      if (!IsA(expr, Const))
3463
0
        execparamids = bms_join(execparamids,
3464
0
                    pull_exec_paramids(expr));
3465
0
    }
3466
0
  }
3467
3468
0
  return execparamids;
3469
0
}
3470
3471
/*
3472
 * perform_pruning_base_step
3473
 *    Determines the indexes of datums that satisfy conditions specified in
3474
 *    'opstep'.
3475
 *
3476
 * Result also contains whether special null-accepting and/or default
3477
 * partition need to be scanned.
3478
 */
3479
static PruneStepResult *
3480
perform_pruning_base_step(PartitionPruneContext *context,
3481
              PartitionPruneStepOp *opstep)
3482
0
{
3483
0
  ListCell   *lc1,
3484
0
         *lc2;
3485
0
  int     keyno,
3486
0
        nvalues;
3487
0
  Datum   values[PARTITION_MAX_KEYS];
3488
0
  FmgrInfo   *partsupfunc;
3489
0
  int     stateidx;
3490
3491
  /*
3492
   * There better be the same number of expressions and compare functions.
3493
   */
3494
0
  Assert(list_length(opstep->exprs) == list_length(opstep->cmpfns));
3495
3496
0
  nvalues = 0;
3497
0
  lc1 = list_head(opstep->exprs);
3498
0
  lc2 = list_head(opstep->cmpfns);
3499
3500
  /*
3501
   * Generate the partition lookup key that will be used by one of the
3502
   * get_matching_*_bounds functions called below.
3503
   */
3504
0
  for (keyno = 0; keyno < context->partnatts; keyno++)
3505
0
  {
3506
    /*
3507
     * For hash partitioning, it is possible that values of some keys are
3508
     * not provided in operator clauses, but instead the planner found
3509
     * that they appeared in a IS NULL clause.
3510
     */
3511
0
    if (bms_is_member(keyno, opstep->nullkeys))
3512
0
      continue;
3513
3514
    /*
3515
     * For range partitioning, we must only perform pruning with values
3516
     * for either all partition keys or a prefix thereof.
3517
     */
3518
0
    if (keyno > nvalues && context->strategy == PARTITION_STRATEGY_RANGE)
3519
0
      break;
3520
3521
0
    if (lc1 != NULL)
3522
0
    {
3523
0
      Expr     *expr;
3524
0
      Datum   datum;
3525
0
      bool    isnull;
3526
0
      Oid     cmpfn;
3527
3528
0
      expr = lfirst(lc1);
3529
0
      stateidx = PruneCxtStateIdx(context->partnatts,
3530
0
                    opstep->step.step_id, keyno);
3531
0
      partkey_datum_from_expr(context, expr, stateidx,
3532
0
                  &datum, &isnull);
3533
3534
      /*
3535
       * Since we only allow strict operators in pruning steps, any
3536
       * null-valued comparison value must cause the comparison to fail,
3537
       * so that no partitions could match.
3538
       */
3539
0
      if (isnull)
3540
0
      {
3541
0
        PruneStepResult *result;
3542
3543
0
        result = palloc_object(PruneStepResult);
3544
0
        result->bound_offsets = NULL;
3545
0
        result->scan_default = false;
3546
0
        result->scan_null = false;
3547
3548
0
        return result;
3549
0
      }
3550
3551
      /* Set up the stepcmpfuncs entry, unless we already did */
3552
0
      cmpfn = lfirst_oid(lc2);
3553
0
      Assert(OidIsValid(cmpfn));
3554
0
      if (cmpfn != context->stepcmpfuncs[stateidx].fn_oid)
3555
0
      {
3556
        /*
3557
         * If the needed support function is the same one cached in
3558
         * the relation's partition key, copy the cached FmgrInfo.
3559
         * Otherwise (i.e., when we have a cross-type comparison), an
3560
         * actual lookup is required.
3561
         */
3562
0
        if (cmpfn == context->partsupfunc[keyno].fn_oid)
3563
0
          fmgr_info_copy(&context->stepcmpfuncs[stateidx],
3564
0
                   &context->partsupfunc[keyno],
3565
0
                   context->ppccontext);
3566
0
        else
3567
0
          fmgr_info_cxt(cmpfn, &context->stepcmpfuncs[stateidx],
3568
0
                  context->ppccontext);
3569
0
      }
3570
3571
0
      values[keyno] = datum;
3572
0
      nvalues++;
3573
3574
0
      lc1 = lnext(opstep->exprs, lc1);
3575
0
      lc2 = lnext(opstep->cmpfns, lc2);
3576
0
    }
3577
0
  }
3578
3579
  /*
3580
   * Point partsupfunc to the entry for the 0th key of this step; the
3581
   * additional support functions, if any, follow consecutively.
3582
   */
3583
0
  stateidx = PruneCxtStateIdx(context->partnatts, opstep->step.step_id, 0);
3584
0
  partsupfunc = &context->stepcmpfuncs[stateidx];
3585
3586
0
  switch (context->strategy)
3587
0
  {
3588
0
    case PARTITION_STRATEGY_HASH:
3589
0
      return get_matching_hash_bounds(context,
3590
0
                      opstep->opstrategy,
3591
0
                      values, nvalues,
3592
0
                      partsupfunc,
3593
0
                      opstep->nullkeys);
3594
3595
0
    case PARTITION_STRATEGY_LIST:
3596
0
      return get_matching_list_bounds(context,
3597
0
                      opstep->opstrategy,
3598
0
                      values[0], nvalues,
3599
0
                      &partsupfunc[0],
3600
0
                      opstep->nullkeys);
3601
3602
0
    case PARTITION_STRATEGY_RANGE:
3603
0
      return get_matching_range_bounds(context,
3604
0
                       opstep->opstrategy,
3605
0
                       values, nvalues,
3606
0
                       partsupfunc,
3607
0
                       opstep->nullkeys);
3608
3609
0
    default:
3610
0
      elog(ERROR, "unexpected partition strategy: %d",
3611
0
         (int) context->strategy);
3612
0
      break;
3613
0
  }
3614
3615
0
  return NULL;
3616
0
}
3617
3618
/*
3619
 * perform_pruning_combine_step
3620
 *    Determines the indexes of datums obtained by combining those given
3621
 *    by the steps identified by cstep->source_stepids using the specified
3622
 *    combination method
3623
 *
3624
 * Since cstep may refer to the result of earlier steps, we also receive
3625
 * step_results here.
3626
 */
3627
static PruneStepResult *
3628
perform_pruning_combine_step(PartitionPruneContext *context,
3629
               PartitionPruneStepCombine *cstep,
3630
               PruneStepResult **step_results)
3631
0
{
3632
0
  PruneStepResult *result = palloc0_object(PruneStepResult);
3633
0
  bool    firststep;
3634
0
  ListCell   *lc1;
3635
3636
  /*
3637
   * A combine step without any source steps is an indication to not perform
3638
   * any partition pruning.  Return all datum indexes in that case.
3639
   */
3640
0
  if (cstep->source_stepids == NIL)
3641
0
  {
3642
0
    PartitionBoundInfo boundinfo = context->boundinfo;
3643
3644
0
    result->bound_offsets =
3645
0
      bms_add_range(NULL, 0, boundinfo->nindexes - 1);
3646
0
    result->scan_default = partition_bound_has_default(boundinfo);
3647
0
    result->scan_null = partition_bound_accepts_nulls(boundinfo);
3648
0
    return result;
3649
0
  }
3650
3651
0
  switch (cstep->combineOp)
3652
0
  {
3653
0
    case PARTPRUNE_COMBINE_UNION:
3654
0
      foreach(lc1, cstep->source_stepids)
3655
0
      {
3656
0
        int     step_id = lfirst_int(lc1);
3657
0
        PruneStepResult *step_result;
3658
3659
        /*
3660
         * step_results[step_id] must contain a valid result, which is
3661
         * confirmed by the fact that cstep's step_id is greater than
3662
         * step_id and the fact that results of the individual steps
3663
         * are evaluated in sequence of their step_ids.
3664
         */
3665
0
        if (step_id >= cstep->step.step_id)
3666
0
          elog(ERROR, "invalid pruning combine step argument");
3667
0
        step_result = step_results[step_id];
3668
0
        Assert(step_result != NULL);
3669
3670
        /* Record any additional datum indexes from this step */
3671
0
        result->bound_offsets = bms_add_members(result->bound_offsets,
3672
0
                            step_result->bound_offsets);
3673
3674
        /* Update whether to scan null and default partitions. */
3675
0
        if (!result->scan_null)
3676
0
          result->scan_null = step_result->scan_null;
3677
0
        if (!result->scan_default)
3678
0
          result->scan_default = step_result->scan_default;
3679
0
      }
3680
0
      break;
3681
3682
0
    case PARTPRUNE_COMBINE_INTERSECT:
3683
0
      firststep = true;
3684
0
      foreach(lc1, cstep->source_stepids)
3685
0
      {
3686
0
        int     step_id = lfirst_int(lc1);
3687
0
        PruneStepResult *step_result;
3688
3689
0
        if (step_id >= cstep->step.step_id)
3690
0
          elog(ERROR, "invalid pruning combine step argument");
3691
0
        step_result = step_results[step_id];
3692
0
        Assert(step_result != NULL);
3693
3694
0
        if (firststep)
3695
0
        {
3696
          /* Copy step's result the first time. */
3697
0
          result->bound_offsets =
3698
0
            bms_copy(step_result->bound_offsets);
3699
0
          result->scan_null = step_result->scan_null;
3700
0
          result->scan_default = step_result->scan_default;
3701
0
          firststep = false;
3702
0
        }
3703
0
        else
3704
0
        {
3705
          /* Record datum indexes common to both steps */
3706
0
          result->bound_offsets =
3707
0
            bms_int_members(result->bound_offsets,
3708
0
                    step_result->bound_offsets);
3709
3710
          /* Update whether to scan null and default partitions. */
3711
0
          if (result->scan_null)
3712
0
            result->scan_null = step_result->scan_null;
3713
0
          if (result->scan_default)
3714
0
            result->scan_default = step_result->scan_default;
3715
0
        }
3716
0
      }
3717
0
      break;
3718
0
  }
3719
3720
0
  return result;
3721
0
}
3722
3723
/*
3724
 * match_boolean_partition_clause
3725
 *
3726
 * If we're able to match the clause to the partition key as specially-shaped
3727
 * boolean clause, set *outconst to a Const containing a true, false or NULL
3728
 * value, set *notclause according to if the clause was in the "not" form,
3729
 * i.e. "IS NOT TRUE", "IS NOT FALSE" or "IS NOT UNKNOWN" and return
3730
 * PARTCLAUSE_MATCH_CLAUSE for "IS [NOT] (TRUE|FALSE)" clauses and
3731
 * PARTCLAUSE_MATCH_NULLNESS for "IS [NOT] UNKNOWN" clauses.  Otherwise,
3732
 * return PARTCLAUSE_UNSUPPORTED if the clause cannot be used for partition
3733
 * pruning, and PARTCLAUSE_NOMATCH for supported clauses that do not match this
3734
 * 'partkey'.
3735
 */
3736
static PartClauseMatchStatus
3737
match_boolean_partition_clause(Oid partopfamily, Expr *clause, const Expr *partkey,
3738
                 Expr **outconst, bool *notclause)
3739
0
{
3740
0
  Expr     *leftop;
3741
3742
0
  *outconst = NULL;
3743
0
  *notclause = false;
3744
3745
  /*
3746
   * Partitioning currently can only use built-in AMs, so checking for
3747
   * built-in boolean opfamilies is good enough.
3748
   */
3749
0
  if (!IsBuiltinBooleanOpfamily(partopfamily))
3750
0
    return PARTCLAUSE_UNSUPPORTED;
3751
3752
0
  if (IsA(clause, BooleanTest))
3753
0
  {
3754
0
    BooleanTest *btest = (BooleanTest *) clause;
3755
3756
0
    leftop = btest->arg;
3757
0
    leftop = (Expr *) strip_noop_phvs((Node *) leftop);
3758
0
    while (IsA(leftop, RelabelType))
3759
0
      leftop = ((RelabelType *) leftop)->arg;
3760
3761
0
    if (equal(leftop, partkey))
3762
0
    {
3763
0
      switch (btest->booltesttype)
3764
0
      {
3765
0
        case IS_NOT_TRUE:
3766
0
          *notclause = true;
3767
0
          pg_fallthrough;
3768
0
        case IS_TRUE:
3769
0
          *outconst = (Expr *) makeBoolConst(true, false);
3770
0
          return PARTCLAUSE_MATCH_CLAUSE;
3771
0
        case IS_NOT_FALSE:
3772
0
          *notclause = true;
3773
0
          pg_fallthrough;
3774
0
        case IS_FALSE:
3775
0
          *outconst = (Expr *) makeBoolConst(false, false);
3776
0
          return PARTCLAUSE_MATCH_CLAUSE;
3777
0
        case IS_NOT_UNKNOWN:
3778
0
          *notclause = true;
3779
0
          pg_fallthrough;
3780
0
        case IS_UNKNOWN:
3781
0
          return PARTCLAUSE_MATCH_NULLNESS;
3782
0
        default:
3783
0
          return PARTCLAUSE_UNSUPPORTED;
3784
0
      }
3785
0
    }
3786
    /* does not match partition key */
3787
0
    return PARTCLAUSE_NOMATCH;
3788
0
  }
3789
0
  else
3790
0
  {
3791
0
    bool    is_not_clause = is_notclause(clause);
3792
3793
0
    leftop = is_not_clause ? get_notclausearg(clause) : clause;
3794
3795
0
    leftop = (Expr *) strip_noop_phvs((Node *) leftop);
3796
0
    while (IsA(leftop, RelabelType))
3797
0
      leftop = ((RelabelType *) leftop)->arg;
3798
3799
    /* Compare to the partition key, and make up a clause ... */
3800
0
    if (equal(leftop, partkey))
3801
0
      *outconst = (Expr *) makeBoolConst(!is_not_clause, false);
3802
0
    else if (equal(negate_clause((Node *) leftop), partkey))
3803
0
      *outconst = (Expr *) makeBoolConst(is_not_clause, false);
3804
0
    else
3805
0
      return PARTCLAUSE_NOMATCH;
3806
3807
0
    return PARTCLAUSE_MATCH_CLAUSE;
3808
0
  }
3809
0
}
3810
3811
/*
3812
 * partkey_datum_from_expr
3813
 *    Evaluate expression for potential partition pruning
3814
 *
3815
 * Evaluate 'expr'; set *value and *isnull to the resulting Datum and nullflag.
3816
 *
3817
 * If expr isn't a Const, its ExprState is in stateidx of the context
3818
 * exprstate array.
3819
 *
3820
 * Note that the evaluated result may be in the per-tuple memory context of
3821
 * context->exprcontext, and we may have leaked other memory there too.
3822
 * This memory must be recovered by resetting that ExprContext after
3823
 * we're done with the pruning operation (see execPartition.c).
3824
 */
3825
static void
3826
partkey_datum_from_expr(PartitionPruneContext *context,
3827
            Expr *expr, int stateidx,
3828
            Datum *value, bool *isnull)
3829
0
{
3830
0
  if (IsA(expr, Const))
3831
0
  {
3832
    /* We can always determine the value of a constant */
3833
0
    Const    *con = (Const *) expr;
3834
3835
0
    *value = con->constvalue;
3836
0
    *isnull = con->constisnull;
3837
0
  }
3838
0
  else
3839
0
  {
3840
0
    ExprState  *exprstate;
3841
0
    ExprContext *ectx;
3842
3843
    /*
3844
     * We should never see a non-Const in a step unless the caller has
3845
     * passed a valid ExprContext.
3846
     */
3847
0
    Assert(context->exprcontext != NULL);
3848
3849
0
    exprstate = context->exprstates[stateidx];
3850
0
    ectx = context->exprcontext;
3851
0
    *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
3852
0
  }
3853
0
}