From 3e4e99a2cc23d0f03be3486ee6aae44c0d552157 Mon Sep 17 00:00:00 2001 From: Robert Haas 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)