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

Go to the source code of this file.

Data Structures

struct  PartClauseInfo
 
struct  GeneratePruningStepsContext
 
struct  PruneStepResult
 

Macros

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

Typedefs

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

Enumerations

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

Functions

static Listadd_part_relids (List *allpartrelids, Bitmapset *partrelids)
 
static Listmake_partitionedrel_pruneinfo (PlannerInfo *root, RelOptInfo *parentrel, List *prunequal, Bitmapset *partrelids, int *relid_subplan_map, Bitmapset **matchedsubplans)
 
static void gen_partprune_steps (RelOptInfo *rel, List *clauses, PartClauseTarget target, GeneratePruningStepsContext *context)
 
static Listgen_partprune_steps_internal (GeneratePruningStepsContext *context, List *clauses)
 
static PartitionPruneStepgen_prune_step_op (GeneratePruningStepsContext *context, StrategyNumber opstrategy, bool op_is_ne, List *exprs, List *cmpfns, Bitmapset *nullkeys)
 
static PartitionPruneStepgen_prune_step_combine (GeneratePruningStepsContext *context, List *source_stepids, PartitionPruneCombineOp combineOp)
 
static Listgen_prune_steps_from_opexps (GeneratePruningStepsContext *context, List **keyclauses, Bitmapset *nullkeys)
 
static PartClauseMatchStatus match_clause_to_partition_key (GeneratePruningStepsContext *context, Expr *clause, Expr *partkey, int partkeyidx, bool *clause_is_not_null, PartClauseInfo **pc, List **clause_steps)
 
static Listget_steps_using_prefix (GeneratePruningStepsContext *context, StrategyNumber step_opstrategy, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, Bitmapset *step_nullkeys, List *prefix)
 
static Listget_steps_using_prefix_recurse (GeneratePruningStepsContext *context, StrategyNumber step_opstrategy, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, Bitmapset *step_nullkeys, List *prefix, ListCell *start, List *step_exprs, List *step_cmpfns)
 
