From a7f5054cd9697e5f183ed5d0b674fa7235800bcb Mon Sep 17 00:00:00 2001 From: amit Date: Tue, 22 Aug 2017 13:48:13 +0900 Subject: [PATCH 5/6] WIP: partition.c interface additions for partition-pruning Add new arguments to get_partitions_for_keys() to allow the caller to specify bounding scan keys, along with other information about the scan keys extracted from the query, such as NULL-ness of the keys, inclusive-ness, etc. Query planner side (the caller) still doesn't pass anything for the new arguments of get_partitions_for_keys. It will, once the logic to extract the relevant scan keys will be implemented in a subsequent commit. ...still using constraint exclusion... --- src/backend/catalog/partition.c | 203 ++++++++++++++++++++++++++++++++-- src/backend/optimizer/path/allpaths.c | 10 +- src/include/catalog/partition.h | 5 +- 3 files changed, 208 insertions(+), 10 deletions(-) diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index ed85eafc32..afb85cbc37 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -1181,17 +1181,204 @@ RelationGetPartitionDispatchInfo(Relation rel, * Returns the list of indexes (from pd->indexes) of the partitions that * will need to be scanned for the given scan keys. * - * TODO: add the interface to pass the query scan keys and the logic to look - * up partitions using those keys. + * minkeys represents the lower bound on the partition key of the records that + * the query will return, while maxkeys represents the upper bound. */ List * -get_partitions_for_keys(PartitionDispatch pd) +get_partitions_for_keys(PartitionDispatch pd, + bool *key_is_null, + Datum *minkeys, int n_minkeys, bool min_inclusive, + Datum *maxkeys, int n_maxkeys, bool max_inclusive) { - int i; + int i, + minoff, + minidx = -1, + maxoff, + maxidx = -1; List *result = NIL; + PartitionKey partkey = pd->key; + PartitionDesc partdesc = pd->partdesc; + PartitionBoundCmpArg arg; + bool is_equal; - for (i = 0; i < pd->partdesc->nparts; i++) - result = lappend_int(result, pd->indexes[i]); + /* + * Check if any of the scan keys are null. If so, return the only + * null-accepting partition if partdesc->boundinfo says there is one. + */ + for (i = 0; i < partkey->partnatts; i++) + { + if (key_is_null[i] && partdesc->boundinfo->null_index < 0) + { + minidx = INT_MAX; + maxidx = INT_MIN; + goto generate_partition_list; + } + else if (key_is_null[i]) + { + minidx = maxidx = partdesc->boundinfo->null_index; + goto generate_partition_list; + } + } + + if (n_minkeys > 0 && partdesc->nparts > 0) + { + memset(&arg, 0, sizeof(PartitionBoundCmpArg)); + arg.datums = minkeys; + arg.ndatums = n_minkeys; + minoff = partition_bound_bsearch(partkey, partdesc->boundinfo, + &arg, &is_equal); + + if (is_equal && arg.ndatums < partkey->partnatts) + { + int32 cmpval; + + is_equal = false; + + do + { + if (min_inclusive) + minoff -= 1; + else + minoff += 1; + if (minoff < 0 || + minoff >= partdesc->boundinfo->ndatums) + break; + cmpval = partition_bound_cmp(partkey, partdesc->boundinfo, + minoff, &arg); + } while (cmpval == 0); + } + + /* Interpret the result per partition strategy. */ + switch (partkey->strategy) + { + case PARTITION_STRATEGY_LIST: + if (is_equal) + minidx = partdesc->boundinfo->indexes[minoff]; + else + minidx = INT_MAX; + break; + + case PARTITION_STRATEGY_RANGE: + /* + * Records returned by the query will be > bounds[minoff], + * because min_scankey is >= bounds[minoff], that is, no + * records of the partition at minoff will be returned. Go + * to the next bound. + */ + if (minoff < partdesc->boundinfo->ndatums - 1) + minoff += 1; + + /* + * Make sure to skip a gap. + * Note: There are ndatums + 1 lots in the indexes array. + */ + if (partdesc->boundinfo->indexes[minoff] < 0 && + partdesc->boundinfo->indexes[minoff + 1] >= 0) + minoff += 1; + + /* + * Make sure we return a valid partition's index. It's + * possible that no valid partition exists, that is, all + * partition bounds were <= min_scankey. + */ + if (partdesc->boundinfo->indexes[minoff] < 0) + minidx = INT_MAX; + else + minidx = partdesc->boundinfo->indexes[minoff]; + break; + } + } + else + minidx = 0; + + if (n_maxkeys > 0 && partdesc->nparts > 0) + { + memset(&arg, 0, sizeof(PartitionBoundCmpArg)); + arg.datums = maxkeys; + arg.ndatums = n_maxkeys; + maxoff = partition_bound_bsearch(partkey, partdesc->boundinfo, + &arg, &is_equal); + if (is_equal && arg.ndatums < partkey->partnatts) + { + int32 cmpval; + + is_equal = false; + + do + { + if (max_inclusive) + maxoff += 1; + else + maxoff -= 1; + if (maxoff < 0 || + maxoff >= partdesc->boundinfo->ndatums) + break; + cmpval = partition_bound_cmp(partkey, partdesc->boundinfo, + maxoff, &arg); + } while (cmpval == 0); + + /* Back up if went too far. */ + if (max_inclusive) + maxoff -= 1; + } + + /* Interpret the result per partition strategy. */ + switch (partkey->strategy) + { + case PARTITION_STRATEGY_LIST: + if (is_equal) + maxidx = partdesc->boundinfo->indexes[maxoff]; + else + maxidx = INT_MIN; + break; + + case PARTITION_STRATEGY_RANGE: + /* + * Because bounds[maxoff] <= max_scankey, we may need to + * to consider the next partition as well, in addition to + * the partition at maxoff and earlier. + */ + if (!is_equal || max_inclusive) + maxoff += 1; + + /* Make sure to skip a gap. */ + if (partdesc->boundinfo->indexes[maxoff] < 0 && maxoff >= 1) + maxoff -= 1; + + /* + * Make sure we return a valid partition's index. It's + * possible that no valid partition exists, that is, all + * partition bounds were > max_scankey. + */ + if (partdesc->boundinfo->indexes[maxoff] < 0) + maxidx = INT_MIN; + else + maxidx = partdesc->boundinfo->indexes[maxoff]; + + break; + } + } + else + maxidx = partdesc->nparts - 1; + +generate_partition_list: + for (i = minidx; i <= maxidx; i++) + { + switch (partkey->strategy) + { + case PARTITION_STRATEGY_LIST: + /* + * Multiple values may belong to the same partition, so make + * sure we don't add the same partition index again. + */ + result = list_append_unique_int(result, pd->indexes[i]); + break; + + case PARTITION_STRATEGY_RANGE: + result = lappend_int(result, pd->indexes[i]); + break; + } + } return result; } @@ -2253,8 +2440,8 @@ partition_rbound_cmp(PartitionKey key, /* * partition_rbound_datum_cmp * - * Return whether range bound (specified in rb_datums, rb_kind, and rb_lower) - * is <, =, or > partition key of tuple (tuple_datums) + * Return whether range bound (specified in rb_datums, rb_kind) is <, =, or > + * partition key of tuple (tuple_datums) */ static int32 partition_rbound_datum_cmp(PartitionKey key, diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index c5c50e3b9d..a5e217674b 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -869,6 +869,9 @@ get_rel_partitions_recurse(RelOptInfo *rootrel, List *result = NIL, *my_live_partitions = NIL; ListCell *l; + bool keyisnull[PARTITION_MAX_KEYS]; + Datum minkeys[PARTITION_MAX_KEYS], + maxkeys[PARTITION_MAX_KEYS]; /* * Create a PartitionAppendInfo to map this table to the child tables @@ -885,9 +888,14 @@ get_rel_partitions_recurse(RelOptInfo *rootrel, * TODO: collect the keys by looking at the clauses in * rootrel->baserestrictinfo considering this table's partition keys. */ + memset(keyisnull, false, sizeof(keyisnull)); + memset(minkeys, 0, sizeof(minkeys)); + memset(maxkeys, 0, sizeof(maxkeys)); /* Ask partition.c which partitions it thinks match the keys. */ - indexes = get_partitions_for_keys(partinfo->pd); + indexes = get_partitions_for_keys(partinfo->pd, keyisnull, + minkeys, 0, false, + maxkeys, 0, false); /* Collect leaf partitions in the result list and recurse for others. */ foreach(l, indexes) diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index 20fc3a89db..fb15498f92 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -96,5 +96,8 @@ extern int get_partition_for_tuple(PartitionTupleRoutingInfo **ptrinfos, TupleTableSlot **failed_slot); /* Planner support stuff. */ -extern List *get_partitions_for_keys(PartitionDispatch pd); +extern List *get_partitions_for_keys(PartitionDispatch pd, + bool *key_is_null, + Datum *minkeys, int n_minkeys, bool min_inclusive, + Datum *maxkeys, int n_maxkeys, bool max_inclusive); #endif /* PARTITION_H */ -- 2.11.0