Refactor code creating PartitionBoundInfo
authorMichael Paquier <michael@paquier.xyz>
Wed, 14 Nov 2018 01:01:49 +0000 (10:01 +0900)
committerMichael Paquier <michael@paquier.xyz>
Wed, 14 Nov 2018 01:01:49 +0000 (10:01 +0900)
The code building PartitionBoundInfo based on the constituent partition
data read from catalogs has been located in partcache.c, with a specific
set of routines dedicated to bound types, like sorting or bound data
creation.  All this logic is moved to partbounds.c and relocates all the
bound-specific logistic into it, with partition_bounds_create() as
principal entry point.

Author: Amit Langote
Reviewed-by: Michael Paquier, Álvaro Herrera
Discussion: https://postgr.es/m/3f289da8-6d10-75fe-814a-635e8b191d43@lab.ntt.co.jp

src/backend/partitioning/partbounds.c
src/backend/utils/cache/partcache.c
src/include/partitioning/partbounds.h

index c94f73aadc14201674ae07ad681595bfaf7f182c..0b5e0dd89f5733b4aaa73949bbeb01792ae602e3 100644 (file)
 #include "utils/ruleutils.h"
 #include "utils/syscache.h"
 
+/*
+ * When qsort'ing partition bounds after reading from the catalog, each bound
+ * is represented with one of the following structs.
+ */
+
+/* One bound of a hash partition */
+typedef struct PartitionHashBound
+{
+       int                     modulus;
+       int                     remainder;
+       int                     index;
+} PartitionHashBound;
+
+/* One value coming from some (index'th) list partition */
+typedef struct PartitionListValue
+{
+       int                     index;
+       Datum           value;
+} PartitionListValue;
+
+/* One bound of a range partition */
+typedef struct PartitionRangeBound
+{
+       int                     index;
+       Datum      *datums;                     /* range bound datums */
+       PartitionRangeDatumKind *kind;  /* the kind of each datum */
+       bool            lower;                  /* this is the lower (vs upper) bound */
+} PartitionRangeBound;
+
+static int32 qsort_partition_hbound_cmp(const void *a, const void *b);
+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 create_hash_bounds(List *boundspecs,
+                                  PartitionKey key,
+                                  int **mapping);
+static PartitionBoundInfo create_list_bounds(List *boundspecs,
+                                  PartitionKey key,
+                                  int **mapping);
+static PartitionBoundInfo create_range_bounds(List *boundspecs,
+                                       PartitionKey key,
+                                       int **mapping);
+static PartitionRangeBound *make_one_partition_rbound(PartitionKey key, int index,
+                                                 List *datums, bool lower);
+static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2,
+                                        int remainder2);
+static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
+                                        Oid *partcollation, Datum *datums1,
+                                        PartitionRangeDatumKind *kind1, bool lower1,
+                                        PartitionRangeBound *b2);
+static int partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
+                                               Oid *partcollation,
+                                               PartitionBoundInfo boundinfo,
+                                               PartitionRangeBound *probe, bool *is_equal);
 static int     get_partition_bound_num_indexes(PartitionBoundInfo b);
 static Expr *make_partition_op_expr(PartitionKey key, int keynum,
                                           uint16 strategy, Expr *arg1, Expr *arg2);
@@ -92,6 +147,521 @@ get_qual_from_partbound(Relation rel, Relation parent,
        return my_qual;
 }
 
