PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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 "optimizer/placeholder.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, const 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, const 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, const 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, const 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 1784 of file partprune.c.

1831{
1833 PartitionScheme part_scheme = context->rel->part_scheme;
1834 Oid partopfamily = part_scheme->partopfamily[partkeyidx],
1835 partcoll = part_scheme->partcollation[partkeyidx];
1836 Expr *expr;
1837 bool notclause;
1838
1839 /*
1840 * Recognize specially shaped clauses that match a Boolean partition key.
1841 */
1842 boolmatchstatus = match_boolean_partition_clause(partopfamily, clause,
1843 partkey, &expr,
1844 &notclause);
1845
1847 {
1849
1850 /*
1851 * For bool tests in the form of partkey IS NOT true and IS NOT false,
1852 * we invert these clauses. Effectively, "partkey IS NOT true"
1853 * becomes "partkey IS false OR partkey IS NULL". We do this by
1854 * building an OR BoolExpr and forming a clause just like that and
1855 * punt it off to gen_partprune_steps_internal() to generate pruning
1856 * steps.
1857 */
1858 if (notclause)
1859 {
1861 List *or_clause;
1864
1865 /* We expect 'notclause' to only be set to true for BooleanTests */
1866 Assert(IsA(clause, BooleanTest));
1867
1868 /* reverse the bool test */
1869 if (new_booltest->booltesttype == IS_NOT_TRUE)
1870 new_booltest->booltesttype = IS_FALSE;
1871 else if (new_booltest->booltesttype == IS_NOT_FALSE)
1872 new_booltest->booltesttype = IS_TRUE;
1873 else
1874 {
1875 /*
1876 * We only expect match_boolean_partition_clause to return
1877 * PARTCLAUSE_MATCH_CLAUSE for IS_NOT_TRUE and IS_NOT_FALSE.
1878 */
1879 Assert(false);
1880 }
1881
1883 nulltest->arg = copyObject(partkey);
1884 nulltest->nulltesttype = IS_NULL;
1885 nulltest->argisrow = false;
1886 nulltest->location = -1;
1887
1890
1891 /* Finally, generate steps */
1893
1894 if (context->contradictory)
1895 return PARTCLAUSE_MATCH_CONTRADICT; /* shouldn't happen */
1896 else if (*clause_steps == NIL)
1897 return PARTCLAUSE_UNSUPPORTED; /* step generation failed */
1899 }
1900
1902 partclause->keyno = partkeyidx;
1903 /* Do pruning with the Boolean equality operator. */
1905 partclause->op_is_ne = false;
1906 partclause->expr = expr;
1907 /* We know that expr is of Boolean type. */
1908 partclause->cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
1909 partclause->op_strategy = InvalidStrategy;
1910
1911 *pc = partclause;
1912
1914 }
1916 {
1917 /*
1918 * Handle IS UNKNOWN and IS NOT UNKNOWN. These just logically
1919 * translate to IS NULL and IS NOT NULL.
1920 */
1923 }
1924 else if (IsA(clause, OpExpr) &&
1925 list_length(((OpExpr *) clause)->args) == 2)
1926 {
1927 OpExpr *opclause = (OpExpr *) clause;
1928 Expr *leftop,
1929 *rightop;
1930 Oid opno,
1934 Oid cmpfn;
1935 int op_strategy;
1936 bool is_opne_listp = false;
1938
1939 leftop = (Expr *) get_leftop(clause);
1941 while (IsA(leftop, RelabelType))
1942 leftop = ((RelabelType *) leftop)->arg;
1943 rightop = (Expr *) get_rightop(clause);
1945 while (IsA(rightop, RelabelType))
1946 rightop = ((RelabelType *) rightop)->arg;
1947 opno = opclause->opno;
1948
1949 /* check if the clause matches this partition key */
1950 if (equal(leftop, partkey))
1951 expr = rightop;
1952 else if (equal(rightop, partkey))
1953 {
1954 /*
1955 * It's only useful if we can commute the operator to put the
1956 * partkey on the left. If we can't, the clause can be deemed
1957 * UNSUPPORTED. Even if its leftop matches some later partkey, we
1958 * now know it has Vars on the right, so it's no use.
1959 */
1960 opno = get_commutator(opno);
1961 if (!OidIsValid(opno))
1963 expr = leftop;
1964 }
1965 else
1966 /* clause does not match this partition key, but perhaps next. */
1967 return PARTCLAUSE_NOMATCH;
1968
1969 /*
1970 * Partition key match also requires collation match. There may be
1971 * multiple partkeys with the same expression but different
1972 * collations, so failure is NOMATCH.
1973 */
1974 if (!PartCollMatchesExprColl(partcoll, opclause->inputcollid))
1975 return PARTCLAUSE_NOMATCH;
1976
1977 /*
1978 * See if the operator is relevant to the partitioning opfamily.
1979 *
1980 * Normally we only care about operators that are listed as being part
1981 * of the partitioning operator family. But there is one exception:
1982 * the not-equals operators are not listed in any operator family
1983 * whatsoever, but their negators (equality) are. We can use one of
1984 * those if we find it, but only for list partitioning.
1985 *
1986 * Note: we report NOMATCH on failure if the negator isn't the
1987 * equality operator for the partkey's opfamily as other partkeys may
1988 * have the same expression but different opfamily. That's unlikely,
1989 * but not much more so than duplicate expressions with different
1990 * collations.
1991 */
1992 if (op_in_opfamily(opno, partopfamily))
1993 {
1994 get_op_opfamily_properties(opno, partopfamily, false,
1995 &op_strategy, &op_lefttype,
1996 &op_righttype);
1997 }
1998 else
1999 {
2000 /* not supported for anything apart from LIST partitioned tables */
2001 if (part_scheme->strategy != PARTITION_STRATEGY_LIST)
2003
2004 /* See if the negator is equality */
2005 negator = get_negator(opno);
2006 if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
2007 {
2008 get_op_opfamily_properties(negator, partopfamily, false,
2009 &op_strategy, &op_lefttype,
2010 &op_righttype);
2011 if (op_strategy == BTEqualStrategyNumber)
2012 is_opne_listp = true; /* bingo */
2013 }
2014
2015 /* Nope, it's not <> either. */
2016 if (!is_opne_listp)
2017 return PARTCLAUSE_NOMATCH;
2018 }
2019
2020 /*
2021 * Only allow strict operators. This will guarantee nulls are
2022 * filtered. (This test is likely useless, since btree and hash
2023 * comparison operators are generally strict.)
2024 */
2025 if (!op_strict(opno))
2027
2028 /*
2029 * OK, we have a match to the partition key and a suitable operator.
2030 * Examine the other argument to see if it's usable for pruning.
2031 *
2032 * In most of these cases, we can return UNSUPPORTED because the same
2033 * failure would occur no matter which partkey it's matched to. (In
2034 * particular, now that we've successfully matched one side of the
2035 * opclause to a partkey, there is no chance that matching the other
2036 * side to another partkey will produce a usable result, since that'd
2037 * mean there are Vars on both sides.)
2038 *
2039 * Also, if we reject an argument for a target-dependent reason, set
2040 * appropriate fields of *context to report that. We postpone these
2041 * tests until after matching the partkey and the operator, so as to
2042 * reduce the odds of setting the context fields for clauses that do
2043 * not end up contributing to pruning steps.
2044 *
2045 * First, check for non-Const argument. (We assume that any immutable
2046 * subexpression will have been folded to a Const already.)
2047 */
2048 if (!IsA(expr, Const))
2049 {
2050 Bitmapset *paramids;
2051
2052 /*
2053 * When pruning in the planner, we only support pruning using
2054 * comparisons to constants. We cannot prune on the basis of
2055 * anything that's not immutable. (Note that has_mutable_arg and
2056 * has_exec_param do not get set for this target value.)
2057 */
2058 if (context->target == PARTTARGET_PLANNER)
2060
2061 /*
2062 * We can never prune using an expression that contains Vars.
2063 */
2064 if (contain_var_clause((Node *) expr))
2066
2067 /*
2068 * And we must reject anything containing a volatile function.
2069 * Stable functions are OK though.
2070 */
2071 if (contain_volatile_functions((Node *) expr))
2073
2074 /*
2075 * See if there are any exec Params. If so, we can only use this
2076 * expression during per-scan pruning.
2077 */
2078 paramids = pull_exec_paramids(expr);
2079 if (!bms_is_empty(paramids))
2080 {
2081 context->has_exec_param = true;
2082 if (context->target != PARTTARGET_EXEC)
2084 }
2085 else
2086 {
2087 /* It's potentially usable, but mutable */
2088 context->has_mutable_arg = true;
2089 }
2090 }
2091
2092 /*
2093 * Check whether the comparison operator itself is immutable. (We
2094 * assume anything that's in a btree or hash opclass is at least
2095 * stable, but we need to check for immutability.)
2096 */
2098 {
2099 context->has_mutable_op = true;
2100
2101 /*
2102 * When pruning in the planner, we cannot prune with mutable
2103 * operators.
2104 */
2105 if (context->target == PARTTARGET_PLANNER)
2107 }
2108
2109 /*
2110 * Now find the procedure to use, based on the types. If the clause's
2111 * other argument is of the same type as the partitioning opclass's
2112 * declared input type, we can use the procedure cached in
2113 * PartitionKey. If not, search for a cross-type one in the same
2114 * opfamily; if one doesn't exist, report no match.
2115 */
2116 if (op_righttype == part_scheme->partopcintype[partkeyidx])
2117 cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid;
2118 else
2119 {
2120 switch (part_scheme->strategy)
2121 {
2122 /*
2123 * For range and list partitioning, we need the ordering
2124 * procedure with lefttype being the partition key's type,
2125 * and righttype the clause's operator's right type.
2126 */
2129 cmpfn =
2131 part_scheme->partopcintype[partkeyidx],
2133 break;
2134
2135 /*
2136 * For hash partitioning, we need the hashing procedure
2137 * for the clause's type.
2138 */
2140 cmpfn =
2144 break;
2145
2146 default:
2147 elog(ERROR, "invalid partition strategy: %c",
2148 part_scheme->strategy);
2149 cmpfn = InvalidOid; /* keep compiler quiet */
2150 break;
2151 }
2152
2153 if (!OidIsValid(cmpfn))
2154 return PARTCLAUSE_NOMATCH;
2155 }
2156
2157 /*
2158 * Build the clause, passing the negator if applicable.
2159 */
2161 partclause->keyno = partkeyidx;
2162 if (is_opne_listp)
2163 {
2165 partclause->opno = negator;
2166 partclause->op_is_ne = true;
2167 partclause->op_strategy = InvalidStrategy;
2168 }
2169 else
2170 {
2171 partclause->opno = opno;
2172 partclause->op_is_ne = false;
2173 partclause->op_strategy = op_strategy;
2174 }
2175 partclause->expr = expr;
2176 partclause->cmpfn = cmpfn;
2177
2178 *pc = partclause;
2179
2181 }
2182 else if (IsA(clause, ScalarArrayOpExpr))
2183 {
2184 ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
2185 Oid saop_op = saop->opno;
2186 Oid saop_coll = saop->inputcollid;
2187 Expr *leftop = (Expr *) linitial(saop->args),
2188 *rightop = (Expr *) lsecond(saop->args);
2190 *elem_clauses;
2191 ListCell *lc1;
2192
2194 while (IsA(leftop, RelabelType))
2195 leftop = ((RelabelType *) leftop)->arg;
2196
2197 /* check if the LHS matches this partition key */
2198 if (!equal(leftop, partkey) ||
2199 !PartCollMatchesExprColl(partcoll, saop->inputcollid))
2200 return PARTCLAUSE_NOMATCH;
2201
2202 /*
2203 * See if the operator is relevant to the partitioning opfamily.
2204 *
2205 * In case of NOT IN (..), we get a '<>', which we handle if list
2206 * partitioning is in use and we're able to confirm that it's negator
2207 * is a btree equality operator belonging to the partitioning operator
2208 * family. As above, report NOMATCH for non-matching operator.
2209 */
2210 if (!op_in_opfamily(saop_op, partopfamily))
2211 {
2212 Oid negator;
2213
2214 if (part_scheme->strategy != PARTITION_STRATEGY_LIST)
2215 return PARTCLAUSE_NOMATCH;
2216
2218 if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
2219 {
2220 int strategy;
2221 Oid lefttype,
2222 righttype;
2223
2224 get_op_opfamily_properties(negator, partopfamily,
2225 false, &strategy,
2226 &lefttype, &righttype);
2227 if (strategy != BTEqualStrategyNumber)
2228 return PARTCLAUSE_NOMATCH;
2229 }
2230 else
2231 return PARTCLAUSE_NOMATCH; /* no useful negator */
2232 }
2233
2234 /*
2235 * Only allow strict operators. This will guarantee nulls are
2236 * filtered. (This test is likely useless, since btree and hash
2237 * comparison operators are generally strict.)
2238 */
2239 if (!op_strict(saop_op))
2241
2242 /*
2243 * OK, we have a match to the partition key and a suitable operator.
2244 * Examine the array argument to see if it's usable for pruning. This
2245 * is identical to the logic for a plain OpExpr.
2246 */
2247 if (!IsA(rightop, Const))
2248 {
2249 Bitmapset *paramids;
2250
2251 /*
2252 * When pruning in the planner, we only support pruning using
2253 * comparisons to constants. We cannot prune on the basis of
2254 * anything that's not immutable. (Note that has_mutable_arg and
2255 * has_exec_param do not get set for this target value.)
2256 */
2257 if (context->target == PARTTARGET_PLANNER)
2259
2260 /*
2261 * We can never prune using an expression that contains Vars.
2262 */
2265
2266 /*
2267 * And we must reject anything containing a volatile function.
2268 * Stable functions are OK though.
2269 */
2272
2273 /*
2274 * See if there are any exec Params. If so, we can only use this
2275 * expression during per-scan pruning.
2276 */
2277 paramids = pull_exec_paramids(rightop);
2278 if (!bms_is_empty(paramids))
2279 {
2280 context->has_exec_param = true;
2281 if (context->target != PARTTARGET_EXEC)
2283 }
2284 else
2285 {
2286 /* It's potentially usable, but mutable */
2287 context->has_mutable_arg = true;
2288 }
2289 }
2290
2291 /*
2292 * Check whether the comparison operator itself is immutable. (We
2293 * assume anything that's in a btree or hash opclass is at least
2294 * stable, but we need to check for immutability.)
2295 */
2297 {
2298 context->has_mutable_op = true;
2299
2300 /*
2301 * When pruning in the planner, we cannot prune with mutable
2302 * operators.
2303 */
2304 if (context->target == PARTTARGET_PLANNER)
2306 }
2307
2308 /*
2309 * Examine the contents of the array argument.
2310 */
2311 elem_exprs = NIL;
2312 if (IsA(rightop, Const))
2313 {
2314 /*
2315 * For a constant array, convert the elements to a list of Const
2316 * nodes, one for each array element (excepting nulls).
2317 */
2318 Const *arr = (Const *) rightop;
2320 int16 elemlen;
2321 bool elembyval;
2322 char elemalign;
2323 Datum *elem_values;
2324 bool *elem_nulls;
2325 int num_elems,
2326 i;
2327
2328 /* If the array itself is null, the saop returns null */
2329 if (arr->constisnull)
2331
2332 arrval = DatumGetArrayTypeP(arr->constvalue);
2334 &elemlen, &elembyval, &elemalign);
2337 elemlen, elembyval, elemalign,
2338 &elem_values, &elem_nulls,
2339 &num_elems);
2340 for (i = 0; i < num_elems; i++)
2341 {
2343
2344 /*
2345 * A null array element must lead to a null comparison result,
2346 * since saop_op is known strict. We can ignore it in the
2347 * useOr case, but otherwise it implies self-contradiction.
2348 */
2349 if (elem_nulls[i])
2350 {
2351 if (saop->useOr)
2352 continue;
2354 }
2355
2357 arr->constcollid, elemlen,
2358 elem_values[i], false, elembyval);
2360 }
2361 }
2362 else if (IsA(rightop, ArrayExpr))
2363 {
2365
2366 /*
2367 * For a nested ArrayExpr, we don't know how to get the actual
2368 * scalar values out into a flat list, so we give up doing
2369 * anything with this ScalarArrayOpExpr.
2370 */
2371 if (arrexpr->multidims)
2373
2374 /*
2375 * Otherwise, we can just use the list of element values.
2376 */
2377 elem_exprs = arrexpr->elements;
2378 }
2379 else
2380 {
2381 /* Give up on any other clause types. */
2383 }
2384
2385 /*
2386 * Now generate a list of clauses, one for each array element, of the
2387 * form leftop saop_op elem_expr
2388 */
2389 elem_clauses = NIL;
2390 foreach(lc1, elem_exprs)
2391 {
2393
2395 leftop, lfirst(lc1),
2398 }
2399
2400 /*
2401 * If we have an ANY clause and multiple elements, now turn the list
2402 * of clauses into an OR expression.
2403 */
2404 if (saop->useOr && list_length(elem_clauses) > 1)
2406
2407 /* Finally, generate steps */
2409 if (context->contradictory)
2411 else if (*clause_steps == NIL)
2412 return PARTCLAUSE_UNSUPPORTED; /* step generation failed */
2414 }
2415 else if (IsA(clause, NullTest))
2416 {
2417 NullTest *nulltest = (NullTest *) clause;
2418 Expr *arg = nulltest->arg;
2419
2420 arg = (Expr *) strip_noop_phvs((Node *) arg);
2421 while (IsA(arg, RelabelType))
2422 arg = ((RelabelType *) arg)->arg;
2423
2424 /* Does arg match with this partition key column? */
2425 if (!equal(arg, partkey))
2426 return PARTCLAUSE_NOMATCH;
2427
2428 *clause_is_not_null = (nulltest->nulltesttype == IS_NOT_NULL);
2429
2431 }
2432
2433 /*
2434 * If we get here then the return value depends on the result of the
2435 * match_boolean_partition_clause call above. If the call returned
2436 * PARTCLAUSE_UNSUPPORTED then we're either not dealing with a bool qual
2437 * or the bool qual is not suitable for pruning. Since the qual didn't
2438 * match up to any of the other qual types supported here, then trying to
2439 * match it against any other partition key is a waste of time, so just
2440 * return PARTCLAUSE_UNSUPPORTED. If the qual just couldn't be matched to
2441 * this partition key, then it may match another, so return
2442 * PARTCLAUSE_NOMATCH. The only other value that
2443 * match_boolean_partition_clause can return is PARTCLAUSE_MATCH_CLAUSE,
2444 * and since that value was already dealt with above, then we can just
2445 * return boolmatchstatus.
2446 */
2447 return boolmatchstatus;
2448}
2449
2450/*
2451 * get_steps_using_prefix
2452 * Generate a list of PartitionPruneStepOps based on the given input.
2453 *
2454 * 'step_lastexpr' and 'step_lastcmpfn' are the Expr and comparison function
2455 * belonging to the final partition key that we have a clause for. 'prefix'
2456 * is a list of PartClauseInfos for partition key numbers prior to the given
2457 * 'step_lastexpr' and 'step_lastcmpfn'. 'prefix' may contain multiple
2458 * PartClauseInfos belonging to a single partition key. We will generate a
2459 * PartitionPruneStepOp for each combination of the given PartClauseInfos
2460 * using, at most, one PartClauseInfo per partition key.
2461 *
2462 * For LIST and RANGE partitioned tables, callers must ensure that
2463 * step_nullkeys is NULL, and that prefix contains at least one clause for
2464 * each of the partition keys prior to the key that 'step_lastexpr' and
2465 * 'step_lastcmpfn' belong to.
2466 *
2467 * For HASH partitioned tables, callers must ensure that 'prefix' contains at
2468 * least one clause for each of the partition keys apart from the final key
2469 * (the expr and comparison function for the final key are in 'step_lastexpr'
2470 * and 'step_lastcmpfn'). A bit set in step_nullkeys can substitute clauses
2471 * in the 'prefix' list for any given key. If a bit is set in 'step_nullkeys'
2472 * for a given key, then there must be no PartClauseInfo for that key in the
2473 * 'prefix' list.
2474 *
2475 * For each of the above cases, callers must ensure that PartClauseInfos in
2476 * 'prefix' are sorted in ascending order of keyno.
2477 */
2478static List *
2481 bool step_op_is_ne,
2485 List *prefix)
2486{
2487 /* step_nullkeys must be empty for RANGE and LIST partitioned tables */
2489 context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH);
2490
2491 /*
2492 * No recursive processing is required when 'prefix' is an empty list.
2493 * This occurs when there is only 1 partition key column.
2494 */
2495 if (prefix == NIL)
2496 {
2497 PartitionPruneStep *step;
2498
2499 step = gen_prune_step_op(context,
2505 return list_make1(step);
2506 }
2507
2508 /* Recurse to generate steps for every combination of clauses. */
2509 return get_steps_using_prefix_recurse(context,
2515 prefix,
2516 list_head(prefix),
2517 NIL, NIL);
2518}
2519
2520/*
2521 * get_steps_using_prefix_recurse
2522 * Generate and return a list of PartitionPruneStepOps using the 'prefix'
2523 * list of PartClauseInfos starting at the 'start' cell.
2524 *
2525 * When 'prefix' contains multiple PartClauseInfos for a single partition key
2526 * we create a PartitionPruneStepOp for each combination of duplicated
2527 * PartClauseInfos. The returned list will contain a PartitionPruneStepOp
2528 * for each unique combination of input PartClauseInfos containing at most one
2529 * PartClauseInfo per partition key.
2530 *
2531 * 'prefix' is the input list of PartClauseInfos sorted by keyno.
2532 * 'start' marks the cell that searching the 'prefix' list should start from.
2533 * 'step_exprs' and 'step_cmpfns' each contains the expressions and cmpfns
2534 * we've generated so far from the clauses for the previous part keys.
2535 */
2536static List *
2539 bool step_op_is_ne,
2543 List *prefix,
2544 ListCell *start,
2547{
2548 List *result = NIL;
2549 ListCell *lc;
2550 int cur_keyno;
2551 int final_keyno;
2552
2553 /* Actually, recursion would be limited by PARTITION_MAX_KEYS. */
2555
2556 Assert(start != NULL);
2557 cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno;
2558 final_keyno = ((PartClauseInfo *) llast(prefix))->keyno;
2559
2560 /* Check if we need to recurse. */
2561 if (cur_keyno < final_keyno)
2562 {
2565
2566 /*
2567 * Find the first PartClauseInfo belonging to the next partition key,
2568 * the next recursive call must start iteration of the prefix list
2569 * from that point.
2570 */
2571 for_each_cell(lc, prefix, start)
2572 {
2573 pc = lfirst(lc);
2574
2575 if (pc->keyno > cur_keyno)
2576 break;
2577 }
2578
2579 /* record where to start iterating in the next recursive call */
2580 next_start = lc;
2581
2582 /*
2583 * For each PartClauseInfo with keyno set to cur_keyno, add its expr
2584 * and cmpfn to step_exprs and step_cmpfns, respectively, and recurse
2585 * using 'next_start' as the starting point in the 'prefix' list.
2586 */
2587 for_each_cell(lc, prefix, start)
2588 {
2589 List *moresteps;
2591 *step_cmpfns1;
2592
2593 pc = lfirst(lc);
2594 if (pc->keyno == cur_keyno)
2595 {
2596 /* Leave the original step_exprs unmodified. */
2599
2600 /* Leave the original step_cmpfns unmodified. */
2603 }
2604 else
2605 {
2606 /* check the 'prefix' list is sorted correctly */
2607 Assert(pc->keyno > cur_keyno);
2608 break;
2609 }
2610
2617 prefix,
2618 next_start,
2620 step_cmpfns1);
2622
2625 }
2626 }
2627 else
2628 {
2629 /*
2630 * End the current recursion cycle and start generating steps, one for
2631 * each clause with cur_keyno, which is all clauses from here onward
2632 * till the end of the list. Note that for hash partitioning,
2633 * step_nullkeys is allowed to be non-empty, in which case step_exprs
2634 * would only contain expressions for the partition keys that are not
2635 * specified in step_nullkeys.
2636 */
2639
2640 /*
2641 * Note also that for hash partitioning, each partition key should
2642 * have either equality clauses or an IS NULL clause, so if a
2643 * partition key doesn't have an expression, it would be specified in
2644 * step_nullkeys.
2645 */
2646 Assert(context->rel->part_scheme->strategy
2649 context->rel->part_scheme->partnatts);
2650 for_each_cell(lc, prefix, start)
2651 {
2653 PartitionPruneStep *step;
2655 *step_cmpfns1;
2656
2657 Assert(pc->keyno == cur_keyno);
2658
2659 /* Leave the original step_exprs unmodified. */
2663
2664 /* Leave the original step_cmpfns unmodified. */
2668
2669 step = gen_prune_step_op(context,
2673 result = lappend(result, step);
2674 }
2675 }
2676
2677 return result;
2678}
2679
2680/*
2681 * get_matching_hash_bounds
2682 * Determine offset of the hash bound matching the specified values,
2683 * considering that all the non-null values come from clauses containing
2684 * a compatible hash equality operator and any keys that are null come
2685 * from an IS NULL clause.
2686 *
2687 * Generally this function will return a single matching bound offset,
2688 * although if a partition has not been setup for a given modulus then we may
2689 * return no matches. If the number of clauses found don't cover the entire
2690 * partition key, then we'll need to return all offsets.
2691 *
2692 * 'opstrategy' if non-zero must be HTEqualStrategyNumber.
2693 *
2694 * 'values' contains Datums indexed by the partition key to use for pruning.
2695 *
2696 * 'nvalues', the number of Datums in the 'values' array.
2697 *
2698 * 'partsupfunc' contains partition hashing functions that can produce correct
2699 * hash for the type of the values contained in 'values'.
2700 *
2701 * 'nullkeys' is the set of partition keys that are null.
2702 */
2703static PruneStepResult *
2705 StrategyNumber opstrategy, const Datum *values, int nvalues,
2706 FmgrInfo *partsupfunc, Bitmapset *nullkeys)
2707{
2709 PartitionBoundInfo boundinfo = context->boundinfo;
2710 int *partindices = boundinfo->indexes;
2711 int partnatts = context->partnatts;
2712 bool isnull[PARTITION_MAX_KEYS];
2713 int i;
2715 int greatest_modulus;
2716 Oid *partcollation = context->partcollation;
2717
2719
2720 /*
2721 * For hash partitioning we can only perform pruning based on equality
2722 * clauses to the partition key or IS NULL clauses. We also can only
2723 * prune if we got values for all keys.
2724 */
2725 if (nvalues + bms_num_members(nullkeys) == partnatts)
2726 {
2727 /*
2728 * If there are any values, they must have come from clauses
2729 * containing an equality operator compatible with hash partitioning.
2730 */
2731 Assert(opstrategy == HTEqualStrategyNumber || nvalues == 0);
2732
2733 for (i = 0; i < partnatts; i++)
2734 isnull[i] = bms_is_member(i, nullkeys);
2735
2736 rowHash = compute_partition_hash_value(partnatts, partsupfunc, partcollation,
2737 values, isnull);
2738
2739 greatest_modulus = boundinfo->nindexes;
2741 result->bound_offsets =
2743 }
2744 else
2745 {
2746 /* Report all valid offsets into the boundinfo->indexes array. */
2747 result->bound_offsets = bms_add_range(NULL, 0,
2748 boundinfo->nindexes - 1);
2749 }
2750
2751 /*
2752 * There is neither a special hash null partition or the default hash
2753 * partition.
2754 */
2755 result->scan_null = result->scan_default = false;
2756
2757 return result;
2758}
2759
2760/*
2761 * get_matching_list_bounds
2762 * Determine the offsets of list bounds matching the specified value,
2763 * according to the semantics of the given operator strategy
2764 *
2765 * scan_default will be set in the returned struct, if the default partition
2766 * needs to be scanned, provided one exists at all. scan_null will be set if
2767 * the special null-accepting partition needs to be scanned.
2768 *
2769 * 'opstrategy' if non-zero must be a btree strategy number.
2770 *
2771 * 'value' contains the value to use for pruning.
2772 *
2773 * 'nvalues', if non-zero, should be exactly 1, because of list partitioning.
2774 *
2775 * 'partsupfunc' contains the list partitioning comparison function to be used
2776 * to perform partition_list_bsearch
2777 *
2778 * 'nullkeys' is the set of partition keys that are null.
2779 */
2780static PruneStepResult *
2782 StrategyNumber opstrategy, Datum value, int nvalues,
2783 FmgrInfo *partsupfunc, Bitmapset *nullkeys)
2784{
2786 PartitionBoundInfo boundinfo = context->boundinfo;
2787 int off,
2788 minoff,
2789 maxoff;
2790 bool is_equal;
2791 bool inclusive = false;
2792 Oid *partcollation = context->partcollation;
2793
2795 Assert(context->partnatts == 1);
2796
2797 result->scan_null = result->scan_default = false;
2798
2799 if (!bms_is_empty(nullkeys))
2800 {
2801 /*
2802 * Nulls may exist in only one partition - the partition whose
2803 * accepted set of values includes null or the default partition if
2804 * the former doesn't exist.
2805 */
2806 if (partition_bound_accepts_nulls(boundinfo))
2807 result->scan_null = true;
2808 else
2809 result->scan_default = partition_bound_has_default(boundinfo);
2810 return result;
2811 }
2812
2813 /*
2814 * If there are no datums to compare keys with, but there are partitions,
2815 * just return the default partition if one exists.
2816 */
2817 if (boundinfo->ndatums == 0)
2818 {
2819 result->scan_default = partition_bound_has_default(boundinfo);
2820 return result;
2821 }
2822
2823 minoff = 0;
2824 maxoff = boundinfo->ndatums - 1;
2825
2826 /*
2827 * If there are no values to compare with the datums in boundinfo, it
2828 * means the caller asked for partitions for all non-null datums. Add
2829 * indexes of *all* partitions, including the default if any.
2830 */
2831 if (nvalues == 0)
2832 {
2833 Assert(boundinfo->ndatums > 0);
2834 result->bound_offsets = bms_add_range(NULL, 0,
2835 boundinfo->ndatums - 1);
2836 result->scan_default = partition_bound_has_default(boundinfo);
2837 return result;
2838 }
2839
2840 /* Special case handling of values coming from a <> operator clause. */
2841 if (opstrategy == InvalidStrategy)
2842 {
2843 /*
2844 * First match to all bounds. We'll remove any matching datums below.
2845 */
2846 Assert(boundinfo->ndatums > 0);
2847 result->bound_offsets = bms_add_range(NULL, 0,
2848 boundinfo->ndatums - 1);
2849
2850 off = partition_list_bsearch(partsupfunc, partcollation, boundinfo,
2851 value, &is_equal);
2852 if (off >= 0 && is_equal)
2853 {
2854
2855 /* We have a match. Remove from the result. */
2856 Assert(boundinfo->indexes[off] >= 0);
2857 result->bound_offsets = bms_del_member(result->bound_offsets,
2858 off);
2859 }
2860
2861 /* Always include the default partition if any. */
2862 result->scan_default = partition_bound_has_default(boundinfo);
2863
2864 return result;
2865 }
2866
2867 /*
2868 * With range queries, always include the default list partition, because
2869 * list partitions divide the key space in a discontinuous manner, not all
2870 * values in the given range will have a partition assigned. This may not
2871 * technically be true for some data types (e.g. integer types), however,
2872 * we currently lack any sort of infrastructure to provide us with proofs
2873 * that would allow us to do anything smarter here.
2874 */
2875 if (opstrategy != BTEqualStrategyNumber)
2876 result->scan_default = partition_bound_has_default(boundinfo);
2877
2878 switch (opstrategy)
2879 {
2881 off = partition_list_bsearch(partsupfunc,
2882 partcollation,
2883 boundinfo, value,
2884 &is_equal);
2885 if (off >= 0 && is_equal)
2886 {
2887 Assert(boundinfo->indexes[off] >= 0);
2888 result->bound_offsets = bms_make_singleton(off);
2889 }
2890 else
2891 result->scan_default = partition_bound_has_default(boundinfo);
2892 return result;
2893
2895 inclusive = true;
2898 off = partition_list_bsearch(partsupfunc,
2899 partcollation,
2900 boundinfo, value,
2901 &is_equal);
2902 if (off >= 0)
2903 {
2904 /* We don't want the matched datum to be in the result. */
2905 if (!is_equal || !inclusive)
2906 off++;
2907 }
2908 else
2909 {
2910 /*
2911 * This case means all partition bounds are greater, which in
2912 * turn means that all partitions satisfy this key.
2913 */
2914 off = 0;
2915 }
2916
2917 /*
2918 * off is greater than the numbers of datums we have partitions
2919 * for. The only possible partition that could contain a match is
2920 * the default partition, but we must've set context->scan_default
2921 * above anyway if one exists.
2922 */
2923 if (off > boundinfo->ndatums - 1)
2924 return result;
2925
2926 minoff = off;
2927 break;
2928
2930 inclusive = true;
2933 off = partition_list_bsearch(partsupfunc,
2934 partcollation,
2935 boundinfo, value,
2936 &is_equal);
2937 if (off >= 0 && is_equal && !inclusive)
2938 off--;
2939
2940 /*
2941 * off is smaller than the datums of all non-default partitions.
2942 * The only possible partition that could contain a match is the
2943 * default partition, but we must've set context->scan_default
2944 * above anyway if one exists.
2945 */
2946 if (off < 0)
2947 return result;
2948
2949 maxoff = off;
2950 break;
2951
2952 default:
2953 elog(ERROR, "invalid strategy number %d", opstrategy);
2954 break;
2955 }
2956
2957 Assert(minoff >= 0 && maxoff >= 0);
2958 result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
2959 return result;
2960}
2961
2962
2963/*
2964 * get_matching_range_bounds
2965 * Determine the offsets of range bounds matching the specified values,
2966 * according to the semantics of the given operator strategy
2967 *
2968 * Each datum whose offset is in result is to be treated as the upper bound of
2969 * the partition that will contain the desired values.
2970 *
2971 * scan_default is set in the returned struct if a default partition exists
2972 * and we're absolutely certain that it needs to be scanned. We do *not* set
2973 * it just because values match portions of the key space uncovered by
2974 * partitions other than default (space which we normally assume to belong to
2975 * the default partition): the final set of bounds obtained after combining
2976 * multiple pruning steps might exclude it, so we infer its inclusion
2977 * elsewhere.
2978 *
2979 * 'opstrategy' must be a btree strategy number.
2980 *
2981 * 'values' contains Datums indexed by the partition key to use for pruning.
2982 *
2983 * 'nvalues', number of Datums in 'values' array. Must be <= context->partnatts.
2984 *
2985 * 'partsupfunc' contains the range partitioning comparison functions to be
2986 * used to perform partition_range_datum_bsearch or partition_rbound_datum_cmp
2987 * using.
2988 *
2989 * 'nullkeys' is the set of partition keys that are null.
2990 */
2991static PruneStepResult *
2993 StrategyNumber opstrategy, const Datum *values, int nvalues,
2994 FmgrInfo *partsupfunc, Bitmapset *nullkeys)
2995{
2997 PartitionBoundInfo boundinfo = context->boundinfo;
2998 Oid *partcollation = context->partcollation;
2999 int partnatts = context->partnatts;
3000 int *partindices = boundinfo->indexes;
3001 int off,
3002 minoff,
3003 maxoff;
3004 bool is_equal;
3005 bool inclusive = false;
3006
3008 Assert(nvalues <= partnatts);
3009
3010 result->scan_null = result->scan_default = false;
3011
3012 /*
3013 * If there are no datums to compare keys with, or if we got an IS NULL
3014 * clause just return the default partition, if it exists.
3015 */
3016 if (boundinfo->ndatums == 0 || !bms_is_empty(nullkeys))
3017 {
3018 result->scan_default = partition_bound_has_default(boundinfo);
3019 return result;
3020 }
3021
3022 minoff = 0;
3023 maxoff = boundinfo->ndatums;
3024
3025 /*
3026 * If there are no values to compare with the datums in boundinfo, it
3027 * means the caller asked for partitions for all non-null datums. Add
3028 * indexes of *all* partitions, including the default partition if one
3029 * exists.
3030 */
3031 if (nvalues == 0)
3032 {
3033 /* ignore key space not covered by any partitions */
3034 if (partindices[minoff] < 0)
3035 minoff++;
3036 if (partindices[maxoff] < 0)
3037 maxoff--;
3038
3039 result->scan_default = partition_bound_has_default(boundinfo);
3040 Assert(partindices[minoff] >= 0 &&
3041 partindices[maxoff] >= 0);
3042 result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3043
3044 return result;
3045 }
3046
3047 /*
3048 * If the query does not constrain all key columns, we'll need to scan the
3049 * default partition, if any.
3050 */
3051 if (nvalues < partnatts)
3052 result->scan_default = partition_bound_has_default(boundinfo);
3053
3054 switch (opstrategy)
3055 {
3057 /* Look for the smallest bound that is = lookup value. */
3058 off = partition_range_datum_bsearch(partsupfunc,
3059 partcollation,
3060 boundinfo,
3061 nvalues, values,
3062 &is_equal);
3063
3064 if (off >= 0 && is_equal)
3065 {
3066 if (nvalues == partnatts)
3067 {
3068 /* There can only be zero or one matching partition. */
3069 result->bound_offsets = bms_make_singleton(off + 1);
3070 return result;
3071 }
3072 else
3073 {
3074 int saved_off = off;
3075
3076 /*
3077 * Since the lookup value contains only a prefix of keys,
3078 * we must find other bounds that may also match the
3079 * prefix. partition_range_datum_bsearch() returns the
3080 * offset of one of them, find others by checking adjacent
3081 * bounds.
3082 */
3083
3084 /*
3085 * First find greatest bound that's smaller than the
3086 * lookup value.
3087 */
3088 while (off >= 1)
3089 {
3090 int32 cmpval;
3091
3092 cmpval =
3093 partition_rbound_datum_cmp(partsupfunc,
3094 partcollation,
3095 boundinfo->datums[off - 1],
3096 boundinfo->kind[off - 1],
3097 values, nvalues);
3098 if (cmpval != 0)
3099 break;
3100 off--;
3101 }
3102
3103 Assert(0 ==
3104 partition_rbound_datum_cmp(partsupfunc,
3105 partcollation,
3106 boundinfo->datums[off],
3107 boundinfo->kind[off],
3108 values, nvalues));
3109
3110 /*
3111 * We can treat 'off' as the offset of the smallest bound
3112 * to be included in the result, if we know it is the
3113 * upper bound of the partition in which the lookup value
3114 * could possibly exist. One case it couldn't is if the
3115 * bound, or precisely the matched portion of its prefix,
3116 * is not inclusive.
3117 */
3118 if (boundinfo->kind[off][nvalues] ==
3120 off++;
3121
3122 minoff = off;
3123
3124 /*
3125 * Now find smallest bound that's greater than the lookup
3126 * value.
3127 */
3128 off = saved_off;
3129 while (off < boundinfo->ndatums - 1)
3130 {
3131 int32 cmpval;
3132
3133 cmpval = partition_rbound_datum_cmp(partsupfunc,
3134 partcollation,
3135 boundinfo->datums[off + 1],
3136 boundinfo->kind[off + 1],
3137 values, nvalues);
3138 if (cmpval != 0)
3139 break;
3140 off++;
3141 }
3142
3143 Assert(0 ==
3144 partition_rbound_datum_cmp(partsupfunc,
3145 partcollation,
3146 boundinfo->datums[off],
3147 boundinfo->kind[off],
3148 values, nvalues));
3149
3150 /*
3151 * off + 1, then would be the offset of the greatest bound
3152 * to be included in the result.
3153 */
3154 maxoff = off + 1;
3155 }
3156
3157 Assert(minoff >= 0 && maxoff >= 0);
3158 result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3159 }
3160 else
3161 {
3162 /*
3163 * The lookup value falls in the range between some bounds in
3164 * boundinfo. 'off' would be the offset of the greatest bound
3165 * that is <= lookup value, so add off + 1 to the result
3166 * instead as the offset of the upper bound of the only
3167 * partition that may contain the lookup value. If 'off' is
3168 * -1 indicating that all bounds are greater, then we simply
3169 * end up adding the first bound's offset, that is, 0.
3170 */
3171 result->bound_offsets = bms_make_singleton(off + 1);
3172 }
3173
3174 return result;
3175
3177 inclusive = true;
3180
3181 /*
3182 * Look for the smallest bound that is > or >= lookup value and
3183 * set minoff to its offset.
3184 */
3185 off = partition_range_datum_bsearch(partsupfunc,
3186 partcollation,
3187 boundinfo,
3188 nvalues, values,
3189 &is_equal);
3190 if (off < 0)
3191 {
3192 /*
3193 * All bounds are greater than the lookup value, so include
3194 * all of them in the result.
3195 */
3196 minoff = 0;
3197 }
3198 else
3199 {
3200 if (is_equal && nvalues < partnatts)
3201 {
3202 /*
3203 * Since the lookup value contains only a prefix of keys,
3204 * we must find other bounds that may also match the
3205 * prefix. partition_range_datum_bsearch() returns the
3206 * offset of one of them, find others by checking adjacent
3207 * bounds.
3208 *
3209 * Based on whether the lookup values are inclusive or
3210 * not, we must either include the indexes of all such
3211 * bounds in the result (that is, set minoff to the index
3212 * of smallest such bound) or find the smallest one that's
3213 * greater than the lookup values and set minoff to that.
3214 */
3215 while (off >= 1 && off < boundinfo->ndatums - 1)
3216 {
3217 int32 cmpval;
3218 int nextoff;
3219
3220 nextoff = inclusive ? off - 1 : off + 1;
3221 cmpval =
3222 partition_rbound_datum_cmp(partsupfunc,
3223 partcollation,
3224 boundinfo->datums[nextoff],
3225 boundinfo->kind[nextoff],
3226 values, nvalues);
3227 if (cmpval != 0)
3228 break;
3229
3230 off = nextoff;
3231 }
3232
3233 Assert(0 ==
3234 partition_rbound_datum_cmp(partsupfunc,
3235 partcollation,
3236 boundinfo->datums[off],
3237 boundinfo->kind[off],
3238 values, nvalues));
3239
3240 minoff = inclusive ? off : off + 1;
3241 }
3242 else
3243 {
3244
3245 /*
3246 * lookup value falls in the range between some bounds in
3247 * boundinfo. off would be the offset of the greatest
3248 * bound that is <= lookup value, so add off + 1 to the
3249 * result instead as the offset of the upper bound of the
3250 * smallest partition that may contain the lookup value.
3251 */
3252 minoff = off + 1;
3253 }
3254 }
3255 break;
3256
3258 inclusive = true;
3261
3262 /*
3263 * Look for the greatest bound that is < or <= lookup value and
3264 * set maxoff to its offset.
3265 */
3266 off = partition_range_datum_bsearch(partsupfunc,
3267 partcollation,
3268 boundinfo,
3269 nvalues, values,
3270 &is_equal);
3271 if (off >= 0)
3272 {
3273 /*
3274 * See the comment above.
3275 */
3276 if (is_equal && nvalues < partnatts)
3277 {
3278 while (off >= 1 && off < boundinfo->ndatums - 1)
3279 {
3280 int32 cmpval;
3281 int nextoff;
3282
3283 nextoff = inclusive ? off + 1 : off - 1;
3284 cmpval = partition_rbound_datum_cmp(partsupfunc,
3285 partcollation,
3286 boundinfo->datums[nextoff],
3287 boundinfo->kind[nextoff],
3288 values, nvalues);
3289 if (cmpval != 0)
3290 break;
3291
3292 off = nextoff;
3293 }
3294
3295 Assert(0 ==
3296 partition_rbound_datum_cmp(partsupfunc,
3297 partcollation,
3298 boundinfo->datums[off],
3299 boundinfo->kind[off],
3300 values, nvalues));
3301
3302 maxoff = inclusive ? off + 1 : off;
3303 }
3304
3305 /*
3306 * The lookup value falls in the range between some bounds in
3307 * boundinfo. 'off' would be the offset of the greatest bound
3308 * that is <= lookup value, so add off + 1 to the result
3309 * instead as the offset of the upper bound of the greatest
3310 * partition that may contain lookup value. If the lookup
3311 * value had exactly matched the bound, but it isn't
3312 * inclusive, no need add the adjacent partition.
3313 */
3314 else if (!is_equal || inclusive)
3315 maxoff = off + 1;
3316 else
3317 maxoff = off;
3318 }
3319 else
3320 {
3321 /*
3322 * 'off' is -1 indicating that all bounds are greater, so just
3323 * set the first bound's offset as maxoff.
3324 */
3325 maxoff = off + 1;
3326 }
3327 break;
3328
3329 default:
3330 elog(ERROR, "invalid strategy number %d", opstrategy);
3331 break;
3332 }
3333
3334 Assert(minoff >= 0 && minoff <= boundinfo->ndatums);
3335 Assert(maxoff >= 0 && maxoff <= boundinfo->ndatums);
3336
3337 /*
3338 * If the smallest partition to return has MINVALUE (negative infinity) as
3339 * its lower bound, increment it to point to the next finite bound
3340 * (supposedly its upper bound), so that we don't inadvertently end up
3341 * scanning the default partition.
3342 */
3343 if (minoff < boundinfo->ndatums && partindices[minoff] < 0)
3344 {
3345 int lastkey = nvalues - 1;
3346
3347 if (boundinfo->kind[minoff][lastkey] ==
3349 {
3350 minoff++;
3351 Assert(boundinfo->indexes[minoff] >= 0);
3352 }
3353 }
3354
3355 /*
3356 * If the previous greatest partition has MAXVALUE (positive infinity) as
3357 * its upper bound (something only possible to do with multi-column range
3358 * partitioning), we scan switch to it as the greatest partition to
3359 * return. Again, so that we don't inadvertently end up scanning the
3360 * default partition.
3361 */
3362 if (maxoff >= 1 && partindices[maxoff] < 0)
3363 {
3364 int lastkey = nvalues - 1;
3365
3366 if (boundinfo->kind[maxoff - 1][lastkey] ==
3368 {
3369 maxoff--;
3370 Assert(boundinfo->indexes[maxoff] >= 0);
3371 }
3372 }
3373
3374 Assert(minoff >= 0 && maxoff >= 0);
3375 if (minoff <= maxoff)
3376 result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
3377
3378 return result;
3379}
3380
3381/*
3382 * pull_exec_paramids
3383 * Returns a Bitmapset containing the paramids of all Params with
3384 * paramkind = PARAM_EXEC in 'expr'.
3385 */
3386static Bitmapset *
3388{
3390
3392
3393 return result;
3394}
3395
3396static bool
3398{
3399 if (node == NULL)
3400 return false;
3401 if (IsA(node, Param))
3402 {
3403 Param *param = (Param *) node;
3404
3405 if (param->paramkind == PARAM_EXEC)
3406 *context = bms_add_member(*context, param->paramid);
3407 return false;
3408 }
3410}
3411
3412/*
3413 * get_partkey_exec_paramids
3414 * Loop through given pruning steps and find out which exec Params
3415 * are used.
3416 *
3417 * Returns a Bitmapset of Param IDs.
3418 */
3419static Bitmapset *
3421{
3422 Bitmapset *execparamids = NULL;
3423 ListCell *lc;
3424
3425 foreach(lc, steps)
3426 {
3428 ListCell *lc2;
3429
3430 if (!IsA(step, PartitionPruneStepOp))
3431 continue;
3432
3433 foreach(lc2, step->exprs)
3434 {
3435 Expr *expr = lfirst(lc2);
3436
3437 /* We can be quick for plain Consts */
3438 if (!IsA(expr, Const))
3439 execparamids = bms_join(execparamids,
3440 pull_exec_paramids(expr));
3441 }
3442 }
3443
3444 return execparamids;
3445}
3446
3447/*
3448 * perform_pruning_base_step
3449 * Determines the indexes of datums that satisfy conditions specified in
3450 * 'opstep'.
3451 *
3452 * Result also contains whether special null-accepting and/or default
3453 * partition need to be scanned.
3454 */
3455static PruneStepResult *
3458{
3459 ListCell *lc1,
3460 *lc2;
3461 int keyno,
3462 nvalues;
3464 FmgrInfo *partsupfunc;
3465 int stateidx;
3466
3467 /*
3468 * There better be the same number of expressions and compare functions.
3469 */
3470 Assert(list_length(opstep->exprs) == list_length(opstep->cmpfns));
3471
3472 nvalues = 0;
3473 lc1 = list_head(opstep->exprs);
3474 lc2 = list_head(opstep->cmpfns);
3475
3476 /*
3477 * Generate the partition lookup key that will be used by one of the
3478 * get_matching_*_bounds functions called below.
3479 */
3480 for (keyno = 0; keyno < context->partnatts; keyno++)
3481 {
3482 /*
3483 * For hash partitioning, it is possible that values of some keys are
3484 * not provided in operator clauses, but instead the planner found
3485 * that they appeared in a IS NULL clause.
3486 */
3487 if (bms_is_member(keyno, opstep->nullkeys))
3488 continue;
3489
3490 /*
3491 * For range partitioning, we must only perform pruning with values
3492 * for either all partition keys or a prefix thereof.
3493 */
3494 if (keyno > nvalues && context->strategy == PARTITION_STRATEGY_RANGE)
3495 break;
3496
3497 if (lc1 != NULL)
3498 {
3499 Expr *expr;
3500 Datum datum;
3501 bool isnull;
3502 Oid cmpfn;
3503
3504 expr = lfirst(lc1);
3506 opstep->step.step_id, keyno);
3507 partkey_datum_from_expr(context, expr, stateidx,
3508 &datum, &isnull);
3509
3510 /*
3511 * Since we only allow strict operators in pruning steps, any
3512 * null-valued comparison value must cause the comparison to fail,
3513 * so that no partitions could match.
3514 */
3515 if (isnull)
3516 {
3518
3520 result->bound_offsets = NULL;
3521 result->scan_default = false;
3522 result->scan_null = false;
3523
3524 return result;
3525 }
3526
3527 /* Set up the stepcmpfuncs entry, unless we already did */
3528 cmpfn = lfirst_oid(lc2);
3529 Assert(OidIsValid(cmpfn));
3530 if (cmpfn != context->stepcmpfuncs[stateidx].fn_oid)
3531 {
3532 /*
3533 * If the needed support function is the same one cached in
3534 * the relation's partition key, copy the cached FmgrInfo.
3535 * Otherwise (i.e., when we have a cross-type comparison), an
3536 * actual lookup is required.
3537 */
3538 if (cmpfn == context->partsupfunc[keyno].fn_oid)
3540 &context->partsupfunc[keyno],
3541 context->ppccontext);
3542 else
3543 fmgr_info_cxt(cmpfn, &context->stepcmpfuncs[stateidx],
3544 context->ppccontext);
3545 }
3546
3547 values[keyno] = datum;
3548 nvalues++;
3549
3550 lc1 = lnext(opstep->exprs, lc1);
3551 lc2 = lnext(opstep->cmpfns, lc2);
3552 }
3553 }
3554
3555 /*
3556 * Point partsupfunc to the entry for the 0th key of this step; the
3557 * additional support functions, if any, follow consecutively.
3558 */
3559 stateidx = PruneCxtStateIdx(context->partnatts, opstep->step.step_id, 0);
3560 partsupfunc = &context->stepcmpfuncs[stateidx];
3561
3562 switch (context->strategy)
3563 {
3565 return get_matching_hash_bounds(context,
3566 opstep->opstrategy,
3567 values, nvalues,
3568 partsupfunc,
3569 opstep->nullkeys);
3570
3572 return get_matching_list_bounds(context,
3573 opstep->opstrategy,
3574 values[0], nvalues,
3575 &partsupfunc[0],
3576 opstep->nullkeys);
3577
3579 return get_matching_range_bounds(context,
3580 opstep->opstrategy,
3581 values, nvalues,
3582 partsupfunc,
3583 opstep->nullkeys);
3584
3585 default:
3586 elog(ERROR, "unexpected partition strategy: %d",
3587 (int) context->strategy);
3588 break;
3589 }
3590
3591 return NULL;
3592}
3593
3594/*
3595 * perform_pruning_combine_step
3596 * Determines the indexes of datums obtained by combining those given
3597 * by the steps identified by cstep->source_stepids using the specified
3598 * combination method
3599 *
3600 * Since cstep may refer to the result of earlier steps, we also receive
3601 * step_results here.
3602 */
3603static PruneStepResult *
3607{
3609 bool firststep;
3610 ListCell *lc1;
3611
3612 /*
3613 * A combine step without any source steps is an indication to not perform
3614 * any partition pruning. Return all datum indexes in that case.
3615 */
3616 if (cstep->source_stepids == NIL)
3617 {
3618 PartitionBoundInfo boundinfo = context->boundinfo;
3619
3620 result->bound_offsets =
3621 bms_add_range(NULL, 0, boundinfo->nindexes - 1);
3622 result->scan_default = partition_bound_has_default(boundinfo);
3623 result->scan_null = partition_bound_accepts_nulls(boundinfo);
3624 return result;
3625 }
3626
3627 switch (cstep->combineOp)
3628 {
3630 foreach(lc1, cstep->source_stepids)
3631 {
3632 int step_id = lfirst_int(lc1);
3634
3635 /*
3636 * step_results[step_id] must contain a valid result, which is
3637 * confirmed by the fact that cstep's step_id is greater than
3638 * step_id and the fact that results of the individual steps
3639 * are evaluated in sequence of their step_ids.
3640 */
3641 if (step_id >= cstep->step.step_id)
3642 elog(ERROR, "invalid pruning combine step argument");
3643 step_result = step_results[step_id];
3645
3646 /* Record any additional datum indexes from this step */
3647 result->bound_offsets = bms_add_members(result->bound_offsets,
3648 step_result->bound_offsets);
3649
3650 /* Update whether to scan null and default partitions. */
3651 if (!result->scan_null)
3652 result->scan_null = step_result->scan_null;
3653 if (!result->scan_default)
3654 result->scan_default = step_result->scan_default;
3655 }
3656 break;
3657
3659 firststep = true;
3660 foreach(lc1, cstep->source_stepids)
3661 {
3662 int step_id = lfirst_int(lc1);
3664
3665 if (step_id >= cstep->step.step_id)
3666 elog(ERROR, "invalid pruning combine step argument");
3667 step_result = step_results[step_id];
3669
3670 if (firststep)
3671 {
3672 /* Copy step's result the first time. */
3673 result->bound_offsets =
3674 bms_copy(step_result->bound_offsets);
3675 result->scan_null = step_result->scan_null;
3676 result->scan_default = step_result->scan_default;
3677 firststep = false;
3678 }
3679 else
3680 {
3681 /* Record datum indexes common to both steps */
3682 result->bound_offsets =
3683 bms_int_members(result->bound_offsets,
3684 step_result->bound_offsets);
3685
3686 /* Update whether to scan null and default partitions. */
3687 if (result->scan_null)
3688 result->scan_null = step_result->scan_null;
3689 if (result->scan_default)
3690 result->scan_default = step_result->scan_default;
3691 }
3692 }
3693 break;
3694 }
3695
3696 return result;
3697}
3698
3699/*
3700 * match_boolean_partition_clause
3701 *
3702 * If we're able to match the clause to the partition key as specially-shaped
3703 * boolean clause, set *outconst to a Const containing a true, false or NULL
3704 * value, set *notclause according to if the clause was in the "not" form,
3705 * i.e. "IS NOT TRUE", "IS NOT FALSE" or "IS NOT UNKNOWN" and return
3706 * PARTCLAUSE_MATCH_CLAUSE for "IS [NOT] (TRUE|FALSE)" clauses and
3707 * PARTCLAUSE_MATCH_NULLNESS for "IS [NOT] UNKNOWN" clauses. Otherwise,
3708 * return PARTCLAUSE_UNSUPPORTED if the clause cannot be used for partition
3709 * pruning, and PARTCLAUSE_NOMATCH for supported clauses that do not match this
3710 * 'partkey'.
3711 */
3713match_boolean_partition_clause(Oid partopfamily, Expr *clause, const Expr *partkey,
3714 Expr **outconst, bool *notclause)
3715{
3716 Expr *leftop;
3717
3718 *outconst = NULL;
3719 *notclause = false;
3720
3721 /*
3722 * Partitioning currently can only use built-in AMs, so checking for
3723 * built-in boolean opfamilies is good enough.
3724 */
3725 if (!IsBuiltinBooleanOpfamily(partopfamily))
3727
3728 if (IsA(clause, BooleanTest))
3729 {
3730 BooleanTest *btest = (BooleanTest *) clause;
3731
3732 leftop = btest->arg;
3734 while (IsA(leftop, RelabelType))
3735 leftop = ((RelabelType *) leftop)->arg;
3736
3737 if (equal(leftop, partkey))
3738 {
3739 switch (btest->booltesttype)
3740 {
3741 case IS_NOT_TRUE:
3742 *notclause = true;
3744 case IS_TRUE:
3745 *outconst = (Expr *) makeBoolConst(true, false);
3747 case IS_NOT_FALSE:
3748 *notclause = true;
3750 case IS_FALSE:
3751 *outconst = (Expr *) makeBoolConst(false, false);
3753 case IS_NOT_UNKNOWN:
3754 *notclause = true;
3756 case IS_UNKNOWN:
3758 default:
3760 }
3761 }
3762 /* does not match partition key */
3763 return PARTCLAUSE_NOMATCH;
3764 }
3765 else
3766 {
3767 bool is_not_clause = is_notclause(clause);
3768
3769 leftop = is_not_clause ? get_notclausearg(clause) : clause;
3770
3772 while (IsA(leftop, RelabelType))
3773 leftop = ((RelabelType *) leftop)->arg;
3774
3775 /* Compare to the partition key, and make up a clause ... */
3776 if (equal(leftop, partkey))
3777 *outconst = (Expr *) makeBoolConst(!is_not_clause, false);
3778 else if (equal(negate_clause((Node *) leftop), partkey))
3780 else
3781 return PARTCLAUSE_NOMATCH;
3782
3784 }
3785}
3786
3787/*
3788 * partkey_datum_from_expr
3789 * Evaluate expression for potential partition pruning
3790 *
3791 * Evaluate 'expr'; set *value and *isnull to the resulting Datum and nullflag.
3792 *
3793 * If expr isn't a Const, its ExprState is in stateidx of the context
3794 * exprstate array.
3795 *
3796 * Note that the evaluated result may be in the per-tuple memory context of
3797 * context->exprcontext, and we may have leaked other memory there too.
3798 * This memory must be recovered by resetting that ExprContext after
3799 * we're done with the pruning operation (see execPartition.c).
3800 */
3801static void
3803 Expr *expr, int stateidx,
3804 Datum *value, bool *isnull)
3805{
3806 if (IsA(expr, Const))
3807 {
3808 /* We can always determine the value of a constant */
3809 Const *con = (Const *) expr;
3810
3811 *value = con->constvalue;
3812 *isnull = con->constisnull;
3813 }
3814 else
3815 {
3816 ExprState *exprstate;
3818
3819 /*
3820 * We should never see a non-Const in a step unless the caller has
3821 * passed a valid ExprContext.
3822 */
3823 Assert(context->exprcontext != NULL);
3824
3825 exprstate = context->exprstates[stateidx];
3826 ectx = context->exprcontext;
3827 *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
3828 }
3829}
#define DatumGetArrayTypeP(X)
Definition array.h:261
#define ARR_ELEMTYPE(a)
Definition array.h:292
void deconstruct_array(const ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Bitmapset * bms_make_singleton(int x)
Definition bitmapset.c:216
Bitmapset * bms_int_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:1093
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition bitmapset.c:1003
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition bitmapset.c:852
int bms_num_members(const Bitmapset *a)
Definition bitmapset.c:744
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:510
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:901
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition bitmapset.c:1214
Bitmapset * bms_copy(const Bitmapset *a)
Definition bitmapset.c:122
#define bms_is_empty(a)
Definition bitmapset.h:118
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define Assert(condition)
Definition c.h:943
int16_t int16
Definition c.h:619
int32_t int32
Definition c.h:620
uint64_t uint64
Definition c.h:625
#define pg_fallthrough
Definition c.h:161
#define OidIsValid(objectId)
Definition c.h:858
uint32 result
bool contain_volatile_functions(Node *clause)
Definition clauses.c:551
Datum arg
Definition elog.c:1323
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
bool equal(const void *a, const void *b)
Definition equalfuncs.c:223
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:446
#define palloc_object(type)
Definition fe_memutils.h:74
#define palloc0_object(type)
Definition fe_memutils.h:75
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition fmgr.c:139
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition fmgr.c:582
#define HASHEXTENDED_PROC
Definition hash.h:356
return str start
static struct @177 value
int i
Definition isn.c:77
List * lappend(List *list, void *datum)
Definition list.c:339
List * list_concat(List *list1, const List *list2)
Definition list.c:561
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 get_op_opfamily_properties(Oid opno, Oid opfamily, bool ordering_op, int *strategy, Oid *lefttype, Oid *righttype)
Definition lsyscache.c:140
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition lsyscache.c:2464
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition lsyscache.c:915
bool op_strict(Oid opno)
Definition lsyscache.c:1670
char op_volatile(Oid opno)
Definition lsyscache.c:1686
bool op_in_opfamily(Oid opno, Oid opfamily)
Definition lsyscache.c:70
Oid get_negator(Oid opno)
Definition lsyscache.c:1726
Oid get_commutator(Oid opno)
Definition lsyscache.c:1702
Expr * makeBoolExpr(BoolExprType boolop, List *args, int location)
Definition makefuncs.c:420
Node * makeBoolConst(bool value, bool isnull)
Definition makefuncs.c:408
Expr * make_opclause(Oid opno, Oid opresulttype, bool opretset, Expr *leftop, Expr *rightop, Oid opcollid, Oid inputcollid)
Definition makefuncs.c:701
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition makefuncs.c:350
#define BTORDER_PROC
Definition nbtree.h:717
static Node * get_rightop(const void *clause)
Definition nodeFuncs.h:95
#define expression_tree_walker(n, w, c)
Definition nodeFuncs.h:153
static bool is_notclause(const void *clause)
Definition nodeFuncs.h:125
static Expr * get_notclausearg(const void *notclause)
Definition nodeFuncs.h:134
static Node * get_leftop(const void *clause)
Definition nodeFuncs.h:83
#define IsA(nodeptr, _type_)
Definition nodes.h:164
#define copyObject(obj)
Definition nodes.h:232
#define makeNode(_type_)
Definition nodes.h:161
#define castNode(_type_, nodeptr)
Definition nodes.h:182
@ PARTITION_STRATEGY_HASH
Definition parsenodes.h:919
@ PARTITION_STRATEGY_LIST
Definition parsenodes.h:917
@ PARTITION_STRATEGY_RANGE
Definition parsenodes.h:918
@ PARTITION_RANGE_DATUM_MAXVALUE
Definition parsenodes.h:971
@ PARTITION_RANGE_DATUM_MINVALUE
Definition parsenodes.h:969
int32 partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, const Datum *rb_datums, PartitionRangeDatumKind *rb_kind, const Datum *tuple_datums, int n_tuple_datums)
uint64 compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc, const Oid *partcollation, const Datum *values, const bool *isnull)
int partition_range_datum_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, const Datum *values, bool *is_equal)
int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
#define partition_bound_has_default(bi)
Definition partbounds.h:99
#define partition_bound_accepts_nulls(bi)
Definition partbounds.h:98
static PruneStepResult * get_matching_range_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, const Datum *values, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
Definition partprune.c:2993
PartClauseMatchStatus
Definition partprune.c:79
@ PARTCLAUSE_MATCH_CONTRADICT
Definition partprune.c:84
@ PARTCLAUSE_UNSUPPORTED
Definition partprune.c:85
@ PARTCLAUSE_NOMATCH
Definition partprune.c:80
@ PARTCLAUSE_MATCH_STEPS
Definition partprune.c:83
@ PARTCLAUSE_MATCH_CLAUSE
Definition partprune.c:81
@ PARTCLAUSE_MATCH_NULLNESS
Definition partprune.c:82
static Bitmapset * get_partkey_exec_paramids(List *steps)
Definition partprune.c:3421
static PruneStepResult * get_matching_list_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum value, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
Definition partprune.c:2782
@ PARTTARGET_PLANNER
Definition partprune.c:94
@ PARTTARGET_EXEC
Definition partprune.c:96
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:2480
static List * gen_partprune_steps_internal(GeneratePruningStepsContext *context, List *clauses)
Definition partprune.c:990
static PartClauseMatchStatus match_boolean_partition_clause(Oid partopfamily, Expr *clause, const Expr *partkey, Expr **outconst, bool *notclause)
Definition partprune.c:3714
static void partkey_datum_from_expr(PartitionPruneContext *context, Expr *expr, int stateidx, Datum *value, bool *isnull)
Definition partprune.c:3803
#define PartCollMatchesExprColl(partcoll, exprcoll)
Definition partprune.c:1784
static PruneStepResult * perform_pruning_base_step(PartitionPruneContext *context, PartitionPruneStepOp *opstep)
Definition partprune.c:3457
static PruneStepResult * perform_pruning_combine_step(PartitionPruneContext *context, PartitionPruneStepCombine *cstep, PruneStepResult **step_results)
Definition partprune.c:3605
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:2538
static Bitmapset * pull_exec_paramids(Expr *expr)
Definition partprune.c:3388
static PartitionPruneStep * gen_prune_step_op(GeneratePruningStepsContext *context, StrategyNumber opstrategy, bool op_is_ne, List *exprs, List *cmpfns, Bitmapset *nullkeys)
Definition partprune.c:1342
static PruneStepResult * get_matching_hash_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, const Datum *values, int nvalues, FmgrInfo *partsupfunc, Bitmapset *nullkeys)
Definition partprune.c:2705
static bool pull_exec_paramids_walker(Node *node, Bitmapset **context)
Definition partprune.c:3398
#define PruneCxtStateIdx(partnatts, step_id, keyno)
Definition partprune.h:70
#define PARTITION_MAX_KEYS
#define lfirst(lc)
Definition pg_list.h:172
#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 lfirst_int(lc)
Definition pg_list.h:173
#define list_make1_oid(x1)
Definition pg_list.h:274
#define list_make1(x1)
Definition pg_list.h:244
#define for_each_cell(cell, lst, initcell)
Definition pg_list.h:470
#define linitial(l)
Definition pg_list.h:178
#define lsecond(l)
Definition pg_list.h:183
static ListCell * list_head(const List *l)
Definition pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition pg_list.h:375
#define lfirst_oid(lc)
Definition pg_list.h:174
#define list_make2(x1, x2)
Definition pg_list.h:246
Node * strip_noop_phvs(Node *node)
@ PARTPRUNE_COMBINE_INTERSECT
Definition plannodes.h:1796
@ PARTPRUNE_COMBINE_UNION
Definition plannodes.h:1795
uint64_t Datum
Definition postgres.h:70
#define InvalidOid
unsigned int Oid
Node * negate_clause(Node *node)
Definition prepqual.c:73
static int fb(int x)
@ IS_NOT_TRUE
Definition primnodes.h:2004
@ IS_NOT_FALSE
Definition primnodes.h:2004
@ IS_NOT_UNKNOWN
Definition primnodes.h:2004
@ IS_TRUE
Definition primnodes.h:2004
@ IS_UNKNOWN
Definition primnodes.h:2004
@ IS_FALSE
Definition primnodes.h:2004
@ OR_EXPR
Definition primnodes.h:964
@ PARAM_EXEC
Definition primnodes.h:386
@ IS_NULL
Definition primnodes.h:1980
@ IS_NOT_NULL
Definition primnodes.h:1980
void check_stack_depth(void)
Definition stack_depth.c:95
uint16 StrategyNumber
Definition stratnum.h:22
#define BTGreaterStrategyNumber
Definition stratnum.h:33
#define InvalidStrategy
Definition stratnum.h:24
#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
Oid fn_oid
Definition fmgr.h:59
Definition pg_list.h:54
Definition nodes.h:135
int paramid
Definition primnodes.h:397
ParamKind paramkind
Definition primnodes.h:396
PartitionRangeDatumKind ** kind
Definition partbounds.h:84
FmgrInfo * partsupfunc
Definition partprune.h:56
ExprContext * exprcontext
Definition partprune.h:60
MemoryContext ppccontext
Definition partprune.h:58
PartitionBoundInfo boundinfo
Definition partprune.h:54
FmgrInfo * stepcmpfuncs
Definition partprune.h:57
ExprState ** exprstates
Definition partprune.h:61
bool contain_var_clause(Node *node)
Definition var.c:406

