From 3e4e99a2cc23d0f03be3486ee6aae44c0d552157 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Fri, 1 Feb 2019 13:16:40 -0500
Subject: [PATCH 6/6] Serialize and restore ParitionDesc/PartitionBound.

---
 src/backend/partitioning/partbounds.c | 238 ++++++++++++++++++++++++++
 src/backend/partitioning/partdesc.c   |  74 ++++++++
 src/include/partitioning/partbounds.h |   6 +
 src/include/partitioning/partdesc.h   |   6 +
 4 files changed, 324 insertions(+)

diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index e71eb3793b..779cbd83f5 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -859,6 +859,244 @@ partition_bounds_copy(PartitionBoundInfo src,
 	return dest;
 }
 
+/*
+ * Estimate the amount of space required to serialize a PartitionBoundInfo.
+ * As usual, we require the PartitionKey as an additional argument to
+ * properly interpret the stored Datums.
+ */
+Size
+partition_bounds_estimate(PartitionBoundInfo bound, PartitionKey key)
+{
+	Size		sz;
+	int			i;
+	int			datums_per_bound;
+
+	/* Assorted sanity checks on the input data. */
+	Assert(bound->strategy == PARTITION_STRATEGY_RANGE ||
+		   bound->strategy == PARTITION_STRATEGY_LIST ||
+		   bound->strategy == PARTITION_STRATEGY_HASH);
+	Assert((bound->strategy == PARTITION_STRATEGY_RANGE) ==
+		   (bound->kind != NULL));
+	Assert(key->partnatts > 0);
+
+	/* Space for strategy, ndatums, null_index, default_index. */
+	sz = sizeof(char) + 3 * sizeof(int);
+
+	/* Space for kind. */
+	if (bound->strategy == PARTITION_STRATEGY_RANGE)
+		sz = add_size(sz,
+					  mul_size(mul_size(bound->ndatums, key->partnatts),
+							   sizeof(PartitionRangeDatumKind)));
+
+	/* Space for datums. */
+	if (key->strategy == PARTITION_STRATEGY_HASH)
+		datums_per_bound = 2;		/* modulus and remainder */
+	else
+		datums_per_bound = key->partnatts;
+	for (i = 0; i < bound->ndatums; i++)
+	{
+		int			j;
+
+		for (j = 0; j < datums_per_bound; j++)
+		{
+			bool		byval;
+			int			typlen;
+
+			if (bound->strategy == PARTITION_STRATEGY_RANGE &&
+				bound->kind[i][j] != PARTITION_RANGE_DATUM_VALUE)
+					continue;
+
+			if (bound->strategy == PARTITION_STRATEGY_HASH)
+			{
+				typlen = sizeof(int32); /* Always int4 */
+				byval = true;	/* int4 is pass-by-value */
+			}
+			else
+			{
+				byval = key->parttypbyval[j];
+				typlen = key->parttyplen[j];
+			}
+
+			sz = add_size(sz,
+						  datumEstimateSpace(bound->datums[i][j], false,
+											 byval, typlen));
+		}
+	}
+
+	/* Space for indexes. */
+	sz = add_size(sz,
+				  mul_size(get_partition_bound_num_indexes(bound),
+						   sizeof(int)));
+
+	return sz;
+}
+
+/*
+ * Serialize a PartitionBoundInfo; the PartitionKey is required for context
+ * both now and when restoring from the serialized state.  Note that the caller
+ * must ensure that enough space is available.  To find out how much space will
+ * be needed, call partition_bounds_estimate().
+ */
+void
+partition_bounds_serialize(PartitionBoundInfo bound, PartitionKey key,
+						   char *start_address)
+{
+	Size		indexbytes;
+	int			i;
+	int			datums_per_bound;
+
+	/*
+	 * Copy fixed-width fields, remembering that start_address may not be
+	 * aligned.
+	 */
+	memcpy(start_address, &bound->strategy, sizeof(char));
+	start_address += sizeof(char);
+	memcpy(start_address, &bound->ndatums, sizeof(int));
+	start_address += sizeof(int);
+	memcpy(start_address, &bound->null_index, sizeof(int));
+	start_address += sizeof(int);
+	memcpy(start_address, &bound->default_index, sizeof(int));
+	start_address += sizeof(int);
+
+	/* Copy kind, if applicable. */
+	if (key->strategy == PARTITION_STRATEGY_RANGE)
+	{
+		for (i = 0; i < bound->ndatums; ++i)
+		{
+			Size	bytes;
+
+			bytes = sizeof(PartitionRangeDatumKind) * key->partnatts;
+			memcpy(start_address, bound->kind[i], bytes);
+			start_address += bytes;
+		}
+	}
+
+	/* Space for datums. */
+	if (key->strategy == PARTITION_STRATEGY_HASH)
+		datums_per_bound = 2;		/* modulus and remainder */
+	else
+		datums_per_bound = key->partnatts;
+	for (i = 0; i < bound->ndatums; i++)
+	{
+		int			j;
+
+		for (j = 0; j < datums_per_bound; j++)
+		{
+			bool		byval;
+			int			typlen;
+
+			if (bound->strategy == PARTITION_STRATEGY_RANGE &&
+				bound->kind[i][j] != PARTITION_RANGE_DATUM_VALUE)
+					continue;
+
+			if (bound->strategy == PARTITION_STRATEGY_HASH)
+			{
+				typlen = sizeof(int32); /* Always int4 */
+				byval = true;	/* int4 is pass-by-value */
+			}
+			else
+			{
+				byval = key->parttypbyval[j];
+				typlen = key->parttyplen[j];
+			}
+
+			datumSerialize(bound->datums[i][j], false, byval, typlen,
+						   &start_address);
+		}
+	}
+
+	/* Copy indexes. */
+	indexbytes = sizeof(int) * get_partition_bound_num_indexes(bound);
+	memcpy(start_address, bound->indexes, indexbytes);
+	start_address += indexbytes;
+}
+
+/*
+ * Restore a previously-serialized PartitionBoundInfo.
+ *
+ * The result is allocated in CurrentMemoryContext.  *start_address is
+ * incremented based on the number of bytes consumed.
+ */
+PartitionBoundInfo
+partition_bounds_restore(char **start_address, PartitionKey key)
+{
+	PartitionBoundInfo bound;
+	int			datums_per_bound;
+	int			i;
+	Size		indexbytes;
+
+	bound = palloc0(sizeof(PartitionBoundInfoData));
+
+	/*
+	 * Restore fixed-width fields, remembering that start_address may not be
+	 * aligned.
+	 */
+	memcpy(&bound->strategy, *start_address, sizeof(char));
+	*start_address += sizeof(char);
+	memcpy(&bound->ndatums, *start_address, sizeof(int));
+	*start_address += sizeof(int);
+	memcpy(&bound->null_index, *start_address, sizeof(int));
+	*start_address += sizeof(int);
+	memcpy(&bound->default_index, *start_address, sizeof(int));
+	*start_address += sizeof(int);
+
+	/* Restore kind, if applicable. */
+	if (key->strategy == PARTITION_STRATEGY_RANGE)
+	{
+		bound->kind =
+			palloc(bound->ndatums * sizeof(PartitionRangeDatumKind *));
+
+		for (i = 0; i < bound->ndatums; ++i)
+		{
+			Size	bytes;
+
+			bytes = sizeof(PartitionRangeDatumKind) * key->partnatts;
+			bound->kind[i] = palloc(bytes);
+			memcpy(bound->kind[i], *start_address, bytes);
+			*start_address += bytes;
+		}
+	}
+
+	/* Restore datums; note that we must have 'kind' already to do this. */
+	if (key->strategy == PARTITION_STRATEGY_HASH)
+		datums_per_bound = 2;		/* modulus and remainder */
+	else
+		datums_per_bound = key->partnatts;
+	bound->datums = palloc(sizeof(Datum *) * bound->ndatums);
+	for (i = 0; i < bound->ndatums; i++)
+	{
+		int			j;
+
+		bound->datums[i] = palloc0(sizeof(Datum) * datums_per_bound);
+		for (j = 0; j < datums_per_bound; j++)
+		{
+			bool		isnull;
+
+			if (bound->strategy == PARTITION_STRATEGY_RANGE &&
+				bound->kind[i][j] != PARTITION_RANGE_DATUM_VALUE)
+					continue;
+
+			/* datumRestore may palloc */
+			bound->datums[i][j] = datumRestore(start_address, &isnull);
+			Assert(!isnull);
+		}
+	}
+
+	/*
+	 * Restore indexes.
+	 *
+	 * Note that we're calling get_partition_bound_num_indexes on the bound
+	 * object even though it isn't complete yet.  Fortunately it doesn't
+	 * depend on the array we're about to restore, so that's OK.
+	 */
+	indexbytes = sizeof(int) * get_partition_bound_num_indexes(bound);
+	bound->indexes = palloc(indexbytes);
+	memcpy(bound->indexes, *start_address, indexbytes);
+	*start_address += indexbytes;
+
+	return bound;
+}
+
 /*
  * check_new_partition_bound
  *
diff --git a/src/backend/partitioning/partdesc.c b/src/backend/partitioning/partdesc.c
index a207ff35ee..84e24646a4 100644
--- a/src/backend/partitioning/partdesc.c
+++ b/src/backend/partitioning/partdesc.c
@@ -331,6 +331,80 @@ equalPartitionDescs(PartitionKey key, PartitionDesc partdesc1,
 	return true;
 }
 
+/*
+ * Estimate the amount of space required to serialize a PartitionDesc.
+ */
+Size
+EstimatePartitionDesc(PartitionDesc partdesc, PartitionKey key)
+{
+	Size	sz = sizeof(int);		/* for nparts */
+
+	/* Space for oids and is_leaf. */
+	sz = add_size(sz, mul_size(partdesc->nparts, sizeof(Oid) + sizeof(bool)));
+
+	/* Space for boundinfo, if required. */
+	if (partdesc->nparts > 0)
+		sz = add_size(sz, partition_bounds_estimate(partdesc->boundinfo, key));
+
+	return sz;
+}
+
+/*
+ * Serialize a PartitionDesc. The caller must use EstimatePartitionDesc to
+ * determine how much space will be needed and pass a sufficiently-large
+ * buffer to this function.
+ */
+void
+SerializePartitionDesc(PartitionDesc partdesc, PartitionKey key,
+					   char *start_address)
+{
+	Size	oids_bytes = sizeof(Oid) * partdesc->nparts;
+	Size	is_leaf_bytes = sizeof(bool) * partdesc->nparts;
+
+	memcpy(start_address, &partdesc->nparts, sizeof(int));
+	start_address += sizeof(int);
+	if (partdesc->nparts > 0)
+	{
+		memcpy(start_address, partdesc->oids, oids_bytes);
+		start_address += oids_bytes;
+		memcpy(start_address, partdesc->is_leaf, is_leaf_bytes);
+		start_address += is_leaf_bytes;
+		partition_bounds_serialize(partdesc->boundinfo, key, start_address);
+	}
+}
+
+/*
+ * Restore a serialized PartitionDesc into CurrentMemoryContext, advancing
+ * *start_address based on the number of bytes consumed.
+ */
+PartitionDesc
+RestorePartitionDesc(char **start_address, PartitionKey key)
+{
+	PartitionDesc	partdesc;
+
+	partdesc = palloc0(sizeof(PartitionDescData));
+	memcpy(&partdesc->nparts, *start_address, sizeof(int));
+	*start_address += sizeof(int);
+
+	if (partdesc->nparts > 0)
+	{
+		Size	oids_bytes = partdesc->nparts * sizeof(Oid);
+		Size	is_leaf_bytes = partdesc->nparts * sizeof(bool);
+
+		partdesc->oids = palloc(oids_bytes);
+		memcpy(partdesc->oids, *start_address, oids_bytes);
+		*start_address += oids_bytes;
+
+		partdesc->is_leaf = palloc(is_leaf_bytes);
+		memcpy(partdesc->is_leaf, *start_address, is_leaf_bytes);
+		*start_address += is_leaf_bytes;
+
+		partdesc->boundinfo = partition_bounds_restore(start_address, key);
+	}
+
+	return partdesc;
+}
+
 /*
  * get_default_oid_from_partdesc
  *
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index b1ae39ad63..0ac6daf319 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -87,6 +87,12 @@ extern bool partition_bounds_equal(int partnatts, int16 *parttyplen,
 					   PartitionBoundInfo b2);
 extern PartitionBoundInfo partition_bounds_copy(PartitionBoundInfo src,
 					  PartitionKey key);
+extern Size partition_bounds_estimate(PartitionBoundInfo bound,
+						  PartitionKey key);
+extern void partition_bounds_serialize(PartitionBoundInfo bound,
+						   PartitionKey key, char *start_address);
+extern PartitionBoundInfo partition_bounds_restore(char **start_address,
+						 PartitionKey key);
 extern void check_new_partition_bound(char *relname, Relation parent,
 						  PartitionBoundSpec *spec);
 extern void check_default_partition_contents(Relation parent,
diff --git a/src/include/partitioning/partdesc.h b/src/include/partitioning/partdesc.h
index 6e384541da..1e679083d5 100644
--- a/src/include/partitioning/partdesc.h
+++ b/src/include/partitioning/partdesc.h
@@ -39,4 +39,10 @@ extern Oid	get_default_oid_from_partdesc(PartitionDesc partdesc);
 extern bool equalPartitionDescs(PartitionKey key, PartitionDesc partdesc1,
 					PartitionDesc partdesc2);
 
+extern Size EstimatePartitionDesc(PartitionDesc partdesc, PartitionKey key);
+extern void SerializePartitionDesc(PartitionDesc partdesc, PartitionKey key,
+					   char *start_address);
+extern PartitionDesc RestorePartitionDesc(char **start_address,
+						 PartitionKey key);
+
 #endif							/* PARTCACHE_H */
-- 
2.17.2 (Apple Git-113)

