From 127f57cf50fde959399e0eff46e15a5fe84c6395 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Wed, 7 Nov 2018 14:09:51 +0900
Subject: [PATCH v3 2/3] Move out the PartitionBoundInfo creation code into a
 function

While doing so, put the code for generating PartitionBoundInfo of
different partitioning methods into different local functions.
Having all of them in the same function made the it bulky and the
indentation isn't great either due to complicated logic in each
case.
---
 src/backend/utils/cache/partcache.c | 1000 +++++++++++++++++++----------------
 1 file changed, 543 insertions(+), 457 deletions(-)

diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c
index 46653c7222..1d9ff17a3c 100644
--- a/src/backend/utils/cache/partcache.c
+++ b/src/backend/utils/cache/partcache.c
@@ -43,6 +43,18 @@ 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);
+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);
 
 
 /*
@@ -269,9 +281,6 @@ RelationBuildPartitionDesc(Relation rel)
 				nparts;
 	PartitionKey key = RelationGetPartitionKey(rel);
 	MemoryContext oldcxt;
-	int			ndatums = 0;
-	int			canon_index = 0;
-	int			default_index = -1;
 	Oid		   *oids_orig;
 	int		   *mapping;
 
@@ -338,460 +347,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[canon_index] = orig_index;
-						listpart_canon_idx[orig_index] = canon_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[canon_index] = null_index;
-						listpart_canon_idx[null_index] = canon_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[canon_index] = default_index;
-					boundinfo->default_index = canon_index++;
-				}
-
-				/* All partition must now have a valid mapping */
-				Assert(canon_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[canon_index] = orig_index;
-
-						boundinfo->indexes[i] = canon_index++;
-					}
-				}
-
-				/* Set default_index, if any. */
-				if (default_index != -1)
-				{
-					Assert(default_index >= 0);
-					/* Add the mapping element. */
-					mapping[canon_index] = default_index;
-					boundinfo->default_index = canon_index++;
-				}
-				boundinfo->indexes[i] = -1;
-
-				/* All partition must now have a valid mapping */
-				Assert(canon_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 +550,532 @@ 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)
+{
+	int		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.
+	 */
+	*mapping = (int *) palloc(sizeof(int) * nparts);
+	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 *));
+
+	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 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);
+
+	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,
+				nparts = list_length(boundspecs);
+	int			ndatums = 0;
+	int			default_index = -1;
+	int			null_index = -1;
+	int			canon_index = 0;
+	List	   *non_null_values = NIL;
+	int		   *listpart_canon_idx;
+
+	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. */
+	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 on non-null
+		 * datums for this partition.
+		 */
+		if (spec->is_default)
+		{
+			default_index = i;
+			i++;
+			continue;
+		}
+
+		foreach(c, spec->listdatums)
+		{
+			PartitionListValue *list_value = NULL;
+			Const	   *val = castNode(Const, lfirst(c));
+
+			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, instead just set
+				 * null_index.  And there cannot be two partitions that both
+				 * allow nulls.
+				 */
+				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 if 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 using the
+	 * 'listpart_canon_idx' array.
+	 *
+	 * First, initialize listpart_canon_idx elements to -1 to indicate that
+	 * that no indexes have 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)[canon_index] = orig_index;
+			listpart_canon_idx[orig_index] = canon_index++;
+		}
+
+		boundinfo->indexes[i] = listpart_canon_idx[orig_index];
+	}
+
+	/*
+	 * Set the canonical value for 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)[canon_index] = null_index;
+			listpart_canon_idx[null_index] = canon_index++;
+		}
+		boundinfo->null_index = listpart_canon_idx[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 gave been assigned an
+		 * index yet.
+		 */
+		Assert(default_index >= 0);
+		Assert(listpart_canon_idx[default_index] == -1);
+		/* Add the mapping element. */
+		(*mapping)[canon_index] = default_index;
+		boundinfo->default_index = canon_index++;
+	}
+
+	/* All partition must now have been assigned canonical indexes. */
+	Assert(canon_index == nparts);
+	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			canon_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);
+
+	/*
+	 * De-duplicate: Save distinct bounds from 'all_bounds' into 'rbounds'.
+	 * In many instances, 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.  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];
+		}
+
+		/* 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)[canon_index] = orig_index;
+
+			boundinfo->indexes[i] = canon_index++;
+		}
+	}
+
+	/* Set the canonical value for default_index, if any. */
+	if (default_index != -1)
+	{
+		Assert(default_index >= 0);
+		/* Add the mapping element. */
+		(*mapping)[canon_index] = default_index;
+		boundinfo->default_index = canon_index++;
+	}
+
+	/* The extra -1 element. */
+	Assert(i == ndatums);
+	boundinfo->indexes[i] = -1;
+
+	/* All partition must now have been assigned canonical indexes. */
+	Assert(canon_index == nparts);
+	return boundinfo;
+}
-- 
2.11.0