Typedef Documentation

◆ GeneratePruningStepsContext

◆ PartClauseInfo

◆ PartClauseMatchStatus

◆ PartClauseTarget

◆ PruneStepResult

Enumeration Type Documentation

◆ PartClauseMatchStatus

Enumerator
PARTCLAUSE_NOMATCH 
PARTCLAUSE_MATCH_CLAUSE 
PARTCLAUSE_MATCH_NULLNESS 
PARTCLAUSE_MATCH_STEPS 
PARTCLAUSE_MATCH_CONTRADICT 
PARTCLAUSE_UNSUPPORTED 

Definition at line 78 of file partprune.c.

◆ PartClauseTarget

Enumerator
PARTTARGET_PLANNER 
PARTTARGET_INITIAL 
PARTTARGET_EXEC 

Definition at line 92 of file partprune.c.

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

Function Documentation

◆ add_part_relids()

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

Definition at line 400 of file partprune.c.

401{
403 ListCell *lc;
404
405 /* We can easily get the lowest set bit this way: */
407 Assert(targetpart > 0);
408
409 /* Look for a matching topmost parent */
410 foreach(lc, allpartrelids)
411 {
414
415 if (targetpart == currtarget)
416 {
417 /* Found a match, so add any new RT indexes to this hierarchy */
420 return allpartrelids;
421 }
422 }
423 /* No match, so add the new partition hierarchy to the list */
425}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1290
unsigned int Index
Definition c.h:698

