From ba3ed61cdf744441fc1e70280525ee05dc6e1250 Mon Sep 17 00:00:00 2001 From: amit Date: Wed, 7 Nov 2018 14:09:51 +0900 Subject: [PATCH v2 2/3] Move out the PartitionBoundInfo creation code into a function --- src/backend/utils/cache/partcache.c | 955 +++++++++++++++++++----------------- 1 file changed, 498 insertions(+), 457 deletions(-) diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c index 399ef82193..342bb46a55 100644 --- a/src/backend/utils/cache/partcache.c +++ b/src/backend/utils/cache/partcache.c @@ -43,6 +43,9 @@ 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, void *arg); +static PartitionBoundInfo partition_bounds_create(List *boundspecs, + PartitionKey key, + int **mapping); /* @@ -269,9 +272,6 @@ RelationBuildPartitionDesc(Relation rel) nparts; PartitionKey key = RelationGetPartitionKey(rel); MemoryContext oldcxt; - int ndatums = 0; - int next_index = 0; - int default_index = -1; Oid *oids_orig; int *mapping; @@ -338,460 +338,8 @@ RelationBuildPartitionDesc(Relation rel) return; } - /* - * For each partitioning method, we first convert the partition bounds - * from their parser node representation to the internal representation, - * along with any additional preprocessing (such performing de-duplication - * on range bounds). For each bound, we remember its partition's position - * (0-based) in the original list, so that we can add it to the mapping - * array. - * - * Resulting bound datums are then added to the 'datums' array in - * PartitionBoundInfo. For each datum added, an integer indicating the - * canonical partition index is added to the 'indexes' array. - */ - boundinfo = (PartitionBoundInfoData *) - palloc0(sizeof(PartitionBoundInfoData)); - boundinfo->strategy = key->strategy; - boundinfo->null_index = -1; - boundinfo->default_index = -1; - mapping = (int *) palloc(sizeof(int) * nparts); - switch (key->strategy) - { - case PARTITION_STRATEGY_HASH: - { - PartitionHashBound **hbounds = NULL; - int greatest_modulus; - - ndatums = nparts; - hbounds = (PartitionHashBound **) - palloc(nparts * sizeof(PartitionHashBound *)); - - i = 0; - foreach(cell, boundspecs) - { - PartitionBoundSpec *spec = castNode(PartitionBoundSpec, - lfirst(cell)); - - if (spec->strategy != PARTITION_STRATEGY_HASH) - elog(ERROR, "invalid strategy in partition bound spec"); - - hbounds[i] = (PartitionHashBound *) - palloc(sizeof(PartitionHashBound)); - - hbounds[i]->modulus = spec->modulus; - hbounds[i]->remainder = spec->remainder; - hbounds[i]->index = i; - i++; - } - - /* Sort all the bounds in ascending order */ - qsort(hbounds, nparts, sizeof(PartitionHashBound *), - qsort_partition_hbound_cmp); - - /* Moduli are stored in ascending order */ - greatest_modulus = hbounds[ndatums - 1]->modulus; - - boundinfo->ndatums = ndatums; - boundinfo->datums = (Datum **) palloc0(ndatums * - sizeof(Datum *)); - boundinfo->indexes = (int *) palloc(greatest_modulus * - sizeof(int)); - - for (i = 0; i < greatest_modulus; i++) - boundinfo->indexes[i] = -1; - - /* - * For hash partitioning, there are as many datums (modulus and - * remainder pairs) as there are partitions. Indexes are - * simply values ranging from 0 to npart - 1. - */ - for (i = 0; i < nparts; i++) - { - int orig_index = hbounds[i]->index; - int modulus = hbounds[i]->modulus; - int remainder = hbounds[i]->remainder; - - boundinfo->datums[i] = (Datum *) palloc(2 * - sizeof(Datum)); - boundinfo->datums[i][0] = Int32GetDatum(modulus); - boundinfo->datums[i][1] = Int32GetDatum(remainder); - - while (remainder < greatest_modulus) - { - /* overlap? */ - Assert(boundinfo->indexes[remainder] == -1); - boundinfo->indexes[remainder] = i; - remainder += modulus; - } - - /* Add the mapping element. */ - mapping[i] = orig_index; - - pfree(hbounds[i]); - } - pfree(hbounds); - break; - } - - case PARTITION_STRATEGY_LIST: - { - PartitionListValue **all_values = NULL; - int null_index = -1; - List *non_null_values = NIL; - int *listpart_canon_idx; - - /* - * Create a unified list of non-null values across all - * partitions. - */ - i = 0; - foreach(cell, boundspecs) - { - PartitionBoundSpec *spec = castNode(PartitionBoundSpec, - lfirst(cell)); - ListCell *c; - - if (spec->strategy != PARTITION_STRATEGY_LIST) - elog(ERROR, "invalid strategy in partition bound spec"); - - /* - * Note the index of the partition bound spec for the - * default partition. There's no datum to add to the list - * of non-null datums for this partition. - */ - if (spec->is_default) - { - default_index = i; - i++; - continue; - } - - foreach(c, spec->listdatums) - { - Const *val = castNode(Const, lfirst(c)); - PartitionListValue *list_value = NULL; - - if (!val->constisnull) - { - list_value = (PartitionListValue *) - palloc0(sizeof(PartitionListValue)); - list_value->index = i; - list_value->value = val->constvalue; - } - else - { - /* - * Never put a null into the values array, flag - * instead for the code further down below where - * we construct the actual relcache struct. - */ - if (null_index != -1) - elog(ERROR, "found null more than once"); - null_index = i; - } - - if (list_value) - non_null_values = lappend(non_null_values, - list_value); - } - - i++; - } - - ndatums = list_length(non_null_values); - - /* - * Collect all list values in one array. Alongside the value, - * we also save the index of partition the value comes from. - */ - all_values = (PartitionListValue **) palloc(ndatums * - sizeof(PartitionListValue *)); - i = 0; - foreach(cell, non_null_values) - { - PartitionListValue *src = lfirst(cell); - - all_values[i] = (PartitionListValue *) - palloc(sizeof(PartitionListValue)); - all_values[i]->value = src->value; - all_values[i]->index = src->index; - i++; - } - - qsort_arg(all_values, ndatums, sizeof(PartitionListValue *), - qsort_partition_list_value_cmp, (void *) key); - - boundinfo->ndatums = ndatums; - boundinfo->datums = (Datum **) palloc0(ndatums * - sizeof(Datum *)); - boundinfo->indexes = (int *) palloc(ndatums * sizeof(int)); - - /* - * Copy values. Indexes are values ranging from 0 to - * npart - 1, assigned to each partition such that all datums - * of a given partition receive the same value. The value - * for a given partition is the index of that partition's - * smallest datum in the all_values[] array. We keep track - * of whether a given partition has been assigned an index - * yet using the 'listpart_canon_idx' mapping array. - * - * Initialize listpart_canon_idx elements to -1 to indicate - * that an index hasn't been assigned yet. - */ - listpart_canon_idx = (int *) palloc(sizeof(int) * nparts); - memset(listpart_canon_idx, -1, sizeof(int) * nparts); - for (i = 0; i < ndatums; i++) - { - int orig_index = all_values[i]->index; - - boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum)); - boundinfo->datums[i][0] = datumCopy(all_values[i]->value, - key->parttypbyval[0], - key->parttyplen[0]); - - /* - * Add the mapping element, if not already done for this - * partition. - */ - if (listpart_canon_idx[orig_index] == -1) - { - /* Add the mapping element. */ - mapping[next_index] = orig_index; - listpart_canon_idx[orig_index] = next_index++; - } - - boundinfo->indexes[i] = listpart_canon_idx[orig_index]; - } - - /* - * Set null_index, if any. - * - * It's possible that the null-accepting partition has not been - * assigned an index yet, which could happen if such partition - * accepts only null and hence not handled in the above loop - * which only looked at non-null values. - */ - if (null_index != -1) - { - Assert(null_index >= 0); - if (listpart_canon_idx[null_index] == -1) - { - /* Add the mapping element. */ - mapping[next_index] = null_index; - listpart_canon_idx[null_index] = next_index++; - } - boundinfo->null_index = listpart_canon_idx[null_index]; - } - - /* Set default_index, if any. */ - if (default_index != -1) - { - /* - * The default partition accepts any value not - * specified in the lists of other partitions, hence - * it should not have been assigned an index yet. - */ - Assert(default_index >= 0); - Assert(listpart_canon_idx[default_index] == -1); - /* Add the mapping element. */ - mapping[next_index] = default_index; - boundinfo->default_index = next_index++; - } - - /* All partition must now have a valid mapping */ - Assert(next_index == nparts); - break; - } - - case PARTITION_STRATEGY_RANGE: - { - PartitionRangeBound **rbounds = NULL; - PartitionRangeBound **all_bounds, - *prev; - int k; - - all_bounds = (PartitionRangeBound **) palloc0(2 * nparts * - sizeof(PartitionRangeBound *)); - - /* Create a unified list of range bounds across all the partitions. */ - i = ndatums = 0; - foreach(cell, boundspecs) - { - PartitionBoundSpec *spec = castNode(PartitionBoundSpec, - lfirst(cell)); - PartitionRangeBound *lower, - *upper; - - if (spec->strategy != PARTITION_STRATEGY_RANGE) - elog(ERROR, "invalid strategy in partition bound spec"); - - /* - * Note the index of the partition bound spec for the - * default partition. There's no datum to add to the - * allbounds array for this partition. - */ - if (spec->is_default) - { - default_index = i++; - continue; - } - - lower = make_one_partition_rbound(key, i, - spec->lowerdatums, - true); - upper = make_one_partition_rbound(key, i, - spec->upperdatums, - false); - all_bounds[ndatums++] = lower; - all_bounds[ndatums++] = upper; - i++; - } - - Assert(ndatums == nparts * 2 || - (default_index != -1 && ndatums == (nparts - 1) * 2)); - - /* Sort all the bounds in ascending order */ - qsort_arg(all_bounds, ndatums, - sizeof(PartitionRangeBound *), - qsort_partition_rbound_cmp, - (void *) key); - - /* - * De-duplicate: Save distinct bounds from 'all_bounds' into - * 'rbounds'. In many cases, lower bound of a partition - * matches exactly with the upper of the previous partition, - * in which case, we only store one. - */ - rbounds = (PartitionRangeBound **) - palloc(ndatums * sizeof(PartitionRangeBound *)); - k = 0; - prev = NULL; - for (i = 0; i < ndatums; i++) - { - PartitionRangeBound *cur = all_bounds[i]; - bool is_distinct = false; - int j; - - /* Is the current bound distinct from the previous one? */ - for (j = 0; j < key->partnatts; j++) - { - Datum cmpval; - - if (prev == NULL || cur->kind[j] != prev->kind[j]) - { - is_distinct = true; - break; - } - - /* - * If the bounds are both MINVALUE or MAXVALUE, stop - * now and treat them as equal, since any values after - * this point must be ignored. - */ - if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE) - break; - - cmpval = FunctionCall2Coll(&key->partsupfunc[j], - key->partcollation[j], - cur->datums[j], - prev->datums[j]); - if (DatumGetInt32(cmpval) != 0) - { - is_distinct = true; - break; - } - } - - /* - * Only if the bound is distinct save it into a temporary - * array i.e. rbounds which is later copied into boundinfo - * datums array. - */ - if (is_distinct) - rbounds[k++] = all_bounds[i]; - - prev = cur; - } - - /* Update ndatums to hold the count of distinct datums. */ - ndatums = k; - - /* - * Add datums to boundinfo. Indexes are values ranging from - * 0 to nparts - 1, assigned in that order to each partition's - * upper bound. For 'datums' elements that are lower bounds, - * there is -1 in 'indexes' array to signify that no partition - * exists for the values less than such a bound and greater - * than or equal to the previous upper bound. - */ - boundinfo->ndatums = ndatums; - boundinfo->datums = (Datum **) palloc0(ndatums * - sizeof(Datum *)); - boundinfo->kind = (PartitionRangeDatumKind **) - palloc(ndatums * - sizeof(PartitionRangeDatumKind *)); - /* - * For range partitioning, an additional value of -1 is stored - * as the last element. - */ - boundinfo->indexes = (int *) palloc((ndatums + 1) * - sizeof(int)); - - for (i = 0; i < ndatums; i++) - { - int j; - - boundinfo->datums[i] = (Datum *) palloc(key->partnatts * - sizeof(Datum)); - boundinfo->kind[i] = (PartitionRangeDatumKind *) - palloc(key->partnatts * - sizeof(PartitionRangeDatumKind)); - for (j = 0; j < key->partnatts; j++) - { - if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE) - boundinfo->datums[i][j] = - datumCopy(rbounds[i]->datums[j], - key->parttypbyval[j], - key->parttyplen[j]); - boundinfo->kind[i][j] = rbounds[i]->kind[j]; - } - - /* Assign -1 to lower bounds as described above. */ - if (rbounds[i]->lower) - boundinfo->indexes[i] = -1; - else - { - int orig_index = rbounds[i]->index; - - /* Add the mapping element. */ - mapping[next_index] = orig_index; - - boundinfo->indexes[i] = next_index++; - } - } - - /* Set default_index, if any. */ - if (default_index != -1) - { - Assert(default_index >= 0); - /* Add the mapping element. */ - mapping[next_index] = default_index; - boundinfo->default_index = next_index++; - } - boundinfo->indexes[i] = -1; - - /* All partition must now have a valid mapping */ - Assert(next_index == nparts); - break; - } - - default: - elog(ERROR, "unexpected partition strategy: %d", - (int) key->strategy); - break; - } - + /* First create the PartitionBoundInfo in caller's context. */ + boundinfo = partition_bounds_create(boundspecs, key, &mapping); oids_orig = (Oid *) palloc(sizeof(Oid) * partdesc->nparts); i = 0; foreach(cell, inhoids) @@ -993,3 +541,496 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg) key->partcollation, b1->datums, b1->kind, b1->lower, b2); } + +/* + * partition_bounds_create + * Build a PartitionBoundInfo struct from a list of PartitionBoundSpec + * nodes + * + * This function creates a PartitionBoundInfo and fills the values of its + * various members based on the input list. Importantly, 'datums' array will + * contain Datum representation of individual bounds (possibly after + * de-duplication as in case of range bounds), sorted in a canonical order + * defined by qsort_partition_* functions of respective partitioning methods. + * 'indexes' array will contain as many elements as there are bounds (specific + * exceptions to this rule are listed in the function body), which represent + * the 0-based canonical positions of partitions. + * + * Upon return from this function, *mapping is set to an array of + * list_length(boundspecs) / nparts elements, each of which maps the canonical + * index of a given partition to its 0-based position in the original list. + * + * Note: All the memory allocated by this function, including that of the + * the returned PartitionBoundInfo and its members is allocated in the context + * that was active when the function was called. + */ +static PartitionBoundInfo +partition_bounds_create(List *boundspecs, PartitionKey key, int **mapping) +{ + PartitionBoundInfo boundinfo; + ListCell *cell; + int i, + nparts; + int ndatums = 0; + int default_index = -1; + int next_index = 0; + + nparts = list_length(boundspecs); + Assert(nparts > 0); + + /* + * For each partitioning method, we first convert the partition bounds + * from their parser node representation to the internal representation, + * along with any additional preprocessing (such performing de-duplication + * on range bounds). For each bound, we remember its partition's position + * (0-based) in the original list, so that we can add it to the *mapping + * array. + * + * Resulting bound datums are then added to the 'datums' array in + * PartitionBoundInfo. For each datum added, an integer indicating the + * canonical partition index is added to the 'indexes' array. + */ + boundinfo = (PartitionBoundInfoData *) + palloc0(sizeof(PartitionBoundInfoData)); + boundinfo->strategy = key->strategy; + boundinfo->null_index = -1; + boundinfo->default_index = -1; + *mapping = (int *) palloc(sizeof(int) * nparts); + switch (key->strategy) + { + case PARTITION_STRATEGY_HASH: + { + PartitionHashBound **hbounds = NULL; + int greatest_modulus; + + ndatums = nparts; + hbounds = (PartitionHashBound **) + palloc(nparts * sizeof(PartitionHashBound *)); + + i = 0; + foreach(cell, boundspecs) + { + PartitionBoundSpec *spec = castNode(PartitionBoundSpec, + lfirst(cell)); + + if (spec->strategy != PARTITION_STRATEGY_HASH) + elog(ERROR, "invalid strategy in partition bound spec"); + + hbounds[i] = (PartitionHashBound *) + palloc(sizeof(PartitionHashBound)); + + hbounds[i]->modulus = spec->modulus; + hbounds[i]->remainder = spec->remainder; + hbounds[i]->index = i; + i++; + } + + /* Sort all the bounds in ascending order */ + qsort(hbounds, nparts, sizeof(PartitionHashBound *), + qsort_partition_hbound_cmp); + + /* Moduli are stored in ascending order */ + greatest_modulus = hbounds[ndatums - 1]->modulus; + + boundinfo->ndatums = ndatums; + boundinfo->datums = (Datum **) palloc0(ndatums * + sizeof(Datum *)); + boundinfo->indexes = (int *) palloc(greatest_modulus * + sizeof(int)); + + for (i = 0; i < greatest_modulus; i++) + boundinfo->indexes[i] = -1; + + /* + * For hash partitioning, there are as many datums (modulus and + * remainder pairs) as there are partitions. Indexes are + * simply values ranging from 0 to npart - 1. + */ + for (i = 0; i < nparts; i++) + { + int orig_index = hbounds[i]->index; + int modulus = hbounds[i]->modulus; + int remainder = hbounds[i]->remainder; + + boundinfo->datums[i] = (Datum *) palloc(2 * + sizeof(Datum)); + boundinfo->datums[i][0] = Int32GetDatum(modulus); + boundinfo->datums[i][1] = Int32GetDatum(remainder); + + while (remainder < greatest_modulus) + { + /* overlap? */ + Assert(boundinfo->indexes[remainder] == -1); + boundinfo->indexes[remainder] = i; + remainder += modulus; + } + + /* Add the mapping element. */ + (*mapping)[i] = orig_index; + + pfree(hbounds[i]); + } + pfree(hbounds); + break; + } + + case PARTITION_STRATEGY_LIST: + { + PartitionListValue **all_values = NULL; + int null_index = -1; + List *non_null_values = NIL; + int *listpart_canon_idx; + + /* + * Create a unified list of non-null values across all + * partitions. + */ + i = 0; + foreach(cell, boundspecs) + { + PartitionBoundSpec *spec = castNode(PartitionBoundSpec, + lfirst(cell)); + ListCell *c; + + if (spec->strategy != PARTITION_STRATEGY_LIST) + elog(ERROR, "invalid strategy in partition bound spec"); + + /* + * Note the index of the partition bound spec for the + * default partition. There's no datum to add to the list + * of non-null datums for this partition. + */ + if (spec->is_default) + { + default_index = i; + i++; + continue; + } + + foreach(c, spec->listdatums) + { + Const *val = castNode(Const, lfirst(c)); + PartitionListValue *list_value = NULL; + + if (!val->constisnull) + { + list_value = (PartitionListValue *) + palloc0(sizeof(PartitionListValue)); + list_value->index = i; + list_value->value = val->constvalue; + } + else + { + /* + * Never put a null into the values array, flag + * instead for the code further down below where + * we construct the actual relcache struct. + */ + if (null_index != -1) + elog(ERROR, "found null more than once"); + null_index = i; + } + + if (list_value) + non_null_values = lappend(non_null_values, + list_value); + } + + i++; + } + + ndatums = list_length(non_null_values); + + /* + * Collect all list values in one array. Alongside the value, + * we also save the index of partition the value comes from. + */ + all_values = (PartitionListValue **) palloc(ndatums * + sizeof(PartitionListValue *)); + i = 0; + foreach(cell, non_null_values) + { + PartitionListValue *src = lfirst(cell); + + all_values[i] = (PartitionListValue *) + palloc(sizeof(PartitionListValue)); + all_values[i]->value = src->value; + all_values[i]->index = src->index; + i++; + } + + qsort_arg(all_values, ndatums, sizeof(PartitionListValue *), + qsort_partition_list_value_cmp, (void *) key); + + boundinfo->ndatums = ndatums; + boundinfo->datums = (Datum **) palloc0(ndatums * + sizeof(Datum *)); + boundinfo->indexes = (int *) palloc(ndatums * sizeof(int)); + + /* + * Copy values. Indexes are values ranging from 0 to + * npart - 1, assigned to each partition such that all datums + * of a given partition receive the same value. The value + * for a given partition is the index of that partition's + * smallest datum in the all_values[] array. We keep track + * of whether a given partition has been assigned an index + * yet using the 'listpart_canon_idx' array. + * + * Initialize listpart_canon_idx elements to -1 to indicate + * that an index hasn't been assigned yet. + */ + listpart_canon_idx = (int *) palloc(sizeof(int) * nparts); + memset(listpart_canon_idx, -1, sizeof(int) * nparts); + for (i = 0; i < ndatums; i++) + { + int orig_index = all_values[i]->index; + + boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum)); + boundinfo->datums[i][0] = datumCopy(all_values[i]->value, + key->parttypbyval[0], + key->parttyplen[0]); + + /* + * Add the mapping element, if not already done for this + * partition. + */ + if (listpart_canon_idx[orig_index] == -1) + { + /* Add the mapping element. */ + (*mapping)[next_index] = orig_index; + listpart_canon_idx[orig_index] = next_index++; + } + + boundinfo->indexes[i] = listpart_canon_idx[orig_index]; + } + + /* + * Set null_index, if any. + * + * It's possible that the null-accepting partition has not been + * assigned an index yet, which could happen if such partition + * accepts only null and hence not handled in the above loop + * which only looked at non-null values. + */ + if (null_index != -1) + { + Assert(null_index >= 0); + if (listpart_canon_idx[null_index] == -1) + { + /* Add the mapping element. */ + (*mapping)[next_index] = null_index; + listpart_canon_idx[null_index] = next_index++; + } + boundinfo->null_index = listpart_canon_idx[null_index]; + } + + /* Set default_index, if any. */ + if (default_index != -1) + { + /* + * The default partition accepts any value not + * specified in the lists of other partitions, hence + * it should not have been assigned an index yet. + */ + Assert(default_index >= 0); + Assert(listpart_canon_idx[default_index] == -1); + /* Add the mapping element. */ + (*mapping)[next_index] = default_index; + boundinfo->default_index = next_index++; + } + + /* All partition must now have a valid mapping */ + Assert(next_index == nparts); + break; + } + + case PARTITION_STRATEGY_RANGE: + { + PartitionRangeBound **rbounds = NULL; + PartitionRangeBound **all_bounds, + *prev; + int k; + + all_bounds = (PartitionRangeBound **) palloc0(2 * nparts * + sizeof(PartitionRangeBound *)); + + /* Create a unified list of range bounds across all the partitions. */ + i = ndatums = 0; + foreach(cell, boundspecs) + { + PartitionBoundSpec *spec = castNode(PartitionBoundSpec, + lfirst(cell)); + PartitionRangeBound *lower, + *upper; + + if (spec->strategy != PARTITION_STRATEGY_RANGE) + elog(ERROR, "invalid strategy in partition bound spec"); + + /* + * Note the index of the partition bound spec for the + * default partition. There's no datum to add to the + * allbounds array for this partition. + */ + if (spec->is_default) + { + default_index = i++; + continue; + } + + lower = make_one_partition_rbound(key, i, + spec->lowerdatums, + true); + upper = make_one_partition_rbound(key, i, + spec->upperdatums, + false); + all_bounds[ndatums++] = lower; + all_bounds[ndatums++] = upper; + i++; + } + + Assert(ndatums == nparts * 2 || + (default_index != -1 && ndatums == (nparts - 1) * 2)); + + /* Sort all the bounds in ascending order */ + qsort_arg(all_bounds, ndatums, + sizeof(PartitionRangeBound *), + qsort_partition_rbound_cmp, + (void *) key); + + /* + * De-duplicate: Save distinct bounds from 'all_bounds' into + * 'rbounds'. In many cases, lower bound of a partition + * matches exactly with the upper of the previous partition, + * in which case, we only store one. + */ + rbounds = (PartitionRangeBound **) + palloc(ndatums * sizeof(PartitionRangeBound *)); + k = 0; + prev = NULL; + for (i = 0; i < ndatums; i++) + { + PartitionRangeBound *cur = all_bounds[i]; + bool is_distinct = false; + int j; + + /* Is the current bound distinct from the previous one? */ + for (j = 0; j < key->partnatts; j++) + { + Datum cmpval; + + if (prev == NULL || cur->kind[j] != prev->kind[j]) + { + is_distinct = true; + break; + } + + /* + * If the bounds are both MINVALUE or MAXVALUE, stop + * now and treat them as equal, since any values after + * this point must be ignored. + */ + if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE) + break; + + cmpval = FunctionCall2Coll(&key->partsupfunc[j], + key->partcollation[j], + cur->datums[j], + prev->datums[j]); + if (DatumGetInt32(cmpval) != 0) + { + is_distinct = true; + break; + } + } + + /* + * Only if the bound is distinct save it into a temporary + * array i.e. rbounds which is later copied into boundinfo + * datums array. + */ + if (is_distinct) + rbounds[k++] = all_bounds[i]; + + prev = cur; + } + + /* Update ndatums to hold the count of distinct datums. */ + ndatums = k; + + /* + * Add datums to boundinfo. Indexes are values ranging from + * 0 to nparts - 1, assigned in that order to each partition's + * upper bound. For 'datums' elements that are lower bounds, + * there is -1 in 'indexes' array to signify that no partition + * exists for the values less than such a bound and greater + * than or equal to the previous upper bound. + */ + boundinfo->ndatums = ndatums; + boundinfo->datums = (Datum **) palloc0(ndatums * + sizeof(Datum *)); + boundinfo->kind = (PartitionRangeDatumKind **) + palloc(ndatums * + sizeof(PartitionRangeDatumKind *)); + /* + * For range partitioning, an additional value of -1 is stored + * as the last element. + */ + boundinfo->indexes = (int *) palloc((ndatums + 1) * + sizeof(int)); + + for (i = 0; i < ndatums; i++) + { + int j; + + boundinfo->datums[i] = (Datum *) palloc(key->partnatts * + sizeof(Datum)); + boundinfo->kind[i] = (PartitionRangeDatumKind *) + palloc(key->partnatts * + sizeof(PartitionRangeDatumKind)); + for (j = 0; j < key->partnatts; j++) + { + if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE) + boundinfo->datums[i][j] = + datumCopy(rbounds[i]->datums[j], + key->parttypbyval[j], + key->parttyplen[j]); + boundinfo->kind[i][j] = rbounds[i]->kind[j]; + } + + /* Assign -1 to lower bounds as described above. */ + if (rbounds[i]->lower) + boundinfo->indexes[i] = -1; + else + { + int orig_index = rbounds[i]->index; + + /* Add the mapping element. */ + (*mapping)[next_index] = orig_index; + + boundinfo->indexes[i] = next_index++; + } + } + + /* Set default_index, if any. */ + if (default_index != -1) + { + Assert(default_index >= 0); + /* Add the mapping element. */ + (*mapping)[next_index] = default_index; + boundinfo->default_index = next_index++; + } + boundinfo->indexes[i] = -1; + + /* All partition must now have a valid mapping */ + Assert(next_index == nparts); + break; + } + + default: + elog(ERROR, "unexpected partition strategy: %d", + (int) key->strategy); + break; + } + + return boundinfo; +} -- 2.11.0