PostgreSQL Source Code  git master
partprune.c File Reference
#include "postgres.h"
#include "access/hash.h"
#include "access/nbtree.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/appendinfo.h"
#include "optimizer/cost.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "parser/parsetree.h"
#include "partitioning/partbounds.h"
#include "partitioning/partprune.h"
#include "rewrite/rewriteManip.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
Include dependency graph for partprune.c:

Go to the source code of this file.

Data Structures

struct  PartClauseInfo
 
struct  GeneratePruningStepsContext
 
struct  PruneStepResult
 

Macros

#define PartCollMatchesExprColl(partcoll, exprcoll)   ((partcoll) == InvalidOid || (partcoll) == (exprcoll))
 

Typedefs

typedef struct PartClauseInfo PartClauseInfo
 
typedef enum PartClauseMatchStatus PartClauseMatchStatus
 
typedef enum PartClauseTarget PartClauseTarget
 
typedef struct GeneratePruningStepsContext GeneratePruningStepsContext
 
typedef struct PruneStepResult PruneStepResult
 

Enumerations

enum  PartClauseMatchStatus {
  PARTCLAUSE_NOMATCH, PARTCLAUSE_MATCH_CLAUSE, PARTCLAUSE_MATCH_NULLNESS, PARTCLAUSE_MATCH_STEPS,
  PARTCLAUSE_MATCH_CONTRADICT, PARTCLAUSE_UNSUPPORTED
}
 
enum  PartClauseTarget { PARTTARGET_PLANNER, PARTTARGET_INITIAL, PARTTARGET_EXEC }
 

Functions

static Listmake_partitionedrel_pruneinfo (PlannerInfo *root, RelOptInfo *parentrel, int *relid_subplan_map, List *partitioned_rels, List *prunequal, Bitmapset **matchedsubplans)
 
static void gen_partprune_steps (RelOptInfo *rel, List *clauses, PartClauseTarget target, GeneratePruningStepsContext *context)
 
static Listgen_partprune_steps_internal (GeneratePruningStepsContext *context, List *clauses)
 
static PartitionPruneStepgen_prune_step_op (GeneratePruningStepsContext *context, StrategyNumber opstrategy, bool op_is_ne, List *exprs, List *cmpfns, Bitmapset *nullkeys)
 
static PartitionPruneStepgen_prune_step_combine (GeneratePruningStepsContext *context, List *source_stepids, PartitionPruneCombineOp combineOp)
 
static PartitionPruneStepgen_prune_steps_from_opexps (GeneratePruningStepsContext *context, List **keyclauses, Bitmapset *nullkeys)
 
static PartClauseMatchStatus match_clause_to_partition_key (GeneratePruningStepsContext *context, Expr *clause, Expr *partkey, int partkeyidx, bool *clause_is_not_null, PartClauseInfo **pc, List **clause_steps)
 
static Listget_steps_using_prefix (GeneratePruningStepsContext *context, StrategyNumber step_opstrategy, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix)
 
static Listget_steps_using_prefix_recurse (GeneratePruningStepsContext *context, StrategyNumber step_opstrategy, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix, ListCell *start, List *step_exprs, List *step_cmpfns)
 
