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 Listadd_part_relids (List *allpartrelids, Bitmapset *partrelids)
 
static Listmake_partitionedrel_pruneinfo (PlannerInfo *root, RelOptInfo *parentrel, List *prunequal, Bitmapset *partrelids, int *relid_subplan_map, 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 Listgen_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 *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 1759 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

◆ add_part_relids()

static List * add_part_relids ( List allpartrelids,
Bitmapset partrelids 
)
static

Definition at line 394 of file partprune.c.

References Assert, bms_add_members(), bms_next_member(), lappend(), and lfirst.

Referenced by make_partition_pruneinfo().

395 {
396  Index targetpart;
397  ListCell *lc;
398 
399  /* We can easily get the lowest set bit this way: */
400  targetpart = bms_next_member(partrelids, -1);
401  Assert(targetpart > 0);
402 
403  /* Look for a matching topmost parent */
404  foreach(lc, allpartrelids)
405  {
406  Bitmapset *currpartrelids = (Bitmapset *) lfirst(lc);
407  Index currtarget = bms_next_member(currpartrelids, -1);
408 
409  if (targetpart == currtarget)
410  {
411  /* Found a match, so add any new RT indexes to this hierarchy */
412  currpartrelids = bms_add_members(currpartrelids, partrelids);
413  lfirst(lc) = currpartrelids;
414  return allpartrelids;
415  }
416  }
417  /* No match, so add the new partition hierarchy to the list */
418  return lappend(allpartrelids, partrelids);
419 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1043
List * lappend(List *list, void *datum)
Definition: list.c:336
unsigned int Index
Definition: c.h:549
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:793

◆ gen_partprune_steps()

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

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

718 {
719  /* Initialize all output values to zero/false/NULL */
720  memset(context, 0, sizeof(GeneratePruningStepsContext));
721  context->rel = rel;
722  context->target = target;
723 
724  /*
725  * If this partitioned table is in turn a partition, and it shares any
726  * partition keys with its parent, then it's possible that the hierarchy
727  * allows the parent a narrower range of values than some of its
728  * partitions (particularly the default one). This is normally not
729  * useful, but it can be to prune the default partition.
730  */
732  {
733  /* Make a copy to avoid modifying the passed-in List */
734  clauses = list_concat_copy(clauses, rel->partition_qual);
735  }
736 
737  /* Down into the rabbit-hole. */
738  (void) gen_partprune_steps_internal(context, clauses);
739 }
List * list_concat_copy(const List *list1, const List *list2)
Definition: list.c:567
#define partition_bound_has_default(bi)
Definition: partbounds.h:99
static List * gen_partprune_steps_internal(GeneratePruningStepsContext *context, List *clauses)
Definition: partprune.c:962
struct PartitionBoundInfoData * boundinfo
Definition: pathnodes.h:764
List * partition_qual
Definition: pathnodes.h:767
PartClauseTarget target
Definition: partprune.c:115

◆ gen_partprune_steps_internal()

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

Definition at line 962 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, llast, 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().

964 {
965  PartitionScheme part_scheme = context->rel->part_scheme;
966  List *keyclauses[PARTITION_MAX_KEYS];
967  Bitmapset *nullkeys = NULL,
968  *notnullkeys = NULL;
969  bool generate_opsteps = false;
970  List *result = NIL;
971  ListCell *lc;
972 
973  /*
974  * If this partitioned relation has a default partition and is itself a
975  * partition (as evidenced by partition_qual being not NIL), we first
976  * check if the clauses contradict the partition constraint. If they do,
977  * there's no need to generate any steps as it'd already be proven that no
978  * partitions need to be scanned.
979  *
980  * This is a measure of last resort only to be used because the default
981  * partition cannot be pruned using the steps generated from clauses that
982  * contradict the parent's partition constraint; regular pruning, which is
983  * cheaper, is sufficient when no default partition exists.
984  */
985  if (partition_bound_has_default(context->rel->boundinfo) &&
986  predicate_refuted_by(context->rel->partition_qual, clauses, false))
987  {
988  context->contradictory = true;
989  return NIL;
990  }
991 
992  memset(keyclauses, 0, sizeof(keyclauses));
993  foreach(lc, clauses)
994  {
995  Expr *clause = (Expr *) lfirst(lc);
996  int i;
997 
998  /* Look through RestrictInfo, if any */
999  if (IsA(clause, RestrictInfo))
1000  clause = ((RestrictInfo *) clause)->clause;
1001 
1002  /* Constant-false-or-null is contradictory */
1003  if (IsA(clause, Const) &&
1004  (((Const *) clause)->constisnull ||
1005  !DatumGetBool(((Const *) clause)->constvalue)))
1006  {
1007  context->contradictory = true;
1008  return NIL;
1009  }
1010 
1011  /* Get the BoolExpr's out of the way. */
1012  if (IsA(clause, BoolExpr))
1013  {
1014  /*
1015  * Generate steps for arguments.
1016  *
1017  * While steps generated for the arguments themselves will be
1018  * added to context->steps during recursion and will be evaluated
1019  * independently, collect their step IDs to be stored in the
1020  * combine step we'll be creating.
1021  */
1022  if (is_orclause(clause))
1023  {
1024  List *arg_stepids = NIL;
1025  bool all_args_contradictory = true;
1026  ListCell *lc1;
1027 
1028  /*
1029  * We can share the outer context area with the recursive
1030  * call, but contradictory had better not be true yet.
1031  */
1032  Assert(!context->contradictory);
1033 
1034  /*
1035  * Get pruning step for each arg. If we get contradictory for
1036  * all args, it means the OR expression is false as a whole.
1037  */
1038  foreach(lc1, ((BoolExpr *) clause)->args)
1039  {
1040  Expr *arg = lfirst(lc1);
1041  bool arg_contradictory;
1042  List *argsteps;
1043 
1044  argsteps = gen_partprune_steps_internal(context,
1045  list_make1(arg));
1046  arg_contradictory = context->contradictory;
1047  /* Keep context->contradictory clear till we're done */
1048  context->contradictory = false;
1049 
1050  if (arg_contradictory)
1051  {
1052  /* Just ignore self-contradictory arguments. */
1053  continue;
1054  }
1055  else
1056  all_args_contradictory = false;
1057 
1058  if (argsteps != NIL)
1059  {
1060  /*
1061  * gen_partprune_steps_internal() always adds a single
1062  * combine step when it generates multiple steps, so
1063  * here we can just pay attention to the last one in
1064  * the list. If it just generated one, then the last
1065  * one in the list is still the one we want.
1066  */
1067  PartitionPruneStep *last = llast(argsteps);
1068 
1069  arg_stepids = lappend_int(arg_stepids, last->step_id);
1070  }
1071  else
1072  {
1073  PartitionPruneStep *orstep;
1074 
1075  /*
1076  * The arg didn't contain a clause matching this
1077  * partition key. We cannot prune using such an arg.
1078  * To indicate that to the pruning code, we must
1079  * construct a dummy PartitionPruneStepCombine whose
1080  * source_stepids is set to an empty List.
1081  */
1082  orstep = gen_prune_step_combine(context, NIL,
1084  arg_stepids = lappend_int(arg_stepids, orstep->step_id);
1085  }
1086  }
1087 
1088  /* If all the OR arms are contradictory, we can stop */
1089  if (all_args_contradictory)
1090  {
1091  context->contradictory = true;
1092  return NIL;
1093  }
1094 
1095  if (arg_stepids != NIL)
1096  {
1097  PartitionPruneStep *step;
1098 
1099  step = gen_prune_step_combine(context, arg_stepids,
1101  result = lappend(result, step);
1102  }
1103  continue;
1104  }
1105  else if (is_andclause(clause))
1106  {
1107  List *args = ((BoolExpr *) clause)->args;
1108  List *argsteps;
1109 
1110  /*
1111  * args may itself contain clauses of arbitrary type, so just
1112  * recurse and later combine the component partitions sets
1113  * using a combine step.
1114  */
1115  argsteps = gen_partprune_steps_internal(context, args);
1116 
1117  /* If any AND arm is contradictory, we can stop immediately */
1118  if (context->contradictory)
1119  return NIL;
1120 
1121  /*
1122  * gen_partprune_steps_internal() always adds a single combine
1123  * step when it generates multiple steps, so here we can just
1124  * pay attention to the last one in the list. If it just
1125  * generated one, then the last one in the list is still the
1126  * one we want.
1127  */
1128  if (argsteps != NIL)
1129  result = lappend(result, llast(argsteps));
1130 
1131  continue;
1132  }
1133 
1134  /*
1135  * Fall-through for a NOT clause, which if it's a Boolean clause,
1136  * will be handled in match_clause_to_partition_key(). We
1137  * currently don't perform any pruning for more complex NOT
1138  * clauses.
1139  */
1140  }
1141 
1142  /*
1143  * See if we can match this clause to any of the partition keys.
1144  */
1145  for (i = 0; i < part_scheme->partnatts; i++)
1146  {
1147  Expr *partkey = linitial(context->rel->partexprs[i]);
1148  bool clause_is_not_null = false;
1149  PartClauseInfo *pc = NULL;
1150  List *clause_steps = NIL;
1151 
1152  switch (match_clause_to_partition_key(context,
1153  clause, partkey, i,
1154  &clause_is_not_null,
1155  &pc, &clause_steps))
1156  {
1158  Assert(pc != NULL);
1159 
1160  /*
1161  * Since we only allow strict operators, check for any
1162  * contradicting IS NULL.
1163  */
1164  if (bms_is_member(i, nullkeys))
1165  {
1166  context->contradictory = true;
1167  return NIL;
1168  }
1169  generate_opsteps = true;
1170  keyclauses[i] = lappend(keyclauses[i], pc);
1171  break;
1172 
1174  if (!clause_is_not_null)
1175  {
1176  /*
1177  * check for conflicting IS NOT NULL as well as
1178  * contradicting strict clauses
1179  */
1180  if (bms_is_member(i, notnullkeys) ||
1181  keyclauses[i] != NIL)
1182  {
1183  context->contradictory = true;
1184  return NIL;
1185  }
1186  nullkeys = bms_add_member(nullkeys, i);
1187  }
1188  else
1189  {
1190  /* check for conflicting IS NULL */
1191  if (bms_is_member(i, nullkeys))
1192  {
1193  context->contradictory = true;
1194  return NIL;
1195  }
1196  notnullkeys = bms_add_member(notnullkeys, i);
1197  }
1198  break;
1199 
1201  Assert(clause_steps != NIL);
1202  result = list_concat(result, clause_steps);
1203  break;
1204 
1206  /* We've nothing more to do if a contradiction was found. */
1207  context->contradictory = true;
1208  return NIL;
1209 
1210  case PARTCLAUSE_NOMATCH:
1211 
1212  /*
1213  * Clause didn't match this key, but it might match the
1214  * next one.
1215  */
1216  continue;
1217 
1219  /* This clause cannot be used for pruning. */
1220  break;
1221  }
1222 
1223  /* done; go check the next clause. */
1224  break;
1225  }
1226  }
1227 
1228  /*-----------
1229  * Now generate some (more) pruning steps. We have three strategies:
1230  *
1231  * 1) Generate pruning steps based on IS NULL clauses:
1232  * a) For list partitioning, null partition keys can only be found in
1233  * the designated null-accepting partition, so if there are IS NULL
1234  * clauses containing partition keys we should generate a pruning
1235  * step that gets rid of all partitions but that one. We can
1236  * disregard any OpExpr we may have found.
1237  * b) For range partitioning, only the default partition can contain
1238  * NULL values, so the same rationale applies.
1239  * c) For hash partitioning, we only apply this strategy if we have
1240  * IS NULL clauses for all the keys. Strategy 2 below will take
1241  * care of the case where some keys have OpExprs and others have
1242  * IS NULL clauses.
1243  *
1244  * 2) If not, generate steps based on OpExprs we have (if any).
1245  *
1246  * 3) If this doesn't work either, we may be able to generate steps to
1247  * prune just the null-accepting partition (if one exists), if we have
1248  * IS NOT NULL clauses for all partition keys.
1249  */
1250  if (!bms_is_empty(nullkeys) &&
1251  (part_scheme->strategy == PARTITION_STRATEGY_LIST ||
1252  part_scheme->strategy == PARTITION_STRATEGY_RANGE ||
1253  (part_scheme->strategy == PARTITION_STRATEGY_HASH &&
1254  bms_num_members(nullkeys) == part_scheme->partnatts)))
1255  {
1256  PartitionPruneStep *step;
1257 
1258  /* Strategy 1 */
1259  step = gen_prune_step_op(context, InvalidStrategy,
1260  false, NIL, NIL, nullkeys);
1261  result = lappend(result, step);
1262  }
1263  else if (generate_opsteps)
1264  {
1265  List *opsteps;
1266 
1267  /* Strategy 2 */
1268  opsteps = gen_prune_steps_from_opexps(context, keyclauses, nullkeys);
1269  result = list_concat(result, opsteps);
1270  }
1271  else if (bms_num_members(notnullkeys) == part_scheme->partnatts)
1272  {
1273  PartitionPruneStep *step;
1274 
1275  /* Strategy 3 */
1276  step = gen_prune_step_op(context, InvalidStrategy,
1277  false, NIL, NIL, NULL);
1278  result = lappend(result, step);
1279  }
1280 
1281  /*
1282  * Finally, if there are multiple steps, since the 'clauses' are mutually
1283  * ANDed, add an INTERSECT step to combine the partition sets resulting
1284  * from them and append it to the result list.
1285  */
1286  if (list_length(result) > 1)
1287  {
1288  List *step_ids = NIL;
1289  PartitionPruneStep *final;
1290 
1291  foreach(lc, result)
1292  {
1293  PartitionPruneStep *step = lfirst(lc);
1294 
1295  step_ids = lappend_int(step_ids, step->step_id);
1296  }
1297 
1298  final = gen_prune_step_combine(context, step_ids,
1300  result = lappend(result, final);
1301  }
1302 
1303  return result;
1304 }
#define InvalidStrategy
Definition: stratnum.h:24
#define NIL
Definition: pg_list.h:65
#define IsA(nodeptr, _type_)
Definition: nodes.h:588
static bool is_orclause(const void *clause)
Definition: nodeFuncs.h:106
#define llast(l)
Definition: pg_list.h:194
static bool is_andclause(const void *clause)
Definition: nodeFuncs.h:97
List * list_concat(List *list1, const List *list2)
Definition: list.c:530
#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:1794
List ** partexprs
Definition: pathnodes.h:774
#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:437
#define partition_bound_has_default(bi)
Definition: partbounds.h:99
static List * gen_partprune_steps_internal(GeneratePruningStepsContext *context, List *clauses)
Definition: partprune.c:962
List * lappend_int(List *list, int datum)
Definition: list.c:354
List * lappend(List *list, void *datum)
Definition: list.c:336
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
struct PartitionBoundInfoData * boundinfo
Definition: pathnodes.h:764
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:826
static List * gen_prune_steps_from_opexps(GeneratePruningStepsContext *context, List **keyclauses, Bitmapset *nullkeys)
Definition: partprune.c:1384
#define Assert(condition)
Definition: c.h:804
#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:827
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:828
int i
void * arg
PartitionScheme part_scheme
Definition: pathnodes.h:760
List * partition_qual
Definition: pathnodes.h:767
static PartitionPruneStep * gen_prune_step_op(GeneratePruningStepsContext *context, StrategyNumber opstrategy, bool op_is_ne, List *exprs, List *cmpfns, Bitmapset *nullkeys)
Definition: partprune.c:1314
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:1347

◆ gen_prune_step_combine()

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

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

1350 {
1352 
1353  cstep->step.step_id = context->next_step_id++;
1354  cstep->combineOp = combineOp;
1355  cstep->source_stepids = source_stepids;
1356 
1357  context->steps = lappend(context->steps, cstep);
1358 
1359  return (PartitionPruneStep *) cstep;
1360 }
PartitionPruneCombineOp combineOp
Definition: plannodes.h:1273
List * lappend(List *list, void *datum)
Definition: list.c:336
#define makeNode(_type_)
Definition: nodes.h:585
PartitionPruneStep step
Definition: plannodes.h:1271

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

1318 {
1320 
1321  opstep->step.step_id = context->next_step_id++;
1322 
1323  /*
1324  * For clauses that contain an <> operator, set opstrategy to
1325  * InvalidStrategy to signal get_matching_list_bounds to do the right
1326  * thing.
1327  */
1328  opstep->opstrategy = op_is_ne ? InvalidStrategy : opstrategy;
1329  Assert(list_length(exprs) == list_length(cmpfns));
1330  opstep->exprs = exprs;
1331  opstep->cmpfns = cmpfns;
1332  opstep->nullkeys = nullkeys;
1333 
1334  context->steps = lappend(context->steps, opstep);
1335 
1336  return (PartitionPruneStep *) opstep;
1337 }
#define InvalidStrategy
Definition: stratnum.h:24
PartitionPruneStep step
Definition: plannodes.h:1249
List * lappend(List *list, void *datum)
Definition: list.c:336
#define makeNode(_type_)
Definition: nodes.h:585
#define Assert(condition)
Definition: c.h:804
Bitmapset * nullkeys
Definition: plannodes.h:1254
static int list_length(const List *l)
Definition: pg_list.h:149
StrategyNumber opstrategy
Definition: plannodes.h:1251

◆ gen_prune_steps_from_opexps()

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

Definition at line 1384 of file partprune.c.

References Assert, bms_is_member(), BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, BTMaxStrategyNumber, PartClauseInfo::cmpfn, elog, ERROR, PartClauseInfo::expr, for_each_cell, get_op_opfamily_properties(), get_steps_using_prefix(), HTEqualStrategyNumber, HTMaxStrategyNumber, i, InvalidStrategy, PartClauseInfo::keyno, lappend(), lfirst, list_concat(), list_head(), 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, GeneratePruningStepsContext::rel, and PartitionSchemeData::strategy.

Referenced by gen_partprune_steps_internal().

1386 {
1387  PartitionScheme part_scheme = context->rel->part_scheme;
1388  List *opsteps = NIL;
1389  List *btree_clauses[BTMaxStrategyNumber + 1],
1390  *hash_clauses[HTMaxStrategyNumber + 1];
1391  int i;
1392  ListCell *lc;
1393 
1394  memset(btree_clauses, 0, sizeof(btree_clauses));
1395  memset(hash_clauses, 0, sizeof(hash_clauses));
1396  for (i = 0; i < part_scheme->partnatts; i++)
1397  {
1398  List *clauselist = keyclauses[i];
1399  bool consider_next_key = true;
1400 
1401  /*
1402  * For range partitioning, if we have no clauses for the current key,
1403  * we can't consider any later keys either, so we can stop here.
1404  */
1405  if (part_scheme->strategy == PARTITION_STRATEGY_RANGE &&
1406  clauselist == NIL)
1407  break;
1408 
1409  /*
1410  * For hash partitioning, if a column doesn't have the necessary
1411  * equality clause, there should be an IS NULL clause, otherwise
1412  * pruning is not possible.
1413  */
1414  if (part_scheme->strategy == PARTITION_STRATEGY_HASH &&
1415  clauselist == NIL && !bms_is_member(i, nullkeys))
1416  return NIL;
1417 
1418  foreach(lc, clauselist)
1419  {
1420  PartClauseInfo *pc = (PartClauseInfo *) lfirst(lc);
1421  Oid lefttype,
1422  righttype;
1423 
1424  /* Look up the operator's btree/hash strategy number. */
1425  if (pc->op_strategy == InvalidStrategy)
1427  part_scheme->partopfamily[i],
1428  false,
1429  &pc->op_strategy,
1430  &lefttype,
1431  &righttype);
1432 
1433  switch (part_scheme->strategy)
1434  {
1437  btree_clauses[pc->op_strategy] =
1438  lappend(btree_clauses[pc->op_strategy], pc);
1439 
1440  /*
1441  * We can't consider subsequent partition keys if the
1442  * clause for the current key contains a non-inclusive
1443  * operator.
1444  */
1445  if (pc->op_strategy == BTLessStrategyNumber ||
1447  consider_next_key = false;
1448  break;
1449 
1452  elog(ERROR, "invalid clause for hash partitioning");
1453  hash_clauses[pc->op_strategy] =
1454  lappend(hash_clauses[pc->op_strategy], pc);
1455  break;
1456 
1457  default:
1458  elog(ERROR, "invalid partition strategy: %c",
1459  part_scheme->strategy);
1460  break;
1461  }
1462  }
1463 
1464  /*
1465  * If we've decided that clauses for subsequent partition keys
1466  * wouldn't be useful for pruning, don't search any further.
1467  */
1468  if (!consider_next_key)
1469  break;
1470  }
1471 
1472  /*
1473  * Now, we have divided clauses according to their operator strategies.
1474  * Check for each strategy if we can generate pruning step(s) by
1475  * collecting a list of expressions whose values will constitute a vector
1476  * that can be used as a lookup key by a partition bound searching
1477  * function.
1478  */
1479  switch (part_scheme->strategy)
1480  {
1483  {
1484  List *eq_clauses = btree_clauses[BTEqualStrategyNumber];
1485  List *le_clauses = btree_clauses[BTLessEqualStrategyNumber];
1486  List *ge_clauses = btree_clauses[BTGreaterEqualStrategyNumber];
1487  int strat;
1488 
1489  /*
1490  * For each clause under consideration for a given strategy,
1491  * we collect expressions from clauses for earlier keys, whose
1492  * operator strategy is inclusive, into a list called
1493  * 'prefix'. By appending the clause's own expression to the
1494  * 'prefix', we'll generate one step using the so generated
1495  * vector and assign the current strategy to it. Actually,
1496  * 'prefix' might contain multiple clauses for the same key,
1497  * in which case, we must generate steps for various
1498  * combinations of expressions of different keys, which
1499  * get_steps_using_prefix takes care of for us.
1500  */
1501  for (strat = 1; strat <= BTMaxStrategyNumber; strat++)
1502  {
1503  foreach(lc, btree_clauses[strat])
1504  {
1505  PartClauseInfo *pc = lfirst(lc);
1506  ListCell *eq_start;
1507  ListCell *le_start;
1508  ListCell *ge_start;
1509  ListCell *lc1;
1510  List *prefix = NIL;
1511  List *pc_steps;
1512  bool prefix_valid = true;
1513  bool pk_has_clauses;
1514  int keyno;
1515 
1516  /*
1517  * If this is a clause for the first partition key,
1518  * there are no preceding expressions; generate a
1519  * pruning step without a prefix.
1520  *
1521  * Note that we pass NULL for step_nullkeys, because
1522  * we don't search list/range partition bounds where
1523  * some keys are NULL.
1524  */
1525  if (pc->keyno == 0)
1526  {
1527  Assert(pc->op_strategy == strat);
1528  pc_steps = get_steps_using_prefix(context, strat,
1529  pc->op_is_ne,
1530  pc->expr,
1531  pc->cmpfn,
1532  0,
1533  NULL,
1534  NIL);
1535  opsteps = list_concat(opsteps, pc_steps);
1536  continue;
1537  }
1538 
1539  eq_start = list_head(eq_clauses);
1540  le_start = list_head(le_clauses);
1541  ge_start = list_head(ge_clauses);
1542 
1543  /*
1544  * We arrange clauses into prefix in ascending order
1545  * of their partition key numbers.
1546  */
1547  for (keyno = 0; keyno < pc->keyno; keyno++)
1548  {
1549  pk_has_clauses = false;
1550 
1551  /*
1552  * Expressions from = clauses can always be in the
1553  * prefix, provided they're from an earlier key.
1554  */
1555  for_each_cell(lc1, eq_clauses, eq_start)
1556  {
1557  PartClauseInfo *eqpc = lfirst(lc1);
1558 
1559  if (eqpc->keyno == keyno)
1560  {
1561  prefix = lappend(prefix, eqpc);
1562  pk_has_clauses = true;
1563  }
1564  else
1565  {
1566  Assert(eqpc->keyno > keyno);
1567  break;
1568  }
1569  }
1570  eq_start = lc1;
1571 
1572  /*
1573  * If we're generating steps for </<= strategy, we
1574  * can add other <= clauses to the prefix,
1575  * provided they're from an earlier key.
1576  */
1577  if (strat == BTLessStrategyNumber ||
1578  strat == BTLessEqualStrategyNumber)
1579  {
1580  for_each_cell(lc1, le_clauses, le_start)
1581  {
1582  PartClauseInfo *lepc = lfirst(lc1);
1583 
1584  if (lepc->keyno == keyno)
1585  {
1586  prefix = lappend(prefix, lepc);
1587  pk_has_clauses = true;
1588  }
1589  else
1590  {
1591  Assert(lepc->keyno > keyno);
1592  break;
1593  }
1594  }
1595  le_start = lc1;
1596  }
1597 
1598  /*
1599  * If we're generating steps for >/>= strategy, we
1600  * can add other >= clauses to the prefix,
1601  * provided they're from an earlier key.
1602  */
1603  if (strat == BTGreaterStrategyNumber ||
1605  {
1606  for_each_cell(lc1, ge_clauses, ge_start)
1607  {
1608  PartClauseInfo *gepc = lfirst(lc1);
1609 
1610  if (gepc->keyno == keyno)
1611  {
1612  prefix = lappend(prefix, gepc);
1613  pk_has_clauses = true;
1614  }
1615  else
1616  {
1617  Assert(gepc->keyno > keyno);
1618  break;
1619  }
1620  }
1621  ge_start = lc1;
1622  }
1623 
1624  /*
1625  * If this key has no clauses, prefix is not valid
1626  * anymore.
1627  */
1628  if (!pk_has_clauses)
1629  {
1630  prefix_valid = false;
1631  break;
1632  }
1633  }
1634 
1635  /*
1636  * If prefix_valid, generate PartitionPruneStepOps.
1637  * Otherwise, we would not find clauses for a valid
1638  * subset of the partition keys anymore for the
1639  * strategy; give up on generating partition pruning
1640  * steps further for the strategy.
1641  *
1642  * As mentioned above, if 'prefix' contains multiple
1643  * expressions for the same key, the following will
1644  * generate multiple steps, one for each combination
1645  * of the expressions for different keys.
1646  *
1647  * Note that we pass NULL for step_nullkeys, because
1648  * we don't search list/range partition bounds where
1649  * some keys are NULL.
1650  */
1651  if (prefix_valid)
1652  {
1653  Assert(pc->op_strategy == strat);
1654  pc_steps = get_steps_using_prefix(context, strat,
1655  pc->op_is_ne,
1656  pc->expr,
1657  pc->cmpfn,
1658  pc->keyno,
1659  NULL,
1660  prefix);
1661  opsteps = list_concat(opsteps, pc_steps);
1662  }
1663  else
1664  break;
1665  }
1666  }
1667  break;
1668  }
1669 
1671  {
1672  List *eq_clauses = hash_clauses[HTEqualStrategyNumber];
1673 
1674  /* For hash partitioning, we have just the = strategy. */
1675  if (eq_clauses != NIL)
1676  {
1677  PartClauseInfo *pc;
1678  List *pc_steps;
1679  List *prefix = NIL;
1680  int last_keyno;
1681  ListCell *lc1;
1682 
1683  /*
1684  * Locate the clause for the greatest column. This may
1685  * not belong to the last partition key, but it is the
1686  * clause belonging to the last partition key we found a
1687  * clause for above.
1688  */
1689  pc = llast(eq_clauses);
1690 
1691  /*
1692  * There might be multiple clauses which matched to that
1693  * partition key; find the first such clause. While at
1694  * it, add all the clauses before that one to 'prefix'.
1695  */
1696  last_keyno = pc->keyno;
1697  foreach(lc, eq_clauses)
1698  {
1699  pc = lfirst(lc);
1700  if (pc->keyno == last_keyno)
1701  break;
1702  prefix = lappend(prefix, pc);
1703  }
1704 
1705  /*
1706  * For each clause for the "last" column, after appending
1707  * the clause's own expression to the 'prefix', we'll
1708  * generate one step using the so generated vector and
1709  * assign = as its strategy. Actually, 'prefix' might
1710  * contain multiple clauses for the same key, in which
1711  * case, we must generate steps for various combinations
1712  * of expressions of different keys, which
1713  * get_steps_using_prefix will take care of for us.
1714  */
1715  for_each_cell(lc1, eq_clauses, lc)
1716  {
1717  pc = lfirst(lc1);
1718 
1719  /*
1720  * Note that we pass nullkeys for step_nullkeys,
1721  * because we need to tell hash partition bound search
1722  * function which of the keys we found IS NULL clauses
1723  * for.
1724  */
1726  pc_steps =
1727  get_steps_using_prefix(context,
1729  false,
1730  pc->expr,
1731  pc->cmpfn,
1732  pc->keyno,
1733  nullkeys,
1734  prefix);
1735  opsteps = list_concat(opsteps, pc_steps);
1736  }
1737  }
1738  break;
1739  }
1740 
1741  default:
1742  elog(ERROR, "invalid partition strategy: %c",
1743  part_scheme->strategy);
1744  break;
1745  }
1746 
1747  return opsteps;
1748 }
#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:417
#define llast(l)
Definition: pg_list.h:194
List * list_concat(List *list1, const List *list2)
Definition: list.c:530
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:2372
#define ERROR
Definition: elog.h:46
#define HTEqualStrategyNumber
Definition: stratnum.h:41
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
List * lappend(List *list, void *datum)
Definition: list.c:336
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:826
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:827
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:828
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:232
int i
PartitionScheme part_scheme
Definition: pathnodes.h:760
#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

◆ 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 2583 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(), HTEqualStrategyNumber, i, PartitionBoundInfoData::indexes, PartitionBoundInfoData::nindexes, 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().

2586 {
2587  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2588  PartitionBoundInfo boundinfo = context->boundinfo;
2589  int *partindices = boundinfo->indexes;
2590  int partnatts = context->partnatts;
2591  bool isnull[PARTITION_MAX_KEYS];
2592  int i;
2593  uint64 rowHash;
2594  int greatest_modulus;
2595  Oid *partcollation = context->partcollation;
2596 
2598 
2599  /*
2600  * For hash partitioning we can only perform pruning based on equality
2601  * clauses to the partition key or IS NULL clauses. We also can only
2602  * prune if we got values for all keys.
2603  */
2604  if (nvalues + bms_num_members(nullkeys) == partnatts)
2605  {
2606  /*
2607  * If there are any values, they must have come from clauses
2608  * containing an equality operator compatible with hash partitioning.
2609  */
2610  Assert(opstrategy == HTEqualStrategyNumber || nvalues == 0);
2611 
2612  for (i = 0; i < partnatts; i++)
2613  isnull[i] = bms_is_member(i, nullkeys);
2614 
2615  rowHash = compute_partition_hash_value(partnatts, partsupfunc, partcollation,
2616  values, isnull);
2617 
2618  greatest_modulus = boundinfo->nindexes;
2619  if (partindices[rowHash % greatest_modulus] >= 0)
2620  result->bound_offsets =
2621  bms_make_singleton(rowHash % greatest_modulus);
2622  }
2623  else
2624  {
2625  /* Report all valid offsets into the boundinfo->indexes array. */
2626  result->bound_offsets = bms_add_range(NULL, 0,
2627  boundinfo->nindexes - 1);
2628  }
2629 
2630  /*
2631  * There is neither a special hash null partition or the default hash
2632  * partition.
2633  */
2634  result->scan_null = result->scan_default = false;
2635 
2636  return result;
2637 }
#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
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:186
void * palloc0(Size size)
Definition: mcxt.c:1093
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:826
uint64 compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *values, bool *isnull)
Definition: partbounds.c:4743
#define Assert(condition)
Definition: c.h:804
static Datum values[MAXATTR]
Definition: bootstrap.c:156
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 2660 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().

2663 {
2664  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2665  PartitionBoundInfo boundinfo = context->boundinfo;
2666  int off,
2667  minoff,
2668  maxoff;
2669  bool is_equal;
2670  bool inclusive = false;
2671  Oid *partcollation = context->partcollation;
2672 
2674  Assert(context->partnatts == 1);
2675 
2676  result->scan_null = result->scan_default = false;
2677 
2678  if (!bms_is_empty(nullkeys))
2679  {
2680  /*
2681  * Nulls may exist in only one partition - the partition whose
2682  * accepted set of values includes null or the default partition if
2683  * the former doesn't exist.
2684  */
2685  if (partition_bound_accepts_nulls(boundinfo))
2686  result->scan_null = true;
2687  else
2688  result->scan_default = partition_bound_has_default(boundinfo);
2689  return result;
2690  }
2691 
2692  /*
2693  * If there are no datums to compare keys with, but there are partitions,
2694  * just return the default partition if one exists.
2695  */
2696  if (boundinfo->ndatums == 0)
2697  {
2698  result->scan_default = partition_bound_has_default(boundinfo);
2699  return result;
2700  }
2701 
2702  minoff = 0;
2703  maxoff = boundinfo->ndatums - 1;
2704 
2705  /*
2706  * If there are no values to compare with the datums in boundinfo, it
2707  * means the caller asked for partitions for all non-null datums. Add
2708  * indexes of *all* partitions, including the default if any.
2709  */
2710  if (nvalues == 0)
2711  {
2712  Assert(boundinfo->ndatums > 0);
2713  result->bound_offsets = bms_add_range(NULL, 0,
2714  boundinfo->ndatums - 1);
2715  result->scan_default = partition_bound_has_default(boundinfo);
2716  return result;
2717  }
2718 
2719  /* Special case handling of values coming from a <> operator clause. */
2720  if (opstrategy == InvalidStrategy)
2721  {
2722  /*
2723  * First match to all bounds. We'll remove any matching datums below.
2724  */
2725  Assert(boundinfo->ndatums > 0);
2726  result->bound_offsets = bms_add_range(NULL, 0,
2727  boundinfo->ndatums - 1);
2728 
2729  off = partition_list_bsearch(partsupfunc, partcollation, boundinfo,
2730  value, &is_equal);
2731  if (off >= 0 && is_equal)
2732  {
2733 
2734  /* We have a match. Remove from the result. */
2735  Assert(boundinfo->indexes[off] >= 0);
2736  result->bound_offsets = bms_del_member(result->bound_offsets,
2737  off);
2738  }
2739 
2740  /* Always include the default partition if any. */
2741  result->scan_default = partition_bound_has_default(boundinfo);
2742 
2743  return result;
2744  }
2745 
2746  /*
2747  * With range queries, always include the default list partition, because
2748  * list partitions divide the key space in a discontinuous manner, not all
2749  * values in the given range will have a partition assigned. This may not
2750  * technically be true for some data types (e.g. integer types), however,
2751  * we currently lack any sort of infrastructure to provide us with proofs
2752  * that would allow us to do anything smarter here.
2753  */
2754  if (opstrategy != BTEqualStrategyNumber)
2755  result->scan_default = partition_bound_has_default(boundinfo);
2756 
2757  switch (opstrategy)
2758  {
2759  case BTEqualStrategyNumber:
2760  off = partition_list_bsearch(partsupfunc,
2761  partcollation,
2762  boundinfo, value,
2763  &is_equal);
2764  if (off >= 0 && is_equal)
2765  {
2766  Assert(boundinfo->indexes[off] >= 0);
2767  result->bound_offsets = bms_make_singleton(off);
2768  }
2769  else
2770  result->scan_default = partition_bound_has_default(boundinfo);
2771  return result;
2772 
2774  inclusive = true;
2775  /* fall through */
2777  off = partition_list_bsearch(partsupfunc,
2778  partcollation,
2779  boundinfo, value,
2780  &is_equal);
2781  if (off >= 0)
2782  {
2783  /* We don't want the matched datum to be in the result. */
2784  if (!is_equal || !inclusive)
2785  off++;
2786  }
2787  else
2788  {
2789  /*
2790  * This case means all partition bounds are greater, which in
2791  * turn means that all partitions satisfy this key.
2792  */
2793  off = 0;
2794  }
2795 
2796  /*
2797  * off is greater than the numbers of datums we have partitions
2798  * for. The only possible partition that could contain a match is
2799  * the default partition, but we must've set context->scan_default
2800  * above anyway if one exists.
2801  */
2802  if (off > boundinfo->ndatums - 1)
2803  return result;
2804 
2805  minoff = off;
2806  break;
2807 
2809  inclusive = true;
2810  /* fall through */
2811  case BTLessStrategyNumber:
2812  off = partition_list_bsearch(partsupfunc,
2813  partcollation,
2814  boundinfo, value,
2815  &is_equal);
2816  if (off >= 0 && is_equal && !inclusive)
2817  off--;
2818 
2819  /*
2820  * off is smaller than the datums of all non-default partitions.
2821  * The only possible partition that could contain a match is the
2822  * default partition, but we must've set context->scan_default
2823  * above anyway if one exists.
2824  */
2825  if (off < 0)
2826  return result;
2827 
2828  maxoff = off;
2829  break;
2830 
2831  default:
2832  elog(ERROR, "invalid strategy number %d", opstrategy);
2833  break;
2834  }
2835 
2836  Assert(minoff >= 0 && maxoff >= 0);
2837  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2838  return result;
2839 }
#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:46
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:186
#define partition_bound_has_default(bi)
Definition: partbounds.h:99
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
void * palloc0(Size size)
Definition: mcxt.c:1093
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:98
int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
Definition: partbounds.c:3623
static struct @143 value
#define Assert(condition)
Definition: c.h:804
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:827
PartitionBoundInfo boundinfo
Definition: partprune.h:53
#define elog(elevel,...)
Definition: elog.h:232
#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 818 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().

819 {
820  Bitmapset *result;
821  int num_steps = list_length(pruning_steps),
822  i;
823  PruneStepResult **results,
824  *final_result;
825  ListCell *lc;
826  bool scan_default;
827 
828  /* If there are no pruning steps then all partitions match. */
829  if (num_steps == 0)
830  {
831  Assert(context->nparts > 0);
832  return bms_add_range(NULL, 0, context->nparts - 1);
833  }
834 
835  /*
836  * Allocate space for individual pruning steps to store its result. Each
837  * slot will hold a PruneStepResult after performing a given pruning step.
838  * Later steps may use the result of one or more earlier steps. The
839  * result of applying all pruning steps is the value contained in the slot
840  * of the last pruning step.
841  */
842  results = (PruneStepResult **)
843  palloc0(num_steps * sizeof(PruneStepResult *));
844  foreach(lc, pruning_steps)
845  {
846  PartitionPruneStep *step = lfirst(lc);
847 
848  switch (nodeTag(step))
849  {
851  results[step->step_id] =
853  (PartitionPruneStepOp *) step);
854  break;
855 
857  results[step->step_id] =
859  (PartitionPruneStepCombine *) step,
860  results);
861  break;
862 
863  default:
864  elog(ERROR, "invalid pruning step type: %d",
865  (int) nodeTag(step));
866  }
867  }
868 
869  /*
870  * At this point we know the offsets of all the datums whose corresponding
871  * partitions need to be in the result, including special null-accepting
872  * and default partitions. Collect the actual partition indexes now.
873  */
874  final_result = results[num_steps - 1];
875  Assert(final_result != NULL);
876  i = -1;
877  result = NULL;
878  scan_default = final_result->scan_default;
879  while ((i = bms_next_member(final_result->bound_offsets, i)) >= 0)
880  {
881  int partindex;
882 
883  Assert(i < context->boundinfo->nindexes);
884  partindex = context->boundinfo->indexes[i];
885 
886  if (partindex < 0)
887  {
888  /*
889  * In range partitioning cases, if a partition index is -1 it
890  * means that the bound at the offset is the upper bound for a
891  * range not covered by any partition (other than a possible
892  * default partition). In hash partitioning, the same means no
893  * partition has been defined for the corresponding remainder
894  * value.
895  *
896  * In either case, the value is still part of the queried range of
897  * values, so mark to scan the default partition if one exists.
898  */
899  scan_default |= partition_bound_has_default(context->boundinfo);
900  continue;
901  }
902 
903  result = bms_add_member(result, partindex);
904  }
905 
906  /* Add the null and/or default partition if needed and present. */
907  if (final_result->scan_null)
908  {
911  result = bms_add_member(result, context->boundinfo->null_index);
912  }
913  if (scan_default)
914  {
916  context->strategy == PARTITION_STRATEGY_RANGE);
918  result = bms_add_member(result, context->boundinfo->default_index);
919  }
920 
921  return result;
922 }
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:3484
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:834
#define ERROR
Definition: elog.h:46
static PruneStepResult * perform_pruning_base_step(PartitionPruneContext *context, PartitionPruneStepOp *opstep)
Definition: partprune.c:3336
#define partition_bound_has_default(bi)
Definition: partbounds.h:99
void * palloc0(Size size)
Definition: mcxt.c:1093
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:98
#define Assert(condition)
Definition: c.h:804
#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:827
#define nodeTag(nodeptr)
Definition: nodes.h:542
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:828
PartitionBoundInfo boundinfo
Definition: partprune.h:53
#define elog(elevel,...)
Definition: elog.h:232
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 2871 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().

2874 {
2875  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2876  PartitionBoundInfo boundinfo = context->boundinfo;
2877  Oid *partcollation = context->partcollation;
2878  int partnatts = context->partnatts;
2879  int *partindices = boundinfo->indexes;
2880  int off,
2881  minoff,
2882  maxoff;
2883  bool is_equal;
2884  bool inclusive = false;
2885 
2887  Assert(nvalues <= partnatts);
2888 
2889  result->scan_null = result->scan_default = false;
2890 
2891  /*
2892  * If there are no datums to compare keys with, or if we got an IS NULL
2893  * clause just return the default partition, if it exists.
2894  */
2895  if (boundinfo->ndatums == 0 || !bms_is_empty(nullkeys))
2896  {
2897  result->scan_default = partition_bound_has_default(boundinfo);
2898  return result;
2899  }
2900 
2901  minoff = 0;
2902  maxoff = boundinfo->ndatums;
2903 
2904  /*
2905  * If there are no values to compare with the datums in boundinfo, it
2906  * means the caller asked for partitions for all non-null datums. Add
2907  * indexes of *all* partitions, including the default partition if one
2908  * exists.
2909  */
2910  if (nvalues == 0)
2911  {
2912  /* ignore key space not covered by any partitions */
2913  if (partindices[minoff] < 0)
2914  minoff++;
2915  if (partindices[maxoff] < 0)
2916  maxoff--;
2917 
2918  result->scan_default = partition_bound_has_default(boundinfo);
2919  Assert(partindices[minoff] >= 0 &&
2920  partindices[maxoff] >= 0);
2921  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2922 
2923  return result;
2924  }
2925 
2926  /*
2927  * If the query does not constrain all key columns, we'll need to scan the
2928  * default partition, if any.
2929  */
2930  if (nvalues < partnatts)
2931  result->scan_default = partition_bound_has_default(boundinfo);
2932 
2933  switch (opstrategy)
2934  {
2935  case BTEqualStrategyNumber:
2936  /* Look for the smallest bound that is = lookup value. */
2937  off = partition_range_datum_bsearch(partsupfunc,
2938  partcollation,
2939  boundinfo,
2940  nvalues, values,
2941  &is_equal);
2942 
2943  if (off >= 0 && is_equal)
2944  {
2945  if (nvalues == partnatts)
2946  {
2947  /* There can only be zero or one matching partition. */
2948  result->bound_offsets = bms_make_singleton(off + 1);
2949  return result;
2950  }
2951  else
2952  {
2953  int saved_off = off;
2954 
2955  /*
2956  * Since the lookup value contains only a prefix of keys,
2957  * we must find other bounds that may also match the
2958  * prefix. partition_range_datum_bsearch() returns the
2959  * offset of one of them, find others by checking adjacent
2960  * bounds.
2961  */
2962 
2963  /*
2964  * First find greatest bound that's smaller than the
2965  * lookup value.
2966  */
2967  while (off >= 1)
2968  {
2969  int32 cmpval;
2970 
2971  cmpval =
2972  partition_rbound_datum_cmp(partsupfunc,
2973  partcollation,
2974  boundinfo->datums[off - 1],
2975  boundinfo->kind[off - 1],
2976  values, nvalues);
2977  if (cmpval != 0)
2978  break;
2979  off--;
2980  }
2981 
2982  Assert(0 ==
2983  partition_rbound_datum_cmp(partsupfunc,
2984  partcollation,
2985  boundinfo->datums[off],
2986  boundinfo->kind[off],
2987  values, nvalues));
2988 
2989  /*
2990  * We can treat 'off' as the offset of the smallest bound
2991  * to be included in the result, if we know it is the
2992  * upper bound of the partition in which the lookup value
2993  * could possibly exist. One case it couldn't is if the
2994  * bound, or precisely the matched portion of its prefix,
2995  * is not inclusive.
2996  */
2997  if (boundinfo->kind[off][nvalues] ==
2999  off++;
3000 
3001  minoff = off;
3002 
3003  /*
3004  * Now find smallest bound that's greater than the lookup
3005  * value.
3006  */
3007  off = saved_off;
3008  while (off < boundinfo->ndatums - 1)
3009  {
3010  int32 cmpval;
3011 
3012  cmpval = partition_rbound_datum_cmp(partsupfunc,
3013  partcollation,
3014  boundinfo->datums[off + 1],
3015  boundinfo->kind[off + 1],
3016  values, nvalues);
3017  if (cmpval != 0)
3018  break;
3019  off++;
3020  }
3021 
3022  Assert(0 ==
3023  partition_rbound_datum_cmp(partsupfunc,
3024  partcollation,
3025  boundinfo->datums[off],
3026  boundinfo->kind[off],
3027  values, nvalues));
3028 
3029  /*
3030  * off + 1, then would be the offset of the greatest bound
3031  * to be included in the result.
3032  */
3033  maxoff = off + 1;
3034  }
3035 
3036  Assert(minoff >= 0 && maxoff >= 0);
3037  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3038  }
3039  else
3040  {
3041  /*
3042  * The lookup value falls in the range between some bounds in
3043  * boundinfo. 'off' would be the offset of the greatest bound
3044  * that is <= lookup value, so add off + 1 to the result
3045  * instead as the offset of the upper bound of the only
3046  * partition that may contain the lookup value. If 'off' is
3047  * -1 indicating that all bounds are greater, then we simply
3048  * end up adding the first bound's offset, that is, 0.
3049  */
3050  result->bound_offsets = bms_make_singleton(off + 1);
3051  }
3052 
3053  return result;
3054 
3056  inclusive = true;
3057  /* fall through */
3059 
3060  /*
3061  * Look for the smallest bound that is > or >= lookup value and
3062  * set minoff to its offset.
3063  */
3064  off = partition_range_datum_bsearch(partsupfunc,
3065  partcollation,
3066  boundinfo,
3067  nvalues, values,
3068  &is_equal);
3069  if (off < 0)
3070  {
3071  /*
3072  * All bounds are greater than the lookup value, so include
3073  * all of them in the result.
3074  */
3075  minoff = 0;
3076  }
3077  else
3078  {
3079  if (is_equal && nvalues < partnatts)
3080  {
3081  /*
3082  * Since the lookup value contains only a prefix of keys,
3083  * we must find other bounds that may also match the
3084  * prefix. partition_range_datum_bsearch() returns the
3085  * offset of one of them, find others by checking adjacent
3086  * bounds.
3087  *
3088  * Based on whether the lookup values are inclusive or
3089  * not, we must either include the indexes of all such
3090  * bounds in the result (that is, set minoff to the index
3091  * of smallest such bound) or find the smallest one that's
3092  * greater than the lookup values and set minoff to that.
3093  */
3094  while (off >= 1 && off < boundinfo->ndatums - 1)
3095  {
3096  int32 cmpval;
3097  int nextoff;
3098 
3099  nextoff = inclusive ? off - 1 : off + 1;
3100  cmpval =
3101  partition_rbound_datum_cmp(partsupfunc,
3102  partcollation,
3103  boundinfo->datums[nextoff],
3104  boundinfo->kind[nextoff],
3105  values, nvalues);
3106  if (cmpval != 0)
3107  break;
3108 
3109  off = nextoff;
3110  }
3111 
3112  Assert(0 ==
3113  partition_rbound_datum_cmp(partsupfunc,
3114  partcollation,
3115  boundinfo->datums[off],
3116  boundinfo->kind[off],
3117  values, nvalues));
3118 
3119  minoff = inclusive ? off : off + 1;
3120  }
3121  else
3122  {
3123 
3124  /*
3125  * lookup value falls in the range between some bounds in
3126  * boundinfo. off would be the offset of the greatest
3127  * bound that is <= lookup value, so add off + 1 to the
3128  * result instead as the offset of the upper bound of the
3129  * smallest partition that may contain the lookup value.
3130  */
3131  minoff = off + 1;
3132  }
3133  }
3134  break;
3135 
3137  inclusive = true;
3138  /* fall through */
3139  case BTLessStrategyNumber:
3140 
3141  /*
3142  * Look for the greatest bound that is < or <= lookup value and
3143  * set maxoff to its offset.
3144  */
3145  off = partition_range_datum_bsearch(partsupfunc,
3146  partcollation,
3147  boundinfo,
3148  nvalues, values,
3149  &is_equal);
3150  if (off >= 0)
3151  {
3152  /*
3153  * See the comment above.
3154  */
3155  if (is_equal && nvalues < partnatts)
3156  {
3157  while (off >= 1 && off < boundinfo->ndatums - 1)
3158  {
3159  int32 cmpval;
3160  int nextoff;
3161 
3162  nextoff = inclusive ? off + 1 : off - 1;
3163  cmpval = partition_rbound_datum_cmp(partsupfunc,
3164  partcollation,
3165  boundinfo->datums[nextoff],
3166  boundinfo->kind[nextoff],
3167  values, nvalues);
3168  if (cmpval != 0)
3169  break;
3170 
3171  off = nextoff;
3172  }
3173 
3174  Assert(0 ==
3175  partition_rbound_datum_cmp(partsupfunc,
3176  partcollation,
3177  boundinfo->datums[off],
3178  boundinfo->kind[off],
3179  values, nvalues));
3180 
3181  maxoff = inclusive ? off + 1 : off;
3182  }
3183 
3184  /*
3185  * The lookup value falls in the range between some bounds in
3186  * boundinfo. 'off' would be the offset of the greatest bound
3187  * that is <= lookup value, so add off + 1 to the result
3188  * instead as the offset of the upper bound of the greatest
3189  * partition that may contain lookup value. If the lookup
3190  * value had exactly matched the bound, but it isn't
3191  * inclusive, no need add the adjacent partition.
3192  */
3193  else if (!is_equal || inclusive)
3194  maxoff = off + 1;
3195  else
3196  maxoff = off;
3197  }
3198  else
3199  {
3200  /*
3201  * 'off' is -1 indicating that all bounds are greater, so just
3202  * set the first bound's offset as maxoff.
3203  */
3204  maxoff = off + 1;
3205  }
3206  break;
3207 
3208  default:
3209  elog(ERROR, "invalid strategy number %d", opstrategy);
3210  break;
3211  }
3212 
3213  Assert(minoff >= 0 && minoff <= boundinfo->ndatums);
3214  Assert(maxoff >= 0 && maxoff <= boundinfo->ndatums);
3215 
3216  /*
3217  * If the smallest partition to return has MINVALUE (negative infinity) as
3218  * its lower bound, increment it to point to the next finite bound
3219  * (supposedly its upper bound), so that we don't inadvertently end up
3220  * scanning the default partition.
3221  */
3222  if (minoff < boundinfo->ndatums && partindices[minoff] < 0)
3223  {
3224  int lastkey = nvalues - 1;
3225 
3226  if (boundinfo->kind[minoff][lastkey] ==
3228  {
3229  minoff++;
3230  Assert(boundinfo->indexes[minoff] >= 0);
3231  }
3232  }
3233 
3234  /*
3235  * If the previous greatest partition has MAXVALUE (positive infinity) as
3236  * its upper bound (something only possible to do with multi-column range
3237  * partitioning), we scan switch to it as the greatest partition to
3238  * return. Again, so that we don't inadvertently end up scanning the
3239  * default partition.
3240  */
3241  if (maxoff >= 1 && partindices[maxoff] < 0)
3242  {
3243  int lastkey = nvalues - 1;
3244 
3245  if (boundinfo->kind[maxoff - 1][lastkey] ==
3247  {
3248  maxoff--;
3249  Assert(boundinfo->indexes[maxoff] >= 0);
3250  }
3251  }
3252 
3253  Assert(minoff >= 0 && maxoff >= 0);
3254  if (minoff <= maxoff)
3255  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3256 
3257  return result;
3258 }
PartitionRangeDatumKind ** kind
Definition: partbounds.h:84
#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:429
#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:3711
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:834
#define ERROR
Definition: elog.h:46
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:3572
#define partition_bound_has_default(bi)
Definition: partbounds.h:99
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
void * palloc0(Size size)
Definition: mcxt.c:1093
#define Assert(condition)
Definition: c.h:804
static Datum values[MAXATTR]
Definition: bootstrap.c:156
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:828
PartitionBoundInfo boundinfo
Definition: partprune.h:53
#define elog(elevel,...)
Definition: elog.h:232
#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 3300 of file partprune.c.

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

Referenced by make_partitionedrel_pruneinfo().

3301 {
3302  Bitmapset *execparamids = NULL;
3303  ListCell *lc;
3304 
3305  foreach(lc, steps)
3306  {
3308  ListCell *lc2;
3309 
3310  if (!IsA(step, PartitionPruneStepOp))
3311  continue;
3312 
3313  foreach(lc2, step->exprs)
3314  {
3315  Expr *expr = lfirst(lc2);
3316 
3317  /* We can be quick for plain Consts */
3318  if (!IsA(expr, Const))
3319  execparamids = bms_join(execparamids,
3320  pull_exec_paramids(expr));
3321  }
3322  }
3323 
3324  return execparamids;
3325 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:588
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition: bitmapset.c:949
static Bitmapset * pull_exec_paramids(Expr *expr)
Definition: partprune.c:3266
#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 2372 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().

2380 {
2381  Assert(step_nullkeys == NULL ||
2383 
2384  /* Quick exit if there are no values to prefix with. */
2385  if (list_length(prefix) == 0)
2386  {
2387  PartitionPruneStep *step;
2388 
2389  step = gen_prune_step_op(context,
2390  step_opstrategy,
2391  step_op_is_ne,
2392  list_make1(step_lastexpr),
2393  list_make1_oid(step_lastcmpfn),
2394  step_nullkeys);
2395  return list_make1(step);
2396  }
2397 
2398  /* Recurse to generate steps for various combinations. */
2399  return get_steps_using_prefix_recurse(context,
2400  step_opstrategy,
2401  step_op_is_ne,
2402  step_lastexpr,
2403  step_lastcmpfn,
2404  step_lastkeyno,
2405  step_nullkeys,
2406  prefix,
2407  list_head(prefix),
2408  NIL, NIL);
2409 }
#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:236
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:826
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:2424
#define Assert(condition)
Definition: c.h:804
static int list_length(const List *l)
Definition: pg_list.h:149
PartitionScheme part_scheme
Definition: pathnodes.h:760
static PartitionPruneStep * gen_prune_step_op(GeneratePruningStepsContext *context, StrategyNumber opstrategy, bool op_is_ne, List *exprs, List *cmpfns, Bitmapset *nullkeys)
Definition: partprune.c:1314

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

2435 {
2436  List *result = NIL;
2437  ListCell *lc;
2438  int cur_keyno;
2439 
2440  /* Actually, recursion would be limited by PARTITION_MAX_KEYS. */
2442 
2443  /* Check if we need to recurse. */
2444  Assert(start != NULL);
2445  cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno;
2446  if (cur_keyno < step_lastkeyno - 1)
2447  {
2448  PartClauseInfo *pc;
2449  ListCell *next_start;
2450 
2451  /*
2452  * For each clause with cur_keyno, add its expr and cmpfn to
2453  * step_exprs and step_cmpfns, respectively, and recurse after setting
2454  * next_start to the ListCell of the first clause for the next
2455  * partition key.
2456  */
2457  for_each_cell(lc, prefix, start)
2458  {
2459  pc = lfirst(lc);
2460 
2461  if (pc->keyno > cur_keyno)
2462  break;
2463  }
2464  next_start = lc;
2465 
2466  for_each_cell(lc, prefix, start)
2467  {
2468  List *moresteps;
2469  List *step_exprs1,
2470  *step_cmpfns1;
2471 
2472  pc = lfirst(lc);
2473  if (pc->keyno == cur_keyno)
2474  {
2475  /* Leave the original step_exprs unmodified. */
2476  step_exprs1 = list_copy(step_exprs);
2477  step_exprs1 = lappend(step_exprs1, pc->expr);
2478 
2479  /* Leave the original step_cmpfns unmodified. */
2480  step_cmpfns1 = list_copy(step_cmpfns);
2481  step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2482  }
2483  else
2484  {
2485  Assert(pc->keyno > cur_keyno);
2486  break;
2487  }
2488 
2489  moresteps = get_steps_using_prefix_recurse(context,
2490  step_opstrategy,
2491  step_op_is_ne,
2492  step_lastexpr,
2493  step_lastcmpfn,
2494  step_lastkeyno,
2495  step_nullkeys,
2496  prefix,
2497  next_start,
2498  step_exprs1,
2499  step_cmpfns1);
2500  result = list_concat(result, moresteps);
2501 
2502  list_free(step_exprs1);
2503  list_free(step_cmpfns1);
2504  }
2505  }
2506  else
2507  {
2508  /*
2509  * End the current recursion cycle and start generating steps, one for
2510  * each clause with cur_keyno, which is all clauses from here onward
2511  * till the end of the list. Note that for hash partitioning,
2512  * step_nullkeys is allowed to be non-empty, in which case step_exprs
2513  * would only contain expressions for the earlier partition keys that
2514  * are not specified in step_nullkeys.
2515  */
2516  Assert(list_length(step_exprs) == cur_keyno ||
2517  !bms_is_empty(step_nullkeys));
2518 
2519  /*
2520  * Note also that for hash partitioning, each partition key should
2521  * have either equality clauses or an IS NULL clause, so if a
2522  * partition key doesn't have an expression, it would be specified in
2523  * step_nullkeys.
2524  */
2525  Assert(context->rel->part_scheme->strategy
2527  list_length(step_exprs) + 2 + bms_num_members(step_nullkeys) ==
2528  context->rel->part_scheme->partnatts);
2529  for_each_cell(lc, prefix, start)
2530  {
2531  PartClauseInfo *pc = lfirst(lc);
2532  PartitionPruneStep *step;
2533  List *step_exprs1,
2534  *step_cmpfns1;
2535 
2536  Assert(pc->keyno == cur_keyno);
2537 
2538  /* Leave the original step_exprs unmodified. */
2539  step_exprs1 = list_copy(step_exprs);
2540  step_exprs1 = lappend(step_exprs1, pc->expr);
2541  step_exprs1 = lappend(step_exprs1, step_lastexpr);
2542 
2543  /* Leave the original step_cmpfns unmodified. */
2544  step_cmpfns1 = list_copy(step_cmpfns);
2545  step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2546  step_cmpfns1 = lappend_oid(step_cmpfns1, step_lastcmpfn);
2547 
2548  step = gen_prune_step_op(context,
2549  step_opstrategy, step_op_is_ne,
2550  step_exprs1, step_cmpfns1,
2551  step_nullkeys);
2552  result = lappend(result, step);
2553  }
2554  }
2555 
2556  return result;
2557 }
#define NIL
Definition: pg_list.h:65
#define for_each_cell(cell, lst, initcell)
Definition: pg_list.h:417
List * list_copy(const List *oldlist)
Definition: list.c:1418
List * list_concat(List *list1, const List *list2)
Definition: list.c:530
List * lappend_oid(List *list, Oid datum)
Definition: list.c:372
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:646
void check_stack_depth(void)
Definition: postgres.c:3469
List * lappend(List *list, void *datum)
Definition: list.c:336
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:826
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:2424
#define Assert(condition)
Definition: c.h:804
#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:1391
PartitionScheme part_scheme
Definition: pathnodes.h:760
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:1314
Definition: pg_list.h:50

◆ make_partition_pruneinfo()

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

Definition at line 222 of file partprune.c.

References add_part_relids(), PlannerInfo::append_rel_array, Assert, bms_add_member(), bms_add_range(), bms_del_members(), bms_join(), bms_num_members(), find_base_rel(), i, IS_PARTITIONED_REL, lappend(), lfirst, list_length(), make_partitionedrel_pruneinfo(), makeNode, NIL, PartitionPruneInfo::other_subplans, palloc0(), Path::parent, AppendRelInfo::parent_relid, pfree(), PartitionPruneInfo::prune_infos, RelOptInfo::relid, RELOPT_OTHER_MEMBER_REL, RelOptInfo::reloptkind, and PlannerInfo::simple_rel_array_size.

Referenced by create_append_plan(), and create_merge_append_plan().

225 {
226  PartitionPruneInfo *pruneinfo;
227  Bitmapset *allmatchedsubplans = NULL;
228  List *allpartrelids;
229  List *prunerelinfos;
230  int *relid_subplan_map;
231  ListCell *lc;
232  int i;
233 
234  /*
235  * Scan the subpaths to see which ones are scans of partition child
236  * relations, and identify their parent partitioned rels. (Note: we must
237  * restrict the parent partitioned rels to be parentrel or children of
238  * parentrel, otherwise we couldn't translate prunequal to match.)
239  *
240  * Also construct a temporary array to map from partition-child-relation
241  * relid to the index in 'subpaths' of the scan plan for that partition.
242  * (Use of "subplan" rather than "subpath" is a bit of a misnomer, but
243  * we'll let it stand.) For convenience, we use 1-based indexes here, so
244  * that zero can represent an un-filled array entry.
245  */
246  allpartrelids = NIL;
247  relid_subplan_map = palloc0(sizeof(int) * root->simple_rel_array_size);
248 
249  i = 1;
250  foreach(lc, subpaths)
251  {
252  Path *path = (Path *) lfirst(lc);
253  RelOptInfo *pathrel = path->parent;
254 
255  /* We don't consider partitioned joins here */
256  if (pathrel->reloptkind == RELOPT_OTHER_MEMBER_REL)
257  {
258  RelOptInfo *prel = pathrel;
259  Bitmapset *partrelids = NULL;
260 
261  /*
262  * Traverse up to the pathrel's topmost partitioned parent,
263  * collecting parent relids as we go; but stop if we reach
264  * parentrel. (Normally, a pathrel's topmost partitioned parent
265  * is either parentrel or a UNION ALL appendrel child of
266  * parentrel. But when handling partitionwise joins of
267  * multi-level partitioning trees, we can see an append path whose
268  * parentrel is an intermediate partitioned table.)
269  */
270  do
271  {
272  AppendRelInfo *appinfo;
273 
274  Assert(prel->relid < root->simple_rel_array_size);
275  appinfo = root->append_rel_array[prel->relid];
276  prel = find_base_rel(root, appinfo->parent_relid);
277  if (!IS_PARTITIONED_REL(prel))
278  break; /* reached a non-partitioned parent */
279  /* accept this level as an interesting parent */
280  partrelids = bms_add_member(partrelids, prel->relid);
281  if (prel == parentrel)
282  break; /* don't traverse above parentrel */
283  } while (prel->reloptkind == RELOPT_OTHER_MEMBER_REL);
284 
285  if (partrelids)
286  {
287  /*
288  * Found some relevant parent partitions, which may or may not
289  * overlap with partition trees we already found. Add new
290  * information to the allpartrelids list.
291  */
292  allpartrelids = add_part_relids(allpartrelids, partrelids);
293  /* Also record the subplan in relid_subplan_map[] */
294  /* No duplicates please */
295  Assert(relid_subplan_map[pathrel->relid] == 0);
296  relid_subplan_map[pathrel->relid] = i;
297  }
298  }
299  i++;
300  }
301 
302  /*
303  * We now build a PartitionedRelPruneInfo for each topmost partitioned rel
304  * (omitting any that turn out not to have useful pruning quals).
305  */
306  prunerelinfos = NIL;
307  foreach(lc, allpartrelids)
308  {
309  Bitmapset *partrelids = (Bitmapset *) lfirst(lc);
310  List *pinfolist;
311  Bitmapset *matchedsubplans = NULL;
312 
313  pinfolist = make_partitionedrel_pruneinfo(root, parentrel,
314  prunequal,
315  partrelids,
316  relid_subplan_map,
317  &matchedsubplans);
318 
319  /* When pruning is possible, record the matched subplans */
320  if (pinfolist != NIL)
321  {
322  prunerelinfos = lappend(prunerelinfos, pinfolist);
323  allmatchedsubplans = bms_join(matchedsubplans,
324  allmatchedsubplans);
325  }
326  }
327 
328  pfree(relid_subplan_map);
329 
330  /*
331  * If none of the partition hierarchies had any useful run-time pruning
332  * quals, then we can just not bother with run-time pruning.
333  */
334  if (prunerelinfos == NIL)
335  return NULL;
336 
337  /* Else build the result data structure */
338  pruneinfo = makeNode(PartitionPruneInfo);
339  pruneinfo->prune_infos = prunerelinfos;
340 
341  /*
342  * Some subplans may not belong to any of the identified partitioned rels.
343  * This can happen for UNION ALL queries which include a non-partitioned
344  * table, or when some of the hierarchies aren't run-time prunable. Build
345  * a bitmapset of the indexes of all such subplans, so that the executor
346  * can identify which subplans should never be pruned.
347  */
348  if (bms_num_members(allmatchedsubplans) < list_length(subpaths))
349  {
350  Bitmapset *other_subplans;
351 
352  /* Create the complement of allmatchedsubplans */
353  other_subplans = bms_add_range(NULL, 0, list_length(subpaths) - 1);
354  other_subplans = bms_del_members(other_subplans, allmatchedsubplans);
355 
356  pruneinfo->other_subplans = other_subplans;
357  }
358  else
359  pruneinfo->other_subplans = NULL;
360 
361  return pruneinfo;
362 }
#define NIL
Definition: pg_list.h:65
RelOptKind reloptkind
Definition: pathnodes.h:678
static List * make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, List *prunequal, Bitmapset *partrelids, int *relid_subplan_map, Bitmapset **matchedsubplans)
Definition: partprune.c:440
void pfree(void *pointer)
Definition: mcxt.c:1169
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:1182
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:646
int simple_rel_array_size
Definition: pathnodes.h:187
Index relid
Definition: pathnodes.h:709
List * lappend(List *list, void *datum)
Definition: list.c:336
void * palloc0(Size size)
Definition: mcxt.c:1093
struct AppendRelInfo ** append_rel_array
Definition: pathnodes.h:202
#define makeNode(_type_)
Definition: nodes.h:585
Bitmapset * other_subplans
Definition: plannodes.h:1165
#define Assert(condition)
Definition: c.h:804
#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
Bitmapset * bms_del_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:928
#define IS_PARTITIONED_REL(rel)
Definition: pathnodes.h:786
int i
RelOptInfo * find_base_rel(PlannerInfo *root, int relid)
Definition: relnode.c:375
Index parent_relid
Definition: pathnodes.h:2306
static List * add_part_relids(List *allpartrelids, Bitmapset *partrelids)
Definition: partprune.c:394
Definition: pg_list.h:50

◆ make_partitionedrel_pruneinfo()

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

Definition at line 440 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, RelOptInfo::live_parts, 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().

445 {
446  RelOptInfo *targetpart = NULL;
447  List *pinfolist = NIL;
448  bool doruntimeprune = false;
449  int *relid_subpart_map;
450  Bitmapset *subplansfound = NULL;
451  ListCell *lc;
452  int rti;
453  int i;
454 
455  /*
456  * Examine each partitioned rel, constructing a temporary array to map
457  * from planner relids to index of the partitioned rel, and building a
458  * PartitionedRelPruneInfo for each partitioned rel.
459  *
460  * In this phase we discover whether runtime pruning is needed at all; if
461  * not, we can avoid doing further work.
462  */
463  relid_subpart_map = palloc0(sizeof(int) * root->simple_rel_array_size);
464 
465  i = 1;
466  rti = -1;
467  while ((rti = bms_next_member(partrelids, rti)) > 0)
468  {
469  RelOptInfo *subpart = find_base_rel(root, rti);
471  List *partprunequal;
472  List *initial_pruning_steps;
473  List *exec_pruning_steps;
474  Bitmapset *execparamids;
476 
477  /*
478  * Fill the mapping array.
479  *
480  * relid_subpart_map maps relid of a non-leaf partition to the index
481  * in the returned PartitionedRelPruneInfo list of the info for that
482  * partition. We use 1-based indexes here, so that zero can represent
483  * an un-filled array entry.
484  */
485  Assert(rti < root->simple_rel_array_size);
486  relid_subpart_map[rti] = i++;
487 
488  /*
489  * Translate pruning qual, if necessary, for this partition.
490  *
491  * The first item in the list is the target partitioned relation.
492  */
493  if (!targetpart)
494  {
495  targetpart = subpart;
496 
497  /*
498  * The prunequal is presented to us as a qual for 'parentrel'.
499  * Frequently this rel is the same as targetpart, so we can skip
500  * an adjust_appendrel_attrs step. But it might not be, and then
501  * we have to translate. We update the prunequal parameter here,
502  * because in later iterations of the loop for child partitions,
503  * we want to translate from parent to child variables.
504  */
505  if (!bms_equal(parentrel->relids, subpart->relids))
506  {
507  int nappinfos;
508  AppendRelInfo **appinfos = find_appinfos_by_relids(root,
509  subpart->relids,
510  &nappinfos);
511 
512  prunequal = (List *) adjust_appendrel_attrs(root, (Node *)
513  prunequal,
514  nappinfos,
515  appinfos);
516 
517  pfree(appinfos);
518  }
519 
520  partprunequal = prunequal;
521  }
522  else
523  {
524  /*
525  * For sub-partitioned tables the columns may not be in the same
526  * order as the parent, so we must translate the prunequal to make
527  * it compatible with this relation.
528  */
529  partprunequal = (List *)
531  (Node *) prunequal,
532  subpart->relids,
533  targetpart->relids);
534  }
535 
536  /*
537  * Convert pruning qual to pruning steps. We may need to do this
538  * twice, once to obtain executor startup pruning steps, and once for
539  * executor per-scan pruning steps. This first pass creates startup
540  * pruning steps and detects whether there's any possibly-useful quals
541  * that would require per-scan pruning.
542  */
543  gen_partprune_steps(subpart, partprunequal, PARTTARGET_INITIAL,
544  &context);
545 
546  if (context.contradictory)
547  {
548  /*
549  * This shouldn't happen as the planner should have detected this
550  * earlier. However, we do use additional quals from parameterized
551  * paths here. These do only compare Params to the partition key,
552  * so this shouldn't cause the discovery of any new qual
553  * contradictions that were not previously discovered as the Param
554  * values are unknown during planning. Anyway, we'd better do
555  * something sane here, so let's just disable run-time pruning.
556  */
557  return NIL;
558  }
559 
560  /*
561  * If no mutable operators or expressions appear in usable pruning
562  * clauses, then there's no point in running startup pruning, because
563  * plan-time pruning should have pruned everything prunable.
564  */
565  if (context.has_mutable_op || context.has_mutable_arg)
566  initial_pruning_steps = context.steps;
567  else
568  initial_pruning_steps = NIL;
569 
570  /*
571  * If no exec Params appear in potentially-usable pruning clauses,
572  * then there's no point in even thinking about per-scan pruning.
573  */
574  if (context.has_exec_param)
575  {
576  /* ... OK, we'd better think about it */
577  gen_partprune_steps(subpart, partprunequal, PARTTARGET_EXEC,
578  &context);
579 
580  if (context.contradictory)
581  {
582  /* As above, skip run-time pruning if anything fishy happens */
583  return NIL;
584  }
585 
586  exec_pruning_steps = context.steps;
587 
588  /*
589  * Detect which exec Params actually got used; the fact that some
590  * were in available clauses doesn't mean we actually used them.
591  * Skip per-scan pruning if there are none.
592  */
593  execparamids = get_partkey_exec_paramids(exec_pruning_steps);
594 
595  if (bms_is_empty(execparamids))
596  exec_pruning_steps = NIL;
597  }
598  else
599  {
600  /* No exec Params anywhere, so forget about scan-time pruning */
601  exec_pruning_steps = NIL;
602  execparamids = NULL;
603  }
604 
605  if (initial_pruning_steps || exec_pruning_steps)
606  doruntimeprune = true;
607 
608  /* Begin constructing the PartitionedRelPruneInfo for this rel */
610  pinfo->rtindex = rti;
611  pinfo->initial_pruning_steps = initial_pruning_steps;
612  pinfo->exec_pruning_steps = exec_pruning_steps;
613  pinfo->execparamids = execparamids;
614  /* Remaining fields will be filled in the next loop */
615 
616  pinfolist = lappend(pinfolist, pinfo);
617  }
618 
619  if (!doruntimeprune)
620  {
621  /* No run-time pruning required. */
622  pfree(relid_subpart_map);
623  return NIL;
624  }
625 
626  /*
627  * Run-time pruning will be required, so initialize other information.
628  * That includes two maps -- one needed to convert partition indexes of
629  * leaf partitions to the indexes of their subplans in the subplan list,
630  * another needed to convert partition indexes of sub-partitioned
631  * partitions to the indexes of their PartitionedRelPruneInfo in the
632  * PartitionedRelPruneInfo list.
633  */
634  foreach(lc, pinfolist)
635  {
636  PartitionedRelPruneInfo *pinfo = lfirst(lc);
637  RelOptInfo *subpart = find_base_rel(root, pinfo->rtindex);
638  Bitmapset *present_parts;
639  int nparts = subpart->nparts;
640  int *subplan_map;
641  int *subpart_map;
642  Oid *relid_map;
643 
644  /*
645  * Construct the subplan and subpart maps for this partitioning level.
646  * Here we convert to zero-based indexes, with -1 for empty entries.
647  * Also construct a Bitmapset of all partitions that are present (that
648  * is, not pruned already).
649  */
650  subplan_map = (int *) palloc(nparts * sizeof(int));
651  memset(subplan_map, -1, nparts * sizeof(int));
652  subpart_map = (int *) palloc(nparts * sizeof(int));
653  memset(subpart_map, -1, nparts * sizeof(int));
654  relid_map = (Oid *) palloc0(nparts * sizeof(Oid));
655  present_parts = NULL;
656 
657  i = -1;
658  while ((i = bms_next_member(subpart->live_parts, i)) >= 0)
659  {
660  RelOptInfo *partrel = subpart->part_rels[i];
661  int subplanidx;
662  int subpartidx;
663 
664  Assert(partrel != NULL);
665 
666  subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;
667  subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
668  relid_map[i] = planner_rt_fetch(partrel->relid, root)->relid;
669  if (subplanidx >= 0)
670  {
671  present_parts = bms_add_member(present_parts, i);
672 
673  /* Record finding this subplan */
674  subplansfound = bms_add_member(subplansfound, subplanidx);
675  }
676  else if (subpartidx >= 0)
677  present_parts = bms_add_member(present_parts, i);
678  }
679 
680  /*
681  * Ensure there were no stray PartitionedRelPruneInfo generated for
682  * partitioned tables that we have no sub-paths or
683  * sub-PartitionedRelPruneInfo for.
684  */
685  Assert(!bms_is_empty(present_parts));
686 
687  /* Record the maps and other information. */
688  pinfo->present_parts = present_parts;
689  pinfo->nparts = nparts;
690  pinfo->subplan_map = subplan_map;
691  pinfo->subpart_map = subpart_map;
692  pinfo->relid_map = relid_map;
693  }
694 
695  pfree(relid_subpart_map);
696 
697  *matchedsubplans = subplansfound;
698 
699  return pinfolist;
700 }
static void gen_partprune_steps(RelOptInfo *rel, List *clauses, PartClauseTarget target, GeneratePruningStepsContext *context)
Definition: partprune.c:716
#define NIL
Definition: pg_list.h:65
Bitmapset * execparamids
Definition: plannodes.h:1204
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1043
Definition: nodes.h:537
unsigned int Oid
Definition: postgres_ext.h:31
Bitmapset * live_parts
Definition: pathnodes.h:770
Node * adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node, Relids child_relids, Relids top_parent_relids)
Definition: appendinfo.c:488
void pfree(void *pointer)
Definition: mcxt.c:1169
#define planner_rt_fetch(rti, root)
Definition: pathnodes.h:388
static Bitmapset * get_partkey_exec_paramids(List *steps)
Definition: partprune.c:3300
int nparts
Definition: pathnodes.h:761
Bitmapset * present_parts
Definition: plannodes.h:1189
Relids relids
Definition: pathnodes.h:681
int simple_rel_array_size
Definition: pathnodes.h:187
Index relid
Definition: pathnodes.h:709
List * lappend(List *list, void *datum)
Definition: list.c:336
AppendRelInfo ** find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
Definition: appendinfo.c:715
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
void * palloc0(Size size)
Definition: mcxt.c:1093
#define makeNode(_type_)
Definition: nodes.h:585
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
struct RelOptInfo ** part_rels
Definition: pathnodes.h:768
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:736
void * palloc(Size size)
Definition: mcxt.c:1062
int i
RelOptInfo * find_base_rel(PlannerInfo *root, int relid)
Definition: relnode.c:375
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:195

◆ match_boolean_partition_clause()

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

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

3593 {
3594  Expr *leftop;
3595 
3596  *outconst = NULL;
3597 
3598  if (!IsBooleanOpfamily(partopfamily))
3599  return PARTCLAUSE_UNSUPPORTED;
3600 
3601  if (IsA(clause, BooleanTest))
3602  {
3603  BooleanTest *btest = (BooleanTest *) clause;
3604 
3605  /* Only IS [NOT] TRUE/FALSE are any good to us */
3606  if (btest->booltesttype == IS_UNKNOWN ||
3607  btest->booltesttype == IS_NOT_UNKNOWN)
3608  return PARTCLAUSE_UNSUPPORTED;
3609 
3610  leftop = btest->arg;
3611  if (IsA(leftop, RelabelType))
3612  leftop = ((RelabelType *) leftop)->arg;
3613 
3614  if (equal(leftop, partkey))
3615  *outconst = (btest->booltesttype == IS_TRUE ||
3616  btest->booltesttype == IS_NOT_FALSE)
3617  ? (Expr *) makeBoolConst(true, false)
3618  : (Expr *) makeBoolConst(false, false);
3619 
3620  if (*outconst)
3621  return PARTCLAUSE_MATCH_CLAUSE;
3622  }
3623  else
3624  {
3625  bool is_not_clause = is_notclause(clause);
3626 
3627  leftop = is_not_clause ? get_notclausearg(clause) : clause;
3628 
3629  if (IsA(leftop, RelabelType))
3630  leftop = ((RelabelType *) leftop)->arg;
3631 
3632  /* Compare to the partition key, and make up a clause ... */
3633  if (equal(leftop, partkey))
3634  *outconst = is_not_clause ?
3635  (Expr *) makeBoolConst(false, false) :
3636  (Expr *) makeBoolConst(true, false);
3637  else if (equal(negate_clause((Node *) leftop), partkey))
3638  *outconst = (Expr *) makeBoolConst(false, false);
3639 
3640  if (*outconst)
3641  return PARTCLAUSE_MATCH_CLAUSE;
3642  }
3643 
3644  return PARTCLAUSE_NOMATCH;
3645 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:588
Node * negate_clause(Node *node)
Definition: prepqual.c:74
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3149
Definition: nodes.h:537
#define false
Definition: c.h:399
#define true
Definition: c.h:395
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:357
Expr * arg
Definition: primnodes.h:1288
static bool is_notclause(const void *clause)
Definition: nodeFuncs.h:115
BoolTestType booltesttype
Definition: primnodes.h:1289
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 1794 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().

1798 {
1799  PartClauseMatchStatus boolmatchstatus;
1800  PartitionScheme part_scheme = context->rel->part_scheme;
1801  Oid partopfamily = part_scheme->partopfamily[partkeyidx],
1802  partcoll = part_scheme->partcollation[partkeyidx];
1803  Expr *expr;
1804 
1805  /*
1806  * Recognize specially shaped clauses that match a Boolean partition key.
1807  */
1808  boolmatchstatus = match_boolean_partition_clause(partopfamily, clause,
1809  partkey, &expr);
1810 
1811  if (boolmatchstatus == PARTCLAUSE_MATCH_CLAUSE)
1812  {
1813  PartClauseInfo *partclause;
1814 
1815  partclause = (PartClauseInfo *) palloc(sizeof(PartClauseInfo));
1816  partclause->keyno = partkeyidx;
1817  /* Do pruning with the Boolean equality operator. */
1818  partclause->opno = BooleanEqualOperator;
1819  partclause->op_is_ne = false;
1820  partclause->expr = expr;
1821  /* We know that expr is of Boolean type. */
1822  partclause->cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
1823  partclause->op_strategy = InvalidStrategy;
1824 
1825  *pc = partclause;
1826 
1827  return PARTCLAUSE_MATCH_CLAUSE;
1828  }
1829  else if (IsA(clause, OpExpr) &&
1830  list_length(((OpExpr *) clause)->args) == 2)
1831  {
1832  OpExpr *opclause = (OpExpr *) clause;
1833  Expr *leftop,
1834  *rightop;
1835  Oid opno,
1836  op_lefttype,
1837  op_righttype,
1838  negator = InvalidOid;
1839  Oid cmpfn;
1840  int op_strategy;
1841  bool is_opne_listp = false;
1842  PartClauseInfo *partclause;
1843 
1844  leftop = (Expr *) get_leftop(clause);
1845  if (IsA(leftop, RelabelType))
1846  leftop = ((RelabelType *) leftop)->arg;
1847  rightop = (Expr *) get_rightop(clause);
1848  if (IsA(rightop, RelabelType))
1849  rightop = ((RelabelType *) rightop)->arg;
1850  opno = opclause->opno;
1851 
1852  /* check if the clause matches this partition key */
1853  if (equal(leftop, partkey))
1854  expr = rightop;
1855  else if (equal(rightop, partkey))
1856  {
1857  /*
1858  * It's only useful if we can commute the operator to put the
1859  * partkey on the left. If we can't, the clause can be deemed
1860  * UNSUPPORTED. Even if its leftop matches some later partkey, we
1861  * now know it has Vars on the right, so it's no use.
1862  */
1863  opno = get_commutator(opno);
1864  if (!OidIsValid(opno))
1865  return PARTCLAUSE_UNSUPPORTED;
1866  expr = leftop;
1867  }
1868  else
1869  /* clause does not match this partition key, but perhaps next. */
1870  return PARTCLAUSE_NOMATCH;
1871 
1872  /*
1873  * Partition key match also requires collation match. There may be
1874  * multiple partkeys with the same expression but different
1875  * collations, so failure is NOMATCH.
1876  */
1877  if (!PartCollMatchesExprColl(partcoll, opclause->inputcollid))
1878  return PARTCLAUSE_NOMATCH;
1879 
1880  /*
1881  * See if the operator is relevant to the partitioning opfamily.
1882  *
1883  * Normally we only care about operators that are listed as being part
1884  * of the partitioning operator family. But there is one exception:
1885  * the not-equals operators are not listed in any operator family
1886  * whatsoever, but their negators (equality) are. We can use one of
1887  * those if we find it, but only for list partitioning.
1888  *
1889  * Note: we report NOMATCH on failure, in case a later partkey has the
1890  * same expression but different opfamily. That's unlikely, but not
1891  * much more so than duplicate expressions with different collations.
1892  */
1893  if (op_in_opfamily(opno, partopfamily))
1894  {
1895  get_op_opfamily_properties(opno, partopfamily, false,
1896  &op_strategy, &op_lefttype,
1897  &op_righttype);
1898  }
1899  else
1900  {
1901  if (part_scheme->strategy != PARTITION_STRATEGY_LIST)
1902  return PARTCLAUSE_NOMATCH;
1903 
1904  /* See if the negator is equality */
1905  negator = get_negator(opno);
1906  if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
1907  {
1908  get_op_opfamily_properties(negator, partopfamily, false,
1909  &op_strategy, &op_lefttype,
1910  &op_righttype);
1911  if (op_strategy == BTEqualStrategyNumber)
1912  is_opne_listp = true; /* bingo */
1913  }
1914 
1915  /* Nope, it's not <> either. */
1916  if (!is_opne_listp)
1917  return PARTCLAUSE_NOMATCH;
1918  }
1919 
1920  /*
1921  * Only allow strict operators. This will guarantee nulls are
1922  * filtered. (This test is likely useless, since btree and hash
1923  * comparison operators are generally strict.)
1924  */
1925  if (!op_strict(opno))
1926  return PARTCLAUSE_UNSUPPORTED;
1927 
1928  /*
1929  * OK, we have a match to the partition key and a suitable operator.
1930  * Examine the other argument to see if it's usable for pruning.
1931  *
1932  * In most of these cases, we can return UNSUPPORTED because the same
1933  * failure would occur no matter which partkey it's matched to. (In
1934  * particular, now that we've successfully matched one side of the
1935  * opclause to a partkey, there is no chance that matching the other
1936  * side to another partkey will produce a usable result, since that'd
1937  * mean there are Vars on both sides.)
1938  *
1939  * Also, if we reject an argument for a target-dependent reason, set
1940  * appropriate fields of *context to report that. We postpone these
1941  * tests until after matching the partkey and the operator, so as to
1942  * reduce the odds of setting the context fields for clauses that do
1943  * not end up contributing to pruning steps.
1944  *
1945  * First, check for non-Const argument. (We assume that any immutable
1946  * subexpression will have been folded to a Const already.)
1947  */
1948  if (!IsA(expr, Const))
1949  {
1950  Bitmapset *paramids;
1951 
1952  /*
1953  * When pruning in the planner, we only support pruning using
1954  * comparisons to constants. We cannot prune on the basis of
1955  * anything that's not immutable. (Note that has_mutable_arg and
1956  * has_exec_param do not get set for this target value.)
1957  */
1958  if (context->target == PARTTARGET_PLANNER)
1959  return PARTCLAUSE_UNSUPPORTED;
1960 
1961  /*
1962  * We can never prune using an expression that contains Vars.
1963  */
1964  if (contain_var_clause((Node *) expr))
1965  return PARTCLAUSE_UNSUPPORTED;
1966 
1967  /*
1968  * And we must reject anything containing a volatile function.
1969  * Stable functions are OK though.
1970  */
1971  if (contain_volatile_functions((Node *) expr))
1972  return PARTCLAUSE_UNSUPPORTED;
1973 
1974  /*
1975  * See if there are any exec Params. If so, we can only use this
1976  * expression during per-scan pruning.
1977  */
1978  paramids = pull_exec_paramids(expr);
1979  if (!bms_is_empty(paramids))
1980  {
1981  context->has_exec_param = true;
1982  if (context->target != PARTTARGET_EXEC)
1983  return PARTCLAUSE_UNSUPPORTED;
1984  }
1985  else
1986  {
1987  /* It's potentially usable, but mutable */
1988  context->has_mutable_arg = true;
1989  }
1990  }
1991 
1992  /*
1993  * Check whether the comparison operator itself is immutable. (We
1994  * assume anything that's in a btree or hash opclass is at least
1995  * stable, but we need to check for immutability.)
1996  */
1997  if (op_volatile(opno) != PROVOLATILE_IMMUTABLE)
1998  {
1999  context->has_mutable_op = true;
2000 
2001  /*
2002  * When pruning in the planner, we cannot prune with mutable
2003  * operators.
2004  */
2005  if (context->target == PARTTARGET_PLANNER)
2006  return PARTCLAUSE_UNSUPPORTED;
2007  }
2008 
2009  /*
2010  * Now find the procedure to use, based on the types. If the clause's
2011  * other argument is of the same type as the partitioning opclass's
2012  * declared input type, we can use the procedure cached in
2013  * PartitionKey. If not, search for a cross-type one in the same
2014  * opfamily; if one doesn't exist, report no match.
2015  */
2016  if (op_righttype == part_scheme->partopcintype[partkeyidx])
2017  cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
2018  else
2019  {
2020  switch (part_scheme->strategy)
2021  {
2022  /*
2023  * For range and list partitioning, we need the ordering
2024  * procedure with lefttype being the partition key's type,
2025  * and righttype the clause's operator's right type.
2026  */
2029  cmpfn =
2030  get_opfamily_proc(part_scheme->partopfamily[partkeyidx],
2031  part_scheme->partopcintype[partkeyidx],
2032  op_righttype, BTORDER_PROC);
2033  break;
2034 
2035  /*
2036  * For hash partitioning, we need the hashing procedure
2037  * for the clause's type.
2038  */
2040  cmpfn =
2041  get_opfamily_proc(part_scheme->partopfamily[partkeyidx],
2042  op_righttype, op_righttype,
2044  break;
2045 
2046  default:
2047  elog(ERROR, "invalid partition strategy: %c",
2048  part_scheme->strategy);
2049  cmpfn = InvalidOid; /* keep compiler quiet */
2050  break;
2051  }
2052 
2053  if (!OidIsValid(cmpfn))
2054  return PARTCLAUSE_NOMATCH;
2055  }
2056 
2057  /*
2058  * Build the clause, passing the negator if applicable.
2059  */
2060  partclause = (PartClauseInfo *) palloc(sizeof(PartClauseInfo));
2061  partclause->keyno = partkeyidx;
2062  if (is_opne_listp)
2063  {
2064  Assert(OidIsValid(negator));
2065  partclause->opno = negator;
2066  partclause->op_is_ne = true;
2067  partclause->op_strategy = InvalidStrategy;
2068  }
2069  else
2070  {
2071  partclause->opno = opno;
2072  partclause->op_is_ne = false;
2073  partclause->op_strategy = op_strategy;
2074  }
2075  partclause->expr = expr;
2076  partclause->cmpfn = cmpfn;
2077 
2078  *pc = partclause;
2079 
2080  return PARTCLAUSE_MATCH_CLAUSE;
2081  }
2082  else if (IsA(clause, ScalarArrayOpExpr))
2083  {
2084  ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
2085  Oid saop_op = saop->opno;
2086  Oid saop_coll = saop->inputcollid;
2087  Expr *leftop = (Expr *) linitial(saop->args),
2088  *rightop = (Expr *) lsecond(saop->args);
2089  List *elem_exprs,
2090  *elem_clauses;
2091  ListCell *lc1;
2092 
2093  if (IsA(leftop, RelabelType))
2094  leftop = ((RelabelType *) leftop)->arg;
2095 
2096  /* check if the LHS matches this partition key */
2097  if (!equal(leftop, partkey) ||
2098  !PartCollMatchesExprColl(partcoll, saop->inputcollid))
2099  return PARTCLAUSE_NOMATCH;
2100 
2101  /*
2102  * See if the operator is relevant to the partitioning opfamily.
2103  *
2104  * In case of NOT IN (..), we get a '<>', which we handle if list
2105  * partitioning is in use and we're able to confirm that it's negator
2106  * is a btree equality operator belonging to the partitioning operator
2107  * family. As above, report NOMATCH for non-matching operator.
2108  */
2109  if (!op_in_opfamily(saop_op, partopfamily))
2110  {
2111  Oid negator;
2112 
2113  if (part_scheme->strategy != PARTITION_STRATEGY_LIST)
2114  return PARTCLAUSE_NOMATCH;
2115 
2116  negator = get_negator(saop_op);
2117  if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
2118  {
2119  int strategy;
2120  Oid lefttype,
2121  righttype;
2122 
2123  get_op_opfamily_properties(negator, partopfamily,
2124  false, &strategy,
2125  &lefttype, &righttype);
2126  if (strategy != BTEqualStrategyNumber)
2127  return PARTCLAUSE_NOMATCH;
2128  }
2129  else
2130  return PARTCLAUSE_NOMATCH; /* no useful negator */
2131  }
2132 
2133  /*
2134  * Only allow strict operators. This will guarantee nulls are
2135  * filtered. (This test is likely useless, since btree and hash
2136  * comparison operators are generally strict.)
2137  */
2138  if (!op_strict(saop_op))
2139  return PARTCLAUSE_UNSUPPORTED;
2140 
2141  /*
2142  * OK, we have a match to the partition key and a suitable operator.
2143  * Examine the array argument to see if it's usable for pruning. This
2144  * is identical to the logic for a plain OpExpr.
2145  */
2146  if (!IsA(rightop, Const))
2147  {
2148  Bitmapset *paramids;
2149 
2150  /*
2151  * When pruning in the planner, we only support pruning using
2152  * comparisons to constants. We cannot prune on the basis of
2153  * anything that's not immutable. (Note that has_mutable_arg and
2154  * has_exec_param do not get set for this target value.)
2155  */
2156  if (context->target == PARTTARGET_PLANNER)
2157  return PARTCLAUSE_UNSUPPORTED;
2158 
2159  /*
2160  * We can never prune using an expression that contains Vars.
2161  */
2162  if (contain_var_clause((Node *) rightop))
2163  return PARTCLAUSE_UNSUPPORTED;
2164 
2165  /*
2166  * And we must reject anything containing a volatile function.
2167  * Stable functions are OK though.
2168  */
2169  if (contain_volatile_functions((Node *) rightop))
2170  return PARTCLAUSE_UNSUPPORTED;
2171 
2172  /*
2173  * See if there are any exec Params. If so, we can only use this
2174  * expression during per-scan pruning.
2175  */
2176  paramids = pull_exec_paramids(rightop);
2177  if (!bms_is_empty(paramids))
2178  {
2179  context->has_exec_param = true;
2180  if (context->target != PARTTARGET_EXEC)
2181  return PARTCLAUSE_UNSUPPORTED;
2182  }
2183  else
2184  {
2185  /* It's potentially usable, but mutable */
2186  context->has_mutable_arg = true;
2187  }
2188  }
2189 
2190  /*
2191  * Check whether the comparison operator itself is immutable. (We
2192  * assume anything that's in a btree or hash opclass is at least
2193  * stable, but we need to check for immutability.)
2194  */
2195  if (op_volatile(saop_op) != PROVOLATILE_IMMUTABLE)
2196  {
2197  context->has_mutable_op = true;
2198 
2199  /*
2200  * When pruning in the planner, we cannot prune with mutable
2201  * operators.
2202  */
2203  if (context->target == PARTTARGET_PLANNER)
2204  return PARTCLAUSE_UNSUPPORTED;
2205  }
2206 
2207  /*
2208  * Examine the contents of the array argument.
2209  */
2210  elem_exprs = NIL;
2211  if (IsA(rightop, Const))
2212  {
2213  /*
2214  * For a constant array, convert the elements to a list of Const
2215  * nodes, one for each array element (excepting nulls).
2216  */
2217  Const *arr = (Const *) rightop;
2218  ArrayType *arrval;
2219  int16 elemlen;
2220  bool elembyval;
2221  char elemalign;
2222  Datum *elem_values;
2223  bool *elem_nulls;
2224  int num_elems,
2225  i;
2226 
2227  /* If the array itself is null, the saop returns null */
2228  if (arr->constisnull)
2230 
2231  arrval = DatumGetArrayTypeP(arr->constvalue);
2233  &elemlen, &elembyval, &elemalign);
2234  deconstruct_array(arrval,
2235  ARR_ELEMTYPE(arrval),
2236  elemlen, elembyval, elemalign,
2237  &elem_values, &elem_nulls,
2238  &num_elems);
2239  for (i = 0; i < num_elems; i++)
2240  {
2241  Const *elem_expr;
2242 
2243  /*
2244  * A null array element must lead to a null comparison result,
2245  * since saop_op is known strict. We can ignore it in the
2246  * useOr case, but otherwise it implies self-contradiction.
2247  */
2248  if (elem_nulls[i])
2249  {
2250  if (saop->useOr)
2251  continue;
2253  }
2254 
2255  elem_expr = makeConst(ARR_ELEMTYPE(arrval), -1,
2256  arr->constcollid, elemlen,
2257  elem_values[i], false, elembyval);
2258  elem_exprs = lappend(elem_exprs, elem_expr);
2259  }
2260  }
2261  else if (IsA(rightop, ArrayExpr))
2262  {
2263  ArrayExpr *arrexpr = castNode(ArrayExpr, rightop);
2264 
2265  /*
2266  * For a nested ArrayExpr, we don't know how to get the actual
2267  * scalar values out into a flat list, so we give up doing
2268  * anything with this ScalarArrayOpExpr.
2269  */
2270  if (arrexpr->multidims)
2271  return PARTCLAUSE_UNSUPPORTED;
2272 
2273  /*
2274  * Otherwise, we can just use the list of element values.
2275  */
2276  elem_exprs = arrexpr->elements;
2277  }
2278  else
2279  {
2280  /* Give up on any other clause types. */
2281  return PARTCLAUSE_UNSUPPORTED;
2282  }
2283 
2284  /*
2285  * Now generate a list of clauses, one for each array element, of the
2286  * form leftop saop_op elem_expr
2287  */
2288  elem_clauses = NIL;
2289  foreach(lc1, elem_exprs)
2290  {
2291  Expr *rightop = (Expr *) lfirst(lc1),
2292  *elem_clause;
2293 
2294  elem_clause = make_opclause(saop_op, BOOLOID, false,
2295  leftop, rightop,
2296  InvalidOid, saop_coll);
2297  elem_clauses = lappend(elem_clauses, elem_clause);
2298  }
2299 
2300  /*
2301  * If we have an ANY clause and multiple elements, now turn the list
2302  * of clauses into an OR expression.
2303  */
2304  if (saop->useOr && list_length(elem_clauses) > 1)
2305  elem_clauses = list_make1(makeBoolExpr(OR_EXPR, elem_clauses, -1));
2306 
2307  /* Finally, generate steps */
2308  *clause_steps = gen_partprune_steps_internal(context, elem_clauses);
2309  if (context->contradictory)
2311  else if (*clause_steps == NIL)
2312  return PARTCLAUSE_UNSUPPORTED; /* step generation failed */
2313  return PARTCLAUSE_MATCH_STEPS;
2314  }
2315  else if (IsA(clause, NullTest))
2316  {
2317  NullTest *nulltest = (NullTest *) clause;
2318  Expr *arg = nulltest->arg;
2319 
2320  if (IsA(arg, RelabelType))
2321  arg = ((RelabelType *) arg)->arg;
2322 
2323  /* Does arg match with this partition key column? */
2324  if (!equal(arg, partkey))
2325  return PARTCLAUSE_NOMATCH;
2326 
2327  *clause_is_not_null = (nulltest->nulltesttype == IS_NOT_NULL);
2328 
2330  }
2331 
2332  /*
2333  * If we get here then the return value depends on the result of the
2334  * match_boolean_partition_clause call above. If the call returned
2335  * PARTCLAUSE_UNSUPPORTED then we're either not dealing with a bool qual
2336  * or the bool qual is not suitable for pruning. Since the qual didn't
2337  * match up to any of the other qual types supported here, then trying to
2338  * match it against any other partition key is a waste of time, so just
2339  * return PARTCLAUSE_UNSUPPORTED. If the qual just couldn't be matched to
2340  * this partition key, then it may match another, so return
2341  * PARTCLAUSE_NOMATCH. The only other value that
2342  * match_boolean_partition_clause can return is PARTCLAUSE_MATCH_CLAUSE,
2343  * and since that value was already dealt with above, then we can just
2344  * return boolmatchstatus.
2345  */
2346  return boolmatchstatus;
2347 }
Datum constvalue
Definition: primnodes.h:219
signed short int16
Definition: c.h:428
#define InvalidStrategy
Definition: stratnum.h:24
bool multidims
Definition: primnodes.h:1038
#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:1448
#define IsA(nodeptr, _type_)
Definition: nodes.h:588
#define BTORDER_PROC
Definition: nbtree.h:700
Oid get_commutator(Oid opno)
Definition: lsyscache.c:1480
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3149
static PartClauseMatchStatus match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey, Expr **outconst)
Definition: partprune.c:3591
#define castNode(_type_, nodeptr)
Definition: nodes.h:606
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition: lsyscache.c:2218
Definition: nodes.h:537
#define HASHEXTENDED_PROC
Definition: hash.h:354
bool contain_var_clause(Node *node)
Definition: var.c:393
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:452
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:710
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:1759
#define linitial(l)
Definition: pg_list.h:174
#define ERROR
Definition: elog.h:46
static Bitmapset * pull_exec_paramids(Expr *expr)
Definition: partprune.c:3266
Expr * arg
Definition: primnodes.h:1265
Oid constcollid
Definition: primnodes.h:217
List * elements
Definition: primnodes.h:1037
static List * gen_partprune_steps_internal(GeneratePruningStepsContext *context, List *clauses)
Definition: partprune.c:962
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:336
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
char op_volatile(Oid opno)
Definition: lsyscache.c:1464
uintptr_t Datum
Definition: postgres.h:411
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:826
NullTestType nulltesttype
Definition: primnodes.h:1266
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:804
#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:419
static int list_length(const List *l)
Definition: pg_list.h:149
Oid inputcollid
Definition: primnodes.h:547
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:827
void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3491
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:828
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:1062
#define elog(elevel,...)
Definition: elog.h:232
int i
void * arg
PartitionScheme part_scheme
Definition: pathnodes.h:760
Oid opno
Definition: primnodes.h:542
Expr * expr
Definition: partprune.c:68
Oid get_negator(Oid opno)
Definition: lsyscache.c:1504
Definition: pg_list.h:50
#define ARR_ELEMTYPE(a)
Definition: array.h:285
PartClauseTarget target
Definition: partprune.c:115
bool constisnull
Definition: primnodes.h:220
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define DatumGetArrayTypeP(X)
Definition: array.h:254

◆ partkey_datum_from_expr()

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

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

3665 {
3666  if (IsA(expr, Const))
3667  {
3668  /* We can always determine the value of a constant */
3669  Const *con = (Const *) expr;
3670 
3671  *value = con->constvalue;
3672  *isnull = con->constisnull;
3673  }
3674  else
3675  {
3676  ExprState *exprstate;
3677  ExprContext *ectx;
3678 
3679  /*
3680  * We should never see a non-Const in a step unless we're running in
3681  * the executor.
3682  */
3683  Assert(context->planstate != NULL);
3684 
3685  exprstate = context->exprstates[stateidx];
3686  ectx = context->planstate->ps_ExprContext;
3687  *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
3688  }
3689 }
Datum constvalue
Definition: primnodes.h:219
#define IsA(nodeptr, _type_)
Definition: nodes.h:588
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:331
ExprContext * ps_ExprContext
Definition: execnodes.h:1006
ExprState ** exprstates
Definition: partprune.h:59
static struct @143 value
#define Assert(condition)
Definition: c.h:804
bool constisnull
Definition: primnodes.h:220
PlanState * planstate
Definition: partprune.h:58

◆ perform_pruning_base_step()

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

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

3338 {
3339  ListCell *lc1,
3340  *lc2;
3341  int keyno,
3342  nvalues;
3344  FmgrInfo *partsupfunc;
3345  int stateidx;
3346 
3347  /*
3348  * There better be the same number of expressions and compare functions.
3349  */
3350  Assert(list_length(opstep->exprs) == list_length(opstep->cmpfns));
3351 
3352  nvalues = 0;
3353  lc1 = list_head(opstep->exprs);
3354  lc2 = list_head(opstep->cmpfns);
3355 
3356  /*
3357  * Generate the partition lookup key that will be used by one of the
3358  * get_matching_*_bounds functions called below.
3359  */
3360  for (keyno = 0; keyno < context->partnatts; keyno++)
3361  {
3362  /*
3363  * For hash partitioning, it is possible that values of some keys are
3364  * not provided in operator clauses, but instead the planner found
3365  * that they appeared in a IS NULL clause.
3366  */
3367  if (bms_is_member(keyno, opstep->nullkeys))
3368  continue;
3369 
3370  /*
3371  * For range partitioning, we must only perform pruning with values
3372  * for either all partition keys or a prefix thereof.
3373  */
3374  if (keyno > nvalues && context->strategy == PARTITION_STRATEGY_RANGE)
3375  break;
3376 
3377  if (lc1 != NULL)
3378  {
3379  Expr *expr;
3380  Datum datum;
3381  bool isnull;
3382  Oid cmpfn;
3383 
3384  expr = lfirst(lc1);
3385  stateidx = PruneCxtStateIdx(context->partnatts,
3386  opstep->step.step_id, keyno);
3387  partkey_datum_from_expr(context, expr, stateidx,
3388  &datum, &isnull);
3389 
3390  /*
3391  * Since we only allow strict operators in pruning steps, any
3392  * null-valued comparison value must cause the comparison to fail,
3393  * so that no partitions could match.
3394  */
3395  if (isnull)
3396  {
3397  PruneStepResult *result;
3398 
3399  result = (PruneStepResult *) palloc(sizeof(PruneStepResult));
3400  result->bound_offsets = NULL;
3401  result->scan_default = false;
3402  result->scan_null = false;
3403 
3404  return result;
3405  }
3406 
3407  /* Set up the stepcmpfuncs entry, unless we already did */
3408  cmpfn = lfirst_oid(lc2);
3409  Assert(OidIsValid(cmpfn));
3410  if (cmpfn != context->stepcmpfuncs[stateidx].fn_oid)
3411  {
3412  /*
3413  * If the needed support function is the same one cached in
3414  * the relation's partition key, copy the cached FmgrInfo.
3415  * Otherwise (i.e., when we have a cross-type comparison), an
3416  * actual lookup is required.
3417  */
3418  if (cmpfn == context->partsupfunc[keyno].fn_oid)
3419  fmgr_info_copy(&context->stepcmpfuncs[stateidx],
3420  &context->partsupfunc[keyno],
3421  context->ppccontext);
3422  else
3423  fmgr_info_cxt(cmpfn, &context->stepcmpfuncs[stateidx],
3424  context->ppccontext);
3425  }
3426 
3427  values[keyno] = datum;
3428  nvalues++;
3429 
3430  lc1 = lnext(opstep->exprs, lc1);
3431  lc2 = lnext(opstep->cmpfns, lc2);
3432  }
3433  }
3434 
3435  /*
3436  * Point partsupfunc to the entry for the 0th key of this step; the
3437  * additional support functions, if any, follow consecutively.
3438  */
3439  stateidx = PruneCxtStateIdx(context->partnatts, opstep->step.step_id, 0);
3440  partsupfunc = &context->stepcmpfuncs[stateidx];
3441 
3442  switch (context->strategy)
3443  {
3445  return get_matching_hash_bounds(context,
3446  opstep->opstrategy,
3447  values, nvalues,
3448  partsupfunc,
3449  opstep->nullkeys);
3450 
3452  return get_matching_list_bounds(context,
3453  opstep->opstrategy,
3454  values[0], nvalues,
3455  &partsupfunc[0],
3456  opstep->nullkeys);
3457 
3459  return get_matching_range_bounds(context,
3460  opstep->opstrategy,
3461  values, nvalues,
3462  partsupfunc,
3463  opstep->nullkeys);
3464 
3465  default:
3466  elog(ERROR, "unexpected partition strategy: %d",
3467  (int) context->strategy);
3468  break;
3469  }
3470 
3471  return NULL;
3472 }
static PruneStepResult * get_matching_hash_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
Definition: partprune.c:2583
Definition: fmgr.h:56
FmgrInfo * partsupfunc
Definition: partprune.h:55
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:322
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:710
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:2660
#define ERROR
Definition: elog.h:46
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition: fmgr.c:608
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
PartitionPruneStep step
Definition: plannodes.h:1249
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:136
uintptr_t Datum
Definition: postgres.h:411
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:826
static void partkey_datum_from_expr(PartitionPruneContext *context, Expr *expr, int stateidx, Datum *value, bool *isnull)
Definition: partprune.c:3662
Oid fn_oid
Definition: fmgr.h:59
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
Bitmapset * nullkeys
Definition: plannodes.h:1254
static int list_length(const List *l)
Definition: pg_list.h:149
StrategyNumber opstrategy
Definition: plannodes.h:1251
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:827
static Datum values[MAXATTR]
Definition: bootstrap.c:156
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:828
void * palloc(Size size)
Definition: mcxt.c:1062
#define elog(elevel,...)
Definition: elog.h:232
static PruneStepResult * get_matching_range_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
Definition: partprune.c:2871
#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 3484 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, NIL, PartitionBoundInfoData::nindexes, palloc0(), partition_bound_accepts_nulls, partition_bound_has_default, PARTPRUNE_COMBINE_INTERSECT, PARTPRUNE_COMBINE_UNION, PruneStepResult::scan_default, PruneStepResult::scan_null, PartitionPruneStepCombine::source_stepids, PartitionPruneStepCombine::step, and PartitionPruneStep::step_id.

Referenced by get_matching_partitions().

3487 {
3488  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
3489  bool firststep;
3490  ListCell *lc1;
3491 
3492  /*
3493  * A combine step without any source steps is an indication to not perform
3494  * any partition pruning. Return all datum indexes in that case.
3495  */
3496  if (cstep->source_stepids == NIL)
3497  {
3498  PartitionBoundInfo boundinfo = context->boundinfo;
3499 
3500  result->bound_offsets =
3501  bms_add_range(NULL, 0, boundinfo->nindexes - 1);
3502  result->scan_default = partition_bound_has_default(boundinfo);
3503  result->scan_null = partition_bound_accepts_nulls(boundinfo);
3504  return result;
3505  }
3506 
3507  switch (cstep->combineOp)
3508  {
3510  foreach(lc1, cstep->source_stepids)
3511  {
3512  int step_id = lfirst_int(lc1);
3513  PruneStepResult *step_result;
3514 
3515  /*
3516  * step_results[step_id] must contain a valid result, which is
3517  * confirmed by the fact that cstep's step_id is greater than
3518  * step_id and the fact that results of the individual steps
3519  * are evaluated in sequence of their step_ids.
3520  */
3521  if (step_id >= cstep->step.step_id)
3522  elog(ERROR, "invalid pruning combine step argument");
3523  step_result = step_results[step_id];
3524  Assert(step_result != NULL);
3525 
3526  /* Record any additional datum indexes from this step */
3527  result->bound_offsets = bms_add_members(result->bound_offsets,
3528  step_result->bound_offsets);
3529 
3530  /* Update whether to scan null and default partitions. */
3531  if (!result->scan_null)
3532  result->scan_null = step_result->scan_null;
3533  if (!result->scan_default)
3534  result->scan_default = step_result->scan_default;
3535  }
3536  break;
3537 
3539  firststep = true;
3540  foreach(lc1, cstep->source_stepids)
3541  {
3542  int step_id = lfirst_int(lc1);
3543  PruneStepResult *step_result;
3544 
3545  if (step_id >= cstep->step.step_id)
3546  elog(ERROR, "invalid pruning combine step argument");
3547  step_result = step_results[step_id];
3548  Assert(step_result != NULL);
3549 
3550  if (firststep)
3551  {
3552  /* Copy step's result the first time. */
3553  result->bound_offsets =
3554  bms_copy(step_result->bound_offsets);
3555  result->scan_null = step_result->scan_null;
3556  result->scan_default = step_result->scan_default;
3557  firststep = false;
3558  }
3559  else
3560  {
3561  /* Record datum indexes common to both steps */
3562  result->bound_offsets =
3564  step_result->bound_offsets);
3565 
3566  /* Update whether to scan null and default partitions. */
3567  if (result->scan_null)
3568  result->scan_null = step_result->scan_null;
3569  if (result->scan_default)
3570  result->scan_default = step_result->scan_default;
3571  }
3572  }
3573  break;
3574  }
3575 
3576  return result;
3577 }
#define NIL
Definition: pg_list.h:65
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:74
PartitionPruneCombineOp combineOp
Definition: plannodes.h:1273
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:46
#define lfirst_int(lc)
Definition: pg_list.h:170
#define partition_bound_has_default(bi)
Definition: partbounds.h:99
void * palloc0(Size size)
Definition: mcxt.c:1093
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:98
#define Assert(condition)
Definition: c.h:804
PartitionBoundInfo boundinfo
Definition: partprune.h:53
#define elog(elevel,...)
Definition: elog.h:232
PartitionPruneStep step
Definition: plannodes.h:1271
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 752 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().

753 {
754  List *clauses = rel->baserestrictinfo;
755  List *pruning_steps;
757  PartitionPruneContext context;
758 
759  Assert(rel->part_scheme != NULL);
760 
761  /* If there are no partitions, return the empty set */
762  if (rel->nparts == 0)
763  return NULL;
764 
765  /*
766  * If pruning is disabled or if there are no clauses to prune with, return
767  * all partitions.
768  */
769  if (!enable_partition_pruning || clauses == NIL)
770  return bms_add_range(NULL, 0, rel->nparts - 1);
771 
772  /*
773  * Process clauses to extract pruning steps that are usable at plan time.
774  * If the clauses are found to be contradictory, we can return the empty
775  * set.
776  */
778  &gcontext);
779  if (gcontext.contradictory)
780  return NULL;
781  pruning_steps = gcontext.steps;
782 
783  /* If there's nothing usable, return all partitions */
784  if (pruning_steps == NIL)
785  return bms_add_range(NULL, 0, rel->nparts - 1);
786 
787  /* Set up PartitionPruneContext */
788  context.strategy = rel->part_scheme->strategy;
789  context.partnatts = rel->part_scheme->partnatts;
790  context.nparts = rel->nparts;
791  context.boundinfo = rel->boundinfo;
792  context.partcollation = rel->part_scheme->partcollation;
793  context.partsupfunc = rel->part_scheme->partsupfunc;
794  context.stepcmpfuncs = (FmgrInfo *) palloc0(sizeof(FmgrInfo) *
795  context.partnatts *
796  list_length(pruning_steps));
798 
799  /* These are not valid when being called from the planner */
800  context.planstate = NULL;
801  context.exprstates = NULL;
802 
803  /* Actual pruning happens here. */
804  return get_matching_partitions(&context, pruning_steps);
805 }
static void gen_partprune_steps(RelOptInfo *rel, List *clauses, PartClauseTarget target, GeneratePruningStepsContext *context)
Definition: partprune.c:716
#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:745
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:761
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
void * palloc0(Size size)
Definition: mcxt.c:1093
struct PartitionBoundInfoData * boundinfo
Definition: pathnodes.h:764
bool enable_partition_pruning
Definition: costsize.c:151
#define Assert(condition)
Definition: c.h:804
struct FmgrInfo * partsupfunc
Definition: pathnodes.h:419
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:818
PartitionScheme part_scheme
Definition: pathnodes.h:760
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 3266 of file partprune.c.

References PartClauseInfo::expr, and pull_exec_paramids_walker().

Referenced by get_partkey_exec_paramids(), and match_clause_to_partition_key().

3267 {
3268  Bitmapset *result = NULL;
3269 
3270  (void) pull_exec_paramids_walker((Node *) expr, &result);
3271 
3272  return result;
3273 }
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context)
Definition: partprune.c:3276
Definition: nodes.h:537

◆ pull_exec_paramids_walker()

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

Definition at line 3276 of file partprune.c.

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

Referenced by pull_exec_paramids().

3277 {
3278  if (node == NULL)
3279  return false;
3280  if (IsA(node, Param))
3281  {
3282  Param *param = (Param *) node;
3283 
3284  if (param->paramkind == PARAM_EXEC)
3285  *context = bms_add_member(*context, param->paramid);
3286  return false;
3287  }
3289  (void *) context);
3290 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:588
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context)
Definition: partprune.c:3276
ParamKind paramkind
Definition: primnodes.h:267
int paramid
Definition: primnodes.h:268
bool expression_tree_walker(Node *node, bool(*walker)(), void *context)
Definition: nodeFuncs.c:1904
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:736