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 "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, 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, 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, bool *notclause)
 
static void partkey_datum_from_expr (PartitionPruneContext *context, Expr *expr, int stateidx, Datum *value, bool *isnull)
 
PartitionPruneInfomake_partition_pruneinfo (PlannerInfo *root, RelOptInfo *parentrel, List *subpaths, List *prunequal)
 
Bitmapsetprune_append_rel_partitions (RelOptInfo *rel)
 
Bitmapsetget_matching_partitions (PartitionPruneContext *context, List *pruning_steps)
 

Macro Definition Documentation

◆ PartCollMatchesExprColl

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

Definition at line 1755 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 77 of file partprune.c.

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

◆ PartClauseTarget

Enumerator
PARTTARGET_PLANNER 
PARTTARGET_INITIAL 
PARTTARGET_EXEC 

Definition at line 91 of file partprune.c.

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

Function Documentation

◆ add_part_relids()

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

Definition at line 392 of file partprune.c.

393 {
394  Index targetpart;
395  ListCell *lc;
396 
397  /* We can easily get the lowest set bit this way: */
398  targetpart = bms_next_member(partrelids, -1);
399  Assert(targetpart > 0);
400 
401  /* Look for a matching topmost parent */
402  foreach(lc, allpartrelids)
403  {
404  Bitmapset *currpartrelids = (Bitmapset *) lfirst(lc);
405  Index currtarget = bms_next_member(currpartrelids, -1);
406 
407  if (targetpart == currtarget)
408  {
409  /* Found a match, so add any new RT indexes to this hierarchy */
410  currpartrelids = bms_add_members(currpartrelids, partrelids);
411  lfirst(lc) = currpartrelids;
412  return allpartrelids;
413  }
414  }
415  /* No match, so add the new partition hierarchy to the list */
416  return lappend(allpartrelids, partrelids);
417 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:917
#define Assert(condition)
Definition: c.h:858
unsigned int Index
Definition: c.h:614
List * lappend(List *list, void *datum)
Definition: list.c:339
#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 714 of file partprune.c.

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

References context, gen_partprune_steps_internal(), list_concat_copy(), partition_bound_has_default, and RelOptInfo::partition_qual.

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

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

1349 {
1351 
1352  cstep->step.step_id = context->next_step_id++;
1353  cstep->combineOp = combineOp;
1354  cstep->source_stepids = source_stepids;
1355 
1356  context->steps = lappend(context->steps, cstep);
1357 
1358  return (PartitionPruneStep *) cstep;
1359 }
#define makeNode(_type_)
Definition: nodes.h:155
PartitionPruneCombineOp combineOp
Definition: plannodes.h:1553
PartitionPruneStep step
Definition: plannodes.h:1551

References PartitionPruneStepCombine::combineOp, context, lappend(), makeNode, PartitionPruneStepCombine::source_stepids, PartitionPruneStepCombine::step, and PartitionPruneStep::step_id.

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

1317 {
1319 
1320  opstep->step.step_id = context->next_step_id++;
1321 
1322  /*
1323  * For clauses that contain an <> operator, set opstrategy to
1324  * InvalidStrategy to signal get_matching_list_bounds to do the right
1325  * thing.
1326  */
1327  opstep->opstrategy = op_is_ne ? InvalidStrategy : opstrategy;
1328  Assert(list_length(exprs) == list_length(cmpfns));
1329  opstep->exprs = exprs;
1330  opstep->cmpfns = cmpfns;
1331  opstep->nullkeys = nullkeys;
1332 
1333  context->steps = lappend(context->steps, opstep);
1334 
1335  return (PartitionPruneStep *) opstep;
1336 }
PartitionPruneStep step
Definition: plannodes.h:1529
StrategyNumber opstrategy
Definition: plannodes.h:1531
Bitmapset * nullkeys
Definition: plannodes.h:1534

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

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

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

References Assert, bms_is_member(), BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, BTMaxStrategyNumber, PartClauseInfo::cmpfn, context, 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, 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 2663 of file partprune.c.

2666 {
2667  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2668  PartitionBoundInfo boundinfo = context->boundinfo;
2669  int *partindices = boundinfo->indexes;
2670  int partnatts = context->partnatts;
2671  bool isnull[PARTITION_MAX_KEYS];
2672  int i;
2673  uint64 rowHash;
2674  int greatest_modulus;
2675  Oid *partcollation = context->partcollation;
2676 
2677  Assert(context->strategy == PARTITION_STRATEGY_HASH);
2678 
2679  /*
2680  * For hash partitioning we can only perform pruning based on equality
2681  * clauses to the partition key or IS NULL clauses. We also can only
2682  * prune if we got values for all keys.
2683  */
2684  if (nvalues + bms_num_members(nullkeys) == partnatts)
2685  {
2686  /*
2687  * If there are any values, they must have come from clauses
2688  * containing an equality operator compatible with hash partitioning.
2689  */
2690  Assert(opstrategy == HTEqualStrategyNumber || nvalues == 0);
2691 
2692  for (i = 0; i < partnatts; i++)
2693  isnull[i] = bms_is_member(i, nullkeys);
2694 
2695  rowHash = compute_partition_hash_value(partnatts, partsupfunc, partcollation,
2696  values, isnull);
2697 
2698  greatest_modulus = boundinfo->nindexes;
2699  if (partindices[rowHash % greatest_modulus] >= 0)
2700  result->bound_offsets =
2701  bms_make_singleton(rowHash % greatest_modulus);
2702  }
2703  else
2704  {
2705  /* Report all valid offsets into the boundinfo->indexes array. */
2706  result->bound_offsets = bms_add_range(NULL, 0,
2707  boundinfo->nindexes - 1);
2708  }
2709 
2710  /*
2711  * There is neither a special hash null partition or the default hash
2712  * partition.
2713  */
2714  result->scan_null = result->scan_default = false;
2715 
2716  return result;
2717 }
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:216
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:1019
static Datum values[MAXATTR]
Definition: bootstrap.c:152
void * palloc0(Size size)
Definition: mcxt.c:1346
uint64 compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc, const Oid *partcollation, const Datum *values, const bool *isnull)
Definition: partbounds.c:4723
Bitmapset * bound_offsets
Definition: partprune.c:133