References Assert, bms_add_members(), bms_next_member(), fb(), 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 744 of file partprune.c.

746{
747 /* Initialize all output values to zero/false/NULL */
748 memset(context, 0, sizeof(GeneratePruningStepsContext));
749 context->rel = rel;
750 context->target = target;
751
752 /*
753 * If this partitioned table is in turn a partition, and it shares any
754 * partition keys with its parent, then it's possible that the hierarchy
755 * allows the parent a narrower range of values than some of its
756 * partitions (particularly the default one). This is normally not
757 * useful, but it can be to prune the default partition.
758 */
759 if (partition_bound_has_default(rel->boundinfo) && rel->partition_qual)
760 {
761 /* Make a copy to avoid modifying the passed-in List */
762 clauses = list_concat_copy(clauses, rel->partition_qual);
763 }
764
765 /* Down into the rabbit-hole. */
766 (void) gen_partprune_steps_internal(context, clauses);
767}
List * list_concat_copy(const List *list1, const List *list2)
Definition list.c:598
PartClauseTarget target
Definition partprune.c:115
List * partition_qual
Definition pathnodes.h:1192

References fb(), 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 990 of file partprune.c.

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

References arg, Assert, bms_add_member(), bms_is_empty, bms_is_member(), bms_num_members(), GeneratePruningStepsContext::contradictory, DatumGetBool(), fb(), 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, PARTPRUNE_COMBINE_INTERSECT, PARTPRUNE_COMBINE_UNION, predicate_refuted_by(), GeneratePruningStepsContext::rel, result, and PartitionPruneStep::step_id.

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

