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)
 
int make_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 1767 of file partprune.c.

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.

79 {
PartClauseMatchStatus
Definition: partprune.c:79
@ PARTCLAUSE_MATCH_CONTRADICT
Definition: partprune.c:84
@ PARTCLAUSE_UNSUPPORTED
Definition: partprune.c:85
@ PARTCLAUSE_NOMATCH
Definition: partprune.c:80
@ PARTCLAUSE_MATCH_STEPS
Definition: partprune.c:83
@ PARTCLAUSE_MATCH_CLAUSE
Definition: partprune.c:81
@ PARTCLAUSE_MATCH_NULLNESS
Definition: partprune.c:82

◆ 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:93
@ PARTTARGET_INITIAL
Definition: partprune.c:95
@ PARTTARGET_PLANNER
Definition: partprune.c:94
@ PARTTARGET_EXEC
Definition: partprune.c:96

Function Documentation

◆ add_part_relids()

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

Definition at line 401 of file partprune.c.

402 {
403  Index targetpart;
404  ListCell *lc;
405 
406  /* We can easily get the lowest set bit this way: */
407  targetpart = bms_next_member(partrelids, -1);
408  Assert(targetpart > 0);
409 
410  /* Look for a matching topmost parent */
411  foreach(lc, allpartrelids)
412  {
413  Bitmapset *currpartrelids = (Bitmapset *) lfirst(lc);
414  Index currtarget = bms_next_member(currpartrelids, -1);
415 
416  if (targetpart == currtarget)
417  {
418  /* Found a match, so add any new RT indexes to this hierarchy */
419  currpartrelids = bms_add_members(currpartrelids, partrelids);
420  lfirst(lc) = currpartrelids;
421  return allpartrelids;
422  }
423  }
424  /* No match, so add the new partition hierarchy to the list */
425  return lappend(allpartrelids, partrelids);
426 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1039
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:818
unsigned int Index
Definition: c.h:598
Assert(fmt[strlen(fmt) - 1] !='\n')
List * lappend(List *list, void *datum)
Definition: list.c:338
#define lfirst(lc)
Definition: pg_list.h:172

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

Referenced by make_partition_pruneinfo().

◆ gen_partprune_steps()

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

Definition at line 723 of file partprune.c.

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

References 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().

◆ gen_partprune_steps_internal()

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

Definition at line 970 of file partprune.c.

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

References arg, generate_unaccent_rules::args, Assert(), bms_add_member(), bms_is_empty, bms_is_member(), bms_num_members(), 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, PARTCLAUSE_MATCH_CLAUSE, PARTCLAUSE_MATCH_CONTRADICT, PARTCLAUSE_MATCH_NULLNESS, PARTCLAUSE_MATCH_STEPS, PARTCLAUSE_NOMATCH, PARTCLAUSE_UNSUPPORTED, 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().

◆ gen_prune_step_combine()

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

Definition at line 1355 of file partprune.c.

1358 {
1360 
1361  cstep->step.step_id = context->next_step_id++;
1362  cstep->combineOp = combineOp;
1363  cstep->source_stepids = source_stepids;
1364 
1365  context->steps = lappend(context->steps, cstep);
1366 
1367  return (PartitionPruneStep *) cstep;
1368 }
#define makeNode(_type_)
Definition: nodes.h:176
PartitionPruneCombineOp combineOp
Definition: plannodes.h:1557
PartitionPruneStep step
Definition: plannodes.h:1555

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

◆ 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 1322 of file partprune.c.

1326 {
1328 
1329  opstep->step.step_id = context->next_step_id++;
1330 
1331  /*
1332  * For clauses that contain an <> operator, set opstrategy to
1333  * InvalidStrategy to signal get_matching_list_bounds to do the right
1334  * thing.
1335  */
1336  opstep->opstrategy = op_is_ne ? InvalidStrategy : opstrategy;
1337  Assert(list_length(exprs) == list_length(cmpfns));
1338  opstep->exprs = exprs;
1339  opstep->cmpfns = cmpfns;
1340  opstep->nullkeys = nullkeys;
1341 
1342  context->steps = lappend(context->steps, opstep);
1343 
1344  return (PartitionPruneStep *) opstep;
1345 }
PartitionPruneStep step
Definition: plannodes.h:1533
StrategyNumber opstrategy
Definition: plannodes.h:1535
Bitmapset * nullkeys
Definition: plannodes.h:1538

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

◆ gen_prune_steps_from_opexps()

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

Definition at line 1392 of file partprune.c.

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

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, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionSchemeData::partnatts, PartitionSchemeData::partopfamily, GeneratePruningStepsContext::rel, and PartitionSchemeData::strategy.

Referenced by gen_partprune_steps_internal().

◆ 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 2590 of file partprune.c.

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

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, PartitionPruneContext::strategy, and values.

Referenced by perform_pruning_base_step().

◆ 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 2667 of file partprune.c.

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

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, PartitionPruneContext::strategy, and value.

Referenced by perform_pruning_base_step().

◆ get_matching_partitions()

Bitmapset* get_matching_partitions ( PartitionPruneContext context,
List pruning_steps 
)

Definition at line 826 of file partprune.c.

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

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, and PartitionPruneContext::strategy.

Referenced by find_matching_subplans_recurse(), and prune_append_rel_partitions().

◆ 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 2878 of file partprune.c.

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

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, PartitionPruneContext::strategy, and values.

Referenced by perform_pruning_base_step().

◆ get_partkey_exec_paramids()

static Bitmapset * get_partkey_exec_paramids ( List steps)
static

Definition at line 3307 of file partprune.c.

3308 {
3309  Bitmapset *execparamids = NULL;
3310  ListCell *lc;
3311 
3312  foreach(lc, steps)
3313  {
3315  ListCell *lc2;
3316 
3317  if (!IsA(step, PartitionPruneStepOp))
3318  continue;
3319 
3320  foreach(lc2, step->exprs)
3321  {
3322  Expr *expr = lfirst(lc2);
3323 
3324  /* We can be quick for plain Consts */
3325  if (!IsA(expr, Const))
3326  execparamids = bms_join(execparamids,
3327  pull_exec_paramids(expr));
3328  }
3329  }
3330 
3331  return execparamids;
3332 }
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition: bitmapset.c:987
static Bitmapset * pull_exec_paramids(Expr *expr)
Definition: partprune.c:3273

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

Referenced by make_partitionedrel_pruneinfo().

◆ 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 2379 of file partprune.c.

2387 {
2388  Assert(step_nullkeys == NULL ||
2389  context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH);
2390 
2391  /* Quick exit if there are no values to prefix with. */
2392  if (prefix == NIL)
2393  {
2394  PartitionPruneStep *step;
2395 
2396  step = gen_prune_step_op(context,
2397  step_opstrategy,
2398  step_op_is_ne,
2399  list_make1(step_lastexpr),
2400  list_make1_oid(step_lastcmpfn),
2401  step_nullkeys);
2402  return list_make1(step);
2403  }
2404 
2405  /* Recurse to generate steps for various combinations. */
2406  return get_steps_using_prefix_recurse(context,
2407  step_opstrategy,
2408  step_op_is_ne,
2409  step_lastexpr,
2410  step_lastcmpfn,
2411  step_lastkeyno,
2412  step_nullkeys,
2413  prefix,
2414  list_head(prefix),
2415  NIL, NIL);
2416 }
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:2431
#define list_make1_oid(x1)
Definition: pg_list.h:242

References Assert(), gen_prune_step_op(), get_steps_using_prefix_recurse(), list_head(), list_make1, list_make1_oid, NIL, PARTITION_STRATEGY_HASH, and GeneratePruningStepsContext::rel.

Referenced by gen_prune_steps_from_opexps().

◆ 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 2431 of file partprune.c.

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

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, PARTITION_STRATEGY_HASH, and GeneratePruningStepsContext::rel.

Referenced by get_steps_using_prefix().

◆ make_partition_pruneinfo()

int make_partition_pruneinfo ( PlannerInfo root,
RelOptInfo parentrel,
List subpaths,
List prunequal 
)

Definition at line 226 of file partprune.c.

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

References add_part_relids(), 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(), AppendRelInfo::parent_relid, PlannerInfo::partPruneInfos, pfree(), PartitionPruneInfo::prune_infos, RelOptInfo::relid, RelOptInfo::relids, RELOPT_OTHER_MEMBER_REL, RelOptInfo::reloptkind, PartitionPruneInfo::root_parent_relids, and PlannerInfo::simple_rel_array_size.

Referenced by create_append_plan(), and create_merge_append_plan().

◆ 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 447 of file partprune.c.

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

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(), PARTTARGET_EXEC, PARTTARGET_INITIAL, pfree(), planner_rt_fetch, PartitionedRelPruneInfo::present_parts, RelOptInfo::relid, RelOptInfo::relids, PartitionedRelPruneInfo::rtindex, PlannerInfo::simple_rel_array_size, and GeneratePruningStepsContext::steps.

Referenced by make_partition_pruneinfo().

◆ match_boolean_partition_clause()

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

Definition at line 3598 of file partprune.c.

3600 {
3601  Expr *leftop;
3602 
3603  *outconst = NULL;
3604 
3605  /*
3606  * Partitioning currently can only use built-in AMs, so checking for
3607  * built-in boolean opfamilies is good enough.
3608  */
3609  if (!IsBuiltinBooleanOpfamily(partopfamily))
3610  return PARTCLAUSE_UNSUPPORTED;
3611 
3612  if (IsA(clause, BooleanTest))
3613  {
3614  BooleanTest *btest = (BooleanTest *) clause;
3615 
3616  /* Only IS [NOT] TRUE/FALSE are any good to us */
3617  if (btest->booltesttype == IS_UNKNOWN ||
3618  btest->booltesttype == IS_NOT_UNKNOWN)
3619  return PARTCLAUSE_UNSUPPORTED;
3620 
3621  leftop = btest->arg;
3622  if (IsA(leftop, RelabelType))
3623  leftop = ((RelabelType *) leftop)->arg;
3624 
3625  if (equal(leftop, partkey))
3626  *outconst = (btest->booltesttype == IS_TRUE ||
3627  btest->booltesttype == IS_NOT_FALSE)
3628  ? (Expr *) makeBoolConst(true, false)
3629  : (Expr *) makeBoolConst(false, false);
3630 
3631  if (*outconst)
3632  return PARTCLAUSE_MATCH_CLAUSE;
3633  }
3634  else
3635  {
3636  bool is_not_clause = is_notclause(clause);
3637 
3638  leftop = is_not_clause ? get_notclausearg(clause) : clause;
3639 
3640  if (IsA(leftop, RelabelType))
3641  leftop = ((RelabelType *) leftop)->arg;
3642 
3643  /* Compare to the partition key, and make up a clause ... */
3644  if (equal(leftop, partkey))
3645  *outconst = is_not_clause ?
3646  (Expr *) makeBoolConst(false, false) :
3647  (Expr *) makeBoolConst(true, false);
3648  else if (equal(negate_clause((Node *) leftop), partkey))
3649  *outconst = (Expr *) makeBoolConst(false, false);
3650 
3651  if (*outconst)
3652  return PARTCLAUSE_MATCH_CLAUSE;
3653  }
3654 
3655  return PARTCLAUSE_NOMATCH;
3656 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
return false
Definition: isn.c:131
return true
Definition: isn.c:126
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:360
static Expr * get_notclausearg(const void *notclause)
Definition: nodeFuncs.h:132
static bool is_notclause(const void *clause)
Definition: nodeFuncs.h:123
Node * negate_clause(Node *node)
Definition: prepqual.c:74
@ IS_NOT_FALSE
Definition: primnodes.h:1631
@ IS_NOT_UNKNOWN
Definition: primnodes.h:1631
@ IS_TRUE
Definition: primnodes.h:1631
@ IS_UNKNOWN
Definition: primnodes.h:1631
BoolTestType booltesttype
Definition: primnodes.h:1638
Expr * arg
Definition: primnodes.h:1637

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

◆ 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 1802 of file partprune.c.

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

References arg, NullTest::arg, generate_unaccent_rules::args, ScalarArrayOpExpr::args, ARR_ELEMTYPE, Assert(), bms_is_empty, BTEqualStrategyNumber, BTORDER_PROC, castNode, PartClauseInfo::cmpfn, 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, InvalidOid, InvalidStrategy, IS_NOT_NULL, IsA, PartClauseInfo::keyno, lappend(), lfirst, linitial, list_length(), list_make1, lsecond, make_opclause(), makeBoolExpr(), makeConst(), match_boolean_partition_clause(), 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(), 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().

◆ partkey_datum_from_expr()

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

Definition at line 3673 of file partprune.c.

3676 {
3677  if (IsA(expr, Const))
3678  {
3679  /* We can always determine the value of a constant */
3680  Const *con = (Const *) expr;
3681 
3682  *value = con->constvalue;
3683  *isnull = con->constisnull;
3684  }
3685  else
3686  {
3687  ExprState *exprstate;
3688  ExprContext *ectx;
3689 
3690  /*
3691  * We should never see a non-Const in a step unless the caller has
3692  * passed a valid ExprContext.
3693  *
3694  * When context->planstate is valid, context->exprcontext is same as
3695  * context->planstate->ps_ExprContext.
3696  */
3697  Assert(context->planstate != NULL || context->exprcontext != NULL);
3698  Assert(context->planstate == NULL ||
3699  (context->exprcontext == context->planstate->ps_ExprContext));
3700 
3701  exprstate = context->exprstates[stateidx];
3702  ectx = context->exprcontext;
3703  *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
3704  }
3705 }
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:346
ExprContext * exprcontext
Definition: partprune.h:60
PlanState * planstate
Definition: partprune.h:59
ExprState ** exprstates
Definition: partprune.h:61
ExprContext * ps_ExprContext
Definition: execnodes.h:1074

References Assert(), ExecEvalExprSwitchContext(), PartitionPruneContext::exprcontext, PartitionPruneContext::exprstates, IsA, PartitionPruneContext::planstate, PlanState::ps_ExprContext, and value.

Referenced by perform_pruning_base_step().

◆ perform_pruning_base_step()

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

Definition at line 3343 of file partprune.c.

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

References Assert(), bms_is_member(), PruneStepResult::bound_offsets, PartitionPruneStepOp::cmpfns, elog(), ERROR, PartitionPruneStepOp::exprs, fmgr_info_copy(), fmgr_info_cxt(), FmgrInfo::fn_oid, get_matching_hash_bounds(), get_matching_list_bounds(), get_matching_range_bounds(), 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().

◆ perform_pruning_combine_step()

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

Definition at line 3491 of file partprune.c.

3494 {
3495  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
3496  bool firststep;
3497  ListCell *lc1;
3498 
3499  /*
3500  * A combine step without any source steps is an indication to not perform
3501  * any partition pruning. Return all datum indexes in that case.
3502  */
3503  if (cstep->source_stepids == NIL)
3504  {
3505  PartitionBoundInfo boundinfo = context->boundinfo;
3506 
3507  result->bound_offsets =
3508  bms_add_range(NULL, 0, boundinfo->nindexes - 1);
3509  result->scan_default = partition_bound_has_default(boundinfo);
3510  result->scan_null = partition_bound_accepts_nulls(boundinfo);
3511  return result;
3512  }
3513 
3514  switch (cstep->combineOp)
3515  {
3517  foreach(lc1, cstep->source_stepids)
3518  {
3519  int step_id = lfirst_int(lc1);
3520  PruneStepResult *step_result;
3521 
3522  /*
3523  * step_results[step_id] must contain a valid result, which is
3524  * confirmed by the fact that cstep's step_id is greater than
3525  * step_id and the fact that results of the individual steps
3526  * are evaluated in sequence of their step_ids.
3527  */
3528  if (step_id >= cstep->step.step_id)
3529  elog(ERROR, "invalid pruning combine step argument");
3530  step_result = step_results[step_id];
3531  Assert(step_result != NULL);
3532 
3533  /* Record any additional datum indexes from this step */
3534  result->bound_offsets = bms_add_members(result->bound_offsets,
3535  step_result->bound_offsets);
3536 
3537  /* Update whether to scan null and default partitions. */
3538  if (!result->scan_null)
3539  result->scan_null = step_result->scan_null;
3540  if (!result->scan_default)
3541  result->scan_default = step_result->scan_default;
3542  }
3543  break;
3544 
3546  firststep = true;
3547  foreach(lc1, cstep->source_stepids)
3548  {
3549  int step_id = lfirst_int(lc1);
3550  PruneStepResult *step_result;
3551 
3552  if (step_id >= cstep->step.step_id)
3553  elog(ERROR, "invalid pruning combine step argument");
3554  step_result = step_results[step_id];
3555  Assert(step_result != NULL);
3556 
3557  if (firststep)
3558  {
3559  /* Copy step's result the first time. */
3560  result->bound_offsets =
3561  bms_copy(step_result->bound_offsets);
3562  result->scan_null = step_result->scan_null;
3563  result->scan_default = step_result->scan_default;
3564  firststep = false;
3565  }
3566  else
3567  {
3568  /* Record datum indexes common to both steps */
3569  result->bound_offsets =
3571  step_result->bound_offsets);
3572 
3573  /* Update whether to scan null and default partitions. */
3574  if (result->scan_null)
3575  result->scan_null = step_result->scan_null;
3576  if (result->scan_default)
3577  result->scan_default = step_result->scan_default;
3578  }
3579  }
3580  break;
3581  }
3582 
3583  return result;
3584 }
Bitmapset * bms_int_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:928
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:74
#define lfirst_int(lc)
Definition: pg_list.h:173

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

◆ prune_append_rel_partitions()

Bitmapset* prune_append_rel_partitions ( RelOptInfo rel)

Definition at line 759 of file partprune.c.

760 {
761  List *clauses = rel->baserestrictinfo;
762  List *pruning_steps;
764  PartitionPruneContext context;
765 
766  Assert(rel->part_scheme != NULL);
767 
768  /* If there are no partitions, return the empty set */
769  if (rel->nparts == 0)
770  return NULL;
771 
772  /*
773  * If pruning is disabled or if there are no clauses to prune with, return
774  * all partitions.
775  */
776  if (!enable_partition_pruning || clauses == NIL)
777  return bms_add_range(NULL, 0, rel->nparts - 1);
778 
779  /*
780  * Process clauses to extract pruning steps that are usable at plan time.
781  * If the clauses are found to be contradictory, we can return the empty
782  * set.
783  */
785  &gcontext);
786  if (gcontext.contradictory)
787  return NULL;
788  pruning_steps = gcontext.steps;
789 
790  /* If there's nothing usable, return all partitions */
791  if (pruning_steps == NIL)
792  return bms_add_range(NULL, 0, rel->nparts - 1);
793 
794  /* Set up PartitionPruneContext */
795  context.strategy = rel->part_scheme->strategy;
796  context.partnatts = rel->part_scheme->partnatts;
797  context.nparts = rel->nparts;
798  context.boundinfo = rel->boundinfo;
799  context.partcollation = rel->part_scheme->partcollation;
800  context.partsupfunc = rel->part_scheme->partsupfunc;
801  context.stepcmpfuncs = (FmgrInfo *) palloc0(sizeof(FmgrInfo) *
802  context.partnatts *
803  list_length(pruning_steps));
805 
806  /* These are not valid when being called from the planner */
807  context.planstate = NULL;
808  context.exprcontext = NULL;
809  context.exprstates = NULL;
810 
811  /* Actual pruning happens here. */
812  return get_matching_partitions(&context, pruning_steps);
813 }
bool enable_partition_pruning
Definition: costsize.c:153
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
Bitmapset * get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
Definition: partprune.c:826
List * baserestrictinfo
Definition: pathnodes.h:970

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

Referenced by expand_partitioned_rtentry().

◆ pull_exec_paramids()

static Bitmapset * pull_exec_paramids ( Expr expr)
static

Definition at line 3273 of file partprune.c.

3274 {
3275  Bitmapset *result = NULL;
3276 
3277  (void) pull_exec_paramids_walker((Node *) expr, &result);
3278 
3279  return result;
3280 }
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context)
Definition: partprune.c:3283

References pull_exec_paramids_walker().

Referenced by get_partkey_exec_paramids(), and match_clause_to_partition_key().

◆ pull_exec_paramids_walker()

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

Definition at line 3283 of file partprune.c.

3284 {
3285  if (node == NULL)
3286  return false;
3287  if (IsA(node, Param))
3288  {
3289  Param *param = (Param *) node;
3290 
3291  if (param->paramkind == PARAM_EXEC)
3292  *context = bms_add_member(*context, param->paramid);
3293  return false;
3294  }
3296  (void *) context);
3297 }
#define expression_tree_walker(n, w, c)
Definition: nodeFuncs.h:151
@ PARAM_EXEC
Definition: primnodes.h:346
int paramid
Definition: primnodes.h:355
ParamKind paramkind
Definition: primnodes.h:354

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

Referenced by pull_exec_paramids().