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, Relids partrelids, 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 1662 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 619 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().

621 {
622  /* Initialize all output values to zero/false/NULL */
623  memset(context, 0, sizeof(GeneratePruningStepsContext));
624  context->rel = rel;
625  context->target = target;
626 
627  /*
628  * If this partitioned table is in turn a partition, and it shares any
629  * partition keys with its parent, then it's possible that the hierarchy
630  * allows the parent a narrower range of values than some of its
631  * partitions (particularly the default one). This is normally not
632  * useful, but it can be to prune the default partition.
633  */
635  {
636  /* Make a copy to avoid modifying the passed-in List */
637  clauses = list_concat_copy(clauses, rel->partition_qual);
638  }
639 
640  /* Down into the rabbit-hole. */
641  (void) gen_partprune_steps_internal(context, clauses);
642 }
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:850
struct PartitionBoundInfoData * boundinfo
Definition: pathnodes.h:747
List * partition_qual
Definition: pathnodes.h:750
PartClauseTarget target
Definition: partprune.c:115

◆ gen_partprune_steps_internal()

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

Definition at line 850 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().

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

◆ gen_prune_step_combine()

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

Definition at line 1241 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().

1244 {
1246 
1247  cstep->step.step_id = context->next_step_id++;
1248  cstep->combineOp = combineOp;
1249  cstep->source_stepids = source_stepids;
1250 
1251  context->steps = lappend(context->steps, cstep);
1252 
1253  return (PartitionPruneStep *) cstep;
1254 }
PartitionPruneCombineOp combineOp
Definition: plannodes.h:1230
List * lappend(List *list, void *datum)
Definition: list.c:321
#define makeNode(_type_)
Definition: nodes.h:575
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 1208 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().

1212 {
1214 
1215  opstep->step.step_id = context->next_step_id++;
1216 
1217  /*
1218  * For clauses that contain an <> operator, set opstrategy to
1219  * InvalidStrategy to signal get_matching_list_bounds to do the right
1220  * thing.
1221  */
1222  opstep->opstrategy = op_is_ne ? InvalidStrategy : opstrategy;
1223  Assert(list_length(exprs) == list_length(cmpfns));
1224  opstep->exprs = exprs;
1225  opstep->cmpfns = cmpfns;
1226  opstep->nullkeys = nullkeys;
1227 
1228  context->steps = lappend(context->steps, opstep);
1229 
1230  return (PartitionPruneStep *) opstep;
1231 }
#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:575
#define Assert(condition)
Definition: c.h:800
Bitmapset * nullkeys
Definition: plannodes.h:1211
static int list_length(const List *l)
Definition: pg_list.h:149
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 1267 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().

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

◆ 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 2485 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().

2488 {
2489  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2490  PartitionBoundInfo boundinfo = context->boundinfo;
2491  int *partindices = boundinfo->indexes;
2492  int partnatts = context->partnatts;
2493  bool isnull[PARTITION_MAX_KEYS];
2494  int i;
2495  uint64 rowHash;
2496  int greatest_modulus;
2497  Oid *partcollation = context->partcollation;
2498 
2500 
2501  /*
2502  * For hash partitioning we can only perform pruning based on equality
2503  * clauses to the partition key or IS NULL clauses. We also can only
2504  * prune if we got values for all keys.
2505  */
2506  if (nvalues + bms_num_members(nullkeys) == partnatts)
2507  {
2508  /*
2509  * If there are any values, they must have come from clauses
2510  * containing an equality operator compatible with hash partitioning.
2511  */
2512  Assert(opstrategy == HTEqualStrategyNumber || nvalues == 0);
2513 
2514  for (i = 0; i < partnatts; i++)
2515  isnull[i] = bms_is_member(i, nullkeys);
2516 
2517  greatest_modulus = get_hash_partition_greatest_modulus(boundinfo);
2518  rowHash = compute_partition_hash_value(partnatts, partsupfunc, partcollation,
2519  values, isnull);
2520 
2521  if (partindices[rowHash % greatest_modulus] >= 0)
2522  result->bound_offsets =
2523  bms_make_singleton(rowHash % greatest_modulus);
2524  }
2525  else
2526  {
2527  /* Getting here means at least one hash partition exists. */
2528  Assert(boundinfo->ndatums > 0);
2529  result->bound_offsets = bms_add_range(NULL, 0,
2530  boundinfo->ndatums - 1);
2531  }
2532 
2533  /*
2534  * There is neither a special hash null partition or the default hash
2535  * partition.
2536  */
2537  result->scan_null = result->scan_default = false;
2538 
2539  return result;
2540 }
#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:3290
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:802
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:800
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 2563 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().

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

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

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

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