static PruneStepResultget_matching_hash_bounds (PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
 
static PruneStepResultget_matching_list_bounds (PartitionPruneContext *context, StrategyNumber opstrategy, Datum value, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
 
static PruneStepResultget_matching_range_bounds (PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
 
static Bitmapsetpull_exec_paramids (Expr *expr)
 
static bool pull_exec_paramids_walker (Node *node, Bitmapset **context)
 
static Bitmapsetget_partkey_exec_paramids (List *steps)
 
static PruneStepResultperform_pruning_base_step (PartitionPruneContext *context, PartitionPruneStepOp *opstep)
 
static PruneStepResultperform_pruning_combine_step (PartitionPruneContext *context, PartitionPruneStepCombine *cstep, PruneStepResult **step_results)
 
static PartClauseMatchStatus match_boolean_partition_clause (Oid partopfamily, Expr *clause, Expr *partkey, Expr **outconst, bool *notclause)
 
static void partkey_datum_from_expr (PartitionPruneContext *context, Expr *expr, int stateidx, Datum *value, bool *isnull)
 
int make_partition_pruneinfo (PlannerInfo *root, RelOptInfo *parentrel, List *subpaths, List *prunequal)
 
Bitmapsetprune_append_rel_partitions (RelOptInfo *rel)
 
Bitmapsetget_matching_partitions (PartitionPruneContext *context, List *pruning_steps)
 

Macro Definition Documentation

◆ PartCollMatchesExprColl

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

Definition at line 1777 of file partprune.c.

Typedef Documentation

◆ GeneratePruningStepsContext

◆ PartClauseInfo

◆ PartClauseMatchStatus

◆ PartClauseTarget

◆ PruneStepResult

Enumeration Type Documentation

◆ PartClauseMatchStatus

Enumerator
PARTCLAUSE_NOMATCH 
PARTCLAUSE_MATCH_CLAUSE 
PARTCLAUSE_MATCH_NULLNESS 
PARTCLAUSE_MATCH_STEPS 
PARTCLAUSE_MATCH_CONTRADICT 
PARTCLAUSE_UNSUPPORTED 

Definition at line 77 of file partprune.c.

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

◆ PartClauseTarget

Enumerator
PARTTARGET_PLANNER 
PARTTARGET_INITIAL 
PARTTARGET_EXEC 

Definition at line 91 of file partprune.c.

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

Function Documentation

◆ add_part_relids()

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

Definition at line 399 of file partprune.c.

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

738{
739 /* Initialize all output values to zero/false/NULL */
740 memset(context, 0, sizeof(GeneratePruningStepsContext));
741 context->rel = rel;
742 context->target = target;
743
744 /*
745 * If this partitioned table is in turn a partition, and it shares any
746 * partition keys with its parent, then it's possible that the hierarchy
747 * allows the parent a narrower range of values than some of its
748 * partitions (particularly the default one). This is normally not
749 * useful, but it can be to prune the default partition.
750 */
751 if (partition_bound_has_default(rel->boundinfo) && rel->partition_qual)
752 {
753 /* Make a copy to avoid modifying the passed-in List */
754 clauses = list_concat_copy(clauses, rel->partition_qual);
755 }
756
757 /* Down into the rabbit-hole. */
758 (void) gen_partprune_steps_internal(context, clauses);
759}
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:983
PartClauseTarget target
Definition: partprune.c:114
List * partition_qual
Definition: pathnodes.h:1046

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

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

1371{
1373
1374 cstep->step.step_id = context->next_step_id++;
1375 cstep->combineOp = combineOp;
1376 cstep->source_stepids = source_stepids;
1377
1378 context->steps = lappend(context->steps, cstep);
1379
1380 return (PartitionPruneStep *) cstep;
1381}
#define makeNode(_type_)
Definition: nodes.h:155
PartitionPruneCombineOp combineOp
Definition: plannodes.h:1719
PartitionPruneStep step
Definition: plannodes.h:1717

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

1339{
1341
1342 opstep->step.step_id = context->next_step_id++;
1343
1344 /*
1345 * For clauses that contain an <> operator, set opstrategy to
1346 * InvalidStrategy to signal get_matching_list_bounds to do the right
1347 * thing.
1348 */
1349 opstep->opstrategy = op_is_ne ? InvalidStrategy : opstrategy;
1350 Assert(list_length(exprs) == list_length(cmpfns));
1351 opstep->exprs = exprs;
1352 opstep->cmpfns = cmpfns;
1353 opstep->nullkeys = nullkeys;
1354
1355 context->steps = lappend(context->steps, opstep);
1356
1357 return (PartitionPruneStep *) opstep;
1358}
PartitionPruneStep step
Definition: plannodes.h:1695
StrategyNumber opstrategy
Definition: plannodes.h:1697
Bitmapset * nullkeys
Definition: plannodes.h:1700

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

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

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

2688{
2690 PartitionBoundInfo boundinfo = context->boundinfo;
2691 int *partindices = boundinfo->indexes;
2692 int partnatts = context->partnatts;
2693 bool isnull[PARTITION_MAX_KEYS];
2694 int i;
2695 uint64 rowHash;
2696 int greatest_modulus;
2697 Oid *partcollation = context->partcollation;
2698
2700
2701 /*
2702 * For hash partitioning we can only perform pruning based on equality
2703 * clauses to the partition key or IS NULL clauses. We also can only
2704 * prune if we got values for all keys.
2705 */
2706 if (nvalues + bms_num_members(nullkeys) == partnatts)
2707 {
2708 /*
2709 * If there are any values, they must have come from clauses
2710 * containing an equality operator compatible with hash partitioning.
2711 */
2712 Assert(opstrategy == HTEqualStrategyNumber || nvalues == 0);
2713
2714 for (i = 0; i < partnatts; i++)
2715 isnull[i] = bms_is_member(i, nullkeys);
2716
2717 rowHash = compute_partition_hash_value(partnatts, partsupfunc, partcollation,
2718 values, isnull);
2719
2720 greatest_modulus = boundinfo->nindexes;
2721 if (partindices[rowHash % greatest_modulus] >= 0)
2722 result->bound_offsets =
2723 bms_make_singleton(rowHash % greatest_modulus);
2724 }
2725 else
2726 {
2727 /* Report all valid offsets into the boundinfo->indexes array. */
2728 result->bound_offsets = bms_add_range(NULL, 0,
2729 boundinfo->nindexes - 1);
2730 }
2731
2732 /*
2733 * There is neither a special hash null partition or the default hash
2734 * partition.
2735 */
2736 result->scan_null = result->scan_default = false;
2737
2738 return result;
2739}
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:216
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:1019
static Datum values[MAXATTR]
Definition: bootstrap.c:151
uint64_t uint64
Definition: c.h:489
void * palloc0(Size size)
Definition: mcxt.c:1347
uint64 compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc, const Oid *partcollation, const Datum *values, const bool *isnull)
Definition: partbounds.c:4722
PartitionBoundInfo boundinfo
Definition: partprune.h:54
Bitmapset * bound_offsets
Definition: partprune.c:133

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

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

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

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

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

3402{
3403 Bitmapset *execparamids = NULL;
3404 ListCell *lc;
3405
3406 foreach(lc, steps)
3407 {
3409 ListCell *lc2;
3410
3411 if (!IsA(step, PartitionPruneStepOp))
3412 continue;
3413
3414 foreach(lc2, step->exprs)
3415 {
3416 Expr *expr = lfirst(lc2);
3417
3418 /* We can be quick for plain Consts */
3419 if (!IsA(expr, Const))
3420 execparamids = bms_join(execparamids,
3421 pull_exec_paramids(expr));
3422 }
3423 }
3424
3425 return execparamids;
3426}
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition: bitmapset.c:1230
static Bitmapset * pull_exec_paramids(Expr *expr)
Definition: partprune.c:3368

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

2467{
2468 /* step_nullkeys must be empty for RANGE and LIST partitioned tables */
2469 Assert(step_nullkeys == NULL ||
2470 context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH);
2471
2472 /*
2473 * No recursive processing is required when 'prefix' is an empty list.
2474 * This occurs when there is only 1 partition key column.
2475 */
2476 if (prefix == NIL)
2477 {
2478 PartitionPruneStep *step;
2479
2480 step = gen_prune_step_op(context,
2481 step_opstrategy,
2482 step_op_is_ne,
2483 list_make1(step_lastexpr),
2484 list_make1_oid(step_lastcmpfn),
2485 step_nullkeys);
2486 return list_make1(step);
2487 }
2488
2489 /* Recurse to generate steps for every combination of clauses. */
2490 return get_steps_using_prefix_recurse(context,
2491 step_opstrategy,
2492 step_op_is_ne,
2493 step_lastexpr,
2494 step_lastcmpfn,
2495 step_nullkeys,
2496 prefix,
2497 list_head(prefix),
2498 NIL, NIL);
2499}
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:2518
#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 2518 of file partprune.c.

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

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

Referenced by get_steps_using_prefix(), and get_steps_using_prefix_recurse().

◆ make_partition_pruneinfo()

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

Definition at line 224 of file partprune.c.

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

References add_part_relids(), Assert, bms_add_member(), bms_add_range(), bms_copy(), 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, RelOptInfo::relids, PartitionPruneInfo::relids, RELOPT_OTHER_MEMBER_REL, RelOptInfo::reloptkind, and root.

Referenced by create_append_plan(), and create_merge_append_plan().

◆ make_partitionedrel_pruneinfo()

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

Definition at line 445 of file partprune.c.

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

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, root, PartitionedRelPruneInfo::rtindex, 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 *  notclause 
)
static

Definition at line 3694 of file partprune.c.

3696{
3697 Expr *leftop;
3698
3699 *outconst = NULL;
3700 *notclause = false;
3701
3702 /*
3703 * Partitioning currently can only use built-in AMs, so checking for
3704 * built-in boolean opfamilies is good enough.
3705 */
3706 if (!IsBuiltinBooleanOpfamily(partopfamily))
3708
3709 if (IsA(clause, BooleanTest))
3710 {
3711 BooleanTest *btest = (BooleanTest *) clause;
3712
3713 leftop = btest->arg;
3714 if (IsA(leftop, RelabelType))
3715 leftop = ((RelabelType *) leftop)->arg;
3716
3717 if (equal(leftop, partkey))
3718 {
3719 switch (btest->booltesttype)
3720 {
3721 case IS_NOT_TRUE:
3722 *notclause = true;
3723 /* fall through */
3724 case IS_TRUE:
3725 *outconst = (Expr *) makeBoolConst(true, false);
3727 case IS_NOT_FALSE:
3728 *notclause = true;
3729 /* fall through */
3730 case IS_FALSE:
3731 *outconst = (Expr *) makeBoolConst(false, false);
3733 case IS_NOT_UNKNOWN:
3734 *notclause = true;
3735 /* fall through */
3736 case IS_UNKNOWN:
3738 default:
3740 }
3741 }
3742 /* does not match partition key */
3743 return PARTCLAUSE_NOMATCH;
3744 }
3745 else
3746 {
3747 bool is_not_clause = is_notclause(clause);
3748
3749 leftop = is_not_clause ? get_notclausearg(clause) : clause;
3750
3751 if (IsA(leftop, RelabelType))
3752 leftop = ((RelabelType *) leftop)->arg;
3753
3754 /* Compare to the partition key, and make up a clause ... */
3755 if (equal(leftop, partkey))
3756 *outconst = (Expr *) makeBoolConst(!is_not_clause, false);
3757 else if (equal(negate_clause((Node *) leftop), partkey))
3758 *outconst = (Expr *) makeBoolConst(is_not_clause, false);
3759 else
3760 return PARTCLAUSE_NOMATCH;
3761
3763 }
3764}
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
Node * makeBoolConst(bool value, bool isnull)
Definition: makefuncs.c:361
static bool is_notclause(const void *clause)
Definition: nodeFuncs.h:125
static Expr * get_notclausearg(const void *notclause)
Definition: nodeFuncs.h:134
Node * negate_clause(Node *node)
Definition: prepqual.c:73
@ IS_NOT_TRUE
Definition: primnodes.h:1981
@ IS_NOT_FALSE
Definition: primnodes.h:1981
@ IS_NOT_UNKNOWN
Definition: primnodes.h:1981
@ IS_TRUE
Definition: primnodes.h:1981
@ IS_UNKNOWN
Definition: primnodes.h:1981
@ IS_FALSE
Definition: primnodes.h:1981
BoolTestType booltesttype
Definition: primnodes.h:1988
Expr * arg
Definition: primnodes.h:1987

References arg, BooleanTest::arg, BooleanTest::booltesttype, equal(), get_notclausearg(), IS_FALSE, IS_NOT_FALSE, IS_NOT_TRUE, IS_NOT_UNKNOWN, is_notclause(), IS_TRUE, IS_UNKNOWN, IsA, makeBoolConst(), negate_clause(), PARTCLAUSE_MATCH_CLAUSE, PARTCLAUSE_MATCH_NULLNESS, PARTCLAUSE_NOMATCH, and PARTCLAUSE_UNSUPPORTED.

Referenced by match_clause_to_partition_key().

◆ match_clause_to_partition_key()

static PartClauseMatchStatus match_clause_to_partition_key ( GeneratePruningStepsContext context,
Expr clause,
Expr partkey,
int  partkeyidx,
bool *  clause_is_not_null,
PartClauseInfo **  pc,
List **  clause_steps 
)
static

Definition at line 1812 of file partprune.c.

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

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

3784{
3785 if (IsA(expr, Const))
3786 {
3787 /* We can always determine the value of a constant */
3788 Const *con = (Const *) expr;
3789
3790 *value = con->constvalue;
3791 *isnull = con->constisnull;
3792 }
3793 else
3794 {
3795 ExprState *exprstate;
3796 ExprContext *ectx;
3797
3798 /*
3799 * We should never see a non-Const in a step unless the caller has
3800 * passed a valid ExprContext.
3801 */
3802 Assert(context->exprcontext != NULL);
3803
3804 exprstate = context->exprstates[stateidx];
3805 ectx = context->exprcontext;
3806 *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
3807 }
3808}
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:361
ExprContext * exprcontext
Definition: partprune.h:60
ExprState ** exprstates
Definition: partprune.h:61

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

Referenced by perform_pruning_base_step().

◆ perform_pruning_base_step()

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

Definition at line 3437 of file partprune.c.

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

3588{
3590 bool firststep;
3591 ListCell *lc1;
3592
3593 /*
3594 * A combine step without any source steps is an indication to not perform
3595 * any partition pruning. Return all datum indexes in that case.
3596 */
3597 if (cstep->source_stepids == NIL)
3598 {
3599 PartitionBoundInfo boundinfo = context->boundinfo;
3600
3601 result->bound_offsets =
3602 bms_add_range(NULL, 0, boundinfo->nindexes - 1);
3603 result->scan_default = partition_bound_has_default(boundinfo);
3604 result->scan_null = partition_bound_accepts_nulls(boundinfo);
3605 return result;
3606 }
3607
3608 switch (cstep->combineOp)
3609 {
3611 foreach(lc1, cstep->source_stepids)
3612 {
3613 int step_id = lfirst_int(lc1);
3614 PruneStepResult *step_result;
3615
3616 /*
3617 * step_results[step_id] must contain a valid result, which is
3618 * confirmed by the fact that cstep's step_id is greater than
3619 * step_id and the fact that results of the individual steps
3620 * are evaluated in sequence of their step_ids.
3621 */
3622 if (step_id >= cstep->step.step_id)
3623 elog(ERROR, "invalid pruning combine step argument");
3624 step_result = step_results[step_id];
3625 Assert(step_result != NULL);
3626
3627 /* Record any additional datum indexes from this step */
3629 step_result->bound_offsets);
3630
3631 /* Update whether to scan null and default partitions. */
3632 if (!result->scan_null)
3633 result->scan_null = step_result->scan_null;
3634 if (!result->scan_default)
3635 result->scan_default = step_result->scan_default;
3636 }
3637 break;
3638
3640 firststep = true;
3641 foreach(lc1, cstep->source_stepids)
3642 {
3643 int step_id = lfirst_int(lc1);
3644 PruneStepResult *step_result;
3645
3646 if (step_id >= cstep->step.step_id)
3647 elog(ERROR, "invalid pruning combine step argument");
3648 step_result = step_results[step_id];
3649 Assert(step_result != NULL);
3650
3651 if (firststep)
3652 {
3653 /* Copy step's result the first time. */
3654 result->bound_offsets =
3655 bms_copy(step_result->bound_offsets);
3656 result->scan_null = step_result->scan_null;
3657 result->scan_default = step_result->scan_default;
3658 firststep = false;
3659 }
3660 else
3661 {
3662 /* Record datum indexes common to both steps */
3663 result->bound_offsets =
3665 step_result->bound_offsets);
3666
3667 /* Update whether to scan null and default partitions. */
3668 if (result->scan_null)
3669 result->scan_null = step_result->scan_null;
3670 if (result->scan_default)
3671 result->scan_default = step_result->scan_default;
3672 }
3673 }
3674 break;
3675 }
3676
3677 return result;
3678}
Bitmapset * bms_int_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:1109
#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 772 of file partprune.c.