+/*
+ *     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) elements, each of which maps the original index of
+ * a partition to its canonical index.
+ *
+ * Note: The objects returned by this function are wholly allocated in the
+ * current memory context.
+ */
+PartitionBoundInfo
+partition_bounds_create(List *boundspecs, PartitionKey key, int **mapping)
+{
+       int                     nparts = list_length(boundspecs);
+       int                     i;
+
+       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 as de-duplicating range
+        * bounds).  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.
+        *
+        * For each bound, we remember its partition's position (0-based) in the
+        * original list to later map it to the canonical index.
+        */
+
+       /*
+        * Initialize mapping array with invalid values, this is filled within
+        * each sub-routine below depending on the bound type.
+        */
+       *mapping = (int *) palloc(sizeof(int) * nparts);
+       for (i = 0; i < nparts; i++)
+               (*mapping)[i] = -1;
+
+       switch (key->strategy)
+       {
+               case PARTITION_STRATEGY_HASH:
+                       return create_hash_bounds(boundspecs, key, mapping);
+
+               case PARTITION_STRATEGY_LIST:
+                       return create_list_bounds(boundspecs, key, mapping);
+
+               case PARTITION_STRATEGY_RANGE:
+                       return create_range_bounds(boundspecs, key, mapping);
+
+               default:
+                       elog(ERROR, "unexpected partition strategy: %d",
+                                (int) key->strategy);
+                       break;
+       }
+
+       Assert(false);
+       return NULL;                            /* keep compiler quiet */
+}
+
+/*
+ * create_hash_bounds
+ *             Create a PartitionBoundInfo for a hash partitioned table
+ */
+static PartitionBoundInfo
+create_hash_bounds(List *boundspecs, PartitionKey key, int **mapping)
+{
+       PartitionBoundInfo boundinfo;
+       PartitionHashBound **hbounds = NULL;
+       ListCell   *cell;
+       int                     i,
+                               nparts = list_length(boundspecs);
+       int                     ndatums = 0;
+       int                     greatest_modulus;
+
+       boundinfo = (PartitionBoundInfoData *)
+               palloc0(sizeof(PartitionBoundInfoData));
+       boundinfo->strategy = key->strategy;
+       /* No special hash partitions. */
+       boundinfo->null_index = -1;
+       boundinfo->default_index = -1;
+
+       ndatums = nparts;
+       hbounds = (PartitionHashBound **)
+               palloc(nparts * sizeof(PartitionHashBound *));
+
+       /* Convert from node to the internal representation */
+       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);
+
+       /* After sorting, moduli are now 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 (nparts - 1).
+        */
+       for (i = 0; i < nparts; i++)
+       {
+               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;
+               }
+
+               (*mapping)[hbounds[i]->index] = i;
+               pfree(hbounds[i]);
+       }
+       pfree(hbounds);
+
+       return boundinfo;
+}
+
+/*
+ * create_list_bounds
+ *             Create a PartitionBoundInfo for a list partitioned table
+ */
+static PartitionBoundInfo
+create_list_bounds(List *boundspecs, PartitionKey key, int **mapping)
+{
+       PartitionBoundInfo boundinfo;
+       PartitionListValue **all_values = NULL;
+       ListCell   *cell;
+       int                     i = 0;
+       int                     ndatums = 0;
+       int                     next_index = 0;
+       int                     default_index = -1;
+       int                     null_index = -1;
+       List       *non_null_values = NIL;
+
+       boundinfo = (PartitionBoundInfoData *)
+               palloc0(sizeof(PartitionBoundInfoData));
+       boundinfo->strategy = key->strategy;
+       /* Will be set correctly below. */
+       boundinfo->null_index = -1;
+       boundinfo->default_index = -1;
+
+       /* Create a unified list of non-null values across all partitions. */
+       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 on 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.  Canonical indexes are values ranging from 0 to (nparts -
+        * 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.
+        */
+       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]);
+
+               /* If the old index has no mapping, assign one */
+               if ((*mapping)[orig_index] == -1)
+                       (*mapping)[orig_index] = next_index++;
+
+               boundinfo->indexes[i] = (*mapping)[orig_index];
+       }
+
+       /*
+        * Set the canonical value for null_index, if any.
+        *
+        * It is 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 ((*mapping)[null_index] == -1)
+                       (*mapping)[null_index] = next_index++;
+               boundinfo->null_index = (*mapping)[null_index];
+       }
+
+       /* Set the canonical value for 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 get mapped index while
+                * assigning those for non-null datums.
+                */
+               Assert(default_index >= 0);
+               Assert((*mapping)[default_index] == -1);
+               (*mapping)[default_index] = next_index++;
+               boundinfo->default_index = (*mapping)[default_index];
+       }
+
+       /* All partition must now have been assigned canonical indexes. */
+       Assert(next_index == list_length(boundspecs));
+       return boundinfo;
+}
+
+/*
+ * create_range_bounds
+ *             Create a PartitionBoundInfo for a range partitioned table
+ */
+static PartitionBoundInfo
+create_range_bounds(List *boundspecs, PartitionKey key, int **mapping)
+{
+       PartitionBoundInfo boundinfo;
+       PartitionRangeBound **rbounds = NULL;
+       PartitionRangeBound **all_bounds,
+                          *prev;
+       ListCell   *cell;
+       int                     i,
+                               k,
+                               nparts = list_length(boundspecs);
+       int                     ndatums = 0;
+       int                     default_index = -1;
+       int                     next_index = 0;
+
+       boundinfo = (PartitionBoundInfoData *)
+               palloc0(sizeof(PartitionBoundInfoData));
+       boundinfo->strategy = key->strategy;
+       /* There is no special null-accepting range partition. */
+       boundinfo->null_index = -1;
+       /* Will be set correctly below. */
+       boundinfo->default_index = -1;
+
+       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 all_bounds 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);
+
+       /* Save distinct bounds from all_bounds into rbounds. */
+       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.  Canonical 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 the
+        * '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];
+               }
+
+               /*
+                * There is no mapping for invalid indexes.
+                *
+                * Any lower bounds in the rbounds array have invalid indexes
+                * assigned, because the values between the previous bound (if there
+                * is one) and this (lower) bound are not part of the range of any
+                * existing partition.
+                */
+               if (rbounds[i]->lower)
+                       boundinfo->indexes[i] = -1;
+               else
+               {
+                       int                     orig_index = rbounds[i]->index;
+
+                       /* If the old index has no mapping, assign one */
+                       if ((*mapping)[orig_index] == -1)
+                               (*mapping)[orig_index] = next_index++;
+
+                       boundinfo->indexes[i] = (*mapping)[orig_index];
+               }
+       }
+
+       /* Set the canonical value for default_index, if any. */
+       if (default_index != -1)
+       {
+               Assert(default_index >= 0 && (*mapping)[default_index] == -1);
+               (*mapping)[default_index] = next_index++;
+               boundinfo->default_index = (*mapping)[default_index];
+       }
+
+       /* The extra -1 element. */
+       Assert(i == ndatums);
+       boundinfo->indexes[i] = -1;
+
+       /* All partition must now have been assigned canonical indexes. */
+       Assert(next_index == nparts);
+       return boundinfo;
+}
+
 /*
  * Are two partition bound collections logically equal?
  *
@@ -763,7 +1333,7 @@ get_hash_partition_greatest_modulus(PartitionBoundInfo bound)
  * and a flag telling whether the bound is lower or not.  Made into a function
  * because there are multiple sites that want to use this facility.
  */