1378{
1380
1381 cstep->step.step_id = context->next_step_id++;
1382 cstep->combineOp = combineOp;
1383 cstep->source_stepids = source_stepids;
1384
1385 context->steps = lappend(context->steps, cstep);
1386
1387 return (PartitionPruneStep *) cstep;
1388}

References fb(), lappend(), makeNode, GeneratePruningStepsContext::next_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 1342 of file partprune.c.

1346{
1348
1349 opstep->step.step_id = context->next_step_id++;
1350
1351 /*
1352 * For clauses that contain an <> operator, set opstrategy to
1353 * InvalidStrategy to signal get_matching_list_bounds to do the right
1354 * thing.
1355 */
1356 opstep->opstrategy = op_is_ne ? InvalidStrategy : opstrategy;
1357 Assert(list_length(exprs) == list_length(cmpfns));
1358 opstep->exprs = exprs;
1359 opstep->cmpfns = cmpfns;
1360 opstep->nullkeys = nullkeys;
1361
1362 context->steps = lappend(context->steps, opstep);
1363
1364 return (PartitionPruneStep *) opstep;
1365}

References Assert, fb(), InvalidStrategy, lappend(), list_length(), makeNode, GeneratePruningStepsContext::next_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 1412 of file partprune.c.

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

References Assert, bms_is_member(), BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, BTMaxStrategyNumber, elog, ERROR, fb(), for_each_cell, get_op_opfamily_properties(), get_steps_using_prefix(), HTEqualStrategyNumber, HTMaxStrategyNumber, i, InvalidStrategy, lappend(), lfirst, list_concat(), list_head(), llast, NIL, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, and GeneratePruningStepsContext::rel.