static PruneStepResultget_matching_hash_bounds (PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
 
static PruneStepResultget_matching_list_bounds (PartitionPruneContext *context, StrategyNumber opstrategy, Datum value, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
 
static PruneStepResultget_matching_range_bounds (PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
 
static Bitmapsetpull_exec_paramids (Expr *expr)
 
static bool pull_exec_paramids_walker (Node *node, Bitmapset **context)
 
static Bitmapsetget_partkey_exec_paramids (List *steps)
 
static PruneStepResultperform_pruning_base_step (PartitionPruneContext *context, PartitionPruneStepOp *opstep)
 
static PruneStepResultperform_pruning_combine_step (PartitionPruneContext *context, PartitionPruneStepCombine *cstep, PruneStepResult **step_results)
 
static PartClauseMatchStatus match_boolean_partition_clause (Oid partopfamily, Expr *clause, Expr *partkey, Expr **outconst)
 
static void partkey_datum_from_expr (PartitionPruneContext *context, Expr *expr, int stateidx, Datum *value, bool *isnull)
 
PartitionPruneInfomake_partition_pruneinfo (PlannerInfo *root, RelOptInfo *parentrel, List *subpaths, List *partitioned_rels, List *prunequal)
 
Bitmapsetprune_append_rel_partitions (RelOptInfo *rel)
 
Bitmapsetget_matching_partitions (PartitionPruneContext *context, List *pruning_steps)
 

Macro Definition Documentation

◆ PartCollMatchesExprColl

#define PartCollMatchesExprColl (   partcoll,
  exprcoll 
)    ((partcoll) == InvalidOid || (partcoll) == (exprcoll))

Definition at line 1657 of file partprune.c.

Referenced by match_clause_to_partition_key().

Typedef Documentation

◆ GeneratePruningStepsContext

◆ PartClauseInfo

◆ PartClauseMatchStatus

◆ PartClauseTarget

◆ PruneStepResult

Enumeration Type Documentation

◆ PartClauseMatchStatus

Enumerator
PARTCLAUSE_NOMATCH 
PARTCLAUSE_MATCH_CLAUSE 
PARTCLAUSE_MATCH_NULLNESS 
PARTCLAUSE_MATCH_STEPS 
PARTCLAUSE_MATCH_CONTRADICT 
PARTCLAUSE_UNSUPPORTED 

Definition at line 78 of file partprune.c.

◆ PartClauseTarget

Enumerator
PARTTARGET_PLANNER 
PARTTARGET_INITIAL 
PARTTARGET_EXEC 

Definition at line 92 of file partprune.c.

93 {
94  PARTTARGET_PLANNER, /* want to prune during planning */
95  PARTTARGET_INITIAL, /* want to prune during executor startup */
96  PARTTARGET_EXEC /* want to prune during each plan node scan */
PartClauseTarget
Definition: partprune.c:92

Function Documentation

◆ gen_partprune_steps()

static void gen_partprune_steps ( RelOptInfo rel,
List clauses,
PartClauseTarget  target,
GeneratePruningStepsContext context 
)
static

Definition at line 614 of file partprune.c.

References RelOptInfo::boundinfo, gen_partprune_steps_internal(), list_concat_copy(), partition_bound_has_default, RelOptInfo::partition_qual, GeneratePruningStepsContext::rel, and GeneratePruningStepsContext::target.

Referenced by make_partitionedrel_pruneinfo(), and prune_append_rel_partitions().

616 {
617  /* Initialize all output values to zero/false/NULL */
618  memset(context, 0, sizeof(GeneratePruningStepsContext));
619  context->rel = rel;
620  context->target = target;
621 
622  /*
623  * If this partitioned table is in turn a partition, and it shares any
624  * partition keys with its parent, then it's possible that the hierarchy
625  * allows the parent a narrower range of values than some of its
626  * partitions (particularly the default one). This is normally not
627  * useful, but it can be to prune the default partition.
628  */
630  {
631  /* Make a copy to avoid modifying the passed-in List */
632  clauses = list_concat_copy(clauses, rel->partition_qual);
633  }
634 
635  /* Down into the rabbit-hole. */
636  (void) gen_partprune_steps_internal(context, clauses);
637 }
List * list_concat_copy(const List *list1, const List *list2)
Definition: list.c:552
#define partition_bound_has_default(bi)
Definition: partbounds.h:75
static List * gen_partprune_steps_internal(GeneratePruningStepsContext *context, List *clauses)
Definition: partprune.c:845
struct PartitionBoundInfoData * boundinfo
Definition: pathnodes.h:746
List * partition_qual
Definition: pathnodes.h:749
PartClauseTarget target
Definition: partprune.c:115

◆ gen_partprune_steps_internal()

static List * gen_partprune_steps_internal ( GeneratePruningStepsContext context,
List clauses 
)
static

Definition at line 845 of file partprune.c.

References arg, generate_unaccent_rules::args, Assert, bms_add_member(), bms_is_empty(), bms_is_member(), bms_num_members(), RelOptInfo::boundinfo, GeneratePruningStepsContext::contradictory, DatumGetBool, gen_prune_step_combine(), gen_prune_step_op(), gen_prune_steps_from_opexps(), i, InvalidStrategy, is_andclause(), is_orclause(), IsA, lappend(), lappend_int(), lfirst, linitial, list_concat(), list_length(), list_make1, match_clause_to_partition_key(), NIL, RelOptInfo::part_scheme, PARTCLAUSE_MATCH_CLAUSE, PARTCLAUSE_MATCH_CONTRADICT, PARTCLAUSE_MATCH_NULLNESS, PARTCLAUSE_MATCH_STEPS, PARTCLAUSE_NOMATCH, PARTCLAUSE_UNSUPPORTED, RelOptInfo::partexprs, partition_bound_has_default, PARTITION_MAX_KEYS, RelOptInfo::partition_qual, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionSchemeData::partnatts, PARTPRUNE_COMBINE_INTERSECT, PARTPRUNE_COMBINE_UNION, predicate_refuted_by(), GeneratePruningStepsContext::rel, PartitionPruneStep::step_id, and PartitionSchemeData::strategy.

Referenced by gen_partprune_steps(), and match_clause_to_partition_key().

847 {
848  PartitionScheme part_scheme = context->rel->part_scheme;
849  List *keyclauses[PARTITION_MAX_KEYS];
850  Bitmapset *nullkeys = NULL,
851  *notnullkeys = NULL;
852  bool generate_opsteps = false;
853  List *result = NIL;
854  ListCell *lc;
855 
856  /*
857  * If this partitioned relation has a default partition and is itself a
858  * partition (as evidenced by partition_qual being not NIL), we first
859  * check if the clauses contradict the partition constraint. If they do,
860  * there's no need to generate any steps as it'd already be proven that no
861  * partitions need to be scanned.
862  *
863  * This is a measure of last resort only to be used because the default
864  * partition cannot be pruned using the steps generated from clauses that
865  * contradict the parent's partition constraint; regular pruning, which is
866  * cheaper, is sufficient when no default partition exists.
867  */
868  if (partition_bound_has_default(context->rel->boundinfo) &&
869  predicate_refuted_by(context->rel->partition_qual, clauses, false))
870  {
871  context->contradictory = true;
872  return NIL;
873  }
874 
875  memset(keyclauses, 0, sizeof(keyclauses));
876  foreach(lc, clauses)
877  {
878  Expr *clause = (Expr *) lfirst(lc);
879  int i;
880 
881  /* Look through RestrictInfo, if any */
882  if (IsA(clause, RestrictInfo))
883  clause = ((RestrictInfo *) clause)->clause;
884 
885  /* Constant-false-or-null is contradictory */
886  if (IsA(clause, Const) &&
887  (((Const *) clause)->constisnull ||
888  !DatumGetBool(((Const *) clause)->constvalue)))
889  {
890  context->contradictory = true;
891  return NIL;
892  }
893 
894  /* Get the BoolExpr's out of the way. */
895  if (IsA(clause, BoolExpr))
896  {
897  /*
898  * Generate steps for arguments.
899  *
900  * While steps generated for the arguments themselves will be
901  * added to context->steps during recursion and will be evaluated
902  * independently, collect their step IDs to be stored in the
903  * combine step we'll be creating.
904  */
905  if (is_orclause(clause))
906  {
907  List *arg_stepids = NIL;
908  bool all_args_contradictory = true;
909  ListCell *lc1;
910 
911  /*
912  * We can share the outer context area with the recursive
913  * call, but contradictory had better not be true yet.
914  */
915  Assert(!context->contradictory);
916 
917  /*
918  * Get pruning step for each arg. If we get contradictory for
919  * all args, it means the OR expression is false as a whole.
920  */
921  foreach(lc1, ((BoolExpr *) clause)->args)
922  {
923  Expr *arg = lfirst(lc1);
924  bool arg_contradictory;
925  List *argsteps;
926 
927  argsteps = gen_partprune_steps_internal(context,
928  list_make1(arg));
929  arg_contradictory = context->contradictory;
930  /* Keep context->contradictory clear till we're done */
931  context->contradictory = false;
932 
933  if (arg_contradictory)
934  {
935  /* Just ignore self-contradictory arguments. */
936  continue;
937  }
938  else
939  all_args_contradictory = false;
940 
941  if (argsteps != NIL)
942  {
943  PartitionPruneStep *step;
944 
945  Assert(list_length(argsteps) == 1);
946  step = (PartitionPruneStep *) linitial(argsteps);
947  arg_stepids = lappend_int(arg_stepids, step->step_id);
948  }
949  else
950  {
951  PartitionPruneStep *orstep;
952 
953  /*
954  * The arg didn't contain a clause matching this
955  * partition key. We cannot prune using such an arg.
956  * To indicate that to the pruning code, we must
957  * construct a dummy PartitionPruneStepCombine whose
958  * source_stepids is set to an empty List.
959  */
960  orstep = gen_prune_step_combine(context, NIL,
962  arg_stepids = lappend_int(arg_stepids, orstep->step_id);
963  }
964  }
965 
966  /* If all the OR arms are contradictory, we can stop */
967  if (all_args_contradictory)
968  {
969  context->contradictory = true;
970  return NIL;
971  }
972 
973  if (arg_stepids != NIL)
974  {
975  PartitionPruneStep *step;
976 
977  step = gen_prune_step_combine(context, arg_stepids,
979  result = lappend(result, step);
980  }
981  continue;
982  }
983  else if (is_andclause(clause))
984  {
985  List *args = ((BoolExpr *) clause)->args;
986  List *argsteps,
987  *arg_stepids = NIL;
988  ListCell *lc1;
989 
990  /*
991  * args may itself contain clauses of arbitrary type, so just
992  * recurse and later combine the component partitions sets
993  * using a combine step.
994  */
995  argsteps = gen_partprune_steps_internal(context, args);
996 
997  /* If any AND arm is contradictory, we can stop immediately */
998  if (context->contradictory)
999  return NIL;
1000 
1001  foreach(lc1, argsteps)
1002  {
1003  PartitionPruneStep *step = lfirst(lc1);
1004 
1005  arg_stepids = lappend_int(arg_stepids, step->step_id);
1006  }
1007 
1008  if (arg_stepids != NIL)
1009  {
1010  PartitionPruneStep *step;
1011 
1012  step = gen_prune_step_combine(context, arg_stepids,
1014  result = lappend(result, step);
1015  }
1016  continue;
1017  }
1018 
1019  /*
1020  * Fall-through for a NOT clause, which if it's a Boolean clause,
1021  * will be handled in match_clause_to_partition_key(). We
1022  * currently don't perform any pruning for more complex NOT
1023  * clauses.
1024  */
1025  }
1026 
1027  /*
1028  * See if we can match this clause to any of the partition keys.
1029  */
1030  for (i = 0; i < part_scheme->partnatts; i++)
1031  {
1032  Expr *partkey = linitial(context->rel->partexprs[i]);
1033  bool clause_is_not_null = false;
1034  PartClauseInfo *pc = NULL;
1035  List *clause_steps = NIL;
1036 
1037  switch (match_clause_to_partition_key(context,
1038  clause, partkey, i,
1039  &clause_is_not_null,
1040  &pc, &clause_steps))
1041  {
1043  Assert(pc != NULL);
1044 
1045  /*
1046  * Since we only allow strict operators, check for any
1047  * contradicting IS NULL.
1048  */
1049  if (bms_is_member(i, nullkeys))
1050  {
1051  context->contradictory = true;
1052  return NIL;
1053  }
1054  generate_opsteps = true;
1055  keyclauses[i] = lappend(keyclauses[i], pc);
1056  break;
1057 
1059  if (!clause_is_not_null)
1060  {
1061  /*
1062  * check for conflicting IS NOT NULL as well as
1063  * contradicting strict clauses
1064  */
1065  if (bms_is_member(i, notnullkeys) ||
1066  keyclauses[i] != NIL)
1067  {
1068  context->contradictory = true;
1069  return NIL;
1070  }
1071  nullkeys = bms_add_member(nullkeys, i);
1072  }
1073  else
1074  {
1075  /* check for conflicting IS NULL */
1076  if (bms_is_member(i, nullkeys))
1077  {
1078  context->contradictory = true;
1079  return NIL;
1080  }
1081  notnullkeys = bms_add_member(notnullkeys, i);
1082  }
1083  break;
1084 
1086  Assert(clause_steps != NIL);
1087  result = list_concat(result, clause_steps);
1088  break;
1089 
1091  /* We've nothing more to do if a contradiction was found. */
1092  context->contradictory = true;
1093  return NIL;
1094 
1095  case PARTCLAUSE_NOMATCH:
1096 
1097  /*
1098  * Clause didn't match this key, but it might match the
1099  * next one.
1100  */
1101  continue;
1102 
1104  /* This clause cannot be used for pruning. */
1105  break;
1106  }
1107 
1108  /* done; go check the next clause. */
1109  break;
1110  }
1111  }
1112 
1113  /*-----------
1114  * Now generate some (more) pruning steps. We have three strategies:
1115  *
1116  * 1) Generate pruning steps based on IS NULL clauses:
1117  * a) For list partitioning, null partition keys can only be found in
1118  * the designated null-accepting partition, so if there are IS NULL
1119  * clauses containing partition keys we should generate a pruning
1120  * step that gets rid of all partitions but that one. We can
1121  * disregard any OpExpr we may have found.
1122  * b) For range partitioning, only the default partition can contain
1123  * NULL values, so the same rationale applies.
1124  * c) For hash partitioning, we only apply this strategy if we have
1125  * IS NULL clauses for all the keys. Strategy 2 below will take
1126  * care of the case where some keys have OpExprs and others have
1127  * IS NULL clauses.
1128  *
1129  * 2) If not, generate steps based on OpExprs we have (if any).
1130  *
1131  * 3) If this doesn't work either, we may be able to generate steps to
1132  * prune just the null-accepting partition (if one exists), if we have
1133  * IS NOT NULL clauses for all partition keys.
1134  */
1135  if (!bms_is_empty(nullkeys) &&
1136  (part_scheme->strategy == PARTITION_STRATEGY_LIST ||
1137  part_scheme->strategy == PARTITION_STRATEGY_RANGE ||
1138  (part_scheme->strategy == PARTITION_STRATEGY_HASH &&
1139  bms_num_members(nullkeys) == part_scheme->partnatts)))
1140  {
1141  PartitionPruneStep *step;
1142 
1143  /* Strategy 1 */
1144  step = gen_prune_step_op(context, InvalidStrategy,
1145  false, NIL, NIL, nullkeys);
1146  result = lappend(result, step);
1147  }
1148  else if (generate_opsteps)
1149  {
1150  PartitionPruneStep *step;
1151 
1152  /* Strategy 2 */
1153  step = gen_prune_steps_from_opexps(context, keyclauses, nullkeys);
1154  if (step != NULL)
1155  result = lappend(result, step);
1156  }
1157  else if (bms_num_members(notnullkeys) == part_scheme->partnatts)
1158  {
1159  PartitionPruneStep *step;
1160 
1161  /* Strategy 3 */
1162  step = gen_prune_step_op(context, InvalidStrategy,
1163  false, NIL, NIL, NULL);
1164  result = lappend(result, step);
1165  }
1166 
1167  /*
1168  * Finally, results from all entries appearing in result should be
1169  * combined using an INTERSECT combine step, if more than one.
1170  */
1171  if (list_length(result) > 1)
1172  {
1173  List *step_ids = NIL;
1174 
1175  foreach(lc, result)
1176  {
1177  PartitionPruneStep *step = lfirst(lc);
1178 
1179  step_ids = lappend_int(step_ids, step->step_id);
1180  }
1181 
1182  if (step_ids != NIL)
1183  {
1184  PartitionPruneStep *step;
1185 
1186  step = gen_prune_step_combine(context, step_ids,
1188  result = lappend(result, step);
1189  }
1190  }
1191 
1192  return result;
1193 }
#define InvalidStrategy
Definition: stratnum.h:24
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:580
static bool is_orclause(const void *clause)
Definition: nodeFuncs.h:106
static PartitionPruneStep * gen_prune_steps_from_opexps(GeneratePruningStepsContext *context, List **keyclauses, Bitmapset *nullkeys)
Definition: partprune.c:1262
static bool is_andclause(const void *clause)
Definition: nodeFuncs.h:97
List * list_concat(List *list1, const List *list2)
Definition: list.c:515
#define PARTITION_MAX_KEYS
#define list_make1(x1)
Definition: pg_list.h:227
static PartClauseMatchStatus match_clause_to_partition_key(GeneratePruningStepsContext *context, Expr *clause, Expr *partkey, int partkeyidx, bool *clause_is_not_null, PartClauseInfo **pc, List **clause_steps)
Definition: partprune.c:1692
List ** partexprs
Definition: pathnodes.h:753
#define linitial(l)
Definition: pg_list.h:195
bool predicate_refuted_by(List *predicate_list, List *clause_list, bool weak)
Definition: predtest.c:221
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:646
#define DatumGetBool(X)
Definition: postgres.h:393
#define partition_bound_has_default(bi)
Definition: partbounds.h:75
static List * gen_partprune_steps_internal(GeneratePruningStepsContext *context, List *clauses)
Definition: partprune.c:845
List * lappend_int(List *list, int datum)
Definition: list.c:339
List * lappend(List *list, void *datum)
Definition: list.c:321
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
struct PartitionBoundInfoData * boundinfo
Definition: pathnodes.h:746
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
#define Assert(condition)
Definition: c.h:745
#define lfirst(lc)
Definition: pg_list.h:190
static int list_length(const List *l)
Definition: pg_list.h:169
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:736
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
int i
void * arg
PartitionScheme part_scheme
Definition: pathnodes.h:742
List * partition_qual
Definition: pathnodes.h:749
static PartitionPruneStep * gen_prune_step_op(GeneratePruningStepsContext *context, StrategyNumber opstrategy, bool op_is_ne, List *exprs, List *cmpfns, Bitmapset *nullkeys)
Definition: partprune.c:1203
Definition: pg_list.h:50
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
static PartitionPruneStep * gen_prune_step_combine(GeneratePruningStepsContext *context, List *source_stepids, PartitionPruneCombineOp combineOp)
Definition: partprune.c:1236

◆ gen_prune_step_combine()

static PartitionPruneStep * gen_prune_step_combine ( GeneratePruningStepsContext context,
List source_stepids,
PartitionPruneCombineOp  combineOp 
)
static

Definition at line 1236 of file partprune.c.

References PartitionPruneStepCombine::combineOp, lappend(), makeNode, GeneratePruningStepsContext::next_step_id, PartitionPruneStepCombine::source_stepids, PartitionPruneStepCombine::step, PartitionPruneStep::step_id, and GeneratePruningStepsContext::steps.

Referenced by gen_partprune_steps_internal(), and gen_prune_steps_from_opexps().

1239 {
1241 
1242  cstep->step.step_id = context->next_step_id++;
1243  cstep->combineOp = combineOp;
1244  cstep->source_stepids = source_stepids;
1245 
1246  context->steps = lappend(context->steps, cstep);
1247 
1248  return (PartitionPruneStep *) cstep;
1249 }
PartitionPruneCombineOp combineOp
Definition: plannodes.h:1230
List * lappend(List *list, void *datum)
Definition: list.c:321
#define makeNode(_type_)
Definition: nodes.h:577
PartitionPruneStep step
Definition: plannodes.h:1228

◆ gen_prune_step_op()

static PartitionPruneStep * gen_prune_step_op ( GeneratePruningStepsContext context,
StrategyNumber  opstrategy,
bool  op_is_ne,
List exprs,
List cmpfns,
Bitmapset nullkeys 
)
static

Definition at line 1203 of file partprune.c.

References Assert, PartitionPruneStepOp::cmpfns, PartitionPruneStepOp::exprs, InvalidStrategy, lappend(), list_length(), makeNode, GeneratePruningStepsContext::next_step_id, PartitionPruneStepOp::nullkeys, PartitionPruneStepOp::opstrategy, PartitionPruneStepOp::step, PartitionPruneStep::step_id, and GeneratePruningStepsContext::steps.

Referenced by gen_partprune_steps_internal(), get_steps_using_prefix(), and get_steps_using_prefix_recurse().

1207 {
1209 
1210  opstep->step.step_id = context->next_step_id++;
1211 
1212  /*
1213  * For clauses that contain an <> operator, set opstrategy to
1214  * InvalidStrategy to signal get_matching_list_bounds to do the right
1215  * thing.
1216  */
1217  opstep->opstrategy = op_is_ne ? InvalidStrategy : opstrategy;
1218  Assert(list_length(exprs) == list_length(cmpfns));
1219  opstep->exprs = exprs;
1220  opstep->cmpfns = cmpfns;
1221  opstep->nullkeys = nullkeys;
1222 
1223  context->steps = lappend(context->steps, opstep);
1224 
1225  return (PartitionPruneStep *) opstep;
1226 }
#define InvalidStrategy
Definition: stratnum.h:24
PartitionPruneStep step
Definition: plannodes.h:1206
List * lappend(List *list, void *datum)
Definition: list.c:321
#define makeNode(_type_)
Definition: nodes.h:577
#define Assert(condition)
Definition: c.h:745
Bitmapset * nullkeys
Definition: plannodes.h:1211
static int list_length(const List *l)
Definition: pg_list.h:169
StrategyNumber opstrategy
Definition: plannodes.h:1208

◆ gen_prune_steps_from_opexps()

static PartitionPruneStep * gen_prune_steps_from_opexps ( GeneratePruningStepsContext context,
List **  keyclauses,
Bitmapset nullkeys 
)
static

Definition at line 1262 of file partprune.c.

References Assert, bms_is_member(), BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, BTMaxStrategyNumber, PartClauseInfo::cmpfn, elog, ERROR, PartClauseInfo::expr, for_each_cell, gen_prune_step_combine(), get_op_opfamily_properties(), get_steps_using_prefix(), HTEqualStrategyNumber, HTMaxStrategyNumber, i, InvalidStrategy, PartClauseInfo::keyno, lappend(), lappend_int(), lfirst, linitial, list_concat(), list_head(), list_length(), llast, NIL, PartClauseInfo::op_is_ne, PartClauseInfo::op_strategy, PartClauseInfo::opno, RelOptInfo::part_scheme, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionSchemeData::partnatts, PartitionSchemeData::partopfamily, PARTPRUNE_COMBINE_INTERSECT, GeneratePruningStepsContext::rel, PartitionPruneStep::step_id, and PartitionSchemeData::strategy.

Referenced by gen_partprune_steps_internal().

1264 {
1265  PartitionScheme part_scheme = context->rel->part_scheme;
1266  List *opsteps = NIL;
1267  List *btree_clauses[BTMaxStrategyNumber + 1],
1268  *hash_clauses[HTMaxStrategyNumber + 1];
1269  int i;
1270  ListCell *lc;
1271 
1272  memset(btree_clauses, 0, sizeof(btree_clauses));
1273  memset(hash_clauses, 0, sizeof(hash_clauses));
1274  for (i = 0; i < part_scheme->partnatts; i++)
1275  {
1276  List *clauselist = keyclauses[i];
1277  bool consider_next_key = true;
1278 
1279  /*
1280  * For range partitioning, if we have no clauses for the current key,
1281  * we can't consider any later keys either, so we can stop here.
1282  */
1283  if (part_scheme->strategy == PARTITION_STRATEGY_RANGE &&
1284  clauselist == NIL)
1285  break;
1286 
1287  /*
1288  * For hash partitioning, if a column doesn't have the necessary
1289  * equality clause, there should be an IS NULL clause, otherwise
1290  * pruning is not possible.
1291  */
1292  if (part_scheme->strategy == PARTITION_STRATEGY_HASH &&
1293  clauselist == NIL && !bms_is_member(i, nullkeys))
1294  return NULL;
1295 
1296  foreach(lc, clauselist)
1297  {
1298  PartClauseInfo *pc = (PartClauseInfo *) lfirst(lc);
1299  Oid lefttype,
1300  righttype;
1301 
1302  /* Look up the operator's btree/hash strategy number. */
1303  if (pc->op_strategy == InvalidStrategy)
1305  part_scheme->partopfamily[i],
1306  false,
1307  &pc->op_strategy,
1308  &lefttype,
1309  &righttype);
1310 
1311  switch (part_scheme->strategy)
1312  {
1315  btree_clauses[pc->op_strategy] =
1316  lappend(btree_clauses[pc->op_strategy], pc);
1317 
1318  /*
1319  * We can't consider subsequent partition keys if the
1320  * clause for the current key contains a non-inclusive
1321  * operator.
1322  */
1323  if (pc->op_strategy == BTLessStrategyNumber ||
1325  consider_next_key = false;
1326  break;
1327 
1330  elog(ERROR, "invalid clause for hash partitioning");
1331  hash_clauses[pc->op_strategy] =
1332  lappend(hash_clauses[pc->op_strategy], pc);
1333  break;
1334 
1335  default:
1336  elog(ERROR, "invalid partition strategy: %c",
1337  part_scheme->strategy);
1338  break;
1339  }
1340  }
1341 
1342  /*
1343  * If we've decided that clauses for subsequent partition keys
1344  * wouldn't be useful for pruning, don't search any further.
1345  */
1346  if (!consider_next_key)
1347  break;
1348  }
1349 
1350  /*
1351  * Now, we have divided clauses according to their operator strategies.
1352  * Check for each strategy if we can generate pruning step(s) by
1353  * collecting a list of expressions whose values will constitute a vector
1354  * that can be used as a lookup key by a partition bound searching
1355  * function.
1356  */
1357  switch (part_scheme->strategy)
1358  {
1361  {
1362  List *eq_clauses = btree_clauses[BTEqualStrategyNumber];
1363  List *le_clauses = btree_clauses[BTLessEqualStrategyNumber];
1364  List *ge_clauses = btree_clauses[BTGreaterEqualStrategyNumber];
1365  int strat;
1366 
1367  /*
1368  * For each clause under consideration for a given strategy,
1369  * we collect expressions from clauses for earlier keys, whose
1370  * operator strategy is inclusive, into a list called
1371  * 'prefix'. By appending the clause's own expression to the
1372  * 'prefix', we'll generate one step using the so generated
1373  * vector and assign the current strategy to it. Actually,
1374  * 'prefix' might contain multiple clauses for the same key,
1375  * in which case, we must generate steps for various
1376  * combinations of expressions of different keys, which
1377  * get_steps_using_prefix takes care of for us.
1378  */
1379  for (strat = 1; strat <= BTMaxStrategyNumber; strat++)
1380  {
1381  foreach(lc, btree_clauses[strat])
1382  {
1383  PartClauseInfo *pc = lfirst(lc);
1384  ListCell *eq_start;
1385  ListCell *le_start;
1386  ListCell *ge_start;
1387  ListCell *lc1;
1388  List *prefix = NIL;
1389  List *pc_steps;
1390  bool prefix_valid = true;
1391  bool pk_has_clauses;
1392  int keyno;
1393 
1394  /*
1395  * If this is a clause for the first partition key,
1396  * there are no preceding expressions; generate a
1397  * pruning step without a prefix.
1398  *
1399  * Note that we pass NULL for step_nullkeys, because
1400  * we don't search list/range partition bounds where
1401  * some keys are NULL.
1402  */
1403  if (pc->keyno == 0)
1404  {
1405  Assert(pc->op_strategy == strat);
1406  pc_steps = get_steps_using_prefix(context, strat,
1407  pc->op_is_ne,
1408  pc->expr,
1409  pc->cmpfn,
1410  0,
1411  NULL,
1412  NIL);
1413  opsteps = list_concat(opsteps, pc_steps);
1414  continue;
1415  }
1416 
1417  eq_start = list_head(eq_clauses);
1418  le_start = list_head(le_clauses);
1419  ge_start = list_head(ge_clauses);
1420 
1421  /*
1422  * We arrange clauses into prefix in ascending order
1423  * of their partition key numbers.
1424  */
1425  for (keyno = 0; keyno < pc->keyno; keyno++)
1426  {
1427  pk_has_clauses = false;
1428 
1429  /*
1430  * Expressions from = clauses can always be in the
1431  * prefix, provided they're from an earlier key.
1432  */
1433  for_each_cell(lc1, eq_clauses, eq_start)
1434  {
1435  PartClauseInfo *eqpc = lfirst(lc1);
1436 
1437  if (eqpc->keyno == keyno)
1438  {
1439  prefix = lappend(prefix, eqpc);
1440  pk_has_clauses = true;
1441  }
1442  else
1443  {
1444  Assert(eqpc->keyno > keyno);
1445  break;
1446  }
1447  }
1448  eq_start = lc1;
1449 
1450  /*
1451  * If we're generating steps for </<= strategy, we
1452  * can add other <= clauses to the prefix,
1453  * provided they're from an earlier key.
1454  */
1455  if (strat == BTLessStrategyNumber ||
1456  strat == BTLessEqualStrategyNumber)
1457  {
1458  for_each_cell(lc1, le_clauses, le_start)
1459  {
1460  PartClauseInfo *lepc = lfirst(lc1);
1461 
1462  if (lepc->keyno == keyno)
1463  {
1464  prefix = lappend(prefix, lepc);
1465  pk_has_clauses = true;
1466  }
1467  else
1468  {
1469  Assert(lepc->keyno > keyno);
1470  break;
1471  }
1472  }
1473  le_start = lc1;
1474  }
1475 
1476  /*
1477  * If we're generating steps for >/>= strategy, we
1478  * can add other >= clauses to the prefix,
1479  * provided they're from an earlier key.
1480  */
1481  if (strat == BTGreaterStrategyNumber ||
1483  {
1484  for_each_cell(lc1, ge_clauses, ge_start)
1485  {
1486  PartClauseInfo *gepc = lfirst(lc1);
1487 
1488  if (gepc->keyno == keyno)
1489  {
1490  prefix = lappend(prefix, gepc);
1491  pk_has_clauses = true;
1492  }
1493  else
1494  {
1495  Assert(gepc->keyno > keyno);
1496  break;
1497  }
1498  }
1499  ge_start = lc1;
1500  }
1501 
1502  /*
1503  * If this key has no clauses, prefix is not valid
1504  * anymore.
1505  */
1506  if (!pk_has_clauses)
1507  {
1508  prefix_valid = false;
1509  break;
1510  }
1511  }
1512 
1513  /*
1514  * If prefix_valid, generate PartitionPruneStepOps.
1515  * Otherwise, we would not find clauses for a valid
1516  * subset of the partition keys anymore for the
1517  * strategy; give up on generating partition pruning
1518  * steps further for the strategy.
1519  *
1520  * As mentioned above, if 'prefix' contains multiple
1521  * expressions for the same key, the following will
1522  * generate multiple steps, one for each combination
1523  * of the expressions for different keys.
1524  *
1525  * Note that we pass NULL for step_nullkeys, because
1526  * we don't search list/range partition bounds where
1527  * some keys are NULL.
1528  */
1529  if (prefix_valid)
1530  {
1531  Assert(pc->op_strategy == strat);
1532  pc_steps = get_steps_using_prefix(context, strat,
1533  pc->op_is_ne,
1534  pc->expr,
1535  pc->cmpfn,
1536  pc->keyno,
1537  NULL,
1538  prefix);
1539  opsteps = list_concat(opsteps, pc_steps);
1540  }
1541  else
1542  break;
1543  }
1544  }
1545  break;
1546  }
1547 
1549  {
1550  List *eq_clauses = hash_clauses[HTEqualStrategyNumber];
1551 
1552  /* For hash partitioning, we have just the = strategy. */
1553  if (eq_clauses != NIL)
1554  {
1555  PartClauseInfo *pc;
1556  List *pc_steps;
1557  List *prefix = NIL;
1558  int last_keyno;
1559  ListCell *lc1;
1560 
1561  /*
1562  * Locate the clause for the greatest column. This may
1563  * not belong to the last partition key, but it is the
1564  * clause belonging to the last partition key we found a
1565  * clause for above.
1566  */
1567  pc = llast(eq_clauses);
1568 
1569  /*
1570  * There might be multiple clauses which matched to that
1571  * partition key; find the first such clause. While at
1572  * it, add all the clauses before that one to 'prefix'.
1573  */
1574  last_keyno = pc->keyno;
1575  foreach(lc, eq_clauses)
1576  {
1577  pc = lfirst(lc);
1578  if (pc->keyno == last_keyno)
1579  break;
1580  prefix = lappend(prefix, pc);
1581  }
1582 
1583  /*
1584  * For each clause for the "last" column, after appending
1585  * the clause's own expression to the 'prefix', we'll
1586  * generate one step using the so generated vector and
1587  * assign = as its strategy. Actually, 'prefix' might
1588  * contain multiple clauses for the same key, in which
1589  * case, we must generate steps for various combinations
1590  * of expressions of different keys, which
1591  * get_steps_using_prefix will take care of for us.
1592  */
1593  for_each_cell(lc1, eq_clauses, lc)
1594  {
1595  pc = lfirst(lc1);
1596 
1597  /*
1598  * Note that we pass nullkeys for step_nullkeys,
1599  * because we need to tell hash partition bound search
1600  * function which of the keys we found IS NULL clauses
1601  * for.
1602  */
1604  pc_steps =
1605  get_steps_using_prefix(context,
1607  false,
1608  pc->expr,
1609  pc->cmpfn,
1610  pc->keyno,
1611  nullkeys,
1612  prefix);
1613  opsteps = list_concat(opsteps, pc_steps);
1614  }
1615  }
1616  break;
1617  }
1618 
1619  default:
1620  elog(ERROR, "invalid partition strategy: %c",
1621  part_scheme->strategy);
1622  break;
1623  }
1624 
1625  /* Lastly, add a combine step to mutually AND these op steps, if needed */
1626  if (list_length(opsteps) > 1)
1627  {
1628  List *opstep_ids = NIL;
1629 
1630  foreach(lc, opsteps)
1631  {
1632  PartitionPruneStep *step = lfirst(lc);
1633 
1634  opstep_ids = lappend_int(opstep_ids, step->step_id);
1635  }
1636 
1637  if (opstep_ids != NIL)
1638  return gen_prune_step_combine(context, opstep_ids,
1640  return NULL;
1641  }
1642  else if (opsteps != NIL)
1643  return linitial(opsteps);
1644 
1645  return NULL;
1646 }
#define InvalidStrategy
Definition: stratnum.h:24
#define NIL
Definition: pg_list.h:65
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
#define HTMaxStrategyNumber
Definition: stratnum.h:43
#define for_each_cell(cell, lst, initcell)
Definition: pg_list.h:390
#define llast(l)
Definition: pg_list.h:215
List * list_concat(List *list1, const List *list2)
Definition: list.c:515
unsigned int Oid
Definition: postgres_ext.h:31
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
static List * get_steps_using_prefix(GeneratePruningStepsContext *context, StrategyNumber step_opstrategy, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix)
Definition: partprune.c:2270
#define linitial(l)
Definition: pg_list.h:195
#define ERROR
Definition: elog.h:43
#define HTEqualStrategyNumber
Definition: stratnum.h:41
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
List * lappend_int(List *list, int datum)
Definition: list.c:339
List * lappend(List *list, void *datum)
Definition: list.c:321
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
#define Assert(condition)
Definition: c.h:745
#define lfirst(lc)
Definition: pg_list.h:190
static int list_length(const List *l)
Definition: pg_list.h:169
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
void get_op_opfamily_properties(Oid opno, Oid opfamily, bool ordering_op, int *strategy, Oid *lefttype, Oid *righttype)
Definition: lsyscache.c:134
#define elog(elevel,...)
Definition: elog.h:214
int i
PartitionScheme part_scheme
Definition: pathnodes.h:742
#define BTMaxStrategyNumber
Definition: stratnum.h:35
Expr * expr
Definition: partprune.c:68
#define BTLessStrategyNumber
Definition: stratnum.h:29
Definition: pg_list.h:50
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32
static PartitionPruneStep * gen_prune_step_combine(GeneratePruningStepsContext *context, List *source_stepids, PartitionPruneCombineOp combineOp)
Definition: partprune.c:1236

◆ get_matching_hash_bounds()

static PruneStepResult * get_matching_hash_bounds ( PartitionPruneContext context,
StrategyNumber  opstrategy,
Datum values,
int  nvalues,
FmgrInfo partsupfunc,
Bitmapset nullkeys 
)
static

Definition at line 2480 of file partprune.c.

References Assert, bms_add_range(), bms_is_member(), bms_make_singleton(), bms_num_members(), PruneStepResult::bound_offsets, PartitionPruneContext::boundinfo, compute_partition_hash_value(), get_hash_partition_greatest_modulus(), HTEqualStrategyNumber, i, PartitionBoundInfoData::indexes, PartitionBoundInfoData::ndatums, palloc0(), PartitionPruneContext::partcollation, PARTITION_MAX_KEYS, PARTITION_STRATEGY_HASH, PartitionPruneContext::partnatts, PruneStepResult::scan_default, PruneStepResult::scan_null, and PartitionPruneContext::strategy.

Referenced by perform_pruning_base_step().

2483 {
2484  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2485  PartitionBoundInfo boundinfo = context->boundinfo;
2486  int *partindices = boundinfo->indexes;
2487  int partnatts = context->partnatts;
2488  bool isnull[PARTITION_MAX_KEYS];
2489  int i;
2490  uint64 rowHash;
2491  int greatest_modulus;
2492  Oid *partcollation = context->partcollation;
2493 
2495 
2496  /*
2497  * For hash partitioning we can only perform pruning based on equality
2498  * clauses to the partition key or IS NULL clauses. We also can only
2499  * prune if we got values for all keys.
2500  */
2501  if (nvalues + bms_num_members(nullkeys) == partnatts)
2502  {
2503  /*
2504  * If there are any values, they must have come from clauses
2505  * containing an equality operator compatible with hash partitioning.
2506  */
2507  Assert(opstrategy == HTEqualStrategyNumber || nvalues == 0);
2508 
2509  for (i = 0; i < partnatts; i++)
2510  isnull[i] = bms_is_member(i, nullkeys);
2511 
2512  greatest_modulus = get_hash_partition_greatest_modulus(boundinfo);
2513  rowHash = compute_partition_hash_value(partnatts, partsupfunc, partcollation,
2514  values, isnull);
2515 
2516  if (partindices[rowHash % greatest_modulus] >= 0)
2517  result->bound_offsets =
2518  bms_make_singleton(rowHash % greatest_modulus);
2519  }
2520  else
2521  {
2522  /* Getting here means at least one hash partition exists. */
2523  Assert(boundinfo->ndatums > 0);
2524  result->bound_offsets = bms_add_range(NULL, 0,
2525  boundinfo->ndatums - 1);
2526  }
2527 
2528  /*
2529  * There is neither a special hash null partition or the default hash
2530  * partition.
2531  */
2532  result->scan_null = result->scan_default = false;
2533 
2534  return result;
2535 }
#define PARTITION_MAX_KEYS
unsigned int Oid
Definition: postgres_ext.h:31
Bitmapset * bound_offsets
Definition: partprune.c:134
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:834
#define HTEqualStrategyNumber
Definition: stratnum.h:41
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:646
int get_hash_partition_greatest_modulus(PartitionBoundInfo bound)
Definition: partbounds.c:3291
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:186
void * palloc0(Size size)
Definition: mcxt.c:981
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
uint64 compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *values, bool *isnull)
Definition: partbounds.c:4644
#define Assert(condition)
Definition: c.h:745
static Datum values[MAXATTR]
Definition: bootstrap.c:165
PartitionBoundInfo boundinfo
Definition: partprune.h:53
int i
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427

◆ get_matching_list_bounds()

static PruneStepResult * get_matching_list_bounds ( PartitionPruneContext context,
StrategyNumber  opstrategy,
Datum  value,
int  nvalues,
FmgrInfo partsupfunc,
Bitmapset nullkeys 
)
static

Definition at line 2558 of file partprune.c.

References Assert, bms_add_range(), bms_del_member(), bms_is_empty(), bms_make_singleton(), PruneStepResult::bound_offsets, PartitionPruneContext::boundinfo, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, elog, ERROR, PartitionBoundInfoData::indexes, InvalidStrategy, PartitionBoundInfoData::ndatums, palloc0(), PartitionPruneContext::partcollation, partition_bound_accepts_nulls, partition_bound_has_default, partition_list_bsearch(), PARTITION_STRATEGY_LIST, PartitionPruneContext::partnatts, PruneStepResult::scan_default, PruneStepResult::scan_null, and PartitionPruneContext::strategy.

Referenced by perform_pruning_base_step().

2561 {
2562  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2563  PartitionBoundInfo boundinfo = context->boundinfo;
2564  int off,
2565  minoff,
2566  maxoff;
2567  bool is_equal;
2568  bool inclusive = false;
2569  Oid *partcollation = context->partcollation;
2570 
2572  Assert(context->partnatts == 1);
2573 
2574  result->scan_null = result->scan_default = false;
2575 
2576  if (!bms_is_empty(nullkeys))
2577  {
2578  /*
2579  * Nulls may exist in only one partition - the partition whose
2580  * accepted set of values includes null or the default partition if
2581  * the former doesn't exist.
2582  */
2583  if (partition_bound_accepts_nulls(boundinfo))
2584  result->scan_null = true;
2585  else
2586  result->scan_default = partition_bound_has_default(boundinfo);
2587  return result;
2588  }
2589 
2590  /*
2591  * If there are no datums to compare keys with, but there are partitions,
2592  * just return the default partition if one exists.
2593  */
2594  if (boundinfo->ndatums == 0)
2595  {
2596  result->scan_default = partition_bound_has_default(boundinfo);
2597  return result;
2598  }
2599 
2600  minoff = 0;
2601  maxoff = boundinfo->ndatums - 1;
2602 
2603  /*
2604  * If there are no values to compare with the datums in boundinfo, it
2605  * means the caller asked for partitions for all non-null datums. Add
2606  * indexes of *all* partitions, including the default if any.
2607  */
2608  if (nvalues == 0)
2609  {
2610  Assert(boundinfo->ndatums > 0);
2611  result->bound_offsets = bms_add_range(NULL, 0,
2612  boundinfo->ndatums - 1);
2613  result->scan_default = partition_bound_has_default(boundinfo);
2614  return result;
2615  }
2616 
2617  /* Special case handling of values coming from a <> operator clause. */
2618  if (opstrategy == InvalidStrategy)
2619  {
2620  /*
2621  * First match to all bounds. We'll remove any matching datums below.
2622  */
2623  Assert(boundinfo->ndatums > 0);
2624  result->bound_offsets = bms_add_range(NULL, 0,
2625  boundinfo->ndatums - 1);
2626 
2627  off = partition_list_bsearch(partsupfunc, partcollation, boundinfo,
2628  value, &is_equal);
2629  if (off >= 0 && is_equal)
2630  {
2631 
2632  /* We have a match. Remove from the result. */
2633  Assert(boundinfo->indexes[off] >= 0);
2634  result->bound_offsets = bms_del_member(result->bound_offsets,
2635  off);
2636  }
2637 
2638  /* Always include the default partition if any. */
2639  result->scan_default = partition_bound_has_default(boundinfo);
2640 
2641  return result;
2642  }
2643 
2644  /*
2645  * With range queries, always include the default list partition, because
2646  * list partitions divide the key space in a discontinuous manner, not all
2647  * values in the given range will have a partition assigned. This may not
2648  * technically be true for some data types (e.g. integer types), however,
2649  * we currently lack any sort of infrastructure to provide us with proofs
2650  * that would allow us to do anything smarter here.
2651  */
2652  if (opstrategy != BTEqualStrategyNumber)
2653  result->scan_default = partition_bound_has_default(boundinfo);
2654 
2655  switch (opstrategy)
2656  {
2657  case BTEqualStrategyNumber:
2658  off = partition_list_bsearch(partsupfunc,
2659  partcollation,
2660  boundinfo, value,
2661  &is_equal);
2662  if (off >= 0 && is_equal)
2663  {
2664  Assert(boundinfo->indexes[off] >= 0);
2665  result->bound_offsets = bms_make_singleton(off);
2666  }
2667  else
2668  result->scan_default = partition_bound_has_default(boundinfo);
2669  return result;
2670 
2672  inclusive = true;
2673  /* fall through */
2675  off = partition_list_bsearch(partsupfunc,
2676  partcollation,
2677  boundinfo, value,
2678  &is_equal);
2679  if (off >= 0)
2680  {
2681  /* We don't want the matched datum to be in the result. */
2682  if (!is_equal || !inclusive)
2683  off++;
2684  }
2685  else
2686  {
2687  /*
2688  * This case means all partition bounds are greater, which in
2689  * turn means that all partitions satisfy this key.
2690  */
2691  off = 0;
2692  }
2693 
2694  /*
2695  * off is greater than the numbers of datums we have partitions
2696  * for. The only possible partition that could contain a match is
2697  * the default partition, but we must've set context->scan_default
2698  * above anyway if one exists.
2699  */
2700  if (off > boundinfo->ndatums - 1)
2701  return result;
2702 
2703  minoff = off;
2704  break;
2705 
2707  inclusive = true;
2708  /* fall through */
2709  case BTLessStrategyNumber:
2710  off = partition_list_bsearch(partsupfunc,
2711  partcollation,
2712  boundinfo, value,
2713  &is_equal);
2714  if (off >= 0 && is_equal && !inclusive)
2715  off--;
2716 
2717  /*
2718  * off is smaller than the datums of all non-default partitions.
2719  * The only possible partition that could contain a match is the
2720  * default partition, but we must've set context->scan_default
2721  * above anyway if one exists.
2722  */
2723  if (off < 0)
2724  return result;
2725 
2726  maxoff = off;
2727  break;
2728 
2729  default:
2730  elog(ERROR, "invalid strategy number %d", opstrategy);
2731  break;
2732  }
2733 
2734  Assert(minoff >= 0 && maxoff >= 0);
2735  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2736  return result;
2737 }
#define InvalidStrategy
Definition: stratnum.h:24
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
unsigned int Oid
Definition: postgres_ext.h:31
Bitmapset * bound_offsets
Definition: partprune.c:134
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:834
#define ERROR
Definition: elog.h:43
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:186
#define partition_bound_has_default(bi)
Definition: partbounds.h:75
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
void * palloc0(Size size)
Definition: mcxt.c:981
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:74
int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
Definition: partbounds.c:3486
static struct @143 value
#define Assert(condition)
Definition: c.h:745
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
PartitionBoundInfo boundinfo
Definition: partprune.h:53
#define elog(elevel,...)
Definition: elog.h:214
#define BTLessStrategyNumber
Definition: stratnum.h:29
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:773
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32

◆ get_matching_partitions()

Bitmapset* get_matching_partitions ( PartitionPruneContext context,
List pruning_steps 
)

Definition at line 716 of file partprune.c.

References Assert, bms_add_member(), bms_add_range(), bms_next_member(), PruneStepResult::bound_offsets, PartitionPruneContext::boundinfo, PartitionBoundInfoData::default_index, elog, ERROR, i, PartitionBoundInfoData::indexes, lfirst, list_length(), nodeTag, PartitionPruneContext::nparts, PartitionBoundInfoData::null_index, palloc0(), partition_bound_accepts_nulls, partition_bound_has_default, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, perform_pruning_base_step(), perform_pruning_combine_step(), PruneStepResult::scan_default, PruneStepResult::scan_null, PartitionPruneStep::step_id, PartitionPruneContext::strategy, T_PartitionPruneStepCombine, and T_PartitionPruneStepOp.

Referenced by find_matching_subplans_recurse(), and prune_append_rel_partitions().

717 {
718  Bitmapset *result;
719  int num_steps = list_length(pruning_steps),
720  i;
721  PruneStepResult **results,
722  *final_result;
723  ListCell *lc;
724  bool scan_default;
725 
726  /* If there are no pruning steps then all partitions match. */
727  if (num_steps == 0)
728  {
729  Assert(context->nparts > 0);
730  return bms_add_range(NULL, 0, context->nparts - 1);
731  }
732 
733  /*
734  * Allocate space for individual pruning steps to store its result. Each
735  * slot will hold a PruneStepResult after performing a given pruning step.
736  * Later steps may use the result of one or more earlier steps. The
737  * result of applying all pruning steps is the value contained in the slot
738  * of the last pruning step.
739  */
740  results = (PruneStepResult **)
741  palloc0(num_steps * sizeof(PruneStepResult *));
742  foreach(lc, pruning_steps)
743  {
744  PartitionPruneStep *step = lfirst(lc);
745 
746  switch (nodeTag(step))
747  {
749  results[step->step_id] =
751  (PartitionPruneStepOp *) step);
752  break;
753 
755  results[step->step_id] =
757  (PartitionPruneStepCombine *) step,
758  results);
759  break;
760 
761  default:
762  elog(ERROR, "invalid pruning step type: %d",
763  (int) nodeTag(step));
764  }
765  }
766 
767  /*
768  * At this point we know the offsets of all the datums whose corresponding
769  * partitions need to be in the result, including special null-accepting
770  * and default partitions. Collect the actual partition indexes now.
771  */
772  final_result = results[num_steps - 1];
773  Assert(final_result != NULL);
774  i = -1;
775  result = NULL;
776  scan_default = final_result->scan_default;
777  while ((i = bms_next_member(final_result->bound_offsets, i)) >= 0)
778  {
779  int partindex = context->boundinfo->indexes[i];
780 
781  if (partindex < 0)
782  {
783  /*
784  * In range partitioning cases, if a partition index is -1 it
785  * means that the bound at the offset is the upper bound for a
786  * range not covered by any partition (other than a possible
787  * default partition). In hash partitioning, the same means no
788  * partition has been defined for the corresponding remainder
789  * value.
790  *
791  * In either case, the value is still part of the queried range of
792  * values, so mark to scan the default partition if one exists.
793  */
794  scan_default |= partition_bound_has_default(context->boundinfo);
795  continue;
796  }
797 
798  result = bms_add_member(result, partindex);
799  }
800 
801  /* Add the null and/or default partition if needed and present. */
802  if (final_result->scan_null)
803  {
806  result = bms_add_member(result, context->boundinfo->null_index);
807  }
808  if (scan_default)
809  {
811  context->strategy == PARTITION_STRATEGY_RANGE);
813  result = bms_add_member(result, context->boundinfo->default_index);
814  }
815 
816  return result;
817 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1043
Bitmapset * bound_offsets
Definition: partprune.c:134
static PruneStepResult * perform_pruning_combine_step(PartitionPruneContext *context, PartitionPruneStepCombine *cstep, PruneStepResult **step_results)
Definition: partprune.c:3382
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:834
#define ERROR
Definition: elog.h:43
static PruneStepResult * perform_pruning_base_step(PartitionPruneContext *context, PartitionPruneStepOp *opstep)
Definition: partprune.c:3234
#define partition_bound_has_default(bi)
Definition: partbounds.h:75
void * palloc0(Size size)
Definition: mcxt.c:981
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:74
#define Assert(condition)
Definition: c.h:745
#define lfirst(lc)
Definition: pg_list.h:190
static int list_length(const List *l)
Definition: pg_list.h:169
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:736
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
#define nodeTag(nodeptr)
Definition: nodes.h:534
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
PartitionBoundInfo boundinfo
Definition: partprune.h:53
#define elog(elevel,...)
Definition: elog.h:214
int i

◆ get_matching_range_bounds()

static PruneStepResult * get_matching_range_bounds ( PartitionPruneContext context,
StrategyNumber  opstrategy,
Datum values,
int  nvalues,
FmgrInfo partsupfunc,
Bitmapset nullkeys 
)
static

Definition at line 2769 of file partprune.c.

References Assert, bms_add_range(), bms_is_empty(), bms_make_singleton(), PruneStepResult::bound_offsets, PartitionPruneContext::boundinfo, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, PartitionBoundInfoData::datums, elog, ERROR, PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, palloc0(), PartitionPruneContext::partcollation, partition_bound_has_default, partition_range_datum_bsearch(), PARTITION_RANGE_DATUM_MAXVALUE, PARTITION_RANGE_DATUM_MINVALUE, partition_rbound_datum_cmp(), PARTITION_STRATEGY_RANGE, PartitionPruneContext::partnatts, PruneStepResult::scan_default, PruneStepResult::scan_null, and PartitionPruneContext::strategy.

Referenced by perform_pruning_base_step().

2772 {
2773  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2774  PartitionBoundInfo boundinfo = context->boundinfo;
2775  Oid *partcollation = context->partcollation;
2776  int partnatts = context->partnatts;
2777  int *partindices = boundinfo->indexes;
2778  int off,
2779  minoff,
2780  maxoff;
2781  bool is_equal;
2782  bool inclusive = false;
2783 
2785  Assert(nvalues <= partnatts);
2786 
2787  result->scan_null = result->scan_default = false;
2788 
2789  /*
2790  * If there are no datums to compare keys with, or if we got an IS NULL
2791  * clause just return the default partition, if it exists.
2792  */
2793  if (boundinfo->ndatums == 0 || !bms_is_empty(nullkeys))
2794  {
2795  result->scan_default = partition_bound_has_default(boundinfo);
2796  return result;
2797  }
2798 
2799  minoff = 0;
2800  maxoff = boundinfo->ndatums;
2801 
2802  /*
2803  * If there are no values to compare with the datums in boundinfo, it
2804  * means the caller asked for partitions for all non-null datums. Add
2805  * indexes of *all* partitions, including the default partition if one
2806  * exists.
2807  */
2808  if (nvalues == 0)
2809  {
2810  /* ignore key space not covered by any partitions */
2811  if (partindices[minoff] < 0)
2812  minoff++;
2813  if (partindices[maxoff] < 0)
2814  maxoff--;
2815 
2816  result->scan_default = partition_bound_has_default(boundinfo);
2817  Assert(partindices[minoff] >= 0 &&
2818  partindices[maxoff] >= 0);
2819  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2820 
2821  return result;
2822  }
2823 
2824  /*
2825  * If the query does not constrain all key columns, we'll need to scan the
2826  * default partition, if any.
2827  */
2828  if (nvalues < partnatts)
2829  result->scan_default = partition_bound_has_default(boundinfo);
2830 
2831  switch (opstrategy)
2832  {
2833  case BTEqualStrategyNumber:
2834  /* Look for the smallest bound that is = lookup value. */
2835  off = partition_range_datum_bsearch(partsupfunc,
2836  partcollation,
2837  boundinfo,
2838  nvalues, values,
2839  &is_equal);
2840 
2841  if (off >= 0 && is_equal)
2842  {
2843  if (nvalues == partnatts)
2844  {
2845  /* There can only be zero or one matching partition. */
2846  result->bound_offsets = bms_make_singleton(off + 1);
2847  return result;
2848  }
2849  else
2850  {
2851  int saved_off = off;
2852 
2853  /*
2854  * Since the lookup value contains only a prefix of keys,
2855  * we must find other bounds that may also match the
2856  * prefix. partition_range_datum_bsearch() returns the
2857  * offset of one of them, find others by checking adjacent
2858  * bounds.
2859  */
2860 
2861  /*
2862  * First find greatest bound that's smaller than the
2863  * lookup value.
2864  */
2865  while (off >= 1)
2866  {
2867  int32 cmpval;
2868 
2869  cmpval =
2870  partition_rbound_datum_cmp(partsupfunc,
2871  partcollation,
2872  boundinfo->datums[off - 1],
2873  boundinfo->kind[off - 1],
2874  values, nvalues);
2875  if (cmpval != 0)
2876  break;
2877  off--;
2878  }
2879 
2880  Assert(0 ==
2881  partition_rbound_datum_cmp(partsupfunc,
2882  partcollation,
2883  boundinfo->datums[off],
2884  boundinfo->kind[off],
2885  values, nvalues));
2886 
2887  /*
2888  * We can treat 'off' as the offset of the smallest bound
2889  * to be included in the result, if we know it is the
2890  * upper bound of the partition in which the lookup value
2891  * could possibly exist. One case it couldn't is if the
2892  * bound, or precisely the matched portion of its prefix,
2893  * is not inclusive.
2894  */
2895  if (boundinfo->kind[off][nvalues] ==
2897  off++;
2898 
2899  minoff = off;
2900 
2901  /*
2902  * Now find smallest bound that's greater than the lookup
2903  * value.
2904  */
2905  off = saved_off;
2906  while (off < boundinfo->ndatums - 1)
2907  {
2908  int32 cmpval;
2909 
2910  cmpval = partition_rbound_datum_cmp(partsupfunc,
2911  partcollation,
2912  boundinfo->datums[off + 1],
2913  boundinfo->kind[off + 1],
2914  values, nvalues);
2915  if (cmpval != 0)
2916  break;
2917  off++;
2918  }
2919 
2920  Assert(0 ==
2921  partition_rbound_datum_cmp(partsupfunc,
2922  partcollation,
2923  boundinfo->datums[off],
2924  boundinfo->kind[off],
2925  values, nvalues));
2926 
2927  /*
2928  * off + 1, then would be the offset of the greatest bound
2929  * to be included in the result.
2930  */
2931  maxoff = off + 1;
2932  }
2933 
2934  Assert(minoff >= 0 && maxoff >= 0);
2935  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2936  }
2937  else
2938  {
2939  /*
2940  * The lookup value falls in the range between some bounds in
2941  * boundinfo. 'off' would be the offset of the greatest bound
2942  * that is <= lookup value, so add off + 1 to the result
2943  * instead as the offset of the upper bound of the only
2944  * partition that may contain the lookup value. If 'off' is
2945  * -1 indicating that all bounds are greater, then we simply
2946  * end up adding the first bound's offset, that is, 0.
2947  */
2948  result->bound_offsets = bms_make_singleton(off + 1);
2949  }
2950 
2951  return result;
2952 
2954  inclusive = true;
2955  /* fall through */
2957 
2958  /*
2959  * Look for the smallest bound that is > or >= lookup value and
2960  * set minoff to its offset.
2961  */
2962  off = partition_range_datum_bsearch(partsupfunc,
2963  partcollation,
2964  boundinfo,
2965  nvalues, values,
2966  &is_equal);
2967  if (off < 0)
2968  {
2969  /*
2970  * All bounds are greater than the lookup value, so include
2971  * all of them in the result.
2972  */
2973  minoff = 0;
2974  }
2975  else
2976  {
2977  if (is_equal && nvalues < partnatts)
2978  {
2979  /*
2980  * Since the lookup value contains only a prefix of keys,
2981  * we must find other bounds that may also match the
2982  * prefix. partition_range_datum_bsearch() returns the
2983  * offset of one of them, find others by checking adjacent
2984  * bounds.
2985  *
2986  * Based on whether the lookup values are inclusive or
2987  * not, we must either include the indexes of all such
2988  * bounds in the result (that is, set minoff to the index
2989  * of smallest such bound) or find the smallest one that's
2990  * greater than the lookup values and set minoff to that.
2991  */
2992  while (off >= 1 && off < boundinfo->ndatums - 1)
2993  {
2994  int32 cmpval;
2995  int nextoff;
2996 
2997  nextoff = inclusive ? off - 1 : off + 1;
2998  cmpval =
2999  partition_rbound_datum_cmp(partsupfunc,
3000  partcollation,
3001  boundinfo->datums[nextoff],
3002  boundinfo->kind[nextoff],
3003  values, nvalues);
3004  if (cmpval != 0)
3005  break;
3006 
3007  off = nextoff;
3008  }
3009 
3010  Assert(0 ==
3011  partition_rbound_datum_cmp(partsupfunc,
3012  partcollation,
3013  boundinfo->datums[off],
3014  boundinfo->kind[off],
3015  values, nvalues));
3016 
3017  minoff = inclusive ? off : off + 1;
3018  }
3019  else
3020  {
3021 
3022  /*
3023  * lookup value falls in the range between some bounds in
3024  * boundinfo. off would be the offset of the greatest
3025  * bound that is <= lookup value, so add off + 1 to the
3026  * result instead as the offset of the upper bound of the
3027  * smallest partition that may contain the lookup value.
3028  */
3029  minoff = off + 1;
3030  }
3031  }
3032  break;
3033 
3035  inclusive = true;
3036  /* fall through */
3037  case BTLessStrategyNumber:
3038 
3039  /*
3040  * Look for the greatest bound that is < or <= lookup value and
3041  * set maxoff to its offset.
3042  */
3043  off = partition_range_datum_bsearch(partsupfunc,
3044  partcollation,
3045  boundinfo,
3046  nvalues, values,
3047  &is_equal);
3048  if (off >= 0)
3049  {
3050  /*
3051  * See the comment above.
3052  */
3053  if (is_equal && nvalues < partnatts)
3054  {
3055  while (off >= 1 && off < boundinfo->ndatums - 1)
3056  {
3057  int32 cmpval;
3058  int nextoff;
3059 
3060  nextoff = inclusive ? off + 1 : off - 1;
3061  cmpval = partition_rbound_datum_cmp(partsupfunc,
3062  partcollation,
3063  boundinfo->datums[nextoff],
3064  boundinfo->kind[nextoff],
3065  values, nvalues);
3066  if (cmpval != 0)
3067  break;
3068 
3069  off = nextoff;
3070  }
3071 
3072  Assert(0 ==
3073  partition_rbound_datum_cmp(partsupfunc,
3074  partcollation,
3075  boundinfo->datums[off],
3076  boundinfo->kind[off],
3077  values, nvalues));
3078 
3079  maxoff = inclusive ? off + 1 : off;
3080  }
3081 
3082  /*
3083  * The lookup value falls in the range between some bounds in
3084  * boundinfo. 'off' would be the offset of the greatest bound
3085  * that is <= lookup value, so add off + 1 to the result
3086  * instead as the offset of the upper bound of the greatest
3087  * partition that may contain lookup value. If the lookup
3088  * value had exactly matched the bound, but it isn't
3089  * inclusive, no need add the adjacent partition.
3090  */
3091  else if (!is_equal || inclusive)
3092  maxoff = off + 1;
3093  else
3094  maxoff = off;
3095  }
3096  else
3097  {
3098  /*
3099  * 'off' is -1 indicating that all bounds are greater, so just
3100  * set the first bound's offset as maxoff.
3101  */
3102  maxoff = off + 1;
3103  }
3104  break;
3105 
3106  default:
3107  elog(ERROR, "invalid strategy number %d", opstrategy);
3108  break;
3109  }
3110 
3111  Assert(minoff >= 0 && minoff <= boundinfo->ndatums);
3112  Assert(maxoff >= 0 && maxoff <= boundinfo->ndatums);
3113 
3114  /*
3115  * If the smallest partition to return has MINVALUE (negative infinity) as
3116  * its lower bound, increment it to point to the next finite bound
3117  * (supposedly its upper bound), so that we don't advertently end up
3118  * scanning the default partition.
3119  */
3120  if (minoff < boundinfo->ndatums && partindices[minoff] < 0)
3121  {
3122  int lastkey = nvalues - 1;
3123 
3124  if (boundinfo->kind[minoff][lastkey] ==
3126  {
3127  minoff++;
3128  Assert(boundinfo->indexes[minoff] >= 0);
3129  }
3130  }
3131 
3132  /*
3133  * If the previous greatest partition has MAXVALUE (positive infinity) as
3134  * its upper bound (something only possible to do with multi-column range
3135  * partitioning), we scan switch to it as the greatest partition to
3136  * return. Again, so that we don't advertently end up scanning the
3137  * default partition.
3138  */
3139  if (maxoff >= 1 && partindices[maxoff] < 0)
3140  {
3141  int lastkey = nvalues - 1;
3142 
3143  if (boundinfo->kind[maxoff - 1][lastkey] ==
3145  {
3146  maxoff--;
3147  Assert(boundinfo->indexes[maxoff] >= 0);
3148  }
3149  }
3150 
3151  Assert(minoff >= 0 && maxoff >= 0);
3152  if (minoff <= maxoff)
3153  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3154 
3155  return result;
3156 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:64
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
unsigned int Oid
Definition: postgres_ext.h:31
Bitmapset * bound_offsets
Definition: partprune.c:134
signed int int32
Definition: c.h:362
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
int partition_range_datum_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, Datum *values, bool *is_equal)
Definition: partbounds.c:3574
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:834
#define ERROR
Definition: elog.h:43
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:186
int32 partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums, int n_tuple_datums)
Definition: partbounds.c:3435
#define partition_bound_has_default(bi)
Definition: partbounds.h:75
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
void * palloc0(Size size)
Definition: mcxt.c:981
#define Assert(condition)
Definition: c.h:745
static Datum values[MAXATTR]
Definition: bootstrap.c:165
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
PartitionBoundInfo boundinfo
Definition: partprune.h:53
#define elog(elevel,...)
Definition: elog.h:214
#define BTLessStrategyNumber
Definition: stratnum.h:29
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32

◆ get_partkey_exec_paramids()

static Bitmapset * get_partkey_exec_paramids ( List steps)
static

Definition at line 3198 of file partprune.c.

References bms_join(), PartClauseInfo::expr, PartitionPruneStepOp::exprs, IsA, lfirst, and pull_exec_paramids().

Referenced by make_partitionedrel_pruneinfo().

3199 {
3200  Bitmapset *execparamids = NULL;
3201  ListCell *lc;
3202 
3203  foreach(lc, steps)
3204  {
3206  ListCell *lc2;
3207 
3208  if (!IsA(step, PartitionPruneStepOp))
3209  continue;
3210 
3211  foreach(lc2, step->exprs)
3212  {
3213  Expr *expr = lfirst(lc2);
3214 
3215  /* We can be quick for plain Consts */
3216  if (!IsA(expr, Const))
3217  execparamids = bms_join(execparamids,
3218  pull_exec_paramids(expr));
3219  }
3220  }
3221 
3222  return execparamids;
3223 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:580
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition: bitmapset.c:949
static Bitmapset * pull_exec_paramids(Expr *expr)
Definition: partprune.c:3164
#define lfirst(lc)
Definition: pg_list.h:190

◆ get_steps_using_prefix()

static List * get_steps_using_prefix ( GeneratePruningStepsContext context,
StrategyNumber  step_opstrategy,
bool  step_op_is_ne,
Expr step_lastexpr,
Oid  step_lastcmpfn,
int  step_lastkeyno,
Bitmapset step_nullkeys,
List prefix 
)
static

Definition at line 2270 of file partprune.c.

References Assert, gen_prune_step_op(), get_steps_using_prefix_recurse(), list_head(), list_length(), list_make1, list_make1_oid, NIL, RelOptInfo::part_scheme, PARTITION_STRATEGY_HASH, GeneratePruningStepsContext::rel, and PartitionSchemeData::strategy.

Referenced by gen_prune_steps_from_opexps().

2278 {
2279  Assert(step_nullkeys == NULL ||
2281 
2282  /* Quick exit if there are no values to prefix with. */
2283  if (list_length(prefix) == 0)
2284  {
2285  PartitionPruneStep *step;
2286 
2287  step = gen_prune_step_op(context,
2288  step_opstrategy,
2289  step_op_is_ne,
2290  list_make1(step_lastexpr),
2291  list_make1_oid(step_lastcmpfn),
2292  step_nullkeys);
2293  return list_make1(step);
2294  }
2295 
2296  /* Recurse to generate steps for various combinations. */
2297  return get_steps_using_prefix_recurse(context,
2298  step_opstrategy,
2299  step_op_is_ne,
2300  step_lastexpr,
2301  step_lastcmpfn,
2302  step_lastkeyno,
2303  step_nullkeys,
2304  prefix,
2305  list_head(prefix),
2306  NIL, NIL);
2307 }
#define NIL
Definition: pg_list.h:65
#define list_make1(x1)
Definition: pg_list.h:227
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
#define list_make1_oid(x1)
Definition: pg_list.h:249
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
static List * get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, StrategyNumber step_opstrategy, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix, ListCell *start, List *step_exprs, List *step_cmpfns)
Definition: partprune.c:2322
#define Assert(condition)
Definition: c.h:745
static int list_length(const List *l)
Definition: pg_list.h:169
PartitionScheme part_scheme
Definition: pathnodes.h:742
static PartitionPruneStep * gen_prune_step_op(GeneratePruningStepsContext *context, StrategyNumber opstrategy, bool op_is_ne, List *exprs, List *cmpfns, Bitmapset *nullkeys)
Definition: partprune.c:1203

◆ get_steps_using_prefix_recurse()

static List * get_steps_using_prefix_recurse ( GeneratePruningStepsContext context,
StrategyNumber  step_opstrategy,
bool  step_op_is_ne,
Expr step_lastexpr,
Oid  step_lastcmpfn,
int  step_lastkeyno,
Bitmapset step_nullkeys,
List prefix,
ListCell start,
List step_exprs,
List step_cmpfns 
)
static

Definition at line 2322 of file partprune.c.

References Assert, bms_is_empty(), bms_num_members(), check_stack_depth(), PartClauseInfo::cmpfn, PartClauseInfo::expr, for_each_cell, gen_prune_step_op(), PartClauseInfo::keyno, lappend(), lappend_oid(), lfirst, list_concat(), list_copy(), list_free(), list_length(), NIL, RelOptInfo::part_scheme, PARTITION_STRATEGY_HASH, PartitionSchemeData::partnatts, GeneratePruningStepsContext::rel, and PartitionSchemeData::strategy.

Referenced by get_steps_using_prefix().

2333 {
2334  List *result = NIL;
2335  ListCell *lc;
2336  int cur_keyno;
2337 
2338  /* Actually, recursion would be limited by PARTITION_MAX_KEYS. */
2340 
2341  /* Check if we need to recurse. */
2342  Assert(start != NULL);
2343  cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno;
2344  if (cur_keyno < step_lastkeyno - 1)
2345  {
2346  PartClauseInfo *pc;
2347  ListCell *next_start;
2348 
2349  /*
2350  * For each clause with cur_keyno, add its expr and cmpfn to
2351  * step_exprs and step_cmpfns, respectively, and recurse after setting
2352  * next_start to the ListCell of the first clause for the next
2353  * partition key.
2354  */
2355  for_each_cell(lc, prefix, start)
2356  {
2357  pc = lfirst(lc);
2358 
2359  if (pc->keyno > cur_keyno)
2360  break;
2361  }
2362  next_start = lc;
2363 
2364  for_each_cell(lc, prefix, start)
2365  {
2366  List *moresteps;
2367  List *step_exprs1,
2368  *step_cmpfns1;
2369 
2370  pc = lfirst(lc);
2371  if (pc->keyno == cur_keyno)
2372  {
2373  /* Leave the original step_exprs unmodified. */
2374  step_exprs1 = list_copy(step_exprs);
2375  step_exprs1 = lappend(step_exprs1, pc->expr);
2376 
2377  /* Leave the original step_cmpfns unmodified. */
2378  step_cmpfns1 = list_copy(step_cmpfns);
2379  step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2380  }
2381  else
2382  {
2383  Assert(pc->keyno > cur_keyno);
2384  break;
2385  }
2386 
2387  moresteps = get_steps_using_prefix_recurse(context,
2388  step_opstrategy,
2389  step_op_is_ne,
2390  step_lastexpr,
2391  step_lastcmpfn,
2392  step_lastkeyno,
2393  step_nullkeys,
2394  prefix,
2395  next_start,
2396  step_exprs1,
2397  step_cmpfns1);
2398  result = list_concat(result, moresteps);
2399 
2400  list_free(step_exprs1);
2401  list_free(step_cmpfns1);
2402  }
2403  }
2404  else
2405  {
2406  /*
2407  * End the current recursion cycle and start generating steps, one for
2408  * each clause with cur_keyno, which is all clauses from here onward
2409  * till the end of the list. Note that for hash partitioning,
2410  * step_nullkeys is allowed to be non-empty, in which case step_exprs
2411  * would only contain expressions for the earlier partition keys that
2412  * are not specified in step_nullkeys.
2413  */
2414  Assert(list_length(step_exprs) == cur_keyno ||
2415  !bms_is_empty(step_nullkeys));
2416  /*
2417  * Note also that for hash partitioning, each partition key should
2418  * have either equality clauses or an IS NULL clause, so if a
2419  * partition key doesn't have an expression, it would be specified
2420  * in step_nullkeys.
2421  */
2422  Assert(context->rel->part_scheme->strategy
2424  list_length(step_exprs) + 2 + bms_num_members(step_nullkeys) ==
2425  context->rel->part_scheme->partnatts);
2426  for_each_cell(lc, prefix, start)
2427  {
2428  PartClauseInfo *pc = lfirst(lc);
2429  PartitionPruneStep *step;
2430  List *step_exprs1,
2431  *step_cmpfns1;
2432 
2433  Assert(pc->keyno == cur_keyno);
2434 
2435  /* Leave the original step_exprs unmodified. */
2436  step_exprs1 = list_copy(step_exprs);
2437  step_exprs1 = lappend(step_exprs1, pc->expr);
2438  step_exprs1 = lappend(step_exprs1, step_lastexpr);
2439 
2440  /* Leave the original step_cmpfns unmodified. */
2441  step_cmpfns1 = list_copy(step_cmpfns);
2442  step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2443  step_cmpfns1 = lappend_oid(step_cmpfns1, step_lastcmpfn);
2444 
2445  step = gen_prune_step_op(context,
2446  step_opstrategy, step_op_is_ne,
2447  step_exprs1, step_cmpfns1,
2448  step_nullkeys);
2449  result = lappend(result, step);
2450  }
2451  }
2452 
2453  return result;
2454 }
#define NIL
Definition: pg_list.h:65
#define for_each_cell(cell, lst, initcell)
Definition: pg_list.h:390
List * list_copy(const List *oldlist)
Definition: list.c:1403
List * list_concat(List *list1, const List *list2)
Definition: list.c:515
List * lappend_oid(List *list, Oid datum)
Definition: list.c:357
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:646
void check_stack_depth(void)
Definition: postgres.c:3312
List * lappend(List *list, void *datum)
Definition: list.c:321
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
static List * get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, StrategyNumber step_opstrategy, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix, ListCell *start, List *step_exprs, List *step_cmpfns)
Definition: partprune.c:2322
#define Assert(condition)
Definition: c.h:745
#define lfirst(lc)
Definition: pg_list.h:190
static int list_length(const List *l)
Definition: pg_list.h:169
void list_free(List *list)
Definition: list.c:1376
PartitionScheme part_scheme
Definition: pathnodes.h:742
Expr * expr
Definition: partprune.c:68
static PartitionPruneStep * gen_prune_step_op(GeneratePruningStepsContext *context, StrategyNumber opstrategy, bool op_is_ne, List *exprs, List *cmpfns, Bitmapset *nullkeys)
Definition: partprune.c:1203
Definition: pg_list.h:50

◆ make_partition_pruneinfo()

PartitionPruneInfo* make_partition_pruneinfo ( PlannerInfo root,
RelOptInfo parentrel,
List subpaths,
List partitioned_rels,
List prunequal 
)

Definition at line 230 of file partprune.c.

References Assert, bms_add_range(), bms_del_members(), bms_join(), bms_num_members(), i, IS_SIMPLE_REL, lappend(), lfirst, list_length(), make_partitionedrel_pruneinfo(), makeNode, NIL, PartitionPruneInfo::other_subplans, palloc0(), Path::parent, pfree(), PartitionPruneInfo::prune_infos, RelOptInfo::relid, and PlannerInfo::simple_rel_array_size.

Referenced by create_append_plan(), and create_merge_append_plan().

233 {
234  PartitionPruneInfo *pruneinfo;
235  Bitmapset *allmatchedsubplans = NULL;
236  int *relid_subplan_map;
237  ListCell *lc;
238  List *prunerelinfos;
239  int i;
240 
241  /*
242  * Construct a temporary array to map from planner relids to subplan
243  * indexes. For convenience, we use 1-based indexes here, so that zero
244  * can represent an un-filled array entry.
245  */
246  relid_subplan_map = palloc0(sizeof(int) * root->simple_rel_array_size);
247 
248  /*
249  * relid_subplan_map maps relid of a leaf partition to the index in
250  * 'subpaths' of the scan plan for that partition.
251  */
252  i = 1;
253  foreach(lc, subpaths)
254  {
255  Path *path = (Path *) lfirst(lc);
256  RelOptInfo *pathrel = path->parent;
257 
258  Assert(IS_SIMPLE_REL(pathrel));
259  Assert(pathrel->relid < root->simple_rel_array_size);
260  /* No duplicates please */
261  Assert(relid_subplan_map[pathrel->relid] == 0);
262 
263  relid_subplan_map[pathrel->relid] = i++;
264  }
265 
266  /* We now build a PartitionedRelPruneInfo for each partitioned rel. */
267  prunerelinfos = NIL;
268  foreach(lc, partitioned_rels)
269  {
270  List *rels = (List *) lfirst(lc);
271  List *pinfolist;
272  Bitmapset *matchedsubplans = NULL;
273 
274  pinfolist = make_partitionedrel_pruneinfo(root, parentrel,
275  relid_subplan_map,
276  rels, prunequal,
277  &matchedsubplans);
278 
279  /* When pruning is possible, record the matched subplans */
280  if (pinfolist != NIL)
281  {
282  prunerelinfos = lappend(prunerelinfos, pinfolist);
283  allmatchedsubplans = bms_join(matchedsubplans,
284  allmatchedsubplans);
285  }
286  }
287 
288  pfree(relid_subplan_map);
289 
290  /*
291  * If none of the partition hierarchies had any useful run-time pruning
292  * quals, then we can just not bother with run-time pruning.
293  */
294  if (prunerelinfos == NIL)
295  return NULL;
296 
297  /* Else build the result data structure */
298  pruneinfo = makeNode(PartitionPruneInfo);
299  pruneinfo->prune_infos = prunerelinfos;
300 
301  /*
302  * Some subplans may not belong to any of the listed partitioned rels.
303  * This can happen for UNION ALL queries which include a non-partitioned
304  * table, or when some of the hierarchies aren't run-time prunable. Build
305  * a bitmapset of the indexes of all such subplans, so that the executor
306  * can identify which subplans should never be pruned.
307  */
308  if (bms_num_members(allmatchedsubplans) < list_length(subpaths))
309  {
310  Bitmapset *other_subplans;
311 
312  /* Create the complement of allmatchedsubplans */
313  other_subplans = bms_add_range(NULL, 0, list_length(subpaths) - 1);
314  other_subplans = bms_del_members(other_subplans, allmatchedsubplans);
315 
316  pruneinfo->other_subplans = other_subplans;
317  }
318  else
319  pruneinfo->other_subplans = NULL;
320 
321  return pruneinfo;
322 }
#define NIL
Definition: pg_list.h:65
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:638
void pfree(void *pointer)
Definition: mcxt.c:1057
static List * make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, int *relid_subplan_map, List *partitioned_rels, List *prunequal, Bitmapset **matchedsubplans)
Definition: partprune.c:343
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:834
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition: bitmapset.c:949
RelOptInfo * parent
Definition: pathnodes.h:1144
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:646
int simple_rel_array_size
Definition: pathnodes.h:204
Index relid
Definition: pathnodes.h:693
List * lappend(List *list, void *datum)
Definition: list.c:321
void * palloc0(Size size)
Definition: mcxt.c:981
#define makeNode(_type_)
Definition: nodes.h:577
Bitmapset * other_subplans
Definition: plannodes.h:1122
#define Assert(condition)
Definition: c.h:745
#define lfirst(lc)
Definition: pg_list.h:190
static int list_length(const List *l)
Definition: pg_list.h:169
Bitmapset * bms_del_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:928
int i
Definition: pg_list.h:50

◆ make_partitionedrel_pruneinfo()

static List * make_partitionedrel_pruneinfo ( PlannerInfo root,
RelOptInfo parentrel,
int *  relid_subplan_map,
List partitioned_rels,
List prunequal,
Bitmapset **  matchedsubplans 
)
static

Definition at line 343 of file partprune.c.

References adjust_appendrel_attrs(), adjust_appendrel_attrs_multilevel(), Assert, bms_add_member(), bms_equal(), bms_is_empty(), GeneratePruningStepsContext::contradictory, PartitionedRelPruneInfo::exec_pruning_steps, PartitionedRelPruneInfo::execparamids, find_appinfos_by_relids(), find_base_rel(), gen_partprune_steps(), get_partkey_exec_paramids(), GeneratePruningStepsContext::has_exec_param, GeneratePruningStepsContext::has_mutable_arg, GeneratePruningStepsContext::has_mutable_op, i, PartitionedRelPruneInfo::initial_pruning_steps, lappend(), lfirst, lfirst_int, makeNode, NIL, RelOptInfo::nparts, PartitionedRelPruneInfo::nparts, palloc(), palloc0(), RelOptInfo::part_rels, PARTTARGET_EXEC, PARTTARGET_INITIAL, pfree(), planner_rt_fetch, PartitionedRelPruneInfo::present_parts, RelOptInfo::relid, PartitionedRelPruneInfo::relid_map, RelOptInfo::relids, PartitionedRelPruneInfo::rtindex, PlannerInfo::simple_rel_array_size, GeneratePruningStepsContext::steps, PartitionedRelPruneInfo::subpart_map, and PartitionedRelPruneInfo::subplan_map.

Referenced by make_partition_pruneinfo().

347 {
348  RelOptInfo *targetpart = NULL;
349  List *pinfolist = NIL;
350  bool doruntimeprune = false;
351  int *relid_subpart_map;
352  Bitmapset *subplansfound = NULL;
353  ListCell *lc;
354  int i;
355 
356  /*
357  * Examine each partitioned rel, constructing a temporary array to map
358  * from planner relids to index of the partitioned rel, and building a
359  * PartitionedRelPruneInfo for each partitioned rel.
360  *
361  * In this phase we discover whether runtime pruning is needed at all; if
362  * not, we can avoid doing further work.
363  */
364  relid_subpart_map = palloc0(sizeof(int) * root->simple_rel_array_size);
365 
366  i = 1;
367  foreach(lc, partitioned_rels)
368  {
369  Index rti = lfirst_int(lc);
370  RelOptInfo *subpart = find_base_rel(root, rti);
372  List *partprunequal;
373  List *initial_pruning_steps;
374  List *exec_pruning_steps;
375  Bitmapset *execparamids;
377 
378  /*
379  * Fill the mapping array.
380  *
381  * relid_subpart_map maps relid of a non-leaf partition to the index
382  * in 'partitioned_rels' of that rel (which will also be the index in
383  * the returned PartitionedRelPruneInfo list of the info for that
384  * partition). We use 1-based indexes here, so that zero can
385  * represent an un-filled array entry.
386  */
387  Assert(rti < root->simple_rel_array_size);
388  /* No duplicates please */
389  Assert(relid_subpart_map[rti] == 0);
390  relid_subpart_map[rti] = i++;
391 
392  /*
393  * Translate pruning qual, if necessary, for this partition.
394  *
395  * The first item in the list is the target partitioned relation.
396  */
397  if (!targetpart)
398  {
399  targetpart = subpart;
400 
401  /*
402  * The prunequal is presented to us as a qual for 'parentrel'.
403  * Frequently this rel is the same as targetpart, so we can skip
404  * an adjust_appendrel_attrs step. But it might not be, and then
405  * we have to translate. We update the prunequal parameter here,
406  * because in later iterations of the loop for child partitions,
407  * we want to translate from parent to child variables.
408  */
409  if (!bms_equal(parentrel->relids, subpart->relids))
410  {
411  int nappinfos;
412  AppendRelInfo **appinfos = find_appinfos_by_relids(root,
413  subpart->relids,
414  &nappinfos);
415 
416  prunequal = (List *) adjust_appendrel_attrs(root, (Node *)
417  prunequal,
418  nappinfos,
419  appinfos);
420 
421  pfree(appinfos);
422  }
423 
424  partprunequal = prunequal;
425  }
426  else
427  {
428  /*
429  * For sub-partitioned tables the columns may not be in the same
430  * order as the parent, so we must translate the prunequal to make
431  * it compatible with this relation.
432  */
433  partprunequal = (List *)
435  (Node *) prunequal,
436  subpart->relids,
437  targetpart->relids);
438  }
439 
440  /*
441  * Convert pruning qual to pruning steps. We may need to do this
442  * twice, once to obtain executor startup pruning steps, and once for
443  * executor per-scan pruning steps. This first pass creates startup
444  * pruning steps and detects whether there's any possibly-useful quals
445  * that would require per-scan pruning.
446  */
447  gen_partprune_steps(subpart, partprunequal, PARTTARGET_INITIAL,
448  &context);
449 
450  if (context.contradictory)
451  {
452  /*
453  * This shouldn't happen as the planner should have detected this
454  * earlier. However, we do use additional quals from parameterized
455  * paths here. These do only compare Params to the partition key,
456  * so this shouldn't cause the discovery of any new qual
457  * contradictions that were not previously discovered as the Param
458  * values are unknown during planning. Anyway, we'd better do
459  * something sane here, so let's just disable run-time pruning.
460  */
461  return NIL;
462  }
463 
464  /*
465  * If no mutable operators or expressions appear in usable pruning
466  * clauses, then there's no point in running startup pruning, because
467  * plan-time pruning should have pruned everything prunable.
468  */
469  if (context.has_mutable_op || context.has_mutable_arg)
470  initial_pruning_steps = context.steps;
471  else
472  initial_pruning_steps = NIL;
473 
474  /*
475  * If no exec Params appear in potentially-usable pruning clauses,
476  * then there's no point in even thinking about per-scan pruning.
477  */
478  if (context.has_exec_param)
479  {
480  /* ... OK, we'd better think about it */
481  gen_partprune_steps(subpart, partprunequal, PARTTARGET_EXEC,
482  &context);
483 
484  if (context.contradictory)
485  {
486  /* As above, skip run-time pruning if anything fishy happens */
487  return NIL;
488  }
489 
490  exec_pruning_steps = context.steps;
491 
492  /*
493  * Detect which exec Params actually got used; the fact that some
494  * were in available clauses doesn't mean we actually used them.
495  * Skip per-scan pruning if there are none.
496  */
497  execparamids = get_partkey_exec_paramids(exec_pruning_steps);
498 
499  if (bms_is_empty(execparamids))
500  exec_pruning_steps = NIL;
501  }
502  else
503  {
504  /* No exec Params anywhere, so forget about scan-time pruning */
505  exec_pruning_steps = NIL;
506  execparamids = NULL;
507  }
508 
509  if (initial_pruning_steps || exec_pruning_steps)
510  doruntimeprune = true;
511 
512  /* Begin constructing the PartitionedRelPruneInfo for this rel */
514  pinfo->rtindex = rti;
515  pinfo->initial_pruning_steps = initial_pruning_steps;
516  pinfo->exec_pruning_steps = exec_pruning_steps;
517  pinfo->execparamids = execparamids;
518  /* Remaining fields will be filled in the next loop */
519 
520  pinfolist = lappend(pinfolist, pinfo);
521  }
522 
523  if (!doruntimeprune)
524  {
525  /* No run-time pruning required. */
526  pfree(relid_subpart_map);
527  return NIL;
528  }
529 
530  /*
531  * Run-time pruning will be required, so initialize other information.
532  * That includes two maps -- one needed to convert partition indexes of
533  * leaf partitions to the indexes of their subplans in the subplan list,
534  * another needed to convert partition indexes of sub-partitioned
535  * partitions to the indexes of their PartitionedRelPruneInfo in the
536  * PartitionedRelPruneInfo list.
537  */
538  foreach(lc, pinfolist)
539  {
540  PartitionedRelPruneInfo *pinfo = lfirst(lc);
541  RelOptInfo *subpart = find_base_rel(root, pinfo->rtindex);
542  Bitmapset *present_parts;
543  int nparts = subpart->nparts;
544  int *subplan_map;
545  int *subpart_map;
546  Oid *relid_map;
547 
548  /*
549  * Construct the subplan and subpart maps for this partitioning level.
550  * Here we convert to zero-based indexes, with -1 for empty entries.
551  * Also construct a Bitmapset of all partitions that are present (that
552  * is, not pruned already).
553  */
554  subplan_map = (int *) palloc(nparts * sizeof(int));
555  memset(subplan_map, -1, nparts * sizeof(int));
556  subpart_map = (int *) palloc(nparts * sizeof(int));
557  memset(subpart_map, -1, nparts * sizeof(int));
558  relid_map = (Oid *) palloc0(nparts * sizeof(Oid));
559  present_parts = NULL;
560 
561  for (i = 0; i < nparts; i++)
562  {
563  RelOptInfo *partrel = subpart->part_rels[i];
564  int subplanidx;
565  int subpartidx;
566 
567  /* Skip processing pruned partitions. */
568  if (partrel == NULL)
569  continue;
570 
571  subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;
572  subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
573  relid_map[i] = planner_rt_fetch(partrel->relid, root)->relid;
574  if (subplanidx >= 0)
575  {
576  present_parts = bms_add_member(present_parts, i);
577 
578  /* Record finding this subplan */
579  subplansfound = bms_add_member(subplansfound, subplanidx);
580  }
581  else if (subpartidx >= 0)
582  present_parts = bms_add_member(present_parts, i);
583  }
584 
585  /* Record the maps and other information. */
586  pinfo->present_parts = present_parts;
587  pinfo->nparts = nparts;
588  pinfo->subplan_map = subplan_map;
589  pinfo->subpart_map = subpart_map;
590  pinfo->relid_map = relid_map;
591  }
592 
593  pfree(relid_subpart_map);
594 
595  *matchedsubplans = subplansfound;
596 
597  return pinfolist;
598 }
static void gen_partprune_steps(RelOptInfo *rel, List *clauses, PartClauseTarget target, GeneratePruningStepsContext *context)
Definition: partprune.c:614
#define NIL
Definition: pg_list.h:65
Bitmapset * execparamids
Definition: plannodes.h:1161
Definition: nodes.h:529
unsigned int Oid
Definition: postgres_ext.h:31
Node * adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node, Relids child_relids, Relids top_parent_relids)
Definition: appendinfo.c:502
void pfree(void *pointer)
Definition: mcxt.c:1057
#define lfirst_int(lc)
Definition: pg_list.h:191
#define planner_rt_fetch(rti, root)
Definition: pathnodes.h:373
static Bitmapset * get_partkey_exec_paramids(List *steps)
Definition: partprune.c:3198
int nparts
Definition: pathnodes.h:743
Bitmapset * present_parts
Definition: plannodes.h:1146
Relids relids
Definition: pathnodes.h:665
int simple_rel_array_size
Definition: pathnodes.h:204
Index relid
Definition: pathnodes.h:693
List * lappend(List *list, void *datum)
Definition: list.c:321
AppendRelInfo ** find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
Definition: appendinfo.c:728
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
void * palloc0(Size size)
Definition: mcxt.c:981
unsigned int Index
Definition: c.h:482
#define makeNode(_type_)
Definition: nodes.h:577
#define Assert(condition)
Definition: c.h:745
#define lfirst(lc)
Definition: pg_list.h:190
struct RelOptInfo ** part_rels
Definition: pathnodes.h:750
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:736
void * palloc(Size size)
Definition: mcxt.c:950
int i
RelOptInfo * find_base_rel(PlannerInfo *root, int relid)
Definition: relnode.c:374
Definition: pg_list.h:50
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:94
Node * adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos, AppendRelInfo **appinfos)
Definition: appendinfo.c:194

◆ match_boolean_partition_clause()

static PartClauseMatchStatus match_boolean_partition_clause ( Oid  partopfamily,
Expr clause,
Expr partkey,
Expr **  outconst 
)
static

Definition at line 3499 of file partprune.c.

References arg, BooleanTest::arg, BooleanTest::booltesttype, equal(), get_notclausearg(), IS_NOT_FALSE, IS_NOT_UNKNOWN, is_notclause(), IS_TRUE, IS_UNKNOWN, IsA, makeBoolConst(), negate_clause(), PARTCLAUSE_MATCH_CLAUSE, PARTCLAUSE_NOMATCH, and PARTCLAUSE_UNSUPPORTED.

Referenced by match_clause_to_partition_key().

3501 {
3502  Expr *leftop;
3503 
3504  *outconst = NULL;
3505 
3506  if (!IsBooleanOpfamily(partopfamily))
3507  return PARTCLAUSE_UNSUPPORTED;
3508 
3509  if (IsA(clause, BooleanTest))
3510  {
3511  BooleanTest *btest = (BooleanTest *) clause;
3512 
3513  /* Only IS [NOT] TRUE/FALSE are any good to us */
3514  if (btest->booltesttype == IS_UNKNOWN ||
3515  btest->booltesttype == IS_NOT_UNKNOWN)
3516  return PARTCLAUSE_UNSUPPORTED;
3517 
3518  leftop = btest->arg;
3519  if (IsA(leftop, RelabelType))
3520  leftop = ((RelabelType *) leftop)->arg;
3521 
3522  if (equal(leftop, partkey))
3523  *outconst = (btest->booltesttype == IS_TRUE ||
3524  btest->booltesttype == IS_NOT_FALSE)
3525  ? (Expr *) makeBoolConst(true, false)
3526  : (Expr *) makeBoolConst(false, false);
3527 
3528  if (*outconst)
3529  return PARTCLAUSE_MATCH_CLAUSE;
3530  }
3531  else
3532  {
3533  bool is_not_clause = is_notclause(clause);
3534 
3535  leftop = is_not_clause ? get_notclausearg(clause) : clause;
3536 
3537  if (IsA(leftop, RelabelType))
3538  leftop = ((RelabelType *) leftop)->arg;
3539 
3540  /* Compare to the partition key, and make up a clause ... */
3541  if (equal(leftop, partkey))
3542  *outconst = is_not_clause ?
3543  (Expr *) makeBoolConst(false, false) :
3544  (Expr *) makeBoolConst(true, false);
3545  else if (equal(negate_clause((Node *) leftop), partkey))
3546  *outconst = (Expr *) makeBoolConst(false, false);
3547 
3548  if (*outconst)
3549  return PARTCLAUSE_MATCH_CLAUSE;
3550  }
3551 
3552  return PARTCLAUSE_NOMATCH;
3553 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:580
Node * negate_clause(Node *node)
Definition: prepqual.c:74
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3032
Definition: nodes.h:529
#define false
Definition: c.h:332
#define true
Definition: c.h:328
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:357
Expr * arg
Definition: primnodes.h:1242
static bool is_notclause(const void *clause)
Definition: nodeFuncs.h:115
BoolTestType booltesttype
Definition: primnodes.h:1243
static Expr * get_notclausearg(const void *notclause)
Definition: nodeFuncs.h:124
void * arg

◆ match_clause_to_partition_key()

static PartClauseMatchStatus match_clause_to_partition_key ( GeneratePruningStepsContext context,
Expr clause,
Expr partkey,
int  partkeyidx,
bool clause_is_not_null,
PartClauseInfo **  pc,
List **  clause_steps 
)
static

Definition at line 1692 of file partprune.c.

References arg, NullTest::arg, generate_unaccent_rules::args, ScalarArrayOpExpr::args, ARR_ELEMTYPE, Assert, bms_is_empty(), BTEqualStrategyNumber, BTORDER_PROC, castNode, PartClauseInfo::cmpfn, Const::constcollid, Const::constisnull, Const::constvalue, contain_var_clause(), contain_volatile_functions(), GeneratePruningStepsContext::contradictory, DatumGetArrayTypeP, deconstruct_array(), ArrayExpr::elements, elog, equal(), ERROR, PartClauseInfo::expr, FmgrInfo::fn_oid, gen_partprune_steps_internal(), get_commutator(), get_leftop(), get_negator(), get_op_opfamily_properties(), get_opfamily_proc(), get_rightop(), get_typlenbyvalalign(), GeneratePruningStepsContext::has_exec_param, GeneratePruningStepsContext::has_mutable_arg, GeneratePruningStepsContext::has_mutable_op, HASHEXTENDED_PROC, i, OpExpr::inputcollid, ScalarArrayOpExpr::inputcollid, InvalidOid, InvalidStrategy, IS_NOT_NULL, IsA, PartClauseInfo::keyno, lappend(), lfirst, linitial, list_length(), list_make1, lsecond, make_opclause(), makeBoolExpr(), makeConst(), match_boolean_partition_clause(), ArrayExpr::multidims, NIL, NullTest::nulltesttype, OidIsValid, op_in_opfamily(), PartClauseInfo::op_is_ne, PartClauseInfo::op_strategy, op_strict(), op_volatile(), PartClauseInfo::opno, OpExpr::opno, ScalarArrayOpExpr::opno, OR_EXPR, palloc(), RelOptInfo::part_scheme, PARTCLAUSE_MATCH_CLAUSE, PARTCLAUSE_MATCH_CONTRADICT, PARTCLAUSE_MATCH_NULLNESS, PARTCLAUSE_MATCH_STEPS, PARTCLAUSE_NOMATCH, PARTCLAUSE_UNSUPPORTED, PartitionSchemeData::partcollation, PartCollMatchesExprColl, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionSchemeData::partopcintype, PartitionSchemeData::partopfamily, PartitionSchemeData::partsupfunc, PARTTARGET_EXEC, PARTTARGET_PLANNER, pull_exec_paramids(), GeneratePruningStepsContext::rel, PartitionSchemeData::strategy, GeneratePruningStepsContext::target, and ScalarArrayOpExpr::useOr.

Referenced by gen_partprune_steps_internal().

1696 {
1697  PartClauseMatchStatus boolmatchstatus;
1698  PartitionScheme part_scheme = context->rel->part_scheme;
1699  Oid partopfamily = part_scheme->partopfamily[partkeyidx],
1700  partcoll = part_scheme->partcollation[partkeyidx];
1701  Expr *expr;
1702 
1703  /*
1704  * Recognize specially shaped clauses that match a Boolean partition key.
1705  */
1706  boolmatchstatus = match_boolean_partition_clause(partopfamily, clause,
1707  partkey, &expr);
1708 
1709  if (boolmatchstatus == PARTCLAUSE_MATCH_CLAUSE)
1710  {
1711  PartClauseInfo *partclause;
1712 
1713  partclause = (PartClauseInfo *) palloc(sizeof(PartClauseInfo));
1714  partclause->keyno = partkeyidx;
1715  /* Do pruning with the Boolean equality operator. */
1716  partclause->opno = BooleanEqualOperator;
1717  partclause->op_is_ne = false;
1718  partclause->expr = expr;
1719  /* We know that expr is of Boolean type. */
1720  partclause->cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
1721  partclause->op_strategy = InvalidStrategy;
1722 
1723  *pc = partclause;
1724 
1725  return PARTCLAUSE_MATCH_CLAUSE;
1726  }
1727  else if (IsA(clause, OpExpr) &&
1728  list_length(((OpExpr *) clause)->args) == 2)
1729  {
1730  OpExpr *opclause = (OpExpr *) clause;
1731  Expr *leftop,
1732  *rightop;
1733  Oid opno,
1734  op_lefttype,
1735  op_righttype,
1736  negator = InvalidOid;
1737  Oid cmpfn;
1738  int op_strategy;
1739  bool is_opne_listp = false;
1740  PartClauseInfo *partclause;
1741 
1742  leftop = (Expr *) get_leftop(clause);
1743  if (IsA(leftop, RelabelType))
1744  leftop = ((RelabelType *) leftop)->arg;
1745  rightop = (Expr *) get_rightop(clause);
1746  if (IsA(rightop, RelabelType))
1747  rightop = ((RelabelType *) rightop)->arg;
1748  opno = opclause->opno;
1749 
1750  /* check if the clause matches this partition key */
1751  if (equal(leftop, partkey))
1752  expr = rightop;
1753  else if (equal(rightop, partkey))
1754  {
1755  /*
1756  * It's only useful if we can commute the operator to put the
1757  * partkey on the left. If we can't, the clause can be deemed
1758  * UNSUPPORTED. Even if its leftop matches some later partkey, we
1759  * now know it has Vars on the right, so it's no use.
1760  */
1761  opno = get_commutator(opno);
1762  if (!OidIsValid(opno))
1763  return PARTCLAUSE_UNSUPPORTED;
1764  expr = leftop;
1765  }
1766  else
1767  /* clause does not match this partition key, but perhaps next. */
1768  return PARTCLAUSE_NOMATCH;
1769 
1770  /*
1771  * Partition key match also requires collation match. There may be
1772  * multiple partkeys with the same expression but different
1773  * collations, so failure is NOMATCH.
1774  */
1775  if (!PartCollMatchesExprColl(partcoll, opclause->inputcollid))
1776  return PARTCLAUSE_NOMATCH;
1777 
1778  /*
1779  * See if the operator is relevant to the partitioning opfamily.
1780  *
1781  * Normally we only care about operators that are listed as being part
1782  * of the partitioning operator family. But there is one exception:
1783  * the not-equals operators are not listed in any operator family
1784  * whatsoever, but their negators (equality) are. We can use one of
1785  * those if we find it, but only for list partitioning.
1786  *
1787  * Note: we report NOMATCH on failure, in case a later partkey has the
1788  * same expression but different opfamily. That's unlikely, but not
1789  * much more so than duplicate expressions with different collations.
1790  */
1791  if (op_in_opfamily(opno, partopfamily))
1792  {
1793  get_op_opfamily_properties(opno, partopfamily, false,
1794  &op_strategy, &op_lefttype,
1795  &op_righttype);
1796  }
1797  else
1798  {
1799  if (part_scheme->strategy != PARTITION_STRATEGY_LIST)
1800  return PARTCLAUSE_NOMATCH;
1801 
1802  /* See if the negator is equality */
1803  negator = get_negator(opno);
1804  if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
1805  {
1806  get_op_opfamily_properties(negator, partopfamily, false,
1807  &op_strategy, &op_lefttype,
1808  &op_righttype);
1809  if (op_strategy == BTEqualStrategyNumber)
1810  is_opne_listp = true; /* bingo */
1811  }
1812 
1813  /* Nope, it's not <> either. */
1814  if (!is_opne_listp)
1815  return PARTCLAUSE_NOMATCH;
1816  }
1817 
1818  /*
1819  * Only allow strict operators. This will guarantee nulls are
1820  * filtered. (This test is likely useless, since btree and hash
1821  * comparison operators are generally strict.)
1822  */
1823  if (!op_strict(opno))
1824  return PARTCLAUSE_UNSUPPORTED;
1825 
1826  /*
1827  * OK, we have a match to the partition key and a suitable operator.
1828  * Examine the other argument to see if it's usable for pruning.
1829  *
1830  * In most of these cases, we can return UNSUPPORTED because the same
1831  * failure would occur no matter which partkey it's matched to. (In
1832  * particular, now that we've successfully matched one side of the
1833  * opclause to a partkey, there is no chance that matching the other
1834  * side to another partkey will produce a usable result, since that'd
1835  * mean there are Vars on both sides.)
1836  *
1837  * Also, if we reject an argument for a target-dependent reason, set
1838  * appropriate fields of *context to report that. We postpone these
1839  * tests until after matching the partkey and the operator, so as to
1840  * reduce the odds of setting the context fields for clauses that do
1841  * not end up contributing to pruning steps.
1842  *
1843  * First, check for non-Const argument. (We assume that any immutable
1844  * subexpression will have been folded to a Const already.)
1845  */
1846  if (!IsA(expr, Const))
1847  {
1848  Bitmapset *paramids;
1849 
1850  /*
1851  * When pruning in the planner, we only support pruning using
1852  * comparisons to constants. We cannot prune on the basis of
1853  * anything that's not immutable. (Note that has_mutable_arg and
1854  * has_exec_param do not get set for this target value.)
1855  */
1856  if (context->target == PARTTARGET_PLANNER)
1857  return PARTCLAUSE_UNSUPPORTED;
1858 
1859  /*
1860  * We can never prune using an expression that contains Vars.
1861  */
1862  if (contain_var_clause((Node *) expr))
1863  return PARTCLAUSE_UNSUPPORTED;
1864 
1865  /*
1866  * And we must reject anything containing a volatile function.
1867  * Stable functions are OK though.
1868  */
1869  if (contain_volatile_functions((Node *) expr))
1870  return PARTCLAUSE_UNSUPPORTED;
1871 
1872  /*
1873  * See if there are any exec Params. If so, we can only use this
1874  * expression during per-scan pruning.
1875  */
1876  paramids = pull_exec_paramids(expr);
1877  if (!bms_is_empty(paramids))
1878  {
1879  context->has_exec_param = true;
1880  if (context->target != PARTTARGET_EXEC)
1881  return PARTCLAUSE_UNSUPPORTED;
1882  }
1883  else
1884  {
1885  /* It's potentially usable, but mutable */
1886  context->has_mutable_arg = true;
1887  }
1888  }
1889 
1890  /*
1891  * Check whether the comparison operator itself is immutable. (We
1892  * assume anything that's in a btree or hash opclass is at least
1893  * stable, but we need to check for immutability.)
1894  */
1895  if (op_volatile(opno) != PROVOLATILE_IMMUTABLE)
1896  {
1897  context->has_mutable_op = true;
1898 
1899  /*
1900  * When pruning in the planner, we cannot prune with mutable
1901  * operators.
1902  */
1903  if (context->target == PARTTARGET_PLANNER)
1904  return PARTCLAUSE_UNSUPPORTED;
1905  }
1906 
1907  /*
1908  * Now find the procedure to use, based on the types. If the clause's
1909  * other argument is of the same type as the partitioning opclass's
1910  * declared input type, we can use the procedure cached in
1911  * PartitionKey. If not, search for a cross-type one in the same
1912  * opfamily; if one doesn't exist, report no match.
1913  */
1914  if (op_righttype == part_scheme->partopcintype[partkeyidx])
1915  cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
1916  else
1917  {
1918  switch (part_scheme->strategy)
1919  {
1920  /*
1921  * For range and list partitioning, we need the ordering
1922  * procedure with lefttype being the partition key's type,
1923  * and righttype the clause's operator's right type.
1924  */
1927  cmpfn =
1928  get_opfamily_proc(part_scheme->partopfamily[partkeyidx],
1929  part_scheme->partopcintype[partkeyidx],
1930  op_righttype, BTORDER_PROC);
1931  break;
1932 
1933  /*
1934  * For hash partitioning, we need the hashing procedure
1935  * for the clause's type.
1936  */
1938  cmpfn =
1939  get_opfamily_proc(part_scheme->partopfamily[partkeyidx],
1940  op_righttype, op_righttype,
1942  break;
1943 
1944  default:
1945  elog(ERROR, "invalid partition strategy: %c",
1946  part_scheme->strategy);
1947  cmpfn = InvalidOid; /* keep compiler quiet */
1948  break;
1949  }
1950 
1951  if (!OidIsValid(cmpfn))
1952  return PARTCLAUSE_NOMATCH;
1953  }
1954 
1955  /*
1956  * Build the clause, passing the negator if applicable.
1957  */
1958  partclause = (PartClauseInfo *) palloc(sizeof(PartClauseInfo));
1959  partclause->keyno = partkeyidx;
1960  if (is_opne_listp)
1961  {
1962  Assert(OidIsValid(negator));
1963  partclause->opno = negator;
1964  partclause->op_is_ne = true;
1965  partclause->op_strategy = InvalidStrategy;
1966  }
1967  else
1968  {
1969  partclause->opno = opno;
1970  partclause->op_is_ne = false;
1971  partclause->op_strategy = op_strategy;
1972  }
1973  partclause->expr = expr;
1974  partclause->cmpfn = cmpfn;
1975 
1976  *pc = partclause;
1977 
1978  return PARTCLAUSE_MATCH_CLAUSE;
1979  }
1980  else if (IsA(clause, ScalarArrayOpExpr))
1981  {
1982  ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
1983  Oid saop_op = saop->opno;
1984  Oid saop_coll = saop->inputcollid;
1985  Expr *leftop = (Expr *) linitial(saop->args),
1986  *rightop = (Expr *) lsecond(saop->args);
1987  List *elem_exprs,
1988  *elem_clauses;
1989  ListCell *lc1;
1990 
1991  if (IsA(leftop, RelabelType))
1992  leftop = ((RelabelType *) leftop)->arg;
1993 
1994  /* check if the LHS matches this partition key */
1995  if (!equal(leftop, partkey) ||
1996  !PartCollMatchesExprColl(partcoll, saop->inputcollid))
1997  return PARTCLAUSE_NOMATCH;
1998 
1999  /*
2000  * See if the operator is relevant to the partitioning opfamily.
2001  *
2002  * In case of NOT IN (..), we get a '<>', which we handle if list
2003  * partitioning is in use and we're able to confirm that it's negator
2004  * is a btree equality operator belonging to the partitioning operator
2005  * family. As above, report NOMATCH for non-matching operator.
2006  */
2007  if (!op_in_opfamily(saop_op, partopfamily))
2008  {
2009  Oid negator;
2010 
2011  if (part_scheme->strategy != PARTITION_STRATEGY_LIST)
2012  return PARTCLAUSE_NOMATCH;
2013 
2014  negator = get_negator(saop_op);
2015  if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
2016  {
2017  int strategy;
2018  Oid lefttype,
2019  righttype;
2020 
2021  get_op_opfamily_properties(negator, partopfamily,
2022  false, &strategy,
2023  &lefttype, &righttype);
2024  if (strategy != BTEqualStrategyNumber)
2025  return PARTCLAUSE_NOMATCH;
2026  }
2027  else
2028  return PARTCLAUSE_NOMATCH; /* no useful negator */
2029  }
2030 
2031  /*
2032  * Only allow strict operators. This will guarantee nulls are
2033  * filtered. (This test is likely useless, since btree and hash
2034  * comparison operators are generally strict.)
2035  */
2036  if (!op_strict(saop_op))
2037  return PARTCLAUSE_UNSUPPORTED;
2038 
2039  /*
2040  * OK, we have a match to the partition key and a suitable operator.
2041  * Examine the array argument to see if it's usable for pruning. This
2042  * is identical to the logic for a plain OpExpr.
2043  */
2044  if (!IsA(rightop, Const))
2045  {
2046  Bitmapset *paramids;
2047 
2048  /*
2049  * When pruning in the planner, we only support pruning using
2050  * comparisons to constants. We cannot prune on the basis of
2051  * anything that's not immutable. (Note that has_mutable_arg and
2052  * has_exec_param do not get set for this target value.)
2053  */
2054  if (context->target == PARTTARGET_PLANNER)
2055  return PARTCLAUSE_UNSUPPORTED;
2056 
2057  /*
2058  * We can never prune using an expression that contains Vars.
2059  */
2060  if (contain_var_clause((Node *) rightop))
2061  return PARTCLAUSE_UNSUPPORTED;
2062 
2063  /*
2064  * And we must reject anything containing a volatile function.
2065  * Stable functions are OK though.
2066  */
2067  if (contain_volatile_functions((Node *) rightop))
2068  return PARTCLAUSE_UNSUPPORTED;
2069 
2070  /*
2071  * See if there are any exec Params. If so, we can only use this
2072  * expression during per-scan pruning.
2073  */
2074  paramids = pull_exec_paramids(rightop);
2075  if (!bms_is_empty(paramids))
2076  {
2077  context->has_exec_param = true;
2078  if (context->target != PARTTARGET_EXEC)
2079  return PARTCLAUSE_UNSUPPORTED;
2080  }
2081  else
2082  {
2083  /* It's potentially usable, but mutable */
2084  context->has_mutable_arg = true;
2085  }
2086  }
2087 
2088  /*
2089  * Check whether the comparison operator itself is immutable. (We
2090  * assume anything that's in a btree or hash opclass is at least
2091  * stable, but we need to check for immutability.)
2092  */
2093  if (op_volatile(saop_op) != PROVOLATILE_IMMUTABLE)
2094  {
2095  context->has_mutable_op = true;
2096 
2097  /*
2098  * When pruning in the planner, we cannot prune with mutable
2099  * operators.
2100  */
2101  if (context->target == PARTTARGET_PLANNER)
2102  return PARTCLAUSE_UNSUPPORTED;
2103  }
2104 
2105  /*
2106  * Examine the contents of the array argument.
2107  */
2108  elem_exprs = NIL;
2109  if (IsA(rightop, Const))
2110  {
2111  /*
2112  * For a constant array, convert the elements to a list of Const
2113  * nodes, one for each array element (excepting nulls).
2114  */
2115  Const *arr = (Const *) rightop;
2116  ArrayType *arrval;
2117  int16 elemlen;
2118  bool elembyval;
2119  char elemalign;
2120  Datum *elem_values;
2121  bool *elem_nulls;
2122  int num_elems,
2123  i;
2124 
2125  /* If the array itself is null, the saop returns null */
2126  if (arr->constisnull)
2128 
2129  arrval = DatumGetArrayTypeP(arr->constvalue);
2131  &elemlen, &elembyval, &elemalign);
2132  deconstruct_array(arrval,
2133  ARR_ELEMTYPE(arrval),
2134  elemlen, elembyval, elemalign,
2135  &elem_values, &elem_nulls,
2136  &num_elems);
2137  for (i = 0; i < num_elems; i++)
2138  {
2139  Const *elem_expr;
2140 
2141  /*
2142  * A null array element must lead to a null comparison result,
2143  * since saop_op is known strict. We can ignore it in the
2144  * useOr case, but otherwise it implies self-contradiction.
2145  */
2146  if (elem_nulls[i])
2147  {
2148  if (saop->useOr)
2149  continue;
2151  }
2152 
2153  elem_expr = makeConst(ARR_ELEMTYPE(arrval), -1,
2154  arr->constcollid, elemlen,
2155  elem_values[i], false, elembyval);
2156  elem_exprs = lappend(elem_exprs, elem_expr);
2157  }
2158  }
2159  else if (IsA(rightop, ArrayExpr))
2160  {
2161  ArrayExpr *arrexpr = castNode(ArrayExpr, rightop);
2162 
2163  /*
2164  * For a nested ArrayExpr, we don't know how to get the actual
2165  * scalar values out into a flat list, so we give up doing
2166  * anything with this ScalarArrayOpExpr.
2167  */
2168  if (arrexpr->multidims)
2169  return PARTCLAUSE_UNSUPPORTED;
2170 
2171  /*
2172  * Otherwise, we can just use the list of element values.
2173  */
2174  elem_exprs = arrexpr->elements;
2175  }
2176  else
2177  {
2178  /* Give up on any other clause types. */
2179  return PARTCLAUSE_UNSUPPORTED;
2180  }
2181 
2182  /*
2183  * Now generate a list of clauses, one for each array element, of the
2184  * form leftop saop_op elem_expr
2185  */
2186  elem_clauses = NIL;
2187  foreach(lc1, elem_exprs)
2188  {
2189  Expr *rightop = (Expr *) lfirst(lc1),
2190  *elem_clause;
2191 
2192  elem_clause = make_opclause(saop_op, BOOLOID, false,
2193  leftop, rightop,
2194  InvalidOid, saop_coll);
2195  elem_clauses = lappend(elem_clauses, elem_clause);
2196  }
2197 
2198  /*
2199  * If we have an ANY clause and multiple elements, now turn the list
2200  * of clauses into an OR expression.
2201  */
2202  if (saop->useOr && list_length(elem_clauses) > 1)
2203  elem_clauses = list_make1(makeBoolExpr(OR_EXPR, elem_clauses, -1));
2204 
2205  /* Finally, generate steps */
2206  *clause_steps = gen_partprune_steps_internal(context, elem_clauses);
2207  if (context->contradictory)
2209  else if (*clause_steps == NIL)
2210  return PARTCLAUSE_UNSUPPORTED; /* step generation failed */
2211  return PARTCLAUSE_MATCH_STEPS;
2212  }
2213  else if (IsA(clause, NullTest))
2214  {
2215  NullTest *nulltest = (NullTest *) clause;
2216  Expr *arg = nulltest->arg;
2217 
2218  if (IsA(arg, RelabelType))
2219  arg = ((RelabelType *) arg)->arg;
2220 
2221  /* Does arg match with this partition key column? */
2222  if (!equal(arg, partkey))
2223  return PARTCLAUSE_NOMATCH;
2224 
2225  *clause_is_not_null = (nulltest->nulltesttype == IS_NOT_NULL);
2226 
2228  }
2229 
2230  /*
2231  * If we get here then the return value depends on the result of the
2232  * match_boolean_partition_clause call above. If the call returned
2233  * PARTCLAUSE_UNSUPPORTED then we're either not dealing with a bool qual
2234  * or the bool qual is not suitable for pruning. Since the qual didn't
2235  * match up to any of the other qual types supported here, then trying to
2236  * match it against any other partition key is a waste of time, so just
2237  * return PARTCLAUSE_UNSUPPORTED. If the qual just couldn't be matched to
2238  * this partition key, then it may match another, so return
2239  * PARTCLAUSE_NOMATCH. The only other value that
2240  * match_boolean_partition_clause can return is PARTCLAUSE_MATCH_CLAUSE,
2241  * and since that value was already dealt with above, then we can just
2242  * return boolmatchstatus.
2243  */
2244  return boolmatchstatus;
2245 }
Datum constvalue
Definition: primnodes.h:214
signed short int16
Definition: c.h:361
#define InvalidStrategy
Definition: stratnum.h:24
bool multidims
Definition: primnodes.h:992
#define NIL
Definition: pg_list.h:65
bool op_in_opfamily(Oid opno, Oid opfamily)
Definition: lsyscache.c:64
bool op_strict(Oid opno)
Definition: lsyscache.c:1389
#define IsA(nodeptr, _type_)
Definition: nodes.h:580
#define BTORDER_PROC
Definition: nbtree.h:575
Oid get_commutator(Oid opno)
Definition: lsyscache.c:1421
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3032
static PartClauseMatchStatus match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey, Expr **outconst)
Definition: partprune.c:3499
#define castNode(_type_, nodeptr)
Definition: nodes.h:598
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition: lsyscache.c:2159
Definition: nodes.h:529
#define HASHEXTENDED_PROC
Definition: hash.h:354
bool contain_var_clause(Node *node)
Definition: var.c:331
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:724
unsigned int Oid
Definition: postgres_ext.h:31
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:299
#define OidIsValid(objectId)
Definition: c.h:651
Expr * make_opclause(Oid opno, Oid opresulttype, bool opretset, Expr *leftop, Expr *rightop, Oid opcollid, Oid inputcollid)
Definition: makefuncs.c:609
#define lsecond(l)
Definition: pg_list.h:200
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition: makefuncs.c:369
#define list_make1(x1)
Definition: pg_list.h:227
#define PartCollMatchesExprColl(partcoll, exprcoll)
Definition: partprune.c:1657
#define linitial(l)
Definition: pg_list.h:195
#define ERROR
Definition: elog.h:43
static Bitmapset * pull_exec_paramids(Expr *expr)
Definition: partprune.c:3164
Expr * arg
Definition: primnodes.h:1219
Oid constcollid
Definition: primnodes.h:212
List * elements
Definition: primnodes.h:991
static List * gen_partprune_steps_internal(GeneratePruningStepsContext *context, List *clauses)
Definition: partprune.c:845
static Node * get_leftop(const void *clause)
Definition: nodeFuncs.h:73
PartClauseMatchStatus
Definition: partprune.c:78
List * lappend(List *list, void *datum)
Definition: list.c:321
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
char op_volatile(Oid opno)
Definition: lsyscache.c:1405
uintptr_t Datum
Definition: postgres.h:367
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
NullTestType nulltesttype
Definition: primnodes.h:1220
static Node * get_rightop(const void *clause)
Definition: nodeFuncs.h:85
#define InvalidOid
Definition: postgres_ext.h:36
Oid fn_oid
Definition: fmgr.h:59
#define Assert(condition)
Definition: c.h:745
#define lfirst(lc)
Definition: pg_list.h:190
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition: lsyscache.c:794
struct FmgrInfo * partsupfunc
Definition: pathnodes.h:404
static int list_length(const List *l)
Definition: pg_list.h:169
Oid inputcollid
Definition: primnodes.h:521
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3483
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
void get_op_opfamily_properties(Oid opno, Oid opfamily, bool ordering_op, int *strategy, Oid *lefttype, Oid *righttype)
Definition: lsyscache.c:134
void * palloc(Size size)
Definition: mcxt.c:950
#define elog(elevel,...)
Definition: elog.h:214
int i
void * arg
PartitionScheme part_scheme
Definition: pathnodes.h:742
Oid opno
Definition: primnodes.h:516
Expr * expr
Definition: partprune.c:68
Oid get_negator(Oid opno)
Definition: lsyscache.c:1445
Definition: pg_list.h:50
#define ARR_ELEMTYPE(a)
Definition: array.h:280
PartClauseTarget target
Definition: partprune.c:115
bool constisnull
Definition: primnodes.h:215
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define DatumGetArrayTypeP(X)
Definition: array.h:249

◆ partkey_datum_from_expr()

static void partkey_datum_from_expr ( PartitionPruneContext context,
Expr expr,
int  stateidx,
Datum value,
bool isnull 
)
static

Definition at line 3570 of file partprune.c.

References Assert, Const::constisnull, Const::constvalue, ExecEvalExprSwitchContext(), PartitionPruneContext::exprstates, IsA, PartitionPruneContext::planstate, and PlanState::ps_ExprContext.

Referenced by perform_pruning_base_step().

3573 {
3574  if (IsA(expr, Const))
3575  {
3576  /* We can always determine the value of a constant */
3577  Const *con = (Const *) expr;
3578 
3579  *value = con->constvalue;
3580  *isnull = con->constisnull;
3581  }
3582  else
3583  {
3584  ExprState *exprstate;
3585  ExprContext *ectx;
3586 
3587  /*
3588  * We should never see a non-Const in a step unless we're running in
3589  * the executor.
3590  */
3591  Assert(context->planstate != NULL);
3592 
3593  exprstate = context->exprstates[stateidx];
3594  ectx = context->planstate->ps_ExprContext;
3595  *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
3596  }
3597 }
Datum constvalue
Definition: primnodes.h:214
#define IsA(nodeptr, _type_)
Definition: nodes.h:580
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:305
ExprContext * ps_ExprContext
Definition: execnodes.h:983
ExprState ** exprstates
Definition: partprune.h:59
static struct @143 value
#define Assert(condition)
Definition: c.h:745
bool constisnull
Definition: primnodes.h:215
PlanState * planstate
Definition: partprune.h:58

◆ perform_pruning_base_step()

static PruneStepResult * perform_pruning_base_step ( PartitionPruneContext context,
PartitionPruneStepOp opstep 
)
static

Definition at line 3234 of file partprune.c.

References Assert, bms_is_member(), PruneStepResult::bound_offsets, PartClauseInfo::cmpfn, PartitionPruneStepOp::cmpfns, elog, ERROR, PartClauseInfo::expr, PartitionPruneStepOp::exprs, fmgr_info_copy(), fmgr_info_cxt(), FmgrInfo::fn_oid, get_matching_hash_bounds(), get_matching_list_bounds(), get_matching_range_bounds(), PartClauseInfo::keyno, lfirst, lfirst_oid, list_head(), list_length(), lnext(), PartitionPruneStepOp::nullkeys, OidIsValid, PartitionPruneStepOp::opstrategy, palloc(), PARTITION_MAX_KEYS, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, partkey_datum_from_expr(), PartitionPruneContext::partnatts, PartitionPruneContext::partsupfunc, PartitionPruneContext::ppccontext, PruneCxtStateIdx, PruneStepResult::scan_default, PruneStepResult::scan_null, PartitionPruneStepOp::step, PartitionPruneStep::step_id, PartitionPruneContext::stepcmpfuncs, PartitionPruneContext::strategy, and values.

Referenced by get_matching_partitions().

3236 {
3237  ListCell *lc1,
3238  *lc2;
3239  int keyno,
3240  nvalues;
3242  FmgrInfo *partsupfunc;
3243  int stateidx;
3244 
3245  /*
3246  * There better be the same number of expressions and compare functions.
3247  */
3248  Assert(list_length(opstep->exprs) == list_length(opstep->cmpfns));
3249 
3250  nvalues = 0;
3251  lc1 = list_head(opstep->exprs);
3252  lc2 = list_head(opstep->cmpfns);
3253 
3254  /*
3255  * Generate the partition lookup key that will be used by one of the
3256  * get_matching_*_bounds functions called below.
3257  */
3258  for (keyno = 0; keyno < context->partnatts; keyno++)
3259  {
3260  /*
3261  * For hash partitioning, it is possible that values of some keys are
3262  * not provided in operator clauses, but instead the planner found
3263  * that they appeared in a IS NULL clause.
3264  */
3265  if (bms_is_member(keyno, opstep->nullkeys))
3266  continue;
3267 
3268  /*
3269  * For range partitioning, we must only perform pruning with values
3270  * for either all partition keys or a prefix thereof.
3271  */
3272  if (keyno > nvalues && context->strategy == PARTITION_STRATEGY_RANGE)
3273  break;
3274 
3275  if (lc1 != NULL)
3276  {
3277  Expr *expr;
3278  Datum datum;
3279  bool isnull;
3280  Oid cmpfn;
3281 
3282  expr = lfirst(lc1);
3283  stateidx = PruneCxtStateIdx(context->partnatts,
3284  opstep->step.step_id, keyno);
3285  partkey_datum_from_expr(context, expr, stateidx,
3286  &datum, &isnull);
3287 
3288  /*
3289  * Since we only allow strict operators in pruning steps, any
3290  * null-valued comparison value must cause the comparison to fail,
3291  * so that no partitions could match.
3292  */
3293  if (isnull)
3294  {
3295  PruneStepResult *result;
3296 
3297  result = (PruneStepResult *) palloc(sizeof(PruneStepResult));
3298  result->bound_offsets = NULL;
3299  result->scan_default = false;
3300  result->scan_null = false;
3301 
3302  return result;
3303  }
3304 
3305  /* Set up the stepcmpfuncs entry, unless we already did */
3306  cmpfn = lfirst_oid(lc2);
3307  Assert(OidIsValid(cmpfn));
3308  if (cmpfn != context->stepcmpfuncs[stateidx].fn_oid)
3309  {
3310  /*
3311  * If the needed support function is the same one cached in
3312  * the relation's partition key, copy the cached FmgrInfo.
3313  * Otherwise (i.e., when we have a cross-type comparison), an
3314  * actual lookup is required.
3315  */
3316  if (cmpfn == context->partsupfunc[keyno].fn_oid)
3317  fmgr_info_copy(&context->stepcmpfuncs[stateidx],
3318  &context->partsupfunc[keyno],
3319  context->ppccontext);
3320  else
3321  fmgr_info_cxt(cmpfn, &context->stepcmpfuncs[stateidx],
3322  context->ppccontext);
3323  }
3324 
3325  values[keyno] = datum;
3326  nvalues++;
3327 
3328  lc1 = lnext(opstep->exprs, lc1);
3329  lc2 = lnext(opstep->cmpfns, lc2);
3330  }
3331  }
3332 
3333  /*
3334  * Point partsupfunc to the entry for the 0th key of this step; the
3335  * additional support functions, if any, follow consecutively.
3336  */
3337  stateidx = PruneCxtStateIdx(context->partnatts, opstep->step.step_id, 0);
3338  partsupfunc = &context->stepcmpfuncs[stateidx];
3339 
3340  switch (context->strategy)
3341  {
3343  return get_matching_hash_bounds(context,
3344  opstep->opstrategy,
3345  values, nvalues,
3346  partsupfunc,
3347  opstep->nullkeys);
3348 
3350  return get_matching_list_bounds(context,
3351  opstep->opstrategy,
3352  values[0], nvalues,
3353  &partsupfunc[0],
3354  opstep->nullkeys);
3355 
3357  return get_matching_range_bounds(context,
3358  opstep->opstrategy,
3359  values, nvalues,
3360  partsupfunc,
3361  opstep->nullkeys);
3362 
3363  default:
3364  elog(ERROR, "unexpected partition strategy: %d",
3365  (int) context->strategy);
3366  break;
3367  }
3368 
3369  return NULL;
3370 }
static PruneStepResult * get_matching_hash_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
Definition: partprune.c:2480
Definition: fmgr.h:56
FmgrInfo * partsupfunc
Definition: partprune.h:55
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:321
FmgrInfo * stepcmpfuncs
Definition: partprune.h:56
MemoryContext ppccontext
Definition: partprune.h:57
#define PARTITION_MAX_KEYS
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:651
Bitmapset * bound_offsets
Definition: partprune.c:134
static PruneStepResult * get_matching_list_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum value, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
Definition: partprune.c:2558
#define ERROR
Definition: elog.h:43
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition: fmgr.c:612
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
PartitionPruneStep step
Definition: plannodes.h:1206
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:136
uintptr_t Datum
Definition: postgres.h:367
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:801
static void partkey_datum_from_expr(PartitionPruneContext *context, Expr *expr, int stateidx, Datum *value, bool *isnull)
Definition: partprune.c:3570
Oid fn_oid
Definition: fmgr.h:59
#define Assert(condition)
Definition: c.h:745
#define lfirst(lc)
Definition: pg_list.h:190
Bitmapset * nullkeys
Definition: plannodes.h:1211
static int list_length(const List *l)
Definition: pg_list.h:169
StrategyNumber opstrategy
Definition: plannodes.h:1208
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:802
static Datum values[MAXATTR]
Definition: bootstrap.c:165
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
void * palloc(Size size)
Definition: mcxt.c:950
#define elog(elevel,...)
Definition: elog.h:214
static PruneStepResult * get_matching_range_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
Definition: partprune.c:2769
#define PruneCxtStateIdx(partnatts, step_id, keyno)
Definition: partprune.h:68
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
#define lfirst_oid(lc)
Definition: pg_list.h:192

◆ perform_pruning_combine_step()

static PruneStepResult * perform_pruning_combine_step ( PartitionPruneContext context,
PartitionPruneStepCombine cstep,
PruneStepResult **  step_results 
)
static

Definition at line 3382 of file partprune.c.

References Assert, bms_add_members(), bms_add_range(), bms_copy(), bms_int_members(), PruneStepResult::bound_offsets, PartitionPruneContext::boundinfo, PartitionPruneStepCombine::combineOp, elog, ERROR, lfirst_int, list_length(), PartitionBoundInfoData::ndatums, palloc0(), partition_bound_accepts_nulls, partition_bound_has_default, PARTITION_STRATEGY_RANGE, PARTPRUNE_COMBINE_INTERSECT, PARTPRUNE_COMBINE_UNION, PruneStepResult::scan_default, PruneStepResult::scan_null, PartitionPruneStepCombine::source_stepids, PartitionPruneStepCombine::step, PartitionPruneStep::step_id, and PartitionPruneContext::strategy.

Referenced by get_matching_partitions().

3385 {
3386  ListCell *lc1;
3387  PruneStepResult *result = NULL;
3388  bool firststep;
3389 
3390  /*
3391  * A combine step without any source steps is an indication to not perform
3392  * any partition pruning. Return all datum indexes in that case.
3393  */
3394  result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
3395  if (list_length(cstep->source_stepids) == 0)
3396  {
3397  PartitionBoundInfo boundinfo = context->boundinfo;
3398  int rangemax;
3399 
3400  /*
3401  * Add all valid offsets into the boundinfo->indexes array. For range
3402  * partitioning, boundinfo->indexes contains (boundinfo->ndatums + 1)
3403  * valid entries; otherwise there are boundinfo->ndatums.
3404  */
3405  rangemax = context->strategy == PARTITION_STRATEGY_RANGE ?
3406  boundinfo->ndatums : boundinfo->ndatums - 1;
3407 
3408  result->bound_offsets =
3409  bms_add_range(result->bound_offsets, 0, rangemax);
3410  result->scan_default = partition_bound_has_default(boundinfo);
3411  result->scan_null = partition_bound_accepts_nulls(boundinfo);
3412  return result;
3413  }
3414 
3415  switch (cstep->combineOp)
3416  {
3418  foreach(lc1, cstep->source_stepids)
3419  {
3420  int step_id = lfirst_int(lc1);
3421  PruneStepResult *step_result;
3422 
3423  /*
3424  * step_results[step_id] must contain a valid result, which is
3425  * confirmed by the fact that cstep's step_id is greater than
3426  * step_id and the fact that results of the individual steps
3427  * are evaluated in sequence of their step_ids.
3428  */
3429  if (step_id >= cstep->step.step_id)
3430  elog(ERROR, "invalid pruning combine step argument");
3431  step_result = step_results[step_id];
3432  Assert(step_result != NULL);
3433 
3434  /* Record any additional datum indexes from this step */
3435  result->bound_offsets = bms_add_members(result->bound_offsets,
3436  step_result->bound_offsets);
3437 
3438  /* Update whether to scan null and default partitions. */
3439  if (!result->scan_null)
3440  result->scan_null = step_result->scan_null;
3441  if (!result->scan_default)
3442  result->scan_default = step_result->scan_default;
3443  }
3444  break;
3445 
3447  firststep = true;
3448  foreach(lc1, cstep->source_stepids)
3449  {
3450  int step_id = lfirst_int(lc1);
3451  PruneStepResult *step_result;
3452 
3453  if (step_id >= cstep->step.step_id)
3454  elog(ERROR, "invalid pruning combine step argument");
3455  step_result = step_results[step_id];
3456  Assert(step_result != NULL);
3457 
3458  if (firststep)
3459  {
3460  /* Copy step's result the first time. */
3461  result->bound_offsets =
3462  bms_copy(step_result->bound_offsets);
3463  result->scan_null = step_result->scan_null;
3464  result->scan_default = step_result->scan_default;
3465  firststep = false;
3466  }
3467  else
3468  {
3469  /* Record datum indexes common to both steps */
3470  result->bound_offsets =
3472  step_result->bound_offsets);
3473 
3474  /* Update whether to scan null and default partitions. */
3475  if (result->scan_null)
3476  result->scan_null = step_result->scan_null;
3477  if (result->scan_default)
3478  result->scan_default = step_result->scan_default;
3479  }
3480  }
3481  break;
3482  }
3483 
3484  return result;
3485 }
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:74
PartitionPruneCombineOp combineOp
Definition: plannodes.h:1230
Bitmapset * bound_offsets
Definition: partprune.c:134
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:834
#define ERROR
Definition: elog.h:43
#define lfirst_int(lc)
Definition: pg_list.h:191
#define partition_bound_has_default(bi)
Definition: partbounds.h:75
void * palloc0(Size size)
Definition: mcxt.c:981
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:74
#define Assert(condition)
Definition: c.h:745
static int list_length(const List *l)
Definition: pg_list.h:169
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:803
PartitionBoundInfo boundinfo
Definition: partprune.h:53
#define elog(elevel,...)
Definition: elog.h:214
PartitionPruneStep step
Definition: plannodes.h:1228
Bitmapset * bms_int_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:902
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:793

◆ prune_append_rel_partitions()

Bitmapset* prune_append_rel_partitions ( RelOptInfo rel)

Definition at line 650 of file partprune.c.

References Assert, RelOptInfo::baserestrictinfo, bms_add_range(), PartitionPruneContext::boundinfo, RelOptInfo::boundinfo, GeneratePruningStepsContext::contradictory, CurrentMemoryContext, enable_partition_pruning, PartitionPruneContext::exprstates, gen_partprune_steps(), get_matching_partitions(), list_length(), NIL, PartitionPruneContext::nparts, RelOptInfo::nparts, palloc0(), RelOptInfo::part_scheme, PartitionPruneContext::partcollation, PartitionSchemeData::partcollation, PartitionPruneContext::partnatts, PartitionSchemeData::partnatts, PartitionPruneContext::partsupfunc, PartitionSchemeData::partsupfunc, PARTTARGET_PLANNER, PartitionPruneContext::planstate, PartitionPruneContext::ppccontext, PartitionPruneContext::stepcmpfuncs, GeneratePruningStepsContext::steps, PartitionPruneContext::strategy, and PartitionSchemeData::strategy.

Referenced by expand_partitioned_rtentry().

651 {
652  List *clauses = rel->baserestrictinfo;
653  List *pruning_steps;
655  PartitionPruneContext context;
656 
657  Assert(rel->part_scheme != NULL);
658 
659  /* If there are no partitions, return the empty set */
660  if (rel->nparts == 0)
661  return NULL;
662 
663  /*
664  * If pruning is disabled or if there are no clauses to prune with, return
665  * all partitions.
666  */
667  if (!enable_partition_pruning || clauses == NIL)
668  return bms_add_range(NULL, 0, rel->nparts - 1);
669 
670  /*
671  * Process clauses to extract pruning steps that are usable at plan time.
672  * If the clauses are found to be contradictory, we can return the empty
673  * set.
674  */
676  &gcontext);
677  if (gcontext.contradictory)
678  return NULL;
679  pruning_steps = gcontext.steps;
680 
681  /* If there's nothing usable, return all partitions */
682  if (pruning_steps == NIL)
683  return bms_add_range(NULL, 0, rel->nparts - 1);
684 
685  /* Set up PartitionPruneContext */
686  context.strategy = rel->part_scheme->strategy;
687  context.partnatts = rel->part_scheme->partnatts;
688  context.nparts = rel->nparts;
689  context.boundinfo = rel->boundinfo;
690  context.partcollation = rel->part_scheme->partcollation;
691  context.partsupfunc = rel->part_scheme->partsupfunc;
692  context.stepcmpfuncs = (FmgrInfo *) palloc0(sizeof(FmgrInfo) *
693  context.partnatts *
694  list_length(pruning_steps));
696 
697  /* These are not valid when being called from the planner */
698  context.planstate = NULL;
699  context.exprstates = NULL;
700 
701  /* Actual pruning happens here. */
702  return get_matching_partitions(&context, pruning_steps);
703 }
static void gen_partprune_steps(RelOptInfo *rel, List *clauses, PartClauseTarget target, GeneratePruningStepsContext *context)
Definition: partprune.c:614
#define NIL
Definition: pg_list.h:65
Definition: fmgr.h:56
FmgrInfo * partsupfunc
Definition: partprune.h:55
FmgrInfo * stepcmpfuncs
Definition: partprune.h:56
MemoryContext ppccontext
Definition: partprune.h:57
List * baserestrictinfo
Definition: pathnodes.h:727
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:834
ExprState ** exprstates
Definition: partprune.h:59
int nparts
Definition: pathnodes.h:743
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
void * palloc0(Size size)
Definition: mcxt.c:981
struct PartitionBoundInfoData * boundinfo
Definition: pathnodes.h:746
bool enable_partition_pruning
Definition: costsize.c:142
#define Assert(condition)
Definition: c.h:745
struct FmgrInfo * partsupfunc
Definition: pathnodes.h:404
static int list_length(const List *l)
Definition: pg_list.h:169
PartitionBoundInfo boundinfo
Definition: partprune.h:53
Bitmapset * get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
Definition: partprune.c:716
PartitionScheme part_scheme
Definition: pathnodes.h:742
Definition: pg_list.h:50
PlanState * planstate
Definition: partprune.h:58

◆ pull_exec_paramids()

static Bitmapset * pull_exec_paramids ( Expr expr)
static

Definition at line 3164 of file partprune.c.

References PartClauseInfo::expr, and pull_exec_paramids_walker().

Referenced by get_partkey_exec_paramids(), and match_clause_to_partition_key().

3165 {
3166  Bitmapset *result = NULL;
3167 
3168  (void) pull_exec_paramids_walker((Node *) expr, &result);
3169 
3170  return result;
3171 }
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context)
Definition: partprune.c:3174
Definition: nodes.h:529

◆ pull_exec_paramids_walker()

static bool pull_exec_paramids_walker ( Node node,
Bitmapset **  context 
)
static

Definition at line 3174 of file partprune.c.

References bms_add_member(), expression_tree_walker(), IsA, PARAM_EXEC, Param::paramid, and Param::paramkind.

Referenced by pull_exec_paramids().

3175 {
3176  if (node == NULL)
3177  return false;
3178  if (IsA(node, Param))
3179  {
3180  Param *param = (Param *) node;
3181 
3182  if (param->paramkind == PARAM_EXEC)
3183  *context = bms_add_member(*context, param->paramid);
3184  return false;
3185  }
3187  (void *) context);
3188 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:580
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context)
Definition: partprune.c:3174
ParamKind paramkind
Definition: primnodes.h:262
int paramid
Definition: primnodes.h:263
bool expression_tree_walker(Node *node, bool(*walker)(), void *context)
Definition: nodeFuncs.c:1888
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:736