Referenced by make_partitionedrel_pruneinfo().

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

◆ 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 2275 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().

2283 {
2284  Assert(step_nullkeys == NULL ||
2286 
2287  /* Quick exit if there are no values to prefix with. */
2288  if (list_length(prefix) == 0)
2289  {
2290  PartitionPruneStep *step;
2291 
2292  step = gen_prune_step_op(context,
2293  step_opstrategy,
2294  step_op_is_ne,
2295  list_make1(step_lastexpr),
2296  list_make1_oid(step_lastcmpfn),
2297  step_nullkeys);
2298  return list_make1(step);
2299  }
2300 
2301  /* Recurse to generate steps for various combinations. */
2302  return get_steps_using_prefix_recurse(context,
2303  step_opstrategy,
2304  step_op_is_ne,
2305  step_lastexpr,
2306  step_lastcmpfn,
2307  step_lastkeyno,
2308  step_nullkeys,
2309  prefix,
2310  list_head(prefix),
2311  NIL, NIL);
2312 }
#define NIL
Definition: pg_list.h:65
#define list_make1(x1)
Definition: pg_list.h:206
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
#define list_make1_oid(x1)
Definition: pg_list.h:228
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:802
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:2327
#define Assert(condition)
Definition: c.h:800
static int list_length(const List *l)
Definition: pg_list.h:149
PartitionScheme part_scheme
Definition: pathnodes.h:743
static PartitionPruneStep * gen_prune_step_op(GeneratePruningStepsContext *context, StrategyNumber opstrategy, bool op_is_ne, List *exprs, List *cmpfns, Bitmapset *nullkeys)
Definition: partprune.c:1208

◆ 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 2327 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().

2338 {
2339  List *result = NIL;
2340  ListCell *lc;
2341  int cur_keyno;
2342 
2343  /* Actually, recursion would be limited by PARTITION_MAX_KEYS. */
2345 
2346  /* Check if we need to recurse. */
2347  Assert(start != NULL);
2348  cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno;
2349  if (cur_keyno < step_lastkeyno - 1)
2350  {
2351  PartClauseInfo *pc;
2352  ListCell *next_start;
2353 
2354  /*
2355  * For each clause with cur_keyno, add its expr and cmpfn to
2356  * step_exprs and step_cmpfns, respectively, and recurse after setting
2357  * next_start to the ListCell of the first clause for the next
2358  * partition key.
2359  */
2360  for_each_cell(lc, prefix, start)
2361  {
2362  pc = lfirst(lc);
2363 
2364  if (pc->keyno > cur_keyno)
2365  break;
2366  }
2367  next_start = lc;
2368 
2369  for_each_cell(lc, prefix, start)
2370  {
2371  List *moresteps;
2372  List *step_exprs1,
2373  *step_cmpfns1;
2374 
2375  pc = lfirst(lc);
2376  if (pc->keyno == cur_keyno)
2377  {
2378  /* Leave the original step_exprs unmodified. */
2379  step_exprs1 = list_copy(step_exprs);
2380  step_exprs1 = lappend(step_exprs1, pc->expr);
2381 
2382  /* Leave the original step_cmpfns unmodified. */
2383  step_cmpfns1 = list_copy(step_cmpfns);
2384  step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2385  }
2386  else
2387  {
2388  Assert(pc->keyno > cur_keyno);
2389  break;
2390  }
2391 
2392  moresteps = get_steps_using_prefix_recurse(context,
2393  step_opstrategy,
2394  step_op_is_ne,
2395  step_lastexpr,
2396  step_lastcmpfn,
2397  step_lastkeyno,
2398  step_nullkeys,
2399  prefix,
2400  next_start,
2401  step_exprs1,
2402  step_cmpfns1);
2403  result = list_concat(result, moresteps);
2404 
2405  list_free(step_exprs1);
2406  list_free(step_cmpfns1);
2407  }
2408  }
2409  else
2410  {
2411  /*
2412  * End the current recursion cycle and start generating steps, one for
2413  * each clause with cur_keyno, which is all clauses from here onward
2414  * till the end of the list. Note that for hash partitioning,
2415  * step_nullkeys is allowed to be non-empty, in which case step_exprs
2416  * would only contain expressions for the earlier partition keys that
2417  * are not specified in step_nullkeys.
2418  */
2419  Assert(list_length(step_exprs) == cur_keyno ||
2420  !bms_is_empty(step_nullkeys));
2421  /*
2422  * Note also that for hash partitioning, each partition key should
2423  * have either equality clauses or an IS NULL clause, so if a
2424  * partition key doesn't have an expression, it would be specified
2425  * in step_nullkeys.
2426  */
2427  Assert(context->rel->part_scheme->strategy
2429  list_length(step_exprs) + 2 + bms_num_members(step_nullkeys) ==
2430  context->rel->part_scheme->partnatts);
2431  for_each_cell(lc, prefix, start)
2432  {
2433  PartClauseInfo *pc = lfirst(lc);
2434  PartitionPruneStep *step;
2435  List *step_exprs1,
2436  *step_cmpfns1;
2437 
2438  Assert(pc->keyno == cur_keyno);
2439 
2440  /* Leave the original step_exprs unmodified. */
2441  step_exprs1 = list_copy(step_exprs);
2442  step_exprs1 = lappend(step_exprs1, pc->expr);
2443  step_exprs1 = lappend(step_exprs1, step_lastexpr);
2444 
2445  /* Leave the original step_cmpfns unmodified. */
2446  step_cmpfns1 = list_copy(step_cmpfns);
2447  step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2448  step_cmpfns1 = lappend_oid(step_cmpfns1, step_lastcmpfn);
2449 
2450  step = gen_prune_step_op(context,
2451  step_opstrategy, step_op_is_ne,
2452  step_exprs1, step_cmpfns1,
2453  step_nullkeys);
2454  result = lappend(result, step);
2455  }
2456  }
2457 
2458  return result;
2459 }
#define NIL
Definition: pg_list.h:65
#define for_each_cell(cell, lst, initcell)
Definition: pg_list.h:405
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:3317
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:802
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:2327
#define Assert(condition)
Definition: c.h:800
#define lfirst(lc)
Definition: pg_list.h:169
static int list_length(const List *l)
Definition: pg_list.h:149
void list_free(List *list)
Definition: list.c:1376
PartitionScheme part_scheme
Definition: pathnodes.h:743
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:1208
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  Relids partrelids = (Relids) lfirst(lc);
271  List *pinfolist;
272  Bitmapset *matchedsubplans = NULL;
273 
274  pinfolist = make_partitionedrel_pruneinfo(root, parentrel,
275  relid_subplan_map,
276  partrelids, 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
static List * make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, int *relid_subplan_map, Relids partrelids, List *prunequal, Bitmapset **matchedsubplans)
Definition: partprune.c:343
#define IS_SIMPLE_REL(rel)
Definition: pathnodes.h:639
void pfree(void *pointer)
Definition: mcxt.c:1057
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:1148
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:646
int simple_rel_array_size
Definition: pathnodes.h:198
Index relid
Definition: pathnodes.h:694
List * lappend(List *list, void *datum)
Definition: list.c:321
void * palloc0(Size size)
Definition: mcxt.c:981
#define makeNode(_type_)
Definition: nodes.h:575
Bitmapset * other_subplans
Definition: plannodes.h:1122
#define Assert(condition)
Definition: c.h:800
#define lfirst(lc)
Definition: pg_list.h:169
static int list_length(const List *l)
Definition: pg_list.h:149
Bitmapset * Relids
Definition: pathnodes.h:28
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,
Relids  partrelids,
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(), bms_next_member(), 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, 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 rti;
355  int i;
356 
357  /*
358  * Examine each partitioned rel, constructing a temporary array to map
359  * from planner relids to index of the partitioned rel, and building a
360  * PartitionedRelPruneInfo for each partitioned rel.
361  *
362  * In this phase we discover whether runtime pruning is needed at all; if
363  * not, we can avoid doing further work.
364  */
365  relid_subpart_map = palloc0(sizeof(int) * root->simple_rel_array_size);
366 
367  i = 1;
368  rti = -1;
369  while ((rti = bms_next_member(partrelids, rti)) > 0)
370  {
371  RelOptInfo *subpart = find_base_rel(root, rti);
373  List *partprunequal;
374  List *initial_pruning_steps;
375  List *exec_pruning_steps;
376  Bitmapset *execparamids;
378 
379  /*
380  * Fill the mapping array.
381  *
382  * relid_subpart_map maps relid of a non-leaf partition to the index
383  * in the returned PartitionedRelPruneInfo list of the info for that
384  * partition. We use 1-based indexes here, so that zero can represent
385  * an un-filled array entry.
386  */
387  Assert(rti < root->simple_rel_array_size);
388  relid_subpart_map[rti] = i++;
389 
390  /*
391  * Translate pruning qual, if necessary, for this partition.
392  *
393  * The first item in the list is the target partitioned relation.
394  */
395  if (!targetpart)
396  {
397  targetpart = subpart;
398 
399  /*
400  * The prunequal is presented to us as a qual for 'parentrel'.
401  * Frequently this rel is the same as targetpart, so we can skip
402  * an adjust_appendrel_attrs step. But it might not be, and then
403  * we have to translate. We update the prunequal parameter here,
404  * because in later iterations of the loop for child partitions,
405  * we want to translate from parent to child variables.
406  */
407  if (!bms_equal(parentrel->relids, subpart->relids))
408  {
409  int nappinfos;
410  AppendRelInfo **appinfos = find_appinfos_by_relids(root,
411  subpart->relids,
412  &nappinfos);
413 
414  prunequal = (List *) adjust_appendrel_attrs(root, (Node *)
415  prunequal,
416  nappinfos,
417  appinfos);
418 
419  pfree(appinfos);
420  }
421 
422  partprunequal = prunequal;
423  }
424  else
425  {
426  /*
427  * For sub-partitioned tables the columns may not be in the same
428  * order as the parent, so we must translate the prunequal to make
429  * it compatible with this relation.
430  */
431  partprunequal = (List *)
433  (Node *) prunequal,
434  subpart->relids,
435  targetpart->relids);
436  }
437 
438  /*
439  * Convert pruning qual to pruning steps. We may need to do this
440  * twice, once to obtain executor startup pruning steps, and once for
441  * executor per-scan pruning steps. This first pass creates startup
442  * pruning steps and detects whether there's any possibly-useful quals
443  * that would require per-scan pruning.
444  */
445  gen_partprune_steps(subpart, partprunequal, PARTTARGET_INITIAL,
446  &context);
447 
448  if (context.contradictory)
449  {
450  /*
451  * This shouldn't happen as the planner should have detected this
452  * earlier. However, we do use additional quals from parameterized
453  * paths here. These do only compare Params to the partition key,
454  * so this shouldn't cause the discovery of any new qual
455  * contradictions that were not previously discovered as the Param
456  * values are unknown during planning. Anyway, we'd better do
457  * something sane here, so let's just disable run-time pruning.
458  */
459  return NIL;
460  }
461 
462  /*
463  * If no mutable operators or expressions appear in usable pruning
464  * clauses, then there's no point in running startup pruning, because
465  * plan-time pruning should have pruned everything prunable.
466  */
467  if (context.has_mutable_op || context.has_mutable_arg)
468  initial_pruning_steps = context.steps;
469  else
470  initial_pruning_steps = NIL;
471 
472  /*
473  * If no exec Params appear in potentially-usable pruning clauses,
474  * then there's no point in even thinking about per-scan pruning.
475  */
476  if (context.has_exec_param)
477  {
478  /* ... OK, we'd better think about it */
479  gen_partprune_steps(subpart, partprunequal, PARTTARGET_EXEC,
480  &context);
481 
482  if (context.contradictory)
483  {
484  /* As above, skip run-time pruning if anything fishy happens */
485  return NIL;
486  }
487 
488  exec_pruning_steps = context.steps;
489 
490  /*
491  * Detect which exec Params actually got used; the fact that some
492  * were in available clauses doesn't mean we actually used them.
493  * Skip per-scan pruning if there are none.
494  */
495  execparamids = get_partkey_exec_paramids(exec_pruning_steps);
496 
497  if (bms_is_empty(execparamids))
498  exec_pruning_steps = NIL;
499  }
500  else
501  {
502  /* No exec Params anywhere, so forget about scan-time pruning */
503  exec_pruning_steps = NIL;
504  execparamids = NULL;
505  }
506 
507  if (initial_pruning_steps || exec_pruning_steps)
508  doruntimeprune = true;
509 
510  /* Begin constructing the PartitionedRelPruneInfo for this rel */
512  pinfo->rtindex = rti;
513  pinfo->initial_pruning_steps = initial_pruning_steps;
514  pinfo->exec_pruning_steps = exec_pruning_steps;
515  pinfo->execparamids = execparamids;
516  /* Remaining fields will be filled in the next loop */
517 
518  pinfolist = lappend(pinfolist, pinfo);
519  }
520 
521  if (!doruntimeprune)
522  {
523  /* No run-time pruning required. */
524  pfree(relid_subpart_map);
525  return NIL;
526  }
527 
528  /*
529  * Run-time pruning will be required, so initialize other information.
530  * That includes two maps -- one needed to convert partition indexes of
531  * leaf partitions to the indexes of their subplans in the subplan list,
532  * another needed to convert partition indexes of sub-partitioned
533  * partitions to the indexes of their PartitionedRelPruneInfo in the
534  * PartitionedRelPruneInfo list.
535  */
536  foreach(lc, pinfolist)
537  {
538  PartitionedRelPruneInfo *pinfo = lfirst(lc);
539  RelOptInfo *subpart = find_base_rel(root, pinfo->rtindex);
540  Bitmapset *present_parts;
541  int nparts = subpart->nparts;
542  int *subplan_map;
543  int *subpart_map;
544  Oid *relid_map;
545 
546  /*
547  * Construct the subplan and subpart maps for this partitioning level.
548  * Here we convert to zero-based indexes, with -1 for empty entries.
549  * Also construct a Bitmapset of all partitions that are present (that
550  * is, not pruned already).
551  */
552  subplan_map = (int *) palloc(nparts * sizeof(int));
553  memset(subplan_map, -1, nparts * sizeof(int));
554  subpart_map = (int *) palloc(nparts * sizeof(int));
555  memset(subpart_map, -1, nparts * sizeof(int));
556  relid_map = (Oid *) palloc0(nparts * sizeof(Oid));
557  present_parts = NULL;
558 
559  for (i = 0; i < nparts; i++)
560  {
561  RelOptInfo *partrel = subpart->part_rels[i];
562  int subplanidx;
563  int subpartidx;
564 
565  /* Skip processing pruned partitions. */
566  if (partrel == NULL)
567  continue;
568 
569  subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;
570  subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
571  relid_map[i] = planner_rt_fetch(partrel->relid, root)->relid;
572  if (subplanidx >= 0)
573  {
574  present_parts = bms_add_member(present_parts, i);
575 
576  /* Record finding this subplan */
577  subplansfound = bms_add_member(subplansfound, subplanidx);
578  }
579  else if (subpartidx >= 0)
580  present_parts = bms_add_member(present_parts, i);
581  }
582 
583  /*
584  * Ensure there were no stray PartitionedRelPruneInfo generated for
585  * partitioned tables that we have no sub-paths or
586  * sub-PartitionedRelPruneInfo for.
587  */
588  Assert(!bms_is_empty(present_parts));
589 
590  /* Record the maps and other information. */
591  pinfo->present_parts = present_parts;
592  pinfo->nparts = nparts;
593  pinfo->subplan_map = subplan_map;
594  pinfo->subpart_map = subpart_map;
595  pinfo->relid_map = relid_map;
596  }
597 
598  pfree(relid_subpart_map);
599 
600  *matchedsubplans = subplansfound;
601 
602  return pinfolist;
603 }
static void gen_partprune_steps(RelOptInfo *rel, List *clauses, PartClauseTarget target, GeneratePruningStepsContext *context)
Definition: partprune.c:619
#define NIL
Definition: pg_list.h:65
Bitmapset * execparamids
Definition: plannodes.h:1161
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1043
Definition: nodes.h:527
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 planner_rt_fetch(rti, root)
Definition: pathnodes.h:377
static Bitmapset * get_partkey_exec_paramids(List *steps)
Definition: partprune.c:3203
int nparts
Definition: pathnodes.h:744
Bitmapset * present_parts
Definition: plannodes.h:1146
Relids relids
Definition: pathnodes.h:666
int simple_rel_array_size
Definition: pathnodes.h:198
Index relid
Definition: pathnodes.h:694
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
#define makeNode(_type_)
Definition: nodes.h:575
#define Assert(condition)
Definition: c.h:800
#define lfirst(lc)
Definition: pg_list.h:169
struct RelOptInfo ** part_rels
Definition: pathnodes.h:751
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:373
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 3504 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().

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

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