-PartitionRangeBound *
+static PartitionRangeBound *
 make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
 {
        PartitionRangeBound *bound;
@@ -819,7 +1389,7 @@ make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
  * structure, which only stores the upper bound of a common boundary between
  * two contiguous partitions.
  */
-int32
+static int32
 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
                                         Oid *partcollation,
                                         Datum *datums1, PartitionRangeDatumKind *kind1,
@@ -914,7 +1484,7 @@ partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation,
  *
  * Compares modulus first, then remainder if modulus is equal.
  */
-int32
+static int32
 partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
 {
        if (modulus1 < modulus2)
@@ -977,7 +1547,7 @@ partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation,
  * *is_equal is set to true if the range bound at the returned index is equal
  * to the input range bound
  */
-int
+static int
 partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
                                                Oid *partcollation,
                                                PartitionBoundInfo boundinfo,
@@ -1101,6 +1671,55 @@ partition_hash_bsearch(PartitionBoundInfo boundinfo,
        return lo;
 }
 
+/*
+ * qsort_partition_hbound_cmp
+ *
+ * Hash bounds are sorted by modulus, then by remainder.
+ */
+static int32
+qsort_partition_hbound_cmp(const void *a, const void *b)
+{
+       PartitionHashBound *h1 = (*(PartitionHashBound *const *) a);
+       PartitionHashBound *h2 = (*(PartitionHashBound *const *) b);
+
+       return partition_hbound_cmp(h1->modulus, h1->remainder,
+                                                               h2->modulus, h2->remainder);
+}
+
+/*
+ * qsort_partition_list_value_cmp
+ *
+ * Compare two list partition bound datums.
+ */
+static int32
+qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
+{
+       Datum           val1 = (*(const PartitionListValue **) a)->value,
+                               val2 = (*(const PartitionListValue **) b)->value;
+       PartitionKey key = (PartitionKey) arg;
+
+       return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
+                                                                                  key->partcollation[0],
+                                                                                  val1, val2));
+}
+
+/*
+ * qsort_partition_rbound_cmp
+ *
+ * Used when sorting range bounds across all range partitions.
+ */
+static int32
+qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
+{
+       PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
+       PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
+       PartitionKey key = (PartitionKey) arg;
+
+       return partition_rbound_cmp(key->partnatts, key->partsupfunc,
+                                                               key->partcollation, b1->datums, b1->kind,
+                                                               b1->lower, b2);
+}
+
 /*
  * get_partition_bound_num_indexes
  *
index 5757301d0547ceb6017956771b38b1531496169f..07653f312bbcd0ec03d39e1f53567c3042e41ca9 100644 (file)
 
 
 static List *generate_partition_qual(Relation rel);
-static int32 qsort_partition_hbound_cmp(const void *a, const void *b);
-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);
-
 
 /*
  * RelationBuildPartitionKey
@@ -260,36 +254,22 @@ RelationBuildPartitionKey(Relation relation)
 void
 RelationBuildPartitionDesc(Relation rel)
 {
-       List       *inhoids,
-                          *partoids;
-       Oid                *oids = NULL;
+       PartitionDesc partdesc;
+       PartitionBoundInfo boundinfo;
+       List       *inhoids;
        List       *boundspecs = NIL;
        ListCell   *cell;
        int                     i,
                                nparts;
        PartitionKey key = RelationGetPartitionKey(rel);
-       PartitionDesc result;
        MemoryContext oldcxt;
-
-       int                     ndatums = 0;
-       int                     default_index = -1;
-
-       /* Hash partitioning specific */
-       PartitionHashBound **hbounds = NULL;
-
-       /* List partitioning specific */
-       PartitionListValue **all_values = NULL;
-       int                     null_index = -1;
-
-       /* Range partitioning specific */
-       PartitionRangeBound **rbounds = NULL;
+       Oid                *oids_orig;
+       int                *mapping;
 
        /* Get partition oids from pg_inherits */
        inhoids = find_inheritance_children(RelationGetRelid(rel), NoLock);
 
        /* Collect bound spec nodes in a list */