Referenced by gen_partprune_steps_internal().

◆ get_matching_hash_bounds()

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

Definition at line 2705 of file partprune.c.

2708{
2710 PartitionBoundInfo boundinfo = context->boundinfo;
2711 int *partindices = boundinfo->indexes;
2712 int partnatts = context->partnatts;
2713 bool isnull[PARTITION_MAX_KEYS];
2714 int i;
2716 int greatest_modulus;
2717 Oid *partcollation = context->partcollation;
2718
2720
2721 /*
2722 * For hash partitioning we can only perform pruning based on equality
2723 * clauses to the partition key or IS NULL clauses. We also can only
2724 * prune if we got values for all keys.
2725 */
2726 if (nvalues + bms_num_members(nullkeys) == partnatts)
2727 {
2728 /*
2729 * If there are any values, they must have come from clauses
2730 * containing an equality operator compatible with hash partitioning.
2731 */
2732 Assert(opstrategy == HTEqualStrategyNumber || nvalues == 0);
2733
2734 for (i = 0; i < partnatts; i++)
2735 isnull[i] = bms_is_member(i, nullkeys);
2736
2737 rowHash = compute_partition_hash_value(partnatts, partsupfunc, partcollation,
2738 values, isnull);
2739
2740 greatest_modulus = boundinfo->nindexes;
2742 result->bound_offsets =
2744 }
2745 else
2746 {
2747 /* Report all valid offsets into the boundinfo->indexes array. */
2748 result->bound_offsets = bms_add_range(NULL, 0,
2749 boundinfo->nindexes - 1);
2750 }
2751
2752 /*
2753 * There is neither a special hash null partition or the default hash
2754 * partition.
2755 */
2756 result->scan_null = result->scan_default = false;
2757
2758 return result;
2759}