773{
774 List *clauses = rel->baserestrictinfo;
775 List *pruning_steps;
777 PartitionPruneContext context;
778
779 Assert(rel->part_scheme != NULL);
780
781 /* If there are no partitions, return the empty set */
782 if (rel->nparts == 0)
783 return NULL;
784
785 /*
786 * If pruning is disabled or if there are no clauses to prune with, return
787 * all partitions.
788 */
789 if (!enable_partition_pruning || clauses == NIL)
790 return bms_add_range(NULL, 0, rel->nparts - 1);
791
792 /*
793 * Process clauses to extract pruning steps that are usable at plan time.
794 * If the clauses are found to be contradictory, we can return the empty
795 * set.
796 */
798 &gcontext);
799 if (gcontext.contradictory)
800 return NULL;
801 pruning_steps = gcontext.steps;
802
803 /* If there's nothing usable, return all partitions */
804 if (pruning_steps == NIL)
805 return bms_add_range(NULL, 0, rel->nparts - 1);
806
807 /* Set up PartitionPruneContext */
808 context.strategy = rel->part_scheme->strategy;
809 context.partnatts = rel->part_scheme->partnatts;
810 context.nparts = rel->nparts;
811 context.boundinfo = rel->boundinfo;
812 context.partcollation = rel->part_scheme->partcollation;
813 context.partsupfunc = rel->part_scheme->partsupfunc;
814 context.stepcmpfuncs = (FmgrInfo *) palloc0(sizeof(FmgrInfo) *
815 context.partnatts *
816 list_length(pruning_steps));
818
819 /* These are not valid when being called from the planner */
820 context.planstate = NULL;
821 context.exprcontext = NULL;
822 context.exprstates = NULL;
823
824 /* Actual pruning happens here. */
825 return get_matching_partitions(&context, pruning_steps);
826}
bool enable_partition_pruning
Definition: costsize.c:163
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
Bitmapset * get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
Definition: partprune.c:839
PlanState * planstate
Definition: partprune.h:59
List * baserestrictinfo
Definition: pathnodes.h:1004

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

3369{
3370 Bitmapset *result = NULL;
3371
3372 (void) pull_exec_paramids_walker((Node *) expr, &result);
3373
3374 return result;
3375}
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context)
Definition: partprune.c:3378

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

3379{
3380 if (node == NULL)
3381 return false;
3382 if (IsA(node, Param))
3383 {
3384 Param *param = (Param *) node;
3385
3386 if (param->paramkind == PARAM_EXEC)
3387 *context = bms_add_member(*context, param->paramid);
3388 return false;
3389 }
3391}
#define expression_tree_walker(n, w, c)
Definition: nodeFuncs.h:153
@ PARAM_EXEC
Definition: primnodes.h:385
int paramid
Definition: primnodes.h:394
ParamKind paramkind
Definition: primnodes.h:393

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

Referenced by pull_exec_paramids(), and pull_exec_paramids_walker().