3578 {
3579  if (IsA(expr, Const))
3580  {
3581  /* We can always determine the value of a constant */
3582  Const *con = (Const *) expr;
3583 
3584  *value = con->constvalue;
3585  *isnull = con->constisnull;
3586  }
3587  else
3588  {
3589  ExprState *exprstate;
3590  ExprContext *ectx;
3591 
3592  /*
3593  * We should never see a non-Const in a step unless we're running in
3594  * the executor.
3595  */
3596  Assert(context->planstate != NULL);
3597 
3598  exprstate = context->exprstates[stateidx];
3599  ectx = context->planstate->ps_ExprContext;
3600  *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
3601  }
3602 }
Datum constvalue
Definition: primnodes.h:214
#define IsA(nodeptr, _type_)
Definition: nodes.h:578
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:307
ExprContext * ps_ExprContext
Definition: execnodes.h:967
ExprState ** exprstates
Definition: partprune.h:59
static struct @143 value
#define Assert(condition)
Definition: c.h:800
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 3239 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().

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

◆ perform_pruning_combine_step()

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

Definition at line 3387 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().

3390 {
3391  ListCell *lc1;
3392  PruneStepResult *result = NULL;
3393  bool firststep;
3394 
3395  /*
3396  * A combine step without any source steps is an indication to not perform
3397  * any partition pruning. Return all datum indexes in that case.
3398  */
3399  result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
3400  if (list_length(cstep->source_stepids) == 0)
3401  {
3402  PartitionBoundInfo boundinfo = context->boundinfo;
3403  int rangemax;
3404 
3405  /*
3406  * Add all valid offsets into the boundinfo->indexes array. For range
3407  * partitioning, boundinfo->indexes contains (boundinfo->ndatums + 1)
3408  * valid entries; otherwise there are boundinfo->ndatums.
3409  */
3410  rangemax = context->strategy == PARTITION_STRATEGY_RANGE ?
3411  boundinfo->ndatums : boundinfo->ndatums - 1;
3412 
3413  result->bound_offsets =
3414  bms_add_range(result->bound_offsets, 0, rangemax);
3415  result->scan_default = partition_bound_has_default(boundinfo);
3416  result->scan_null = partition_bound_accepts_nulls(boundinfo);
3417  return result;
3418  }
3419 
3420  switch (cstep->combineOp)
3421  {
3423  foreach(lc1, cstep->source_stepids)
3424  {
3425  int step_id = lfirst_int(lc1);
3426  PruneStepResult *step_result;
3427 
3428  /*
3429  * step_results[step_id] must contain a valid result, which is
3430  * confirmed by the fact that cstep's step_id is greater than
3431  * step_id and the fact that results of the individual steps
3432  * are evaluated in sequence of their step_ids.
3433  */
3434  if (step_id >= cstep->step.step_id)
3435  elog(ERROR, "invalid pruning combine step argument");
3436  step_result = step_results[step_id];
3437  Assert(step_result != NULL);
3438 
3439  /* Record any additional datum indexes from this step */
3440  result->bound_offsets = bms_add_members(result->bound_offsets,
3441  step_result->bound_offsets);
3442 
3443  /* Update whether to scan null and default partitions. */
3444  if (!result->scan_null)
3445  result->scan_null = step_result->scan_null;
3446  if (!result->scan_default)
3447  result->scan_default = step_result->scan_default;
3448  }
3449  break;
3450 
3452  firststep = true;
3453  foreach(lc1, cstep->source_stepids)
3454  {
3455  int step_id = lfirst_int(lc1);
3456  PruneStepResult *step_result;
3457 
3458  if (step_id >= cstep->step.step_id)
3459  elog(ERROR, "invalid pruning combine step argument");
3460  step_result = step_results[step_id];
3461  Assert(step_result != NULL);
3462 
3463  if (firststep)
3464  {
3465  /* Copy step's result the first time. */
3466  result->bound_offsets =
3467  bms_copy(step_result->bound_offsets);
3468  result->scan_null = step_result->scan_null;
3469  result->scan_default = step_result->scan_default;
3470  firststep = false;
3471  }
3472  else
3473  {
3474  /* Record datum indexes common to both steps */
3475  result->bound_offsets =
3477  step_result->bound_offsets);
3478 
3479  /* Update whether to scan null and default partitions. */
3480  if (result->scan_null)
3481  result->scan_null = step_result->scan_null;
3482  if (result->scan_default)
3483  result->scan_default = step_result->scan_default;
3484  }
3485  }
3486  break;
3487  }
3488 
3489  return result;
3490 }
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:170
#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:800
static int list_length(const List *l)
Definition: pg_list.h:149
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:804
PartitionBoundInfo boundinfo
Definition: partprune.h:53
#define elog(elevel,...)
Definition: elog.h:228
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 655 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().