-       i = 0;
-       partoids = NIL;
        foreach(cell, inhoids)
        {
                Oid                     inhrelid = lfirst_oid(cell);
@@ -325,245 +305,10 @@ RelationBuildPartitionDesc(Relation rel)
                }
 
                boundspecs = lappend(boundspecs, boundspec);
-               partoids = lappend_oid(partoids, inhrelid);
                ReleaseSysCache(tuple);
        }
 
-       nparts = list_length(partoids);
-
-       if (nparts > 0)
-       {
-               oids = (Oid *) palloc(nparts * sizeof(Oid));
-               i = 0;
-               foreach(cell, partoids)
-                       oids[i++] = lfirst_oid(cell);
-
-               /* Convert from node to the internal representation */
-               if (key->strategy == PARTITION_STRATEGY_HASH)
-               {
-                       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);
-               }
-               else if (key->strategy == PARTITION_STRATEGY_LIST)
-               {
-                       List       *non_null_values = NIL;
-
-                       /*
-                        * Create a unified list of non-null values across all partitions.
-                        */
-                       i = 0;
-                       null_index = -1;
-                       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);
-               }
-               else if (key->strategy == PARTITION_STRATEGY_RANGE)
-               {
-                       int                     k;
-                       PartitionRangeBound **all_bounds,
-                                          *prev;
-
-                       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);
-
-                       /* Save distinct bounds from all_bounds into rbounds. */
-                       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;
-               }
-               else
-                       elog(ERROR, "unexpected partition strategy: %d",
-                                (int) key->strategy);
-       }
+       nparts = list_length(boundspecs);
 
        /* Now build the actual relcache partition descriptor */
        rel->rd_pdcxt = AllocSetContextCreate(CacheMemoryContext,
@@ -572,210 +317,41 @@ RelationBuildPartitionDesc(Relation rel)
        MemoryContextCopyAndSetIdentifier(rel->rd_pdcxt, RelationGetRelationName(rel));
 
        oldcxt = MemoryContextSwitchTo(rel->rd_pdcxt);
+       partdesc = (PartitionDescData *) palloc0(sizeof(PartitionDescData));
+       partdesc->nparts = nparts;
+       /* oids and boundinfo are allocated below. */
+
+       MemoryContextSwitchTo(oldcxt);
 
-       result = (PartitionDescData *) palloc0(sizeof(PartitionDescData));
-       result->nparts = nparts;
-       if (nparts > 0)
+       if (nparts == 0)
        {
-               PartitionBoundInfo boundinfo;
-               int                *mapping;
-               int                     next_index = 0;
-
-               result->oids = (Oid *) palloc0(nparts * sizeof(Oid));
-
-               boundinfo = (PartitionBoundInfoData *)
-                       palloc0(sizeof(PartitionBoundInfoData));
-               boundinfo->strategy = key->strategy;
-               boundinfo->default_index = -1;
-               boundinfo->ndatums = ndatums;
-               boundinfo->null_index = -1;
-               boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
-
-               /* Initialize mapping array with invalid values */
-               mapping = (int *) palloc(sizeof(int) * nparts);
-               for (i = 0; i < nparts; i++)
-                       mapping[i] = -1;
-
-               switch (key->strategy)
-               {
-                       case PARTITION_STRATEGY_HASH:
-                               {
-                                       /* Moduli are stored in ascending order */
-                                       int                     greatest_modulus = hbounds[ndatums - 1]->modulus;
-
-                                       boundinfo->indexes = (int *) palloc(greatest_modulus *
-                                                                                                               sizeof(int));
-
-                                       for (i = 0; i < greatest_modulus; i++)
-                                               boundinfo->indexes[i] = -1;
-
-                                       for (i = 0; i < nparts; i++)
-                                       {
-                                               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;
-                                               }
-
-                                               mapping[hbounds[i]->index] = i;
-                                               pfree(hbounds[i]);
-                                       }
-                                       pfree(hbounds);
-                                       break;
-                               }
-
-                       case PARTITION_STRATEGY_LIST:
-                               {
-                                       boundinfo->indexes = (int *) palloc(ndatums * sizeof(int));
-
-                                       /*
-                                        * Copy values.  Indexes of individual values are mapped
-                                        * to canonical values so that they match for any two list
-                                        * partitioned tables with same number of partitions and
-                                        * same lists per partition.  One way to canonicalize is
-                                        * to assign the index in all_values[] of the smallest
-                                        * value of each partition, as the index of all of the
-                                        * partition's values.
-                                        */
-                                       for (i = 0; i < ndatums; i++)
-                                       {
-                                               boundinfo->datums[i] = (Datum *) palloc(sizeof(Datum));
-                                               boundinfo->datums[i][0] = datumCopy(all_values[i]->value,
-                                                                                                                       key->parttypbyval[0],
-                                                                                                                       key->parttyplen[0]);
-
-                                               /* If the old index has no mapping, assign one */
-                                               if (mapping[all_values[i]->index] == -1)
-                                                       mapping[all_values[i]->index] = next_index++;
-
-                                               boundinfo->indexes[i] = mapping[all_values[i]->index];
-                                       }
-
-                                       /*
-                                        * If null-accepting partition has no mapped index yet,
-                                        * assign one.  This could happen if such partition
-                                        * accepts only null and hence not covered in the above
-                                        * loop which only handled non-null values.
-                                        */
-                                       if (null_index != -1)
-                                       {
-                                               Assert(null_index >= 0);
-                                               if (mapping[null_index] == -1)
-                                                       mapping[null_index] = next_index++;
-                                               boundinfo->null_index = mapping[null_index];
-                                       }
-
-                                       /* Assign mapped index for the default partition. */
-                                       if (default_index != -1)
-                                       {
-                                               /*
-                                                * The default partition accepts any value not
-                                                * specified in the lists of other partitions, hence
-                                                * it should not get mapped index while assigning
-                                                * those for non-null datums.
-                                                */
-                                               Assert(default_index >= 0 &&
-                                                          mapping[default_index] == -1);
-                                               mapping[default_index] = next_index++;
-                                               boundinfo->default_index = mapping[default_index];
-                                       }
-
-                                       /* All partition must now have a valid mapping */
-                                       Assert(next_index == nparts);
-                                       break;
-                               }
-
-                       case PARTITION_STRATEGY_RANGE:
-                               {
-                                       boundinfo->kind = (PartitionRangeDatumKind **)
-                                               palloc(ndatums *
-                                                          sizeof(PartitionRangeDatumKind *));
-                                       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];
-                                               }
-
-                                               /*
-                                                * There is no mapping for invalid indexes.
-                                                *
-                                                * Any lower bounds in the rbounds array have invalid
-                                                * indexes assigned, because the values between the
-                                                * previous bound (if there is one) and this (lower)
-                                                * bound are not part of the range of any existing
-                                                * partition.
-                                                */
-                                               if (rbounds[i]->lower)
-                                                       boundinfo->indexes[i] = -1;
-                                               else
-                                               {
-                                                       int                     orig_index = rbounds[i]->index;
-
-                                                       /* If the old index has no mapping, assign one */
-                                                       if (mapping[orig_index] == -1)
-                                                               mapping[orig_index] = next_index++;
-
-                                                       boundinfo->indexes[i] = mapping[orig_index];
-                                               }
-                                       }
-
-                                       /* Assign mapped index for the default partition. */
-                                       if (default_index != -1)
-                                       {
-                                               Assert(default_index >= 0 && mapping[default_index] == -1);
-                                               mapping[default_index] = next_index++;
-                                               boundinfo->default_index = mapping[default_index];
-                                       }
-                                       boundinfo->indexes[i] = -1;
-                                       break;
-                               }
-
-                       default:
-                               elog(ERROR, "unexpected partition strategy: %d",
-                                        (int) key->strategy);
-               }
+               rel->rd_partdesc = partdesc;
+               return;
+       }
 
