From b39fe19609c4cdec4961455540cef50beebce33a Mon Sep 17 00:00:00 2001 From: amit Date: Tue, 22 Aug 2017 13:48:13 +0900 Subject: [PATCH 3/4] WIP: Implement get_partitions_for_keys() Disable constraint exclusion that occurs using internal partition constraints, so that it's apparent what the new partition-pruning code still needs to do to able to create a plan matching the plain the the traditional constraint exclusion based partition-pruning would result in. --- src/backend/catalog/partition.c | 210 ++++++++++++++++++++++++++++++++++- src/backend/optimizer/util/plancat.c | 4 + 2 files changed, 209 insertions(+), 5 deletions(-) diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 0133748234..9d4b7c1a7f 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -1391,8 +1391,6 @@ get_partition_dispatch_recurse(Relation rel, Relation parent, * datum that the query's bounding keys allow to be returned for the query. * Similarly, *max_datum_index. *null_partition_chosen returns whether * the null partition will be scanned. - * - * TODO: Implement. */ List * get_partitions_for_keys(Relation rel, @@ -1403,12 +1401,214 @@ get_partitions_for_keys(Relation rel, bool *null_partition_chosen, bool *default_partition_chosen) { + int i, + minoff, + maxoff; List *result = NIL; - int i; + PartitionKey partkey = RelationGetPartitionKey(rel); PartitionDesc partdesc = RelationGetPartitionDesc(rel); + PartitionBoundCmpArg arg; + bool is_equal, + scan_default = false; + int null_partition_idx = partdesc->boundinfo->null_index; - for (i = 0; i < partdesc->nparts; i++) - result = lappend_int(result, i); + *null_partition_chosen = false; + + /* + * 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 (keynullness[i] == IS_NULL) + { + if (null_partition_idx >= 0) + { + *null_partition_chosen = true; + result = list_make1_int(null_partition_idx); + } + else + result = NIL; + + return result; + } + } + + /* + * If query provides no quals, don't forget to scan the default partition. + */ + if (n_minkeys == 0 && n_maxkeys == 0) + scan_default = true; + + 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: + /* + * Found, but if the query may have asked us to exclude it. + */ + if (is_equal && !min_inclusive) + minoff++; + 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; + break; + } + } + else + minoff = 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: + /* + * Found, but if the query may have asked us to exclude it. + */ + if (is_equal && !max_inclusive) + maxoff--; + 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; + break; + } + } + else + maxoff = partdesc->boundinfo->ndatums - 1; + + *min_datum_index = minoff; + *max_datum_index = maxoff; + + switch (partkey->strategy) + { + case PARTITION_STRATEGY_LIST: + for (i = minoff; i <= maxoff; i++) + { + int partition_idx = partdesc->boundinfo->indexes[i]; + + /* + * 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, partition_idx); + } + + /* If no bounding keys exist, include the null partition too. */ + if (null_partition_idx >= 0 && + (keynullness[0] == -1 || keynullness[0] != IS_NOT_NULL)) + { + *null_partition_chosen = true; + result = list_append_unique_int(result, null_partition_idx); + } + + break; + + case PARTITION_STRATEGY_RANGE: + for (i = minoff; i <= maxoff; i++) + { + int partition_idx = partdesc->boundinfo->indexes[i]; + + /* + * If a valid partition exists for this range, add its + * index, if not, the default partition (if any) would be + * covering that range, so request to include the same. + */ + if (partition_idx >= 0) + result = lappend_int(result, partition_idx); + else + scan_default = true; + } + break; + } + + if (scan_default && partdesc->boundinfo->default_index >= 0) + result = lappend_int(result, partdesc->boundinfo->default_index); return result; } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 49578c0684..ef84dac7f2 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -1160,7 +1160,9 @@ get_relation_constraints(PlannerInfo *root, Index varno = rel->relid; Relation relation; TupleConstr *constr; +#ifdef USE_PARTITION_CONSTRAINT_FOR_PRUNING List *pcqual; +#endif /* * We assume the relation has already been safely locked. @@ -1246,6 +1248,7 @@ get_relation_constraints(PlannerInfo *root, } } +#ifdef USE_PARTITION_CONSTRAINT_FOR_PRUNING /* Append partition predicates, if any */ pcqual = RelationGetPartitionQual(relation); if (pcqual) @@ -1263,6 +1266,7 @@ get_relation_constraints(PlannerInfo *root, result = list_concat(result, pcqual); } +#endif heap_close(relation, NoLock); -- 2.11.0