References Assert, bms_add_range(), bms_is_member(), bms_make_singleton(), bms_num_members(), PruneStepResult::bound_offsets, compute_partition_hash_value(), context, HTEqualStrategyNumber, i, PartitionBoundInfoData::indexes, PartitionBoundInfoData::nindexes, palloc0(), PARTITION_MAX_KEYS, PARTITION_STRATEGY_HASH, PruneStepResult::scan_default, PruneStepResult::scan_null, 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 2740 of file partprune.c.

2743 {
2744  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2745  PartitionBoundInfo boundinfo = context->boundinfo;
2746  int off,
2747  minoff,
2748  maxoff;
2749  bool is_equal;
2750  bool inclusive = false;
2751  Oid *partcollation = context->partcollation;
2752 
2753  Assert(context->strategy == PARTITION_STRATEGY_LIST);
2754  Assert(context->partnatts == 1);
2755 
2756  result->scan_null = result->scan_default = false;
2757 
2758  if (!bms_is_empty(nullkeys))
2759  {
2760  /*
2761  * Nulls may exist in only one partition - the partition whose
2762  * accepted set of values includes null or the default partition if
2763  * the former doesn't exist.
2764  */
2765  if (partition_bound_accepts_nulls(boundinfo))
2766  result->scan_null = true;
2767  else
2768  result->scan_default = partition_bound_has_default(boundinfo);
2769  return result;
2770  }
2771 
2772  /*
2773  * If there are no datums to compare keys with, but there are partitions,
2774  * just return the default partition if one exists.
2775  */
2776  if (boundinfo->ndatums == 0)
2777  {
2778  result->scan_default = partition_bound_has_default(boundinfo);
2779  return result;
2780  }
2781 
2782  minoff = 0;
2783  maxoff = boundinfo->ndatums - 1;
2784 
2785  /*
2786  * If there are no values to compare with the datums in boundinfo, it
2787  * means the caller asked for partitions for all non-null datums. Add
2788  * indexes of *all* partitions, including the default if any.
2789  */
2790  if (nvalues == 0)
2791  {
2792  Assert(boundinfo->ndatums > 0);
2793  result->bound_offsets = bms_add_range(NULL, 0,
2794  boundinfo->ndatums - 1);
2795  result->scan_default = partition_bound_has_default(boundinfo);
2796  return result;
2797  }
2798 
2799  /* Special case handling of values coming from a <> operator clause. */
2800  if (opstrategy == InvalidStrategy)
2801  {
2802  /*
2803  * First match to all bounds. We'll remove any matching datums below.
2804  */
2805  Assert(boundinfo->ndatums > 0);
2806  result->bound_offsets = bms_add_range(NULL, 0,
2807  boundinfo->ndatums - 1);
2808 
2809  off = partition_list_bsearch(partsupfunc, partcollation, boundinfo,
2810  value, &is_equal);
2811  if (off >= 0 && is_equal)
2812  {
2813 
2814  /* We have a match. Remove from the result. */
2815  Assert(boundinfo->indexes[off] >= 0);
2816  result->bound_offsets = bms_del_member(result->bound_offsets,
2817  off);
2818  }
2819 
2820  /* Always include the default partition if any. */
2821  result->scan_default = partition_bound_has_default(boundinfo);
2822 
2823  return result;
2824  }
2825 
2826  /*
2827  * With range queries, always include the default list partition, because
2828  * list partitions divide the key space in a discontinuous manner, not all
2829  * values in the given range will have a partition assigned. This may not
2830  * technically be true for some data types (e.g. integer types), however,
2831  * we currently lack any sort of infrastructure to provide us with proofs
2832  * that would allow us to do anything smarter here.
2833  */
2834  if (opstrategy != BTEqualStrategyNumber)
2835  result->scan_default = partition_bound_has_default(boundinfo);
2836 
2837  switch (opstrategy)
2838  {
2839  case BTEqualStrategyNumber:
2840  off = partition_list_bsearch(partsupfunc,
2841  partcollation,
2842  boundinfo, value,
2843  &is_equal);
2844  if (off >= 0 && is_equal)
2845  {
2846  Assert(boundinfo->indexes[off] >= 0);
2847  result->bound_offsets = bms_make_singleton(off);
2848  }
2849  else
2850  result->scan_default = partition_bound_has_default(boundinfo);
2851  return result;
2852 
2854  inclusive = true;
2855  /* fall through */
2857  off = partition_list_bsearch(partsupfunc,
2858  partcollation,
2859  boundinfo, value,
2860  &is_equal);
2861  if (off >= 0)
2862  {
2863  /* We don't want the matched datum to be in the result. */
2864  if (!is_equal || !inclusive)
2865  off++;
2866  }
2867  else
2868  {
2869  /*
2870  * This case means all partition bounds are greater, which in
2871  * turn means that all partitions satisfy this key.
2872  */
2873  off = 0;
2874  }
2875 
2876  /*
2877  * off is greater than the numbers of datums we have partitions
2878  * for. The only possible partition that could contain a match is
2879  * the default partition, but we must've set context->scan_default
2880  * above anyway if one exists.
2881  */
2882  if (off > boundinfo->ndatums - 1)
2883  return result;
2884 
2885  minoff = off;
2886  break;
2887 
2889  inclusive = true;
2890  /* fall through */
2891  case BTLessStrategyNumber:
2892  off = partition_list_bsearch(partsupfunc,
2893  partcollation,
2894  boundinfo, value,
2895  &is_equal);
2896  if (off >= 0 && is_equal && !inclusive)
2897  off--;
2898 
2899  /*
2900  * off is smaller than the datums of all non-default partitions.
2901  * The only possible partition that could contain a match is the
2902  * default partition, but we must've set context->scan_default
2903  * above anyway if one exists.
2904  */
2905  if (off < 0)
2906  return result;
2907 
2908  maxoff = off;
2909  break;
2910 
2911  default:
2912  elog(ERROR, "invalid strategy number %d", opstrategy);
2913  break;
2914  }
2915 
2916  Assert(minoff >= 0 && maxoff >= 0);
2917  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2918  return result;
2919 }
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:868
static struct @155 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, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, context, elog, ERROR, PartitionBoundInfoData::indexes, InvalidStrategy, PartitionBoundInfoData::ndatums, palloc0(), partition_bound_accepts_nulls, partition_bound_has_default, partition_list_bsearch(), PARTITION_STRATEGY_LIST, PruneStepResult::scan_default, PruneStepResult::scan_null, and value.