656 {
657  List *clauses = rel->baserestrictinfo;
658  List *pruning_steps;
660  PartitionPruneContext context;
661 
662  Assert(rel->part_scheme != NULL);
663 
664  /* If there are no partitions, return the empty set */
665  if (rel->nparts == 0)
666  return NULL;
667 
668  /*
669  * If pruning is disabled or if there are no clauses to prune with, return
670  * all partitions.
671  */
672  if (!enable_partition_pruning || clauses == NIL)
673  return bms_add_range(NULL, 0, rel->nparts - 1);
674 
675  /*
676  * Process clauses to extract pruning steps that are usable at plan time.
677  * If the clauses are found to be contradictory, we can return the empty
678  * set.
679  */
681  &gcontext);
682  if (gcontext.contradictory)
683  return NULL;
684  pruning_steps = gcontext.steps;
685 
686  /* If there's nothing usable, return all partitions */
687  if (pruning_steps == NIL)
688  return bms_add_range(NULL, 0, rel->nparts - 1);
689 
690  /* Set up PartitionPruneContext */
691  context.strategy = rel->part_scheme->strategy;
692  context.partnatts = rel->part_scheme->partnatts;
693  context.nparts = rel->nparts;
694  context.boundinfo = rel->boundinfo;
695  context.partcollation = rel->part_scheme->partcollation;
696  context.partsupfunc = rel->part_scheme->partsupfunc;
697  context.stepcmpfuncs = (FmgrInfo *) palloc0(sizeof(FmgrInfo) *
698  context.partnatts *
699  list_length(pruning_steps));
701 
702  /* These are not valid when being called from the planner */
703  context.planstate = NULL;
704  context.exprstates = NULL;
705 
706  /* Actual pruning happens here. */
707  return get_matching_partitions(&context, pruning_steps);
708 }
static void gen_partprune_steps(RelOptInfo *rel, List *clauses, PartClauseTarget target, GeneratePruningStepsContext *context)
Definition: partprune.c:619
#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:728
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:744
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
void * palloc0(Size size)
Definition: mcxt.c:981
struct PartitionBoundInfoData * boundinfo
Definition: pathnodes.h:747
bool enable_partition_pruning
Definition: costsize.c:149
#define Assert(condition)
Definition: c.h:800
struct FmgrInfo * partsupfunc
Definition: pathnodes.h:408
static int list_length(const List *l)
Definition: pg_list.h:149
PartitionBoundInfo boundinfo
Definition: partprune.h:53
Bitmapset * get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
Definition: partprune.c:721
PartitionScheme part_scheme
Definition: pathnodes.h:743
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 3169 of file partprune.c.

References PartClauseInfo::expr, and pull_exec_paramids_walker().

Referenced by get_partkey_exec_paramids(), and match_clause_to_partition_key().

3170 {
3171  Bitmapset *result = NULL;
3172 
3173  (void) pull_exec_paramids_walker((Node *) expr, &result);
3174 
3175  return result;
3176 }
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context)
Definition: partprune.c:3179
Definition: nodes.h:527

◆ pull_exec_paramids_walker()

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

Definition at line 3179 of file partprune.c.

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

Referenced by pull_exec_paramids().

3180 {
3181  if (node == NULL)
3182  return false;
3183  if (IsA(node, Param))
3184  {
3185  Param *param = (Param *) node;
3186 
3187  if (param->paramkind == PARAM_EXEC)
3188  *context = bms_add_member(*context, param->paramid);
3189  return false;
3190  }
3192  (void *) context);
3193 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:578
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context)
Definition: partprune.c:3179
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