-               result->boundinfo = boundinfo;
+       /* First create PartitionBoundInfo */
+       boundinfo = partition_bounds_create(boundspecs, key, &mapping);
+       oids_orig = (Oid *) palloc(sizeof(Oid) * partdesc->nparts);
+       i = 0;
+       foreach(cell, inhoids)
+               oids_orig[i++] = lfirst_oid(cell);
 
-               /*
-                * Now assign OIDs from the original array into mapped indexes of the
-                * result array.  Order of OIDs in the former is defined by the
-                * catalog scan that retrieved them, whereas that in the latter is
-                * defined by canonicalized representation of the partition bounds.
-                */
-               for (i = 0; i < nparts; i++)
-                       result->oids[mapping[i]] = oids[i];
-               pfree(mapping);
-       }
+       /* Now copy boundinfo and oids into partdesc. */
+       oldcxt = MemoryContextSwitchTo(rel->rd_pdcxt);
+       partdesc->boundinfo = partition_bounds_copy(boundinfo, key);
+       partdesc->oids = (Oid *) palloc(partdesc->nparts * sizeof(Oid));
 
+       /*
+        * Now assign OIDs from the original array into mapped indexes of the
+        * result array.  Order of OIDs in the former is defined by the catalog
+        * scan that retrieved them, whereas that in the latter is defined by
+        * canonicalized representation of the partition bounds.
+        */
+       for (i = 0; i < partdesc->nparts; i++)
+               partdesc->oids[mapping[i]] = oids_orig[i];
        MemoryContextSwitchTo(oldcxt);