Referenced by perform_pruning_base_step().

◆ get_matching_partitions()

Bitmapset* get_matching_partitions ( PartitionPruneContext context,
List pruning_steps 
)

Definition at line 817 of file partprune.c.

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

References Assert, bms_add_member(), bms_add_range(), bms_next_member(), PruneStepResult::bound_offsets, context, elog, ERROR, i, lfirst, list_length(), nodeTag, 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, and PartitionPruneStep::step_id.

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

2954 {
2955  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
2956  PartitionBoundInfo boundinfo = context->boundinfo;
2957  Oid *partcollation = context->partcollation;
2958  int partnatts = context->partnatts;
2959  int *partindices = boundinfo->indexes;
2960  int off,
2961  minoff,
2962  maxoff;
2963  bool is_equal;
2964  bool inclusive = false;
2965 
2966  Assert(context->strategy == PARTITION_STRATEGY_RANGE);
2967  Assert(nvalues <= partnatts);
2968 
2969  result->scan_null = result->scan_default = false;
2970 
2971  /*
2972  * If there are no datums to compare keys with, or if we got an IS NULL
2973  * clause just return the default partition, if it exists.
2974  */
2975  if (boundinfo->ndatums == 0 || !bms_is_empty(nullkeys))
2976  {
2977  result->scan_default = partition_bound_has_default(boundinfo);
2978  return result;
2979  }
2980 
2981  minoff = 0;
2982  maxoff = boundinfo->ndatums;
2983 
2984  /*
2985  * If there are no values to compare with the datums in boundinfo, it
2986  * means the caller asked for partitions for all non-null datums. Add
2987  * indexes of *all* partitions, including the default partition if one
2988  * exists.
2989  */
2990  if (nvalues == 0)
2991  {
2992  /* ignore key space not covered by any partitions */
2993  if (partindices[minoff] < 0)
2994  minoff++;
2995  if (partindices[maxoff] < 0)
2996  maxoff--;
2997 
2998  result->scan_default = partition_bound_has_default(boundinfo);
2999  Assert(partindices[minoff] >= 0 &&
3000  partindices[maxoff] >= 0);
3001  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3002 
3003  return result;
3004  }
3005 
3006  /*
3007  * If the query does not constrain all key columns, we'll need to scan the
3008  * default partition, if any.
3009  */
3010  if (nvalues < partnatts)
3011  result->scan_default = partition_bound_has_default(boundinfo);
3012 
3013  switch (opstrategy)
3014  {
3015  case BTEqualStrategyNumber:
3016  /* Look for the smallest bound that is = lookup value. */
3017  off = partition_range_datum_bsearch(partsupfunc,
3018  partcollation,
3019  boundinfo,
3020  nvalues, values,
3021  &is_equal);
3022 
3023  if (off >= 0 && is_equal)
3024  {
3025  if (nvalues == partnatts)
3026  {
3027  /* There can only be zero or one matching partition. */
3028  result->bound_offsets = bms_make_singleton(off + 1);
3029  return result;
3030  }
3031  else
3032  {
3033  int saved_off = off;
3034 
3035  /*
3036  * Since the lookup value contains only a prefix of keys,
3037  * we must find other bounds that may also match the
3038  * prefix. partition_range_datum_bsearch() returns the
3039  * offset of one of them, find others by checking adjacent
3040  * bounds.
3041  */
3042 
3043  /*
3044  * First find greatest bound that's smaller than the
3045  * lookup value.
3046  */
3047  while (off >= 1)
3048  {
3049  int32 cmpval;
3050 
3051  cmpval =
3052  partition_rbound_datum_cmp(partsupfunc,
3053  partcollation,
3054  boundinfo->datums[off - 1],
3055  boundinfo->kind[off - 1],
3056  values, nvalues);
3057  if (cmpval != 0)
3058  break;
3059  off--;
3060  }
3061 
3062  Assert(0 ==
3063  partition_rbound_datum_cmp(partsupfunc,
3064  partcollation,
3065  boundinfo->datums[off],
3066  boundinfo->kind[off],
3067  values, nvalues));
3068 
3069  /*
3070  * We can treat 'off' as the offset of the smallest bound
3071  * to be included in the result, if we know it is the
3072  * upper bound of the partition in which the lookup value
3073  * could possibly exist. One case it couldn't is if the
3074  * bound, or precisely the matched portion of its prefix,
3075  * is not inclusive.
3076  */
3077  if (boundinfo->kind[off][nvalues] ==
3079  off++;
3080 
3081  minoff = off;
3082 
3083  /*
3084  * Now find smallest bound that's greater than the lookup
3085  * value.
3086  */
3087  off = saved_off;
3088  while (off < boundinfo->ndatums - 1)
3089  {
3090  int32 cmpval;
3091 
3092  cmpval = partition_rbound_datum_cmp(partsupfunc,
3093  partcollation,
3094  boundinfo->datums[off + 1],
3095  boundinfo->kind[off + 1],
3096  values, nvalues);
3097  if (cmpval != 0)
3098  break;
3099  off++;
3100  }
3101 
3102  Assert(0 ==
3103  partition_rbound_datum_cmp(partsupfunc,
3104  partcollation,
3105  boundinfo->datums[off],
3106  boundinfo->kind[off],
3107  values, nvalues));
3108 
3109  /*
3110  * off + 1, then would be the offset of the greatest bound
3111  * to be included in the result.
3112  */
3113  maxoff = off + 1;
3114  }
3115 
3116  Assert(minoff >= 0 && maxoff >= 0);
3117  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3118  }
3119  else
3120  {
3121  /*
3122  * The lookup value falls in the range between some bounds in
3123  * boundinfo. 'off' would be the offset of the greatest bound
3124  * that is <= lookup value, so add off + 1 to the result
3125  * instead as the offset of the upper bound of the only
3126  * partition that may contain the lookup value. If 'off' is
3127  * -1 indicating that all bounds are greater, then we simply
3128  * end up adding the first bound's offset, that is, 0.
3129  */
3130  result->bound_offsets = bms_make_singleton(off + 1);
3131  }
3132 
3133  return result;
3134 
3136  inclusive = true;
3137  /* fall through */
3139 
3140  /*
3141  * Look for the smallest bound that is > or >= lookup value and
3142  * set minoff to its offset.
3143  */
3144  off = partition_range_datum_bsearch(partsupfunc,
3145  partcollation,
3146  boundinfo,
3147  nvalues, values,
3148  &is_equal);
3149  if (off < 0)
3150  {
3151  /*
3152  * All bounds are greater than the lookup value, so include
3153  * all of them in the result.
3154  */
3155  minoff = 0;
3156  }
3157  else
3158  {
3159  if (is_equal && nvalues < partnatts)
3160  {
3161  /*
3162  * Since the lookup value contains only a prefix of keys,
3163  * we must find other bounds that may also match the
3164  * prefix. partition_range_datum_bsearch() returns the
3165  * offset of one of them, find others by checking adjacent
3166  * bounds.
3167  *
3168  * Based on whether the lookup values are inclusive or
3169  * not, we must either include the indexes of all such
3170  * bounds in the result (that is, set minoff to the index
3171  * of smallest such bound) or find the smallest one that's
3172  * greater than the lookup values and set minoff to that.
3173  */
3174  while (off >= 1 && off < boundinfo->ndatums - 1)
3175  {
3176  int32 cmpval;
3177  int nextoff;
3178 
3179  nextoff = inclusive ? off - 1 : off + 1;
3180  cmpval =
3181  partition_rbound_datum_cmp(partsupfunc,
3182  partcollation,
3183  boundinfo->datums[nextoff],
3184  boundinfo->kind[nextoff],
3185  values, nvalues);
3186  if (cmpval != 0)
3187  break;
3188 
3189  off = nextoff;
3190  }
3191 
3192  Assert(0 ==
3193  partition_rbound_datum_cmp(partsupfunc,
3194  partcollation,
3195  boundinfo->datums[off],
3196  boundinfo->kind[off],
3197  values, nvalues));
3198 
3199  minoff = inclusive ? off : off + 1;
3200  }
3201  else
3202  {
3203 
3204  /*
3205  * lookup value falls in the range between some bounds in
3206  * boundinfo. off would be the offset of the greatest
3207  * bound that is <= lookup value, so add off + 1 to the
3208  * result instead as the offset of the upper bound of the
3209  * smallest partition that may contain the lookup value.
3210  */
3211  minoff = off + 1;
3212  }
3213  }
3214  break;
3215 
3217  inclusive = true;
3218  /* fall through */
3219  case BTLessStrategyNumber:
3220 
3221  /*
3222  * Look for the greatest bound that is < or <= lookup value and
3223  * set maxoff to its offset.
3224  */
3225  off = partition_range_datum_bsearch(partsupfunc,
3226  partcollation,
3227  boundinfo,
3228  nvalues, values,
3229  &is_equal);
3230  if (off >= 0)
3231  {
3232  /*
3233  * See the comment above.
3234  */
3235  if (is_equal && nvalues < partnatts)
3236  {
3237  while (off >= 1 && off < boundinfo->ndatums - 1)
3238  {
3239  int32 cmpval;
3240  int nextoff;
3241 
3242  nextoff = inclusive ? off + 1 : off - 1;
3243  cmpval = partition_rbound_datum_cmp(partsupfunc,
3244  partcollation,
3245  boundinfo->datums[nextoff],
3246  boundinfo->kind[nextoff],
3247  values, nvalues);
3248  if (cmpval != 0)
3249  break;
3250 
3251  off = nextoff;
3252  }
3253 
3254  Assert(0 ==
3255  partition_rbound_datum_cmp(partsupfunc,
3256  partcollation,
3257  boundinfo->datums[off],
3258  boundinfo->kind[off],
3259  values, nvalues));
3260 
3261  maxoff = inclusive ? off + 1 : off;
3262  }
3263 
3264  /*
3265  * The lookup value falls in the range between some bounds in
3266  * boundinfo. 'off' would be the offset of the greatest bound
3267  * that is <= lookup value, so add off + 1 to the result
3268  * instead as the offset of the upper bound of the greatest
3269  * partition that may contain lookup value. If the lookup
3270  * value had exactly matched the bound, but it isn't
3271  * inclusive, no need add the adjacent partition.
3272  */
3273  else if (!is_equal || inclusive)
3274  maxoff = off + 1;
3275  else
3276  maxoff = off;
3277  }
3278  else
3279  {
3280  /*
3281  * 'off' is -1 indicating that all bounds are greater, so just
3282  * set the first bound's offset as maxoff.
3283  */
3284  maxoff = off + 1;
3285  }
3286  break;
3287 
3288  default:
3289  elog(ERROR, "invalid strategy number %d", opstrategy);
3290  break;
3291  }
3292 
3293  Assert(minoff >= 0 && minoff <= boundinfo->ndatums);
3294  Assert(maxoff >= 0 && maxoff <= boundinfo->ndatums);
3295 
3296  /*
3297  * If the smallest partition to return has MINVALUE (negative infinity) as
3298  * its lower bound, increment it to point to the next finite bound
3299  * (supposedly its upper bound), so that we don't inadvertently end up
3300  * scanning the default partition.
3301  */
3302  if (minoff < boundinfo->ndatums && partindices[minoff] < 0)
3303  {
3304  int lastkey = nvalues - 1;
3305 
3306  if (boundinfo->kind[minoff][lastkey] ==
3308  {
3309  minoff++;
3310  Assert(boundinfo->indexes[minoff] >= 0);
3311  }
3312  }
3313 
3314  /*
3315  * If the previous greatest partition has MAXVALUE (positive infinity) as
3316  * its upper bound (something only possible to do with multi-column range
3317  * partitioning), we scan switch to it as the greatest partition to
3318  * return. Again, so that we don't inadvertently end up scanning the
3319  * default partition.
3320  */
3321  if (maxoff >= 1 && partindices[maxoff] < 0)
3322  {
3323  int lastkey = nvalues - 1;
3324 
3325  if (boundinfo->kind[maxoff - 1][lastkey] ==
3327  {
3328  maxoff--;
3329  Assert(boundinfo->indexes[maxoff] >= 0);
3330  }
3331  }
3332 
3333  Assert(minoff >= 0 && maxoff >= 0);
3334  if (minoff <= maxoff)
3335  result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3336 
3337  return result;
3338 }
signed int int32
Definition: c.h:494
@ PARTITION_RANGE_DATUM_MAXVALUE
Definition: parsenodes.h:926
@ PARTITION_RANGE_DATUM_MINVALUE
Definition: parsenodes.h:924
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, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, context, PartitionBoundInfoData::datums, elog, ERROR, PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, palloc0(), partition_bound_has_default, partition_range_datum_bsearch(), PARTITION_RANGE_DATUM_MAXVALUE, PARTITION_RANGE_DATUM_MINVALUE, partition_rbound_datum_cmp(), PARTITION_STRATEGY_RANGE, PruneStepResult::scan_default, PruneStepResult::scan_null, and values.

Referenced by perform_pruning_base_step().

◆ get_partkey_exec_paramids()

static Bitmapset * get_partkey_exec_paramids ( List steps)
static

Definition at line 3380 of file partprune.c.

3381 {
3382  Bitmapset *execparamids = NULL;
3383  ListCell *lc;
3384 
3385  foreach(lc, steps)
3386  {
3388  ListCell *lc2;
3389 
3390  if (!IsA(step, PartitionPruneStepOp))
3391  continue;
3392 
3393  foreach(lc2, step->exprs)
3394  {
3395  Expr *expr = lfirst(lc2);
3396 
3397  /* We can be quick for plain Consts */
3398  if (!IsA(expr, Const))
3399  execparamids = bms_join(execparamids,
3400  pull_exec_paramids(expr));
3401  }
3402  }
3403 
3404  return execparamids;
3405 }
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition: bitmapset.c:1230
static Bitmapset * pull_exec_paramids(Expr *expr)
Definition: partprune.c:3346

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,
Bitmapset step_nullkeys,
List prefix 
)
static

Definition at line 2438 of file partprune.c.

2445 {
2446  /* step_nullkeys must be empty for RANGE and LIST partitioned tables */
2447  Assert(step_nullkeys == NULL ||
2448  context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH);
2449 
2450  /*
2451  * No recursive processing is required when 'prefix' is an empty list.
2452  * This occurs when there is only 1 partition key column.
2453  */
2454  if (prefix == NIL)
2455  {
2456  PartitionPruneStep *step;
2457 
2458  step = gen_prune_step_op(context,
2459  step_opstrategy,
2460  step_op_is_ne,
2461  list_make1(step_lastexpr),
2462  list_make1_oid(step_lastcmpfn),
2463  step_nullkeys);
2464  return list_make1(step);
2465  }
2466 
2467  /* Recurse to generate steps for every combination of clauses. */
2469  step_opstrategy,
2470  step_op_is_ne,
2471  step_lastexpr,
2472  step_lastcmpfn,
2473  step_nullkeys,
2474  prefix,
2475  list_head(prefix),
2476  NIL, NIL);
2477 }
static List * get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, StrategyNumber step_opstrategy, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, Bitmapset *step_nullkeys, List *prefix, ListCell *start, List *step_exprs, List *step_cmpfns)
Definition: partprune.c:2496
#define list_make1_oid(x1)
Definition: pg_list.h:242

References Assert, context, gen_prune_step_op(), get_steps_using_prefix_recurse(), list_head(), list_make1, list_make1_oid, NIL, and PARTITION_STRATEGY_HASH.

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,
Bitmapset step_nullkeys,
List prefix,
ListCell start,
List step_exprs,
List step_cmpfns 
)
static

Definition at line 2496 of file partprune.c.

2506 {
2507  List *result = NIL;
2508  ListCell *lc;
2509  int cur_keyno;
2510  int final_keyno;
2511 
2512  /* Actually, recursion would be limited by PARTITION_MAX_KEYS. */
2514 
2515  Assert(start != NULL);
2516  cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno;
2517  final_keyno = ((PartClauseInfo *) llast(prefix))->keyno;
2518 
2519  /* Check if we need to recurse. */
2520  if (cur_keyno < final_keyno)
2521  {
2522  PartClauseInfo *pc;
2523  ListCell *next_start;
2524 
2525  /*
2526  * Find the first PartClauseInfo belonging to the next partition key,
2527  * the next recursive call must start iteration of the prefix list
2528  * from that point.
2529  */
2530  for_each_cell(lc, prefix, start)
2531  {
2532  pc = lfirst(lc);
2533 
2534  if (pc->keyno > cur_keyno)
2535  break;
2536  }
2537 
2538  /* record where to start iterating in the next recursive call */
2539  next_start = lc;
2540 
2541  /*
2542  * For each PartClauseInfo with keyno set to cur_keyno, add its expr
2543  * and cmpfn to step_exprs and step_cmpfns, respectively, and recurse
2544  * using 'next_start' as the starting point in the 'prefix' list.
2545  */
2546  for_each_cell(lc, prefix, start)
2547  {
2548  List *moresteps;
2549  List *step_exprs1,
2550  *step_cmpfns1;
2551 
2552  pc = lfirst(lc);
2553  if (pc->keyno == cur_keyno)
2554  {
2555  /* Leave the original step_exprs unmodified. */
2556  step_exprs1 = list_copy(step_exprs);
2557  step_exprs1 = lappend(step_exprs1, pc->expr);
2558 
2559  /* Leave the original step_cmpfns unmodified. */
2560  step_cmpfns1 = list_copy(step_cmpfns);
2561  step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2562  }
2563  else
2564  {
2565  /* check the 'prefix' list is sorted correctly */
2566  Assert(pc->keyno > cur_keyno);
2567  break;
2568  }
2569 
2571  step_opstrategy,
2572  step_op_is_ne,
2573  step_lastexpr,
2574  step_lastcmpfn,
2575  step_nullkeys,
2576  prefix,
2577  next_start,
2578  step_exprs1,
2579  step_cmpfns1);
2580  result = list_concat(result, moresteps);
2581 
2582  list_free(step_exprs1);
2583  list_free(step_cmpfns1);
2584  }
2585  }
2586  else
2587  {
2588  /*
2589  * End the current recursion cycle and start generating steps, one for
2590  * each clause with cur_keyno, which is all clauses from here onward
2591  * till the end of the list. Note that for hash partitioning,
2592  * step_nullkeys is allowed to be non-empty, in which case step_exprs
2593  * would only contain expressions for the partition keys that are not
2594  * specified in step_nullkeys.
2595  */
2596  Assert(list_length(step_exprs) == cur_keyno ||
2597  !bms_is_empty(step_nullkeys));
2598 
2599  /*
2600  * Note also that for hash partitioning, each partition key should
2601  * have either equality clauses or an IS NULL clause, so if a
2602  * partition key doesn't have an expression, it would be specified in
2603  * step_nullkeys.
2604  */
2605  Assert(context->rel->part_scheme->strategy
2607  list_length(step_exprs) + 2 + bms_num_members(step_nullkeys) ==
2608  context->rel->part_scheme->partnatts);
2609  for_each_cell(lc, prefix, start)
2610  {
2611  PartClauseInfo *pc = lfirst(lc);
2612  PartitionPruneStep *step;
2613  List *step_exprs1,
2614  *step_cmpfns1;
2615 
2616  Assert(pc->keyno == cur_keyno);
2617 
2618  /* Leave the original step_exprs unmodified. */
2619  step_exprs1 = list_copy(step_exprs);
2620  step_exprs1 = lappend(step_exprs1, pc->expr);
2621  step_exprs1 = lappend(step_exprs1, step_lastexpr);
2622 
2623  /* Leave the original step_cmpfns unmodified. */
2624  step_cmpfns1 = list_copy(step_cmpfns);
2625  step_cmpfns1 = lappend_oid(step_cmpfns1, pc->cmpfn);
2626  step_cmpfns1 = lappend_oid(step_cmpfns1, step_lastcmpfn);
2627 
2628  step = gen_prune_step_op(context,
2629  step_opstrategy, step_op_is_ne,
2630  step_exprs1, step_cmpfns1,
2631  step_nullkeys);
2632  result = lappend(result, step);
2633  }
2634  }
2635 
2636  return result;
2637 }
return str start
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
List * list_copy(const List *oldlist)
Definition: list.c:1573
void list_free(List *list)
Definition: list.c:1546
void check_stack_depth(void)
Definition: postgres.c:3531

References Assert, bms_is_empty, bms_num_members(), check_stack_depth(), PartClauseInfo::cmpfn, context, PartClauseInfo::expr, for_each_cell, gen_prune_step_op(), PartClauseInfo::keyno, lappend(), lappend_oid(), lfirst, list_concat(), list_copy(), list_free(), list_length(), llast, NIL, PARTITION_STRATEGY_HASH, and start.

Referenced by get_steps_using_prefix().

◆ make_partition_pruneinfo()

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

Definition at line 220 of file partprune.c.

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

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, pfree(), PartitionPruneInfo::prune_infos, RelOptInfo::relid, RELOPT_OTHER_MEMBER_REL, RelOptInfo::reloptkind, and root.

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

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

References adjust_appendrel_attrs(), adjust_appendrel_attrs_multilevel(), Assert, bms_add_member(), bms_equal(), bms_is_empty, bms_next_member(), context, PartitionedRelPruneInfo::exec_pruning_steps, PartitionedRelPruneInfo::execparamids, find_appinfos_by_relids(), find_base_rel(), gen_partprune_steps(), get_partkey_exec_paramids(), 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, root, and PartitionedRelPruneInfo::rtindex.

Referenced by make_partition_pruneinfo().

◆ match_boolean_partition_clause()

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

Definition at line 3673 of file partprune.c.

3675 {
3676  Expr *leftop;
3677 
3678  *outconst = NULL;
3679  *notclause = false;
3680 
3681  /*
3682  * Partitioning currently can only use built-in AMs, so checking for
3683  * built-in boolean opfamilies is good enough.
3684  */
3685  if (!IsBuiltinBooleanOpfamily(partopfamily))
3686  return PARTCLAUSE_UNSUPPORTED;
3687 
3688  if (IsA(clause, BooleanTest))
3689  {
3690  BooleanTest *btest = (BooleanTest *) clause;
3691 
3692  leftop = btest->arg;
3693  if (IsA(leftop, RelabelType))
3694  leftop = ((RelabelType *) leftop)->arg;
3695 
3696  if (equal(leftop, partkey))
3697  {
3698  switch (btest->booltesttype)
3699  {
3700  case IS_NOT_TRUE:
3701  *notclause = true;
3702  /* fall through */
3703  case IS_TRUE:
3704  *outconst = (Expr *) makeBoolConst(true, false);
3705  return PARTCLAUSE_MATCH_CLAUSE;
3706  case IS_NOT_FALSE:
3707  *notclause = true;
3708  /* fall through */
3709  case IS_FALSE:
3710  *outconst = (Expr *) makeBoolConst(false, false);
3711  return PARTCLAUSE_MATCH_CLAUSE;
3712  case IS_NOT_UNKNOWN:
3713  *notclause = true;
3714  /* fall through */
3715  case IS_UNKNOWN:
3717  default:
3718  return PARTCLAUSE_UNSUPPORTED;
3719  }
3720  }
3721  /* does not match partition key */
3722  return PARTCLAUSE_NOMATCH;
3723  }
3724  else
3725  {
3726  bool is_not_clause = is_notclause(clause);
3727 
3728  leftop = is_not_clause ? get_notclausearg(clause) : clause;
3729 
3730  if (IsA(leftop, RelabelType))
3731  leftop = ((RelabelType *) leftop)->arg;
3732 
3733  /* Compare to the partition key, and make up a clause ... */
3734  if (equal(leftop, partkey))
3735  *outconst = (Expr *) makeBoolConst(!is_not_clause, false);
3736  else if (equal(negate_clause((Node *) leftop), partkey))
3737  *outconst = (Expr *) makeBoolConst(is_not_clause, false);
3738  else
3739  return PARTCLAUSE_NOMATCH;
3740 
3741  return PARTCLAUSE_MATCH_CLAUSE;
3742  }
3743 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:359
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:73
@ IS_NOT_TRUE
Definition: primnodes.h:1978
@ IS_NOT_FALSE
Definition: primnodes.h:1978
@ IS_NOT_UNKNOWN
Definition: primnodes.h:1978
@ IS_TRUE
Definition: primnodes.h:1978
@ IS_UNKNOWN
Definition: primnodes.h:1978
@ IS_FALSE
Definition: primnodes.h:1978
BoolTestType booltesttype
Definition: primnodes.h:1985
Expr * arg
Definition: primnodes.h:1984

References arg, BooleanTest::arg, BooleanTest::booltesttype, equal(), get_notclausearg(), IS_FALSE, IS_NOT_FALSE, IS_NOT_TRUE, IS_NOT_UNKNOWN, is_notclause(), IS_TRUE, IS_UNKNOWN, IsA, makeBoolConst(), negate_clause(), PARTCLAUSE_MATCH_CLAUSE, PARTCLAUSE_MATCH_NULLNESS, 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 1790 of file partprune.c.

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

3763 {
3764  if (IsA(expr, Const))
3765  {
3766  /* We can always determine the value of a constant */
3767  Const *con = (Const *) expr;
3768 
3769  *value = con->constvalue;
3770  *isnull = con->constisnull;
3771  }
3772  else
3773  {
3774  ExprState *exprstate;
3775  ExprContext *ectx;
3776 
3777  /*
3778  * We should never see a non-Const in a step unless the caller has
3779  * passed a valid ExprContext.
3780  *
3781  * When context->planstate is valid, context->exprcontext is same as
3782  * context->planstate->ps_ExprContext.
3783  */
3784  Assert(context->planstate != NULL || context->exprcontext != NULL);
3785  Assert(context->planstate == NULL ||
3786  (context->exprcontext == context->planstate->ps_ExprContext));
3787 
3788  exprstate = context->exprstates[stateidx];
3789  ectx = context->exprcontext;
3790  *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
3791  }
3792 }
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:348

References Assert, context, ExecEvalExprSwitchContext(), IsA, 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 3416 of file partprune.c.

3418 {
3419  ListCell *lc1,
3420  *lc2;
3421  int keyno,
3422  nvalues;
3424  FmgrInfo *partsupfunc;
3425  int stateidx;
3426 
3427  /*
3428  * There better be the same number of expressions and compare functions.
3429  */
3430  Assert(list_length(opstep->exprs) == list_length(opstep->cmpfns));
3431 
3432  nvalues = 0;
3433  lc1 = list_head(opstep->exprs);
3434  lc2 = list_head(opstep->cmpfns);
3435 
3436  /*
3437  * Generate the partition lookup key that will be used by one of the
3438  * get_matching_*_bounds functions called below.
3439  */
3440  for (keyno = 0; keyno < context->partnatts; keyno++)
3441  {
3442  /*
3443  * For hash partitioning, it is possible that values of some keys are
3444  * not provided in operator clauses, but instead the planner found
3445  * that they appeared in a IS NULL clause.
3446  */
3447  if (bms_is_member(keyno, opstep->nullkeys))
3448  continue;
3449 
3450  /*
3451  * For range partitioning, we must only perform pruning with values
3452  * for either all partition keys or a prefix thereof.
3453  */
3454  if (keyno > nvalues && context->strategy == PARTITION_STRATEGY_RANGE)
3455  break;
3456 
3457  if (lc1 != NULL)
3458  {
3459  Expr *expr;
3460  Datum datum;
3461  bool isnull;
3462  Oid cmpfn;
3463 
3464  expr = lfirst(lc1);
3465  stateidx = PruneCxtStateIdx(context->partnatts,
3466  opstep->step.step_id, keyno);
3467  partkey_datum_from_expr(context, expr, stateidx,
3468  &datum, &isnull);
3469 
3470  /*
3471  * Since we only allow strict operators in pruning steps, any
3472  * null-valued comparison value must cause the comparison to fail,
3473  * so that no partitions could match.
3474  */
3475  if (isnull)
3476  {
3477  PruneStepResult *result;
3478 
3479  result = (PruneStepResult *) palloc(sizeof(PruneStepResult));
3480  result->bound_offsets = NULL;
3481  result->scan_default = false;
3482  result->scan_null = false;
3483 
3484  return result;
3485  }
3486 
3487  /* Set up the stepcmpfuncs entry, unless we already did */
3488  cmpfn = lfirst_oid(lc2);
3489  Assert(OidIsValid(cmpfn));
3490  if (cmpfn != context->stepcmpfuncs[stateidx].fn_oid)
3491  {
3492  /*
3493  * If the needed support function is the same one cached in
3494  * the relation's partition key, copy the cached FmgrInfo.
3495  * Otherwise (i.e., when we have a cross-type comparison), an
3496  * actual lookup is required.
3497  */
3498  if (cmpfn == context->partsupfunc[keyno].fn_oid)
3499  fmgr_info_copy(&context->stepcmpfuncs[stateidx],
3500  &context->partsupfunc[keyno],
3501  context->ppccontext);
3502  else
3503  fmgr_info_cxt(cmpfn, &context->stepcmpfuncs[stateidx],
3504  context->ppccontext);
3505  }
3506 
3507  values[keyno] = datum;
3508  nvalues++;
3509 
3510  lc1 = lnext(opstep->exprs, lc1);
3511  lc2 = lnext(opstep->cmpfns, lc2);
3512  }
3513  }
3514 
3515  /*
3516  * Point partsupfunc to the entry for the 0th key of this step; the
3517  * additional support functions, if any, follow consecutively.
3518  */
3519  stateidx = PruneCxtStateIdx(context->partnatts, opstep->step.step_id, 0);
3520  partsupfunc = &context->stepcmpfuncs[stateidx];
3521 
3522  switch (context->strategy)
3523  {
3526  opstep->opstrategy,
3527  values, nvalues,
3528  partsupfunc,
3529  opstep->nullkeys);
3530 
3533  opstep->opstrategy,
3534  values[0], nvalues,
3535  &partsupfunc[0],
3536  opstep->nullkeys);
3537 
3540  opstep->opstrategy,
3541  values, nvalues,
3542  partsupfunc,
3543  opstep->nullkeys);
3544 
3545  default:
3546  elog(ERROR, "unexpected partition strategy: %d",
3547  (int) context->strategy);
3548  break;
3549  }
3550 
3551  return NULL;
3552 }
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:2740
static PruneStepResult * get_matching_hash_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
Definition: partprune.c:2663
static void partkey_datum_from_expr(PartitionPruneContext *context, Expr *expr, int stateidx, Datum *value, bool *isnull)
Definition: partprune.c:3760
static PruneStepResult * get_matching_range_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
Definition: partprune.c:2951
#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

References Assert, bms_is_member(), PruneStepResult::bound_offsets, PartitionPruneStepOp::cmpfns, context, elog, ERROR, PartitionPruneStepOp::exprs, fmgr_info_copy(), fmgr_info_cxt(), 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(), PruneCxtStateIdx, PruneStepResult::scan_default, PruneStepResult::scan_null, PartitionPruneStepOp::step, PartitionPruneStep::step_id, 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 3564 of file partprune.c.

3567 {
3568  PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
3569  bool firststep;
3570  ListCell *lc1;
3571 
3572  /*
3573  * A combine step without any source steps is an indication to not perform
3574  * any partition pruning. Return all datum indexes in that case.
3575  */
3576  if (cstep->source_stepids == NIL)
3577  {
3578  PartitionBoundInfo boundinfo = context->boundinfo;
3579 
3580  result->bound_offsets =
3581  bms_add_range(NULL, 0, boundinfo->nindexes - 1);
3582  result->scan_default = partition_bound_has_default(boundinfo);
3583  result->scan_null = partition_bound_accepts_nulls(boundinfo);
3584  return result;
3585  }
3586 
3587  switch (cstep->combineOp)
3588  {
3590  foreach(lc1, cstep->source_stepids)
3591  {
3592  int step_id = lfirst_int(lc1);
3593  PruneStepResult *step_result;
3594 
3595  /*
3596  * step_results[step_id] must contain a valid result, which is
3597  * confirmed by the fact that cstep's step_id is greater than
3598  * step_id and the fact that results of the individual steps
3599  * are evaluated in sequence of their step_ids.
3600  */
3601  if (step_id >= cstep->step.step_id)
3602  elog(ERROR, "invalid pruning combine step argument");
3603  step_result = step_results[step_id];
3604  Assert(step_result != NULL);
3605 
3606  /* Record any additional datum indexes from this step */
3607  result->bound_offsets = bms_add_members(result->bound_offsets,
3608  step_result->bound_offsets);
3609 
3610  /* Update whether to scan null and default partitions. */
3611  if (!result->scan_null)
3612  result->scan_null = step_result->scan_null;
3613  if (!result->scan_default)
3614  result->scan_default = step_result->scan_default;
3615  }
3616  break;
3617 
3619  firststep = true;
3620  foreach(lc1, cstep->source_stepids)
3621  {
3622  int step_id = lfirst_int(lc1);
3623  PruneStepResult *step_result;
3624 
3625  if (step_id >= cstep->step.step_id)
3626  elog(ERROR, "invalid pruning combine step argument");
3627  step_result = step_results[step_id];
3628  Assert(step_result != NULL);
3629 
3630  if (firststep)
3631  {
3632  /* Copy step's result the first time. */
3633  result->bound_offsets =
3634  bms_copy(step_result->bound_offsets);
3635  result->scan_null = step_result->scan_null;
3636  result->scan_default = step_result->scan_default;
3637  firststep = false;
3638  }
3639  else
3640  {
3641  /* Record datum indexes common to both steps */
3642  result->bound_offsets =
3644  step_result->bound_offsets);
3645 
3646  /* Update whether to scan null and default partitions. */
3647  if (result->scan_null)
3648  result->scan_null = step_result->scan_null;
3649  if (result->scan_default)
3650  result->scan_default = step_result->scan_default;
3651  }
3652  }
3653  break;
3654  }
3655 
3656  return result;
3657 }
Bitmapset * bms_int_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:1109
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:122
#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, PartitionPruneStepCombine::combineOp, context, 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 750 of file partprune.c.

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

References Assert, RelOptInfo::baserestrictinfo, bms_add_range(), context, GeneratePruningStepsContext::contradictory, CurrentMemoryContext, enable_partition_pruning, gen_partprune_steps(), get_matching_partitions(), list_length(), NIL, RelOptInfo::nparts, palloc0(), PARTTARGET_PLANNER, and GeneratePruningStepsContext::steps.

Referenced by expand_partitioned_rtentry().

◆ pull_exec_paramids()

static Bitmapset * pull_exec_paramids ( Expr expr)
static

Definition at line 3346 of file partprune.c.

3347 {
3348  Bitmapset *result = NULL;
3349 
3350  (void) pull_exec_paramids_walker((Node *) expr, &result);
3351 
3352  return result;
3353 }
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context)
Definition: partprune.c:3356

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

3357 {
3358  if (node == NULL)
3359  return false;
3360  if (IsA(node, Param))
3361  {
3362  Param *param = (Param *) node;
3363 
3364  if (param->paramkind == PARAM_EXEC)
3365  *context = bms_add_member(*context, param->paramid);
3366  return false;
3367  }
3369  (void *) context);
3370 }
#define expression_tree_walker(n, w, c)
Definition: nodeFuncs.h:151
@ PARAM_EXEC
Definition: primnodes.h:368
int paramid
Definition: primnodes.h:377
ParamKind paramkind
Definition: primnodes.h:376

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

Referenced by pull_exec_paramids().