From 7bd8adf227aa280104af146f6f42650b8893779d Mon Sep 17 00:00:00 2001 From: amit Date: Tue, 22 Aug 2017 13:48:13 +0900 Subject: [PATCH 4/7] Implement get_partitions_from_clauses This now actually processes partclauses and classifies them into a set of keys that can be used to look up partitions in the partition descriptor, although there is still no support for the latter. --- src/backend/catalog/partition.c | 949 +++++++++++++++++++++++++++++++++++++++- src/include/catalog/partition.h | 3 +- 2 files changed, 947 insertions(+), 5 deletions(-) diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 5e601dd0a4..17b6a8a258 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -27,6 +27,8 @@ #include "catalog/pg_inherits.h" #include "catalog/pg_inherits_fn.h" #include "catalog/pg_opclass.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_opfamily.h" #include "catalog/pg_partitioned_table.h" #include "catalog/pg_type.h" #include "commands/tablecmds.h" @@ -37,6 +39,8 @@ #include "nodes/parsenodes.h" #include "optimizer/clauses.h" #include "optimizer/planmain.h" +#include "optimizer/planner.h" +#include "optimizer/predtest.h" #include "optimizer/prep.h" #include "optimizer/var.h" #include "rewrite/rewriteManip.h" @@ -111,6 +115,67 @@ typedef struct PartitionRangeBound bool lower; /* this is the lower (vs upper) bound */ } PartitionRangeBound; +/* + * Information about a clause matched with a partition key column kept to + * avoid recomputing the same in remove_redundant_clauses(). + */ +typedef struct +{ + OpExpr *op; + Expr *constarg; + + /* cached info. */ + bool valid_cache; /* Is the following information initialized? */ + int op_strategy; + Oid op_subtype; + FmgrInfo op_func; +} PartClause; + +/* + * PartScanKeyInfo + * Bounding scan keys to look up a table's partitions obtained from + * mutually-ANDed clauses containing partitioning-compatible operators + */ +typedef struct PartScanKeyInfo +{ + /* + * Constants constituting the *whole* partition key compared using + * partitioning-compatible equality operator(s). When n_eqkeys > 0, other + * keys (minkeys and maxkeys) are irrelevant. + */ + Datum eqkeys[PARTITION_MAX_KEYS]; + int n_eqkeys; + + /* + * Constants that constitute the lower bound on the partition key or a + * prefix thereof. The last of those constants is compared using > or >= + * operator compatible with partitioning, making this the lower bound in + * a range query. + */ + Datum minkeys[PARTITION_MAX_KEYS]; + int n_minkeys; + bool min_incl; + + /* + * Constants that constitute the upper bound on the partition key or a + * prefix thereof. The last of those constants is compared using < or <= + * operator compatible with partitioning, making this the upper bound in + * a range query. + */ + Datum maxkeys[PARTITION_MAX_KEYS]; + int n_maxkeys; + bool max_incl; + + /* + * Specifies the type of NullTest that was applied to each of the + * partition key columns or -1 if none was applied. Partitioning handles + * null partition keys specially depending on the partitioning method in + * use, so get_partitions_for_keys can return partitions according to + * the nullness condition for partition keys. + */ + NullTestType keynullness[PARTITION_MAX_KEYS]; +} PartScanKeyInfo; + static int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg); static int32 qsort_partition_rbound_cmp(const void *a, const void *b, @@ -150,6 +215,21 @@ static int partition_bound_bsearch(PartitionKey key, static void get_partition_dispatch_recurse(Relation rel, Relation parent, List **pds, List **leaf_part_oids); +static Bitmapset *get_partitions_from_clauses_guts(Relation relation, + List *clauses); +static int classify_partition_bounding_keys(Relation relation, List *clauses, + PartScanKeyInfo *keys, bool *constfalse, + List **or_clauses); +static void remove_redundant_clauses(PartitionKey partkey, + int partattoff, List *all_clauses, + List **result, bool *constfalse); +static bool partition_cmp_args(Oid partopfamily, Oid partopcintype, + PartClause *op, PartClause *leftarg, PartClause *rightarg, + bool *result); +static bool partkey_datum_from_expr(const Expr *expr, Datum *value); +static Bitmapset *get_partitions_for_keys(Relation rel, + PartScanKeyInfo *keys); + /* * RelationBuildPartitionDesc * Form rel's partition descriptor @@ -1422,7 +1502,7 @@ get_partition_dispatch_recurse(Relation rel, Relation parent, } /* - * get_partitions_using_clauses + * get_partitions_from_clauses * Determine the set of partitions of relation that will satisfy all * the clauses contained in partclauses * @@ -1432,16 +1512,877 @@ get_partition_dispatch_recurse(Relation rel, Relation parent, Bitmapset * get_partitions_from_clauses(Relation relation, List *partclauses) { - PartitionDesc partdesc = RelationGetPartitionDesc(relation); - Bitmapset *result = NULL; + Bitmapset *result; + List *partconstr = RelationGetPartitionQual(relation); + + if (partconstr) + partconstr = (List *) expression_planner((Expr *) partconstr); + partclauses = list_concat(partclauses, partconstr); + + result = get_partitions_from_clauses_guts(relation, partclauses); - result = bms_add_range(result, 0, partdesc->nparts - 1); return result; } /* Module-local functions */ /* + * get_partitions_from_clauses_guts + * Determine relation's partitions that satisfy *all* of the clauses + * in the list + * + * Return value is a Bitmapset containing the indexes of selected partitions. + */ +static Bitmapset * +get_partitions_from_clauses_guts(Relation relation, List *clauses) +{ + PartitionDesc partdesc = RelationGetPartitionDesc(relation); + Bitmapset *result = NULL; + PartScanKeyInfo keys; + int nkeys; + bool constfalse; + List *or_clauses; + ListCell *lc; + + nkeys = classify_partition_bounding_keys(relation, clauses, + &keys, &constfalse, + &or_clauses); + /* + * Only look up in the partition decriptor if the query provides + * constraints on the keys at all. + */ + if (nkeys > 0 && !constfalse) + result = get_partitions_for_keys(relation, &keys); + else if (!constfalse) + /* No constraints on the keys, so, return *all* partitions. */ + result = bms_add_range(result, 0, partdesc->nparts - 1); + + foreach(lc, or_clauses) + { + BoolExpr *or = (BoolExpr *) lfirst(lc); + ListCell *lc1; + Bitmapset *or_partset = NULL; + + foreach(lc1, or->args) + { + Expr *orarg = lfirst(lc1); + Bitmapset *arg_partset; + + arg_partset = get_partitions_from_clauses_guts(relation, + list_make1(orarg)); + + /* Combine partition sets obtained from mutually ORed clauses. */ + or_partset = bms_union(or_partset, arg_partset); + } + + /* Combine partition sets obtained from mutually ANDed clauses. */ + result = bms_intersect(result, or_partset); + } + + return result; +} + +/* + * classify_partition_bounding_keys + * Classify partition clauses into equal, min, max keys, along with any + * Nullness constraints and return that information in the output + * argument keys (number of keys is the return value) + * + * Clauses in the provided list are implicitly ANDed, each of which is known + * to match some partition key column. Map them to individual key columns + * and for each column, determine the equal bound or "best" min and max bound. + * For example, of a > 1, a > 2, and a >= 5, "5" is the best min bound for + * for the column a, which also happens to be an inclusive bound. + * + * For multi-column keys, an equal bound is returned only if all the columns + * are constrained by equality clauses. Min and maximum bounds could contain + * bound values for only a prefix of key columns. + * + * If the list contains a pseudo-constant clause, *constfalse is set to true + * and no keys are set. It is also set if we encounter mutually contradictory + * clauses in this function ourselves, for example, having both a > 1 and + * a = 0 in the list. + * + * All the OR clauses encountered in the list are added to *or_clauses. It's + * the responsibility of the caller to process the argument clauses of each of + * the OR clauses, which would involve recursively calling this function. + */ +static int +classify_partition_bounding_keys(Relation relation, List *clauses, + PartScanKeyInfo *keys, bool *constfalse, + List **or_clauses) +{ + PartitionKey partkey = RelationGetPartitionKey(relation); + int i; + ListCell *lc; + List *keyclauses_all[PARTITION_MAX_KEYS], + *keyclauses[PARTITION_MAX_KEYS]; + bool only_or_clauses = true; + Expr *eqkey_exprs[PARTITION_MAX_KEYS], + *minkey_exprs[PARTITION_MAX_KEYS], + *maxkey_exprs[PARTITION_MAX_KEYS]; + NullTestType keynullness[PARTITION_MAX_KEYS]; + bool need_next_eq, + need_next_min, + need_next_max, + eqkey_set[PARTITION_MAX_KEYS], + minkey_set[PARTITION_MAX_KEYS], + maxkey_set[PARTITION_MAX_KEYS], + min_incl, + max_incl; + int n_eqkeys = 0, + n_minkeys = 0, + n_maxkeys = 0, + n_keynullness = 0, + n_total = 0; + + *or_clauses = NIL; + *constfalse = false; + memset(keyclauses_all, 0, PARTITION_MAX_KEYS * sizeof(List *)); + /* -1 represents an invalid value of NullTestType. */ + memset(keynullness, -1, PARTITION_MAX_KEYS * sizeof(NullTestType)); + + foreach(lc, clauses) + { + Expr *clause; + ListCell *partexprs_item; + + if (IsA(lfirst(lc), RestrictInfo)) + { + RestrictInfo *rinfo = lfirst(lc); + + clause = rinfo->clause; + if (rinfo->pseudoconstant && + !DatumGetBool(((Const *) clause)->constvalue)) + { + *constfalse = true; + continue; + } + } + else + clause = (Expr *) lfirst(lc); + + /* Get the BoolExpr's out of the way.*/ + if (IsA(clause, BoolExpr)) + { + if (or_clause((Node *) clause)) + { + *or_clauses = lappend(*or_clauses, clause); + continue; + } + else if (and_clause((Node *) clause)) + { + clauses = list_concat(clauses, + list_copy(((BoolExpr *) clause)->args)); + continue; + } + /* Fall-through for a NOT clause, which is handled below. */ + } + + partexprs_item = list_head(partkey->partexprs); + for (i = 0; i < partkey->partnatts; i++) + { + Oid partopfamily = partkey->partopfamily[i], + partcoll = partkey->partcollation[i]; + AttrNumber partattno = partkey->partattrs[i]; + Expr *partexpr = NULL; + PartClause *pc; + + if (partattno == 0) + { + partexpr = lfirst(partexprs_item); + partexprs_item = lnext(partexprs_item); + } + + if (IsA(clause, OpExpr)) + { + OpExpr *opclause; + Expr *leftop, + *rightop; + + opclause = (OpExpr *) clause; + leftop = linitial(opclause->args); + if (IsA(leftop, RelabelType)) + leftop = ((RelabelType *) leftop)->arg; + rightop = lsecond(opclause->args); + /* Skip if leftop doesn't match this partition key column. */ + if ((!IsA(leftop, Var) || + ((Var *) leftop)->varattno != partattno) && + !equal(leftop, partexpr)) + continue; + + /* + * Deal with <> operators that the planner allows if it finds + * out that <>'s negator is indeed a valid partopfamily member. + * Make an equivalent OR expression and add to the *or_clauses + * list. That is, we convert a <> opclause into + * (leftop < rightop) OR (leftop > rightop). + */ + if (!op_in_opfamily(opclause->opno, partopfamily) && + (partkey->strategy == PARTITION_STRATEGY_RANGE || + partkey->strategy == PARTITION_STRATEGY_LIST)) + { + Expr *ltexpr, + *gtexpr; + Oid negator, + ltop, + gtop; + int strategy; + Oid lefttype, + righttype; + + negator = get_negator(opclause->opno); + get_op_opfamily_properties(negator, partopfamily, false, + &strategy, + &lefttype, &righttype); + if (strategy != BTEqualStrategyNumber) + elog(LOG, "unexpected negator of '<>' operator"); + ltop = get_opfamily_member(partopfamily, + lefttype, righttype, + BTLessStrategyNumber); + gtop = get_opfamily_member(partopfamily, + lefttype, righttype, + BTGreaterStrategyNumber); + ltexpr = make_opclause(ltop, BOOLOID, false, + (Expr *) leftop, (Expr *) rightop, + InvalidOid, partcoll); + gtexpr = make_opclause(gtop, BOOLOID, false, + (Expr *) leftop, (Expr *) rightop, + InvalidOid, partcoll); + *or_clauses = lappend(*or_clauses, + makeBoolExpr(OR_EXPR, + list_make2(ltexpr, gtexpr), + -1)); + continue; + } + + pc = palloc0(sizeof(PartClause)); + pc->op = opclause; + pc->constarg = rightop; + keyclauses_all[i] = lappend(keyclauses_all[i], pc); + + /* A strict operator implies NOT NULL argument. */ + if (keynullness[i] == -1) + { + keynullness[i] = IS_NOT_NULL; + n_keynullness++; + } + only_or_clauses = false; + } + else if (IsA(clause, ScalarArrayOpExpr)) + { + ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; + Oid saop_op = saop->opno; + Oid saop_opfuncid = saop->opfuncid; + Oid saop_coll = saop->inputcollid; + Node *leftop = (Node *) linitial(saop->args), + *rightop = (Node *) lsecond(saop->args); + List *elem_exprs, + *elem_clauses; + ListCell *lc1; + bool negated = false; + + /* + * Planner must have accepted this saop iff saop_op's negator + * was found to be a valid partopfamily member. + */ + if (!op_in_opfamily(saop_op, partopfamily)) + negated = true; + + /* + * First generate a list of Const nodes, one for each array + * element. + */ + elem_exprs = NIL; + if (IsA(rightop, Const)) + { + Const *arr = (Const *) lsecond(saop->args); + ArrayType *arrval = DatumGetArrayTypeP(arr->constvalue); + int16 elemlen; + bool elembyval; + char elemalign; + Datum *elem_values; + bool *elem_nulls; + int num_elems; + + get_typlenbyvalalign(ARR_ELEMTYPE(arrval), + &elemlen, &elembyval, &elemalign); + deconstruct_array(arrval, + ARR_ELEMTYPE(arrval), + elemlen, elembyval, elemalign, + &elem_values, &elem_nulls, + &num_elems); + for (i = 0; i < num_elems; i++) + { + if (!elem_nulls[i]) + elem_exprs = lappend(elem_exprs, + makeConst(ARR_ELEMTYPE(arrval), + -1, arr->constcollid, + elemlen, elem_values[i], + false, elembyval)); + else + elem_exprs = lappend(elem_exprs, + makeNullConst(ARR_ELEMTYPE(arrval), + -1, + arr->constcollid)); + } + } + else + { + ArrayExpr *arrexpr = castNode(ArrayExpr, rightop); + + elem_exprs = list_copy(arrexpr->elements); + } + + /* + * Now generate a list of clauses, one for each array element, + * of the form: saop_leftop saop_op elem_expr + */ + elem_clauses = NIL; + foreach(lc1, elem_exprs) + { + Const *rightop = castNode(Const, lfirst(lc1)); + Expr *elem_clause; + + if (rightop->constisnull) + { + NullTest *nulltest = makeNode(NullTest); + + nulltest->arg = (Expr *) leftop; + nulltest->nulltesttype = !negated ? IS_NULL + : IS_NOT_NULL; + nulltest->argisrow = false; + nulltest->location = -1; + elem_clause = (Expr *) nulltest; + } + else + { + OpExpr *opexpr = makeNode(OpExpr); + + opexpr->opno = saop_op; + opexpr->opfuncid = saop_opfuncid; + opexpr->opresulttype = BOOLOID; + opexpr->opretset = false; + opexpr->opcollid = InvalidOid; + opexpr->inputcollid = saop_coll; + opexpr->args = list_make2(leftop, rightop); + opexpr->location = -1; + elem_clause = (Expr *) opexpr; + } + + elem_clauses = lappend(elem_clauses, elem_clause); + } + + /* + * Build the OR clause if needed or add the clauses to the end + * of the list that's being processed currently. + */ + if (saop->useOr) + *or_clauses = lappend(*or_clauses, + makeBoolExpr(OR_EXPR, elem_clauses, + -1)); + else + clauses = list_concat(clauses, elem_clauses); + } + else if (IsA(clause, NullTest)) + { + NullTest *nulltest = (NullTest *) clause; + Expr *arg = nulltest->arg; + + /* Does leftop match with this partition key column? */ + if ((IsA(arg, Var) && partattno != 0 && + ((Var *) arg)->varattno == partattno) || + !equal(arg, partexpr)) + { + if (keynullness[i] == -1) + { + keynullness[i] = nulltest->nulltesttype; + n_keynullness++; + } + only_or_clauses = false; + } + } + /* + * Boolean conditions have a special shape, which would've been + * accepted if the partitioning opfamily accepts Boolean + * conditions. + */ + else if (IsBooleanOpfamily(partopfamily) && + (IsA(clause, BooleanTest) || + IsA(clause, Var) || + not_clause((Node *) clause))) + { + Expr *leftop, + *rightop; + + pc = palloc0(sizeof(PartClause)); + + if (IsA(clause, BooleanTest)) + { + BooleanTest *btest = (BooleanTest *) clause; + + leftop = btest->arg; + rightop = (btest->booltesttype == IS_TRUE || + btest->booltesttype == IS_NOT_FALSE) + ? (Expr *) makeBoolConst(true, false) + : (Expr *) makeBoolConst(false, false); + } + else + { + leftop = IsA(clause, Var) + ? (Expr *) clause + : (Expr *) get_notclausearg((Expr *) clause); + rightop = IsA(clause, Var) + ? (Expr *) makeBoolConst(true, false) + : (Expr *) makeBoolConst(false, false); + } + pc->op = (OpExpr *) make_opclause(BooleanEqualOperator, + BOOLOID, false, + leftop, rightop, + InvalidOid, InvalidOid); + pc->constarg = rightop; + keyclauses_all[i] = lappend(keyclauses_all[i], pc); + only_or_clauses = false; + } + } + } + + /* Return if no work to do below. */ + if (only_or_clauses || *constfalse) + return 0; + + /* + * Redundant key elimination using btree-semantics based tricks. + * + * Only list and range partitioning use btree operator semantics, so + * skip otherwise. Also, if there are expressions whose value is yet + * unknown, skip this step, because we need to compare actual values + * below. + */ + memset(keyclauses, 0, PARTITION_MAX_KEYS * sizeof(List *)); + if (partkey->strategy == PARTITION_STRATEGY_LIST || + partkey->strategy == PARTITION_STRATEGY_RANGE) + { + for (i = 0; i < partkey->partnatts; i++) + { + remove_redundant_clauses(partkey, i, + keyclauses_all[i], + &keyclauses[i], + constfalse); + if (*constfalse) + return 0; + } + } + + /* + * Now, generate the bounding tuples that can serve as equal, min, and + * max keys. An equal bounding key must contain all partition key + * columns, whereas a prefix of all partition key columns is admissible + * as min and max keys. + */ + memset(eqkey_exprs, 0, sizeof(eqkey_exprs)); + memset(minkey_exprs, 0, sizeof(minkey_exprs)); + memset(maxkey_exprs, 0, sizeof(maxkey_exprs)); + memset(eqkey_set, false, sizeof(eqkey_set)); + memset(minkey_set, false, sizeof(minkey_set)); + memset(maxkey_set, false, sizeof(maxkey_set)); + + need_next_eq = true; + need_next_min = true; + need_next_max = true; + for (i = 0; i < partkey->partnatts; i++) + { + /* + * If no scan key existed for the previous column, we are done. + */ + if (i > n_eqkeys) + need_next_eq = false; + + if (i > n_minkeys) + need_next_min = false; + + if (i > n_maxkeys) + need_next_max = false; + + foreach(lc, keyclauses[i]) + { + PartClause *clause = lfirst(lc); + int strategy = clause->op_strategy; + + switch (strategy) + { + case BTLessStrategyNumber: + case BTLessEqualStrategyNumber: + if (need_next_max) + { + maxkey_exprs[i] = clause->constarg; + if (!maxkey_set[i]) + n_maxkeys++; + maxkey_set[i] = true; + max_incl = (strategy == BTLessEqualStrategyNumber); + + if (strategy == BTLessStrategyNumber) + need_next_eq = need_next_max = false; + } + break; + + case BTGreaterStrategyNumber: + case BTGreaterEqualStrategyNumber: + if (need_next_min) + { + minkey_exprs[i] = clause->constarg; + if (!minkey_set[i]) + n_minkeys++; + minkey_set[i] = true; + min_incl = (strategy == BTGreaterEqualStrategyNumber); + + if (strategy == BTGreaterStrategyNumber) + need_next_eq = need_next_min = false; + } + break; + + case BTEqualStrategyNumber: + if (need_next_eq) + { + eqkey_exprs[i] = clause->constarg; + if (!eqkey_set[i]) + n_eqkeys++; + eqkey_set[i] = true; + } + + if (need_next_min) + { + minkey_exprs[i] = clause->constarg; + if (!minkey_set[i]) + n_minkeys++; + minkey_set[i] = true; + min_incl = true; + } + + if (need_next_max) + { + maxkey_exprs[i] = clause->constarg; + if (!maxkey_set[i]) + n_maxkeys++; + maxkey_set[i] = true; + max_incl = true; + } + break; + + /* + * Ideally, never get here, because 1. we don't support + * operators that are not btree operators and 2. clauses + * containing '<>' which are not listed in the btree operator + * families have already been handled by the higher-level + * code. + */ + default: + break; + } + } + } + + /* + * If we have equal keys for all the partition key columns, then mark + * their copies in minkeys and maxkeys as invalid, so that we perform + * partition lookup using only eqkeys. Don't pass as the equal key + * otherwise. + */ + if (n_eqkeys == partkey->partnatts) + n_minkeys = n_maxkeys = 0; + else + n_eqkeys = 0; + + /* Populate keys. */ + memset(keys, 0, sizeof(PartScanKeyInfo)); + if (n_eqkeys + n_minkeys + n_maxkeys + n_keynullness > 0) + { + Datum value; + int n_datums_resolved; + + n_datums_resolved = 0; + for (i = 0; i < n_eqkeys; i++) + { + if (partkey_datum_from_expr(eqkey_exprs[i], &value)) + { + keys->eqkeys[i] = value; + n_datums_resolved++; + } + } + keys->n_eqkeys = n_datums_resolved; + n_total += keys->n_eqkeys; + + n_datums_resolved = 0; + for (i = 0; i < n_minkeys; i++) + { + if (partkey_datum_from_expr(minkey_exprs[i], &value)) + { + keys->minkeys[i] = value; + n_datums_resolved++; + } + } + keys->n_minkeys = n_datums_resolved; + n_total += keys->n_minkeys; + keys->min_incl = min_incl; + + n_datums_resolved = 0; + for (i = 0; i < n_maxkeys; i++) + { + if (partkey_datum_from_expr(maxkey_exprs[i], &value)) + { + keys->maxkeys[i] = value; + n_datums_resolved++; + } + } + keys->n_maxkeys = n_datums_resolved; + n_total += keys->n_maxkeys; + keys->max_incl = max_incl; + + for (i = 0; i < partkey->partnatts; i++) + keys->keynullness[i] = keynullness[i]; + n_total += n_keynullness; + } + + return n_total; +} + +/* + * partkey_datum_from_expr + * Extract constant value from expr and set *datum to that value + */ +static bool +partkey_datum_from_expr(const Expr *expr, Datum *value) +{ + /* + * Add more expression types here as needed to support higher-level + * code. + */ + switch (nodeTag(expr)) + { + case T_Const: + *value = ((Const *) expr)->constvalue; + return true; + + default: + return false; + } + + Assert(false); /* don't ever get here */ + return false; +} + +static void +remove_redundant_clauses(PartitionKey partkey, + int partattoff, List *all_clauses, + List **result, bool *constfalse) +{ + Oid partopfamily = partkey->partopfamily[partattoff]; + Oid partopcintype = partkey->partopcintype[partattoff]; + PartClause *xform[BTMaxStrategyNumber]; + ListCell *lc; + int s; + bool test_result; + + *result = NIL; + + /* + * xform[s] points to the currently best scan key of strategy type s+1; it + * is NULL if we haven't yet found such a key for this attr. + */ + memset(xform, 0, sizeof(xform)); + foreach(lc, all_clauses) + { + PartClause *cur = lfirst(lc); + + if (!cur->valid_cache) + { + Oid lefttype; + get_op_opfamily_properties(cur->op->opno, partopfamily, false, + &cur->op_strategy, + &lefttype, + &cur->op_subtype); + fmgr_info(get_opcode(cur->op->opno), &cur->op_func); + cur->valid_cache = true; + } + + s = cur->op_strategy - 1; + /* Have we seen a clause of this strategy before?. */ + if (xform[s] == NULL) + { + /* nope, so assign. */ + xform[s] = cur; + } + else + { + /* yup, keep only the more restrictive key. */ + if (partition_cmp_args(partopfamily, partopcintype, + cur, cur, xform[s], + &test_result)) + { + if (test_result) + xform[s] = cur; + else if (s == BTEqualStrategyNumber - 1) + { + *constfalse = true; + return; + } + /* else the old key is more restrictive, keep around. */ + } + else + { + /* + * we couldn't determine which one is more restrictive. Keep + * the previous one in xform[s] and push this one directly + * to the output list. + */ + *result = lappend(*result, cur); + } + } + } + + /* Finished processing all clauses. Now compare across strategies. */ + if (xform[BTEqualStrategyNumber - 1]) + { + PartClause *eq = xform[BTEqualStrategyNumber - 1]; + + for (s = BTMaxStrategyNumber; --s >= 0;) + { + PartClause *chk = xform[s]; + + if (!chk || s == (BTEqualStrategyNumber - 1)) + continue; + + if (partition_cmp_args(partopfamily, partopcintype, chk, eq, chk, + &test_result)) + { + if (!test_result) + { + *constfalse = true; + return; + } + /* discard the redundant key. */ + xform[s] = NULL; + } + } + } + + /* try to keep only one of <, <= */ + if (xform[BTLessStrategyNumber - 1] && + xform[BTLessEqualStrategyNumber - 1]) + { + PartClause *lt = xform[BTLessStrategyNumber - 1], + *le = xform[BTLessEqualStrategyNumber - 1]; + + if (partition_cmp_args(partopfamily, partopcintype, le, lt, le, + &test_result)) + { + if (test_result) + xform[BTLessEqualStrategyNumber - 1] = NULL; + else + xform[BTLessStrategyNumber - 1] = NULL; + } + } + + /* try to keep only one of >, >= */ + if (xform[BTGreaterStrategyNumber - 1] && + xform[BTGreaterEqualStrategyNumber - 1]) + { + PartClause *gt = xform[BTGreaterStrategyNumber - 1], + *ge = xform[BTGreaterEqualStrategyNumber - 1]; + + if (partition_cmp_args(partopfamily, partopcintype, ge, gt, ge, + &test_result)) + { + if (test_result) + xform[BTGreaterEqualStrategyNumber - 1] = NULL; + else + xform[BTGreaterStrategyNumber - 1] = NULL; + } + } + + /* + * xform now contains "best" clauses for i'th partition key column + * for given btree strategy number. Copy them to keyclauses[i]. + */ + for (s = BTMaxStrategyNumber; --s >= 0;) + if (xform[s]) + *result = lappend(*result, xform[s]); +} + +static bool +partition_cmp_args(Oid partopfamily, Oid partopcintype, + PartClause *op, PartClause *leftarg, PartClause *rightarg, + bool *result) +{ + Datum leftarg_const, + rightarg_const; + + Assert(op->valid_cache && leftarg->valid_cache && rightarg->valid_cache); + if (!partkey_datum_from_expr(leftarg->constarg, &leftarg_const)) + return false; + if (!partkey_datum_from_expr(rightarg->constarg, &rightarg_const)) + return false; + + /* + * If the leftarg and rightarg clauses' constants are both of the type + * expected by "op" clause's operator, then compare then using the + * latter's comparison function. + */ + if (leftarg->op_subtype == partopcintype && + rightarg->op_subtype == op->op_subtype) + { + *result = DatumGetBool(FunctionCall2Coll(&op->op_func, + op->op->inputcollid, + leftarg_const, + rightarg_const)); + return true; + } + else + { + /* Otherwise, look one up in the partitioning operator family. */ + Oid cmp_op = get_opfamily_member(partopfamily, + leftarg->op_subtype, + rightarg->op_subtype, + op->op_strategy); + if (OidIsValid(cmp_op)) + { + *result = DatumGetBool(OidFunctionCall2Coll(get_opcode(cmp_op), + op->op->inputcollid, + leftarg_const, + rightarg_const)); + return true; + } + } + + /* Couldn't do the comparison. */ + *result = false; + return false; +} + +/* + * get_partitions_for_keys + * Returns the partitions that will need to be scanned for the given + * bounding keys + * + * Input: + * See the comments above the definition of PartScanKeyInfo to see what + * kind of information is received here. + * + * Outputs: + * Partition set satisfying the keys. + */ +static Bitmapset * +get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys) +{ + PartitionDesc partdesc = RelationGetPartitionDesc(rel); + Bitmapset *result = NULL; + + result = bms_add_range(result, 0, partdesc->nparts - 1); + + return result; +} + +/* * get_partition_operator * * Return oid of the operator of given strategy for a given partition key diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index 4a1ce92569..81c626fa4a 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -109,5 +109,6 @@ extern void check_default_allows_bound(Relation parent, Relation defaultRel, extern List *get_proposed_default_constraint(List *new_part_constaints); /* For partition-pruning */ -Bitmapset get_partitions_from_clauses(Relation relation, List *partclauses); +extern Bitmapset *get_partitions_from_clauses(Relation relation, + List *partclauses); #endif /* PARTITION_H */ -- 2.11.0