-       rel->rd_partdesc = result;
+
+       rel->rd_partdesc = partdesc;
 }
 
 /*
@@ -917,48 +493,3 @@ generate_partition_qual(Relation rel)
 
        return result;
 }
-
-/*
- * qsort_partition_hbound_cmp
- *
- * We sort hash bounds by modulus, then by remainder.
- */
-static int32
-qsort_partition_hbound_cmp(const void *a, const void *b)
-{
-       PartitionHashBound *h1 = (*(PartitionHashBound *const *) a);
-       PartitionHashBound *h2 = (*(PartitionHashBound *const *) b);
-
-       return partition_hbound_cmp(h1->modulus, h1->remainder,
-                                                               h2->modulus, h2->remainder);
-}
-
-/*
- * qsort_partition_list_value_cmp
- *
- * Compare two list partition bound datums
- */
-static int32
-qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
-{
-       Datum           val1 = (*(const PartitionListValue **) a)->value,
-                               val2 = (*(const PartitionListValue **) b)->value;
-       PartitionKey key = (PartitionKey) arg;
-
-       return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
-                                                                                  key->partcollation[0],
-                                                                                  val1, val2));
-}
-
-/* Used when sorting range bounds across all range partitions */
-static int32
-qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
-{
-       PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
-       PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
-       PartitionKey key = (PartitionKey) arg;
-
-       return partition_rbound_cmp(key->partnatts, key->partsupfunc,
-                                                               key->partcollation, b1->datums, b1->kind,
-                                                               b1->lower, b2);
-}
index c7535e32fc861d191c2c7fdba9cdc86197dd3639..7a697d1c0a719cb123c03357c33f86276db07326 100644 (file)
@@ -75,40 +75,14 @@ typedef struct PartitionBoundInfoData
 #define partition_bound_accepts_nulls(bi) ((bi)->null_index != -1)
 #define partition_bound_has_default(bi) ((bi)->default_index != -1)
 