References Assert, bms_add_range(), bms_is_member(), bms_make_singleton(), bms_num_members(), PartitionPruneContext::boundinfo, compute_partition_hash_value(), fb(), HTEqualStrategyNumber, i, PartitionBoundInfoData::indexes, PartitionBoundInfoData::nindexes, palloc0_object, PartitionPruneContext::partcollation, PARTITION_MAX_KEYS, PARTITION_STRATEGY_HASH, PartitionPruneContext::partnatts, result, 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 2782 of file partprune.c.

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

References Assert, bms_add_range(), bms_del_member(), bms_is_empty, bms_make_singleton(), PartitionPruneContext::boundinfo, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, elog, ERROR, fb(), PartitionBoundInfoData::indexes, InvalidStrategy, PartitionBoundInfoData::ndatums, palloc0_object, PartitionPruneContext::partcollation, partition_bound_accepts_nulls, partition_bound_has_default, partition_list_bsearch(), PARTITION_STRATEGY_LIST, PartitionPruneContext::partnatts, pg_fallthrough, result, 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 846 of file partprune.c.

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

References Assert, bms_add_member(), bms_add_range(), bms_next_member(), PartitionPruneContext::boundinfo, PartitionBoundInfoData::default_index, elog, ERROR, fb(), 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(), result, 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,
const Datum values,
int  nvalues,
FmgrInfo partsupfunc,
Bitmapset nullkeys 
)
static

Definition at line 2993 of file partprune.c.

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

References Assert, bms_add_range(), bms_is_empty, bms_make_singleton(), PartitionPruneContext::boundinfo, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, PartitionBoundInfoData::datums, elog, ERROR, fb(), PartitionBoundInfoData::indexes, PartitionBoundInfoData::kind, PartitionBoundInfoData::ndatums, palloc0_object, 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, pg_fallthrough, result, 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 3421 of file partprune.c.

3422{
3423 Bitmapset *execparamids = NULL;
3424 ListCell *lc;
3425
3426 foreach(lc, steps)
3427 {
3429 ListCell *lc2;
3430
3431 if (!IsA(step, PartitionPruneStepOp))
3432 continue;
3433
3434 foreach(lc2, step->exprs)
3435 {
3436 Expr *expr = lfirst(lc2);
3437
3438 /* We can be quick for plain Consts */
3439 if (!IsA(expr, Const))
3440 execparamids = bms_join(execparamids,
3441 pull_exec_paramids(expr));
3442 }
3443 }
3444
3445 return execparamids;
3446}

References bms_join(), PartitionPruneStepOp::exprs, fb(), 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 2480 of file partprune.c.

2487{
2488 /* step_nullkeys must be empty for RANGE and LIST partitioned tables */
2490 context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH);
2491
2492 /*
2493 * No recursive processing is required when 'prefix' is an empty list.
2494 * This occurs when there is only 1 partition key column.
2495 */
2496 if (prefix == NIL)
2497 {
2498 PartitionPruneStep *step;
2499
2500 step = gen_prune_step_op(context,
2506 return list_make1(step);
2507 }
2508
2509 /* Recurse to generate steps for every combination of clauses. */
2510 return get_steps_using_prefix_recurse(context,
2516 prefix,
2517 list_head(prefix),
2518 NIL, NIL);
2519}

References Assert, fb(), 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 2538 of file partprune.c.

2548{
2549 List *result = NIL;
2550 ListCell *lc;
2551 int cur_keyno;
2552 int final_keyno;
2553
2554 /* Actually, recursion would be limited by PARTITION_MAX_KEYS. */
2556
2557 Assert(start != NULL);
2558 cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno;
2559 final_keyno = ((PartClauseInfo *) llast(prefix))->keyno;
2560
2561 /* Check if we need to recurse. */
2562 if (cur_keyno < final_keyno)
2563 {
2566
2567 /*
2568 * Find the first PartClauseInfo belonging to the next partition key,
2569 * the next recursive call must start iteration of the prefix list
2570 * from that point.
2571 */
2572 for_each_cell(lc, prefix, start)
2573 {
2574 pc = lfirst(lc);
2575
2576 if (pc->keyno > cur_keyno)
2577 break;
2578 }
2579
2580 /* record where to start iterating in the next recursive call */
2581 next_start = lc;
2582
2583 /*
2584 * For each PartClauseInfo with keyno set to cur_keyno, add its expr
2585 * and cmpfn to step_exprs and step_cmpfns, respectively, and recurse
2586 * using 'next_start' as the starting point in the 'prefix' list.
2587 */
2588 for_each_cell(lc, prefix, start)
2589 {
2590 List *moresteps;
2592 *step_cmpfns1;
2593
2594 pc = lfirst(lc);
2595 if (pc->keyno == cur_keyno)
2596 {
2597 /* Leave the original step_exprs unmodified. */
2600
2601 /* Leave the original step_cmpfns unmodified. */
2604 }
2605 else
2606 {
2607 /* check the 'prefix' list is sorted correctly */
2608 Assert(pc->keyno > cur_keyno);
2609 break;
2610 }
2611
2618 prefix,
2619 next_start,
2621 step_cmpfns1);
2623
2626 }
2627 }
2628 else
2629 {
2630 /*
2631 * End the current recursion cycle and start generating steps, one for
2632 * each clause with cur_keyno, which is all clauses from here onward
2633 * till the end of the list. Note that for hash partitioning,
2634 * step_nullkeys is allowed to be non-empty, in which case step_exprs
2635 * would only contain expressions for the partition keys that are not
2636 * specified in step_nullkeys.
2637 */
2640
2641 /*
2642 * Note also that for hash partitioning, each partition key should
2643 * have either equality clauses or an IS NULL clause, so if a
2644 * partition key doesn't have an expression, it would be specified in
2645 * step_nullkeys.
2646 */
2647 Assert(context->rel->part_scheme->strategy
2650 context->rel->part_scheme->partnatts);
2651 for_each_cell(lc, prefix, start)
2652 {
2654 PartitionPruneStep *step;
2656 *step_cmpfns1;
2657
2658 Assert(pc->keyno == cur_keyno);
2659
2660 /* Leave the original step_exprs unmodified. */
2664
2665 /* Leave the original step_cmpfns unmodified. */
2669
2670 step = gen_prune_step_op(context,
2674 result = lappend(result, step);
2675 }
2676 }
2677
2678 return result;
2679}

References Assert, bms_is_empty, bms_num_members(), check_stack_depth(), fb(), for_each_cell, gen_prune_step_op(), get_steps_using_prefix_recurse(), lappend(), lappend_oid(), lfirst, list_concat(), list_copy(), list_free(), list_length(), llast, NIL, PARTITION_STRATEGY_HASH, GeneratePruningStepsContext::rel, result, 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 225 of file partprune.c.

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

References add_part_relids(), Assert, bms_add_member(), bms_add_range(), bms_copy(), bms_del_members(), bms_join(), bms_num_members(), fb(), find_base_rel(), i, IS_PARTITIONED_REL, lappend(), lfirst, list_length(), make_partitionedrel_pruneinfo(), makeNode, NIL, palloc0_array, pfree(), RELOPT_OTHER_MEMBER_REL, 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 446 of file partprune.c.

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

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, fb(), 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, makeNode, NIL, RelOptInfo::nparts, PartitionedRelPruneInfo::nparts, palloc(), palloc0(), palloc0_array, PARTTARGET_EXEC, PARTTARGET_INITIAL, pfree(), planner_rt_fetch, PartitionedRelPruneInfo::present_parts, RelOptInfo::relid, 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,
const Expr partkey,
Expr **  outconst,
bool notclause 
)
static

Definition at line 3714 of file partprune.c.

3716{
3717 Expr *leftop;
3718
3719 *outconst = NULL;
3720 *notclause = false;
3721
3722 /*
3723 * Partitioning currently can only use built-in AMs, so checking for
3724 * built-in boolean opfamilies is good enough.
3725 */
3726 if (!IsBuiltinBooleanOpfamily(partopfamily))
3728
3729 if (IsA(clause, BooleanTest))
3730 {
3731 BooleanTest *btest = (BooleanTest *) clause;
3732
3733 leftop = btest->arg;
3735 while (IsA(leftop, RelabelType))
3736 leftop = ((RelabelType *) leftop)->arg;
3737
3738 if (equal(leftop, partkey))
3739 {
3740 switch (btest->booltesttype)
3741 {
3742 case IS_NOT_TRUE:
3743 *notclause = true;
3745 case IS_TRUE:
3746 *outconst = (Expr *) makeBoolConst(true, false);
3748 case IS_NOT_FALSE:
3749 *notclause = true;
3751 case IS_FALSE:
3752 *outconst = (Expr *) makeBoolConst(false, false);
3754 case IS_NOT_UNKNOWN:
3755 *notclause = true;
3757 case IS_UNKNOWN:
3759 default:
3761 }
3762 }
3763 /* does not match partition key */
3764 return PARTCLAUSE_NOMATCH;
3765 }
3766 else
3767 {
3768 bool is_not_clause = is_notclause(clause);
3769
3770 leftop = is_not_clause ? get_notclausearg(clause) : clause;
3771
3773 while (IsA(leftop, RelabelType))
3774 leftop = ((RelabelType *) leftop)->arg;
3775
3776 /* Compare to the partition key, and make up a clause ... */
3777 if (equal(leftop, partkey))
3778 *outconst = (Expr *) makeBoolConst(!is_not_clause, false);
3779 else if (equal(negate_clause((Node *) leftop), partkey))
3781 else
3782 return PARTCLAUSE_NOMATCH;
3783
3785 }
3786}

References arg, equal(), fb(), 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, PARTCLAUSE_UNSUPPORTED, pg_fallthrough, and strip_noop_phvs().

Referenced by match_clause_to_partition_key().

◆ match_clause_to_partition_key()

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

Definition at line 1828 of file partprune.c.

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

References arg, ScalarArrayOpExpr::args, ARR_ELEMTYPE, Assert, bms_is_empty, BTEqualStrategyNumber, BTORDER_PROC, castNode, contain_var_clause(), contain_volatile_functions(), GeneratePruningStepsContext::contradictory, copyObject, DatumGetArrayTypeP, deconstruct_array(), elog, equal(), ERROR, fb(), 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, lappend(), lfirst, linitial, list_length(), list_make1, list_make2, lsecond, make_opclause(), makeBoolExpr(), makeConst(), makeNode, match_boolean_partition_clause(), NIL, OidIsValid, op_in_opfamily(), op_strict(), op_volatile(), ScalarArrayOpExpr::opno, OR_EXPR, palloc_object, PARTCLAUSE_MATCH_CLAUSE, PARTCLAUSE_MATCH_CONTRADICT, PARTCLAUSE_MATCH_NULLNESS, PARTCLAUSE_MATCH_STEPS, PARTCLAUSE_NOMATCH, PARTCLAUSE_UNSUPPORTED, PartCollMatchesExprColl, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PARTTARGET_EXEC, PARTTARGET_PLANNER, pull_exec_paramids(), GeneratePruningStepsContext::rel, strip_noop_phvs(), 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 3803 of file partprune.c.

3806{
3807 if (IsA(expr, Const))
3808 {
3809 /* We can always determine the value of a constant */
3810 Const *con = (Const *) expr;
3811
3812 *value = con->constvalue;
3813 *isnull = con->constisnull;
3814 }
3815 else
3816 {
3817 ExprState *exprstate;
3819
3820 /*
3821 * We should never see a non-Const in a step unless the caller has
3822 * passed a valid ExprContext.
3823 */
3824 Assert(context->exprcontext != NULL);
3825
3826 exprstate = context->exprstates[stateidx];
3827 ectx = context->exprcontext;
3828 *value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
3829 }
3830}

References Assert, ExecEvalExprSwitchContext(), PartitionPruneContext::exprcontext, PartitionPruneContext::exprstates, fb(), 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 3457 of file partprune.c.

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

References Assert, bms_is_member(), elog, ERROR, fb(), 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(), OidIsValid, palloc_object, PARTITION_MAX_KEYS, PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, partkey_datum_from_expr(), PartitionPruneContext::partnatts, PartitionPruneContext::partsupfunc, PartitionPruneContext::ppccontext, PruneCxtStateIdx, result, 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 3605 of file partprune.c.

3608{
3610 bool firststep;
3611 ListCell *lc1;
3612
3613 /*
3614 * A combine step without any source steps is an indication to not perform
3615 * any partition pruning. Return all datum indexes in that case.
3616 */
3617 if (cstep->source_stepids == NIL)
3618 {
3619 PartitionBoundInfo boundinfo = context->boundinfo;
3620
3621 result->bound_offsets =
3622 bms_add_range(NULL, 0, boundinfo->nindexes - 1);
3623 result->scan_default = partition_bound_has_default(boundinfo);
3624 result->scan_null = partition_bound_accepts_nulls(boundinfo);
3625 return result;
3626 }
3627
3628 switch (cstep->combineOp)
3629 {
3631 foreach(lc1, cstep->source_stepids)
3632 {
3633 int step_id = lfirst_int(lc1);
3635
3636 /*
3637 * step_results[step_id] must contain a valid result, which is
3638 * confirmed by the fact that cstep's step_id is greater than
3639 * step_id and the fact that results of the individual steps
3640 * are evaluated in sequence of their step_ids.
3641 */
3642 if (step_id >= cstep->step.step_id)
3643 elog(ERROR, "invalid pruning combine step argument");
3644 step_result = step_results[step_id];
3646
3647 /* Record any additional datum indexes from this step */
3648 result->bound_offsets = bms_add_members(result->bound_offsets,
3649 step_result->bound_offsets);
3650
3651 /* Update whether to scan null and default partitions. */
3652 if (!result->scan_null)
3653 result->scan_null = step_result->scan_null;
3654 if (!result->scan_default)
3655 result->scan_default = step_result->scan_default;
3656 }
3657 break;
3658
3660 firststep = true;
3661 foreach(lc1, cstep->source_stepids)
3662 {
3663 int step_id = lfirst_int(lc1);
3665
3666 if (step_id >= cstep->step.step_id)
3667 elog(ERROR, "invalid pruning combine step argument");
3668 step_result = step_results[step_id];
3670
3671 if (firststep)
3672 {
3673 /* Copy step's result the first time. */
3674 result->bound_offsets =
3675 bms_copy(step_result->bound_offsets);
3676 result->scan_null = step_result->scan_null;
3677 result->scan_default = step_result->scan_default;
3678 firststep = false;
3679 }
3680 else
3681 {
3682 /* Record datum indexes common to both steps */
3683 result->bound_offsets =
3684 bms_int_members(result->bound_offsets,
3685 step_result->bound_offsets);
3686
3687 /* Update whether to scan null and default partitions. */
3688 if (result->scan_null)
3689 result->scan_null = step_result->scan_null;
3690 if (result->scan_default)
3691 result->scan_default = step_result->scan_default;
3692 }
3693 }
3694 break;
3695 }
3696
3697 return result;
3698}

References Assert, bms_add_members(), bms_add_range(), bms_copy(), bms_int_members(), PartitionPruneContext::boundinfo, elog, ERROR, fb(), lfirst_int, NIL, PartitionBoundInfoData::nindexes, palloc0_object, partition_bound_accepts_nulls, partition_bound_has_default, PARTPRUNE_COMBINE_INTERSECT, PARTPRUNE_COMBINE_UNION, and result.

Referenced by get_matching_partitions().

◆ prune_append_rel_partitions()

Bitmapset * prune_append_rel_partitions ( RelOptInfo rel)

Definition at line 780 of file partprune.c.

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

References Assert, RelOptInfo::baserestrictinfo, bms_add_range(), PartitionPruneContext::boundinfo, GeneratePruningStepsContext::contradictory, CurrentMemoryContext, enable_partition_pruning, PartitionPruneContext::exprcontext, PartitionPruneContext::exprstates, fb(), gen_partprune_steps(), get_matching_partitions(), list_length(), NIL, RelOptInfo::nparts, PartitionPruneContext::nparts, palloc0_array, 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 3388 of file partprune.c.

3389{
3391
3393
3394 return result;
3395}

References fb(), pull_exec_paramids_walker(), and result.

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

3399{
3400 if (node == NULL)
3401 return false;
3402 if (IsA(node, Param))
3403 {
3404 Param *param = (Param *) node;
3405
3406 if (param->paramkind == PARAM_EXEC)
3407 *context = bms_add_member(*context, param->paramid);
3408 return false;
3409 }
3411}

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

Referenced by pull_exec_paramids(), and pull_exec_paramids_walker().