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

Go to the source code of this file.

Data Structures

struct  PartClauseInfo
 
struct  GeneratePruningStepsContext
 
struct  PruneStepResult
 

Macros

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

Typedefs

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

Enumerations

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

Functions

static Listadd_part_relids (List *allpartrelids, Bitmapset *partrelids)
 
static Listmake_partitionedrel_pruneinfo (PlannerInfo *root, RelOptInfo *parentrel, List *prunequal, Bitmapset *partrelids, int *relid_subplan_map, Bitmapset **matchedsubplans)
 
static void gen_partprune_steps (RelOptInfo *rel, List *clauses, PartClauseTarget target, GeneratePruningStepsContext *context)
 
static Listgen_partprune_steps_internal (GeneratePruningStepsContext *context, List *clauses)
 
static PartitionPruneStepgen_prune_step_op (GeneratePruningStepsContext *context, StrategyNumber opstrategy, bool op_is_ne, List *exprs, List *cmpfns, Bitmapset *nullkeys)
 
static PartitionPruneStepgen_prune_step_combine (GeneratePruningStepsContext *context, List *source_stepids, PartitionPruneCombineOp combineOp)
 
static Listgen_prune_steps_from_opexps (GeneratePruningStepsContext *context, List **keyclauses, Bitmapset *nullkeys)
 
static PartClauseMatchStatus match_clause_to_partition_key (GeneratePruningStepsContext *context, Expr *clause, Expr *partkey, int partkeyidx, bool *clause_is_not_null, PartClauseInfo **pc, List **clause_steps)
 
static Listget_steps_using_prefix (GeneratePruningStepsContext *context, StrategyNumber step_opstrategy, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, 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 *noteq)
 
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 1756 of file partprune.c.

Typedef Documentation

◆ GeneratePruningStepsContext

◆ PartClauseInfo

◆ PartClauseMatchStatus

◆ PartClauseTarget

◆ PruneStepResult

Enumeration Type Documentation

◆ PartClauseMatchStatus

Enumerator
PARTCLAUSE_NOMATCH 
PARTCLAUSE_MATCH_CLAUSE 
PARTCLAUSE_MATCH_NULLNESS 
PARTCLAUSE_MATCH_STEPS 
PARTCLAUSE_MATCH_CONTRADICT 
PARTCLAUSE_UNSUPPORTED 

Definition at line 78 of file partprune.c.

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

◆ PartClauseTarget

Enumerator
PARTTARGET_PLANNER 
PARTTARGET_INITIAL 
PARTTARGET_EXEC 

Definition at line 92 of file partprune.c.

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

Function Documentation

◆ add_part_relids()

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

Definition at line 393 of file partprune.c.

394 {
395  Index targetpart;
396  ListCell *lc;
397 
398  /* We can easily get the lowest set bit this way: */
399  targetpart = bms_next_member(partrelids, -1);
400  Assert(targetpart > 0);
401 
402  /* Look for a matching topmost parent */
403  foreach(lc, allpartrelids)
404  {
405  Bitmapset *currpartrelids = (Bitmapset *) lfirst(lc);
406  Index currtarget = bms_next_member(currpartrelids, -1);
407 
408  if (targetpart == currtarget)
409  {
410  /* Found a match, so add any new RT indexes to this hierarchy */
411  currpartrelids = bms_add_members(currpartrelids, partrelids);
412  lfirst(lc) = currpartrelids;
413  return allpartrelids;
414  }
415  }
416  /* No match, so add the new partition hierarchy to the list */
417  return lappend(allpartrelids, partrelids);
418 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1319
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:930
unsigned int Index
Definition: c.h:603
Assert(fmt[strlen(fmt) - 1] !='\n')
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 715 of file partprune.c.

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

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

Referenced by make_partitionedrel_pruneinfo(), and prune_append_rel_partitions().

◆ gen_partprune_steps_internal()

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

Definition at line 962 of file partprune.c.

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

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

Referenced by gen_partprune_steps(), and match_clause_to_partition_key().

◆ gen_prune_step_combine()

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

Definition at line 1347 of file partprune.c.

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

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

Referenced by gen_partprune_steps_internal().

◆ gen_prune_step_op()

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

Definition at line 1314 of file partprune.c.

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

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

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

◆ gen_prune_steps_from_opexps()

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

Definition at line 1384 of file partprune.c.

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

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

Referenced by gen_partprune_steps_internal().

◆ get_matching_hash_bounds()

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

Definition at line 2655 of file partprune.c.

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

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

Referenced by perform_pruning_base_step().

◆ get_matching_list_bounds()

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

Definition at line 2732 of file partprune.c.

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

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

Referenced by perform_pruning_base_step().

◆ get_matching_partitions()

Bitmapset* get_matching_partitions ( PartitionPruneContext context,
List pruning_steps 
)

Definition at line 818 of file partprune.c.

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

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

Referenced by find_matching_subplans_recurse(), and prune_append_rel_partitions().

◆ get_matching_range_bounds()

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

Definition at line 2943 of file partprune.c.

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

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

Referenced by perform_pruning_base_step().

◆ get_partkey_exec_paramids()

static Bitmapset * get_partkey_exec_paramids ( List steps)
static

Definition at line 3372 of file partprune.c.

3373 {
3374  Bitmapset *execparamids = NULL;
3375  ListCell *lc;
3376 
3377  foreach(lc, steps)
3378  {
3380  ListCell *lc2;
3381 
3382  if (!IsA(step, PartitionPruneStepOp))
3383  continue;
3384 
3385  foreach(lc2, step->exprs)
3386  {
3387  Expr *expr = lfirst(lc2);
3388 
3389  /* We can be quick for plain Consts */
3390  if (!IsA(expr, Const))
3391  execparamids = bms_join(execparamids,
3392  pull_exec_paramids(expr));
3393  }
3394  }
3395 
3396  return execparamids;
3397 }
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition: bitmapset.c:1243
static Bitmapset * pull_exec_paramids(Expr *expr)
Definition: partprune.c:3338

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

2437 {
2438  /* step_nullkeys must be empty for RANGE and LIST partitioned tables */
2439  Assert(step_nullkeys == NULL ||
2440  context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH);
2441 
2442  /*
2443  * No recursive processing is required when 'prefix' is an empty list.
2444  * This occurs when there is only 1 partition key column.
2445  */
2446  if (prefix == NIL)
2447  {
2448  PartitionPruneStep *step;
2449 
2450  step = gen_prune_step_op(context,
2451  step_opstrategy,
2452  step_op_is_ne,
2453  list_make1(step_lastexpr),
2454  list_make1_oid(step_lastcmpfn),
2455  step_nullkeys);
2456  return list_make1(step);
2457  }
2458 
2459  /* Recurse to generate steps for every combination of clauses. */
2460  return get_steps_using_prefix_recurse(context,
2461  step_opstrategy,
2462  step_op_is_ne,
2463  step_lastexpr,
2464  step_lastcmpfn,
2465  step_nullkeys,
2466  prefix,
2467  list_head(prefix),
2468  NIL, NIL);
2469 }
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:2488
#define list_make1_oid(x1)
Definition: pg_list.h:242

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

Referenced by gen_prune_steps_from_opexps().

◆ get_steps_using_prefix_recurse()

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

Definition at line 2488 of file partprune.c.

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

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

Referenced by get_steps_using_prefix().

◆ make_partition_pruneinfo()

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

Definition at line 221 of file partprune.c.

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

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 PlannerInfo::simple_rel_array_size.

Referenced by create_append_plan(), and create_merge_append_plan().

◆ make_partitionedrel_pruneinfo()

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

Definition at line 439 of file partprune.c.

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

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

Referenced by make_partition_pruneinfo().

◆ match_boolean_partition_clause()

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

Definition at line 3664 of file partprune.c.

3666 {
3667  Expr *leftop;
3668 
3669  *outconst = NULL;
3670  *noteq = false;
3671 
3672  /*
3673  * Partitioning currently can only use built-in AMs, so checking for
3674  * built-in boolean opfamilies is good enough.
3675  */
3676  if (!IsBuiltinBooleanOpfamily(partopfamily))
3677  return PARTCLAUSE_UNSUPPORTED;
3678 
3679  if (IsA(clause, BooleanTest))
3680  {
3681  BooleanTest *btest = (BooleanTest *) clause;
3682 
3683  /* Only IS [NOT] TRUE/FALSE are any good to us */
3684  if (btest->booltesttype == IS_UNKNOWN ||
3685  btest->booltesttype == IS_NOT_UNKNOWN)
3686  return PARTCLAUSE_UNSUPPORTED;
3687 
3688  leftop = btest->arg;
3689  if (IsA(leftop, RelabelType))
3690  leftop = ((RelabelType *) leftop)->arg;
3691 
3692  if (equal(leftop, partkey))
3693  {
3694  switch (btest->booltesttype)
3695  {
3696  case IS_NOT_TRUE:
3697  *noteq = true;
3698  /* fall through */
3699  case IS_TRUE:
3700  *outconst = (Expr *) makeBoolConst(true, false);
3701  break;
3702  case IS_NOT_FALSE:
3703  *noteq = true;
3704  /* fall through */
3705  case IS_FALSE:
3706  *outconst = (Expr *) makeBoolConst(false, false);
3707  break;
3708  default:
3709  return PARTCLAUSE_UNSUPPORTED;
3710  }
3711  }
3712  if (*outconst)
3713  return PARTCLAUSE_MATCH_CLAUSE;
3714  }
3715  else
3716  {
3717  bool is_not_clause = is_notclause(clause);
3718 
3719  leftop = is_not_clause ? get_notclausearg(clause) : clause;
3720 
3721  if (IsA(leftop, RelabelType))
3722  leftop = ((RelabelType *) leftop)->arg;
3723 
3724  /* Compare to the partition key, and make up a clause ... */
3725  if (equal(leftop, partkey))
3726  *outconst = (Expr *) makeBoolConst(!is_not_clause, false);
3727  else if (equal(negate_clause((Node *) leftop), partkey))
3728  *outconst = (Expr *) makeBoolConst(is_not_clause, false);
3729 
3730  if (*outconst)
3731  return PARTCLAUSE_MATCH_CLAUSE;
3732  }
3733 
3734  return PARTCLAUSE_NOMATCH;
3735 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:360
static Expr * get_notclausearg(const void *notclause)
Definition: nodeFuncs.h:132
static bool is_notclause(const void *clause)
Definition: nodeFuncs.h:123
Node * negate_clause(Node *node)
Definition: prepqual.c:74
@ IS_NOT_TRUE
Definition: primnodes.h:1718
@ IS_NOT_FALSE
Definition: primnodes.h:1718
@ IS_NOT_UNKNOWN
Definition: primnodes.h:1718
@ IS_TRUE
Definition: primnodes.h:1718
@ IS_UNKNOWN
Definition: primnodes.h:1718
@ IS_FALSE
Definition: primnodes.h:1718
BoolTestType booltesttype
Definition: primnodes.h:1725
Expr * arg
Definition: primnodes.h:1724

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

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

Referenced by gen_partprune_steps_internal().

◆ partkey_datum_from_expr()

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

Definition at line 3752 of file partprune.c.

3755 {
3756  if (IsA(expr, Const))
3757  {
3758  /* We can always determine the value of a constant */
3759  Const *con = (Const *) expr;
3760 
3761  *value = con->constvalue;
3762  *isnull = con->constisnull;
3763  }
3764  else
3765  {
3766  ExprState *exprstate;
3767  ExprContext *ectx;
3768 
3769  /*
3770  * We should never see a non-Const in a step unless the caller has
3771  * passed a valid ExprContext.
3772  *
3773  * When context->planstate is valid, context->exprcontext is same as
3774  * context->planstate->ps_ExprContext.
3775  */
3776  Assert(context->planstate != NULL || context->exprcontext != NULL);
3777  Assert(context->planstate == NULL ||
3778  (context->exprcontext == context->planstate->ps_ExprContext));
3779 
3780  exprstate = context->exprstates[stateidx];
3781  ectx = context->exprcontext;
3782  *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
3783  }
3784 }
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:347
ExprContext * exprcontext
Definition: partprune.h:60
PlanState * planstate
Definition: partprune.h:59
ExprState ** exprstates
Definition: partprune.h:61
ExprContext * ps_ExprContext
Definition: execnodes.h:1082

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

Referenced by perform_pruning_base_step().

◆ perform_pruning_base_step()

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

Definition at line 3408 of file partprune.c.

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

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

Referenced by get_matching_partitions().

◆ perform_pruning_combine_step()

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

Definition at line 3556 of file partprune.c.

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

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

Referenced by get_matching_partitions().

◆ prune_append_rel_partitions()

Bitmapset* prune_append_rel_partitions ( RelOptInfo rel)

Definition at line 751 of file partprune.c.

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

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

Referenced by expand_partitioned_rtentry().

◆ pull_exec_paramids()

static Bitmapset * pull_exec_paramids ( Expr expr)
static

Definition at line 3338 of file partprune.c.

3339 {
3340  Bitmapset *result = NULL;
3341 
3342  (void) pull_exec_paramids_walker((Node *) expr, &result);
3343 
3344  return result;
3345 }
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context)
Definition: partprune.c:3348

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

3349 {
3350  if (node == NULL)
3351  return false;
3352  if (IsA(node, Param))
3353  {
3354  Param *param = (Param *) node;
3355 
3356  if (param->paramkind == PARAM_EXEC)
3357  *context = bms_add_member(*context, param->paramid);
3358  return false;
3359  }
3361  (void *) context);
3362 }
#define expression_tree_walker(n, w, c)
Definition: nodeFuncs.h:151
@ PARAM_EXEC
Definition: primnodes.h:354
int paramid
Definition: primnodes.h:363
ParamKind paramkind
Definition: primnodes.h:362

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

Referenced by pull_exec_paramids().