-/*
- * When qsort'ing partition bounds after reading from the catalog, each bound
- * is represented with one of the following structs.
- */
-
-/* One bound of a hash partition */
-typedef struct PartitionHashBound
-{
-       int                     modulus;
-       int                     remainder;
-       int                     index;
-} PartitionHashBound;
-
-/* One value coming from some (index'th) list partition */
-typedef struct PartitionListValue
-{
-       int                     index;
-       Datum           value;
-} PartitionListValue;
-
-/* One bound of a range partition */
-typedef struct PartitionRangeBound
-{
-       int                     index;
-       Datum      *datums;                     /* range bound datums */
-       PartitionRangeDatumKind *kind;  /* the kind of each datum */
-       bool            lower;                  /* this is the lower (vs upper) bound */
-} PartitionRangeBound;
-
 extern int     get_hash_partition_greatest_modulus(PartitionBoundInfo b);
 extern uint64 compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc,
                                                         Datum *values, bool *isnull);
 extern List *get_qual_from_partbound(Relation rel, Relation parent,
                                                PartitionBoundSpec *spec);
+extern PartitionBoundInfo partition_bounds_create(List *boundspecs,
+                                               PartitionKey key,
+                                               int **mapping);
 extern bool partition_bounds_equal(int partnatts, int16 *parttyplen,
                                           bool *parttypbyval, PartitionBoundInfo b1,
                                           PartitionBoundInfo b2);
@@ -120,14 +94,6 @@ extern void check_default_partition_contents(Relation parent,
                                                                 Relation defaultRel,
                                                                 PartitionBoundSpec *new_spec);
 
-extern PartitionRangeBound *make_one_partition_rbound(PartitionKey key, int index,
-                                                 List *datums, bool lower);
-extern int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2,
-                                        int remainder2);
-extern int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
-                                        Oid *partcollation, Datum *datums1,
-                                        PartitionRangeDatumKind *kind1, bool lower1,
-                                        PartitionRangeBound *b2);
 extern int32 partition_rbound_datum_cmp(FmgrInfo *partsupfunc,
                                                   Oid *partcollation,
                                                   Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
@@ -136,10 +102,6 @@ extern int partition_list_bsearch(FmgrInfo *partsupfunc,
                                           Oid *partcollation,
                                           PartitionBoundInfo boundinfo,
                                           Datum value, bool *is_equal);
-extern int partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
-                                               Oid *partcollation,
-                                               PartitionBoundInfo boundinfo,
-                                               PartitionRangeBound *probe, bool *is_equal);
 extern int partition_range_datum_bsearch(FmgrInfo *partsupfunc,
                                                          Oid *partcollation,
                                                          PartitionBoundInfo boundinfo,