From 4de902b93f14e391e4960c5ad0703a6e8cc14d59 Mon Sep 17 00:00:00 2001 From: amit Date: Tue, 10 Apr 2018 15:38:19 +0900 Subject: [PATCH v1] Overhaul partition information caching This started out as a bugfix patch to set the partsupfunc FmgrInfo's fn_mcxt correctly after discovering that not doing so would cause crash when partsupfunc contained user-defined SQL functions. --- src/backend/catalog/heap.c | 2 +- src/backend/catalog/partition.c | 1248 +++--------------------------- src/backend/catalog/pg_constraint.c | 8 +- src/backend/commands/indexcmds.c | 8 +- src/backend/commands/tablecmds.c | 75 +- src/backend/commands/trigger.c | 22 +- src/backend/executor/execPartition.c | 155 +++- src/backend/optimizer/prep/prepunion.c | 13 +- src/backend/optimizer/util/plancat.c | 75 +- src/backend/parser/parse_utilcmd.c | 4 +- src/backend/partitioning/partprune.c | 88 ++- src/backend/utils/cache/relcache.c | 1331 +++++++++++++++++++++++++++++--- src/include/catalog/partition.h | 41 +- src/include/executor/execPartition.h | 12 +- src/include/nodes/relation.h | 2 +- src/include/partitioning/partbounds.h | 16 +- src/include/partitioning/partprune.h | 2 +- src/include/utils/rel.h | 37 +- 18 files changed, 1638 insertions(+), 1501 deletions(-) diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 8f329a6299..ddd0fa25fc 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -3472,7 +3472,7 @@ StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound) * relcache entry for that partition every time a partition is added or * removed. */ - defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(parent)); + defaultPartOid = RelationGetDefaultPartitionOid(parent); if (OidIsValid(defaultPartOid)) CacheInvalidateRelcacheByRelid(defaultPartOid); diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 0f5932feee..19d7519f20 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -55,15 +55,9 @@ #include "utils/ruleutils.h" #include "utils/syscache.h" - static Oid get_partition_parent_worker(Relation inhRel, Oid relid); static void get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors); -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 Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, bool *need_relabel); @@ -80,764 +74,6 @@ static List *get_qual_for_list(Relation parent, PartitionBoundSpec *spec); static List *get_qual_for_range(Relation parent, PartitionBoundSpec *spec, bool for_default); static List *get_range_nulltest(PartitionKey key); -static List *generate_partition_qual(Relation rel); - -static PartitionRangeBound *make_one_range_bound(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 get_partition_bound_num_indexes(PartitionBoundInfo b); - - -/* - * RelationBuildPartitionDesc - * Form rel's partition descriptor - * - * Not flushed from the cache by RelationClearRelation() unless changed because - * of addition or removal of partition. - */ -void -RelationBuildPartitionDesc(Relation rel) -{ - List *inhoids, - *partoids; - Oid *oids = NULL; - 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; - - /* 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); - HeapTuple tuple; - Datum datum; - bool isnull; - Node *boundspec; - - tuple = SearchSysCache1(RELOID, inhrelid); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "cache lookup failed for relation %u", inhrelid); - - /* - * It is possible that the pg_class tuple of a partition has not been - * updated yet to set its relpartbound field. The only case where - * this happens is when we open the parent relation to check using its - * partition descriptor that a new partition's bound does not overlap - * some existing partition. - */ - if (!((Form_pg_class) GETSTRUCT(tuple))->relispartition) - { - ReleaseSysCache(tuple); - continue; - } - - datum = SysCacheGetAttr(RELOID, tuple, - Anum_pg_class_relpartbound, - &isnull); - Assert(!isnull); - boundspec = (Node *) stringToNode(TextDatumGetCString(datum)); - - /* - * Sanity check: If the PartitionBoundSpec says this is the default - * partition, its OID should correspond to whatever's stored in - * pg_partitioned_table.partdefid; if not, the catalog is corrupt. - */ - if (castNode(PartitionBoundSpec, boundspec)->is_default) - { - Oid partdefid; - - partdefid = get_default_partition_oid(RelationGetRelid(rel)); - if (partdefid != inhrelid) - elog(ERROR, "expected partdefid %u, but got %u", - inhrelid, partdefid); - } - - 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_range_bound(key, i, spec->lowerdatums, - true); - upper = make_one_range_bound(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); - } - - /* Now build the actual relcache partition descriptor */ - rel->rd_pdcxt = AllocSetContextCreate(CacheMemoryContext, - "partition descriptor", - ALLOCSET_DEFAULT_SIZES); - MemoryContextCopyAndSetIdentifier(rel->rd_pdcxt, RelationGetRelationName(rel)); - - oldcxt = MemoryContextSwitchTo(rel->rd_pdcxt); - - result = (PartitionDescData *) palloc0(sizeof(PartitionDescData)); - result->nparts = nparts; - 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: - { - /* Modulus 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); - } - - result->boundinfo = boundinfo; - - /* - * 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); - } - - MemoryContextSwitchTo(oldcxt); - rel->rd_partdesc = result; -} - -/* - * Are two partition bound collections logically equal? - * - * Used in the keep logic of relcache.c (ie, in RelationClearRelation()). - * This is also useful when b1 and b2 are bound collections of two separate - * relations, respectively, because PartitionBoundInfo is a canonical - * representation of partition bounds. - */ -bool -partition_bounds_equal(int partnatts, int16 *parttyplen, bool *parttypbyval, - PartitionBoundInfo b1, PartitionBoundInfo b2) -{ - int i; - - if (b1->strategy != b2->strategy) - return false; - - if (b1->ndatums != b2->ndatums) - return false; - - if (b1->null_index != b2->null_index) - return false; - - if (b1->default_index != b2->default_index) - return false; - - if (b1->strategy == PARTITION_STRATEGY_HASH) - { - int greatest_modulus = get_hash_partition_greatest_modulus(b1); - - /* - * If two hash partitioned tables have different greatest moduli, - * their partition schemes don't match. - */ - if (greatest_modulus != get_hash_partition_greatest_modulus(b2)) - return false; - - /* - * We arrange the partitions in the ascending order of their modulus - * and remainders. Also every modulus is factor of next larger - * modulus. Therefore we can safely store index of a given partition - * in indexes array at remainder of that partition. Also entries at - * (remainder + N * modulus) positions in indexes array are all same - * for (modulus, remainder) specification for any partition. Thus - * datums array from both the given bounds are same, if and only if - * their indexes array will be same. So, it suffices to compare - * indexes array. - */ - for (i = 0; i < greatest_modulus; i++) - if (b1->indexes[i] != b2->indexes[i]) - return false; - -#ifdef USE_ASSERT_CHECKING - - /* - * Nonetheless make sure that the bounds are indeed same when the - * indexes match. Hash partition bound stores modulus and remainder - * at b1->datums[i][0] and b1->datums[i][1] position respectively. - */ - for (i = 0; i < b1->ndatums; i++) - Assert((b1->datums[i][0] == b2->datums[i][0] && - b1->datums[i][1] == b2->datums[i][1])); -#endif - } - else - { - for (i = 0; i < b1->ndatums; i++) - { - int j; - - for (j = 0; j < partnatts; j++) - { - /* For range partitions, the bounds might not be finite. */ - if (b1->kind != NULL) - { - /* The different kinds of bound all differ from each other */ - if (b1->kind[i][j] != b2->kind[i][j]) - return false; - - /* - * Non-finite bounds are equal without further - * examination. - */ - if (b1->kind[i][j] != PARTITION_RANGE_DATUM_VALUE) - continue; - } - - /* - * Compare the actual values. Note that it would be both - * incorrect and unsafe to invoke the comparison operator - * derived from the partitioning specification here. It would - * be incorrect because we want the relcache entry to be - * updated for ANY change to the partition bounds, not just - * those that the partitioning operator thinks are - * significant. It would be unsafe because we might reach - * this code in the context of an aborted transaction, and an - * arbitrary partitioning operator might not be safe in that - * context. datumIsEqual() should be simple enough to be - * safe. - */ - if (!datumIsEqual(b1->datums[i][j], b2->datums[i][j], - parttypbyval[j], parttyplen[j])) - return false; - } - - if (b1->indexes[i] != b2->indexes[i]) - return false; - } - - /* There are ndatums+1 indexes in case of range partitions */ - if (b1->strategy == PARTITION_STRATEGY_RANGE && - b1->indexes[i] != b2->indexes[i]) - return false; - } - return true; -} - -/* - * Return a copy of given PartitionBoundInfo structure. The data types of bounds - * are described by given partition key specification. - */ -PartitionBoundInfo -partition_bounds_copy(PartitionBoundInfo src, - PartitionKey key) -{ - PartitionBoundInfo dest; - int i; - int ndatums; - int partnatts; - int num_indexes; - - dest = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData)); - - dest->strategy = src->strategy; - ndatums = dest->ndatums = src->ndatums; - partnatts = key->partnatts; - - num_indexes = get_partition_bound_num_indexes(src); - - /* List partitioned tables have only a single partition key. */ - Assert(key->strategy != PARTITION_STRATEGY_LIST || partnatts == 1); - - dest->datums = (Datum **) palloc(sizeof(Datum *) * ndatums); - - if (src->kind != NULL) - { - dest->kind = (PartitionRangeDatumKind **) palloc(ndatums * - sizeof(PartitionRangeDatumKind *)); - for (i = 0; i < ndatums; i++) - { - dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts * - sizeof(PartitionRangeDatumKind)); - - memcpy(dest->kind[i], src->kind[i], - sizeof(PartitionRangeDatumKind) * key->partnatts); - } - } - else - dest->kind = NULL; - - for (i = 0; i < ndatums; i++) - { - int j; - - /* - * For a corresponding to hash partition, datums array will have two - * elements - modulus and remainder. - */ - bool hash_part = (key->strategy == PARTITION_STRATEGY_HASH); - int natts = hash_part ? 2 : partnatts; - - dest->datums[i] = (Datum *) palloc(sizeof(Datum) * natts); - - for (j = 0; j < natts; j++) - { - bool byval; - int typlen; - - if (hash_part) - { - typlen = sizeof(int32); /* Always int4 */ - byval = true; /* int4 is pass-by-value */ - } - else - { - byval = key->parttypbyval[j]; - typlen = key->parttyplen[j]; - } - - if (dest->kind == NULL || - dest->kind[i][j] == PARTITION_RANGE_DATUM_VALUE) - dest->datums[i][j] = datumCopy(src->datums[i][j], - byval, typlen); - } - } - - dest->indexes = (int *) palloc(sizeof(int) * num_indexes); - memcpy(dest->indexes, src->indexes, sizeof(int) * num_indexes); - - dest->null_index = src->null_index; - dest->default_index = src->default_index; - - return dest; -} /* * check_new_partition_bound @@ -849,9 +85,9 @@ void check_new_partition_bound(char *relname, Relation parent, PartitionBoundSpec *spec) { - PartitionKey key = RelationGetPartitionKey(parent); - PartitionDesc partdesc = RelationGetPartitionDesc(parent); - PartitionBoundInfo boundinfo = partdesc->boundinfo; + PartitionInfo *partinfo = RelationGetPartitionInfo(parent); + PartitionKey key = partinfo->key; + PartitionBoundInfo boundinfo = partinfo->boundinfo; ParseState *pstate = make_parsestate(NULL); int with = -1; bool overlap = false; @@ -865,7 +101,7 @@ check_new_partition_bound(char *relname, Relation parent, ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("partition \"%s\" conflicts with existing default partition \"%s\"", - relname, get_rel_name(partdesc->oids[boundinfo->default_index])), + relname, get_rel_name(partinfo->oids[boundinfo->default_index])), parser_errposition(pstate, spec->location))); } @@ -876,9 +112,8 @@ check_new_partition_bound(char *relname, Relation parent, Assert(spec->strategy == PARTITION_STRATEGY_HASH); Assert(spec->remainder >= 0 && spec->remainder < spec->modulus); - if (partdesc->nparts > 0) + if (partinfo->nparts > 0) { - PartitionBoundInfo boundinfo = partdesc->boundinfo; Datum **datums = boundinfo->datums; int ndatums = boundinfo->ndatums; int greatest_modulus; @@ -956,9 +191,16 @@ check_new_partition_bound(char *relname, Relation parent, case PARTITION_STRATEGY_LIST: { + FmgrInfo partsupfuncinfo; + Assert(spec->strategy == PARTITION_STRATEGY_LIST); - if (partdesc->nparts > 0) + /* Get partsupfunc FmgrInfo for the only key. */ + fmgr_info_copy(&partsupfuncinfo, + partition_getprocinfo(parent, key, 0), + CurrentMemoryContext); + + if (partinfo->nparts > 0) { ListCell *cell; @@ -977,7 +219,7 @@ check_new_partition_bound(char *relname, Relation parent, int offset; bool equal; - offset = partition_list_bsearch(key->partsupfunc, + offset = partition_list_bsearch(&partsupfuncinfo, key->partcollation, boundinfo, val->constvalue, @@ -1005,6 +247,16 @@ check_new_partition_bound(char *relname, Relation parent, { PartitionRangeBound *lower, *upper; + FmgrInfo partsupfuncinfo[PARTITION_MAX_KEYS]; + int i; + + /* Get partsupfunc FmgrInfo's. */ + for (i = 0; i < key->partnatts; i++) + { + fmgr_info_copy(&partsupfuncinfo[i], + partition_getprocinfo(parent, key, i), + CurrentMemoryContext); + } Assert(spec->strategy == PARTITION_STRATEGY_RANGE); lower = make_one_range_bound(key, -1, spec->lowerdatums, true); @@ -1014,7 +266,7 @@ check_new_partition_bound(char *relname, Relation parent, * First check if the resulting range would be empty with * specified lower and upper bounds */ - if (partition_rbound_cmp(key->partnatts, key->partsupfunc, + if (partition_rbound_cmp(key->partnatts, partsupfuncinfo, key->partcollation, lower->datums, lower->kind, true, upper) >= 0) { @@ -1028,9 +280,8 @@ check_new_partition_bound(char *relname, Relation parent, parser_errposition(pstate, spec->location))); } - if (partdesc->nparts > 0) + if (partinfo->nparts > 0) { - PartitionBoundInfo boundinfo = partdesc->boundinfo; int offset; bool equal; @@ -1055,7 +306,7 @@ check_new_partition_bound(char *relname, Relation parent, * at the end. */ offset = partition_range_bsearch(key->partnatts, - key->partsupfunc, + partsupfuncinfo, key->partcollation, boundinfo, lower, &equal); @@ -1080,7 +331,7 @@ check_new_partition_bound(char *relname, Relation parent, is_lower = (boundinfo->indexes[offset + 1] == -1); cmpval = partition_rbound_cmp(key->partnatts, - key->partsupfunc, + partsupfuncinfo, key->partcollation, datums, kind, is_lower, upper); @@ -1121,7 +372,7 @@ check_new_partition_bound(char *relname, Relation parent, ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("partition \"%s\" would overlap partition \"%s\"", - relname, get_rel_name(partdesc->oids[with])), + relname, get_rel_name(partinfo->oids[with])), parser_errposition(pstate, spec->location))); } } @@ -1394,7 +645,7 @@ List * get_qual_from_partbound(Relation rel, Relation parent, PartitionBoundSpec *spec) { - PartitionKey key = RelationGetPartitionKey(parent); + PartitionKey key = RelationGetPartitionKey(parent); List *my_qual = NIL; Assert(key != NULL); @@ -1470,55 +721,6 @@ map_partition_varattnos(List *expr, int fromrel_varno, } /* - * RelationGetPartitionQual - * - * Returns a list of partition quals - */ -List * -RelationGetPartitionQual(Relation rel) -{ - /* Quick exit */ - if (!rel->rd_rel->relispartition) - return NIL; - - return generate_partition_qual(rel); -} - -/* - * get_partition_qual_relid - * - * Returns an expression tree describing the passed-in relation's partition - * constraint. If there is no partition constraint returns NULL; this can - * happen if the default partition is the only partition. - */ -Expr * -get_partition_qual_relid(Oid relid) -{ - Relation rel = heap_open(relid, AccessShareLock); - Expr *result = NULL; - List *and_args; - - /* Do the work only if this relation is a partition. */ - if (rel->rd_rel->relispartition) - { - and_args = generate_partition_qual(rel); - - if (and_args == NIL) - result = NULL; - else if (list_length(and_args) > 1) - result = makeBoolExpr(AND_EXPR, and_args, -1); - else - result = linitial(and_args); - } - - /* Keep the lock. */ - heap_close(rel, NoLock); - - return result; -} - - -/* * get_partition_operator * * Return oid of the operator of given strategy for a given partition key @@ -1690,7 +892,7 @@ make_partition_op_expr(PartitionKey key, int keynum, static List * get_qual_for_hash(Relation parent, PartitionBoundSpec *spec) { - PartitionKey key = RelationGetPartitionKey(parent); + PartitionKey key = RelationGetPartitionKey(parent); FuncExpr *fexpr; Node *relidConst; Node *modulusConst; @@ -1773,7 +975,8 @@ get_qual_for_hash(Relation parent, PartitionBoundSpec *spec) static List * get_qual_for_list(Relation parent, PartitionBoundSpec *spec) { - PartitionKey key = RelationGetPartitionKey(parent); + PartitionInfo *partinfo = RelationGetPartitionInfo(parent); + PartitionKey key = partinfo->key; List *result; Expr *keyCol; Expr *opexpr; @@ -1808,8 +1011,7 @@ get_qual_for_list(Relation parent, PartitionBoundSpec *spec) { int i; int ndatums = 0; - PartitionDesc pdesc = RelationGetPartitionDesc(parent); - PartitionBoundInfo boundinfo = pdesc->boundinfo; + PartitionBoundInfo boundinfo = partinfo->boundinfo; if (boundinfo) { @@ -2092,7 +1294,7 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec, j; PartitionRangeDatum *ldatum, *udatum; - PartitionKey key = RelationGetPartitionKey(parent); + PartitionKey key = RelationGetPartitionKey(parent); Expr *keyCol; Const *lower_val, *upper_val; @@ -2108,11 +1310,11 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec, if (spec->is_default) { List *or_expr_args = NIL; - PartitionDesc pdesc = RelationGetPartitionDesc(parent); - Oid *inhoids = pdesc->oids; - int nparts = pdesc->nparts, + Oid *inhoids = RelationGetPartitionOids(parent); + int nparts = RelationGetPartitionCount(parent), i; + Assert(inhoids != NULL || nparts == 0); for (i = 0; i < nparts; i++) { Oid inhrelid = inhoids[i]; @@ -2429,193 +1631,6 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec, } /* - * generate_partition_qual - * - * Generate partition predicate from rel's partition bound expression. The - * function returns a NIL list if there is no predicate. - * - * Result expression tree is stored CacheMemoryContext to ensure it survives - * as long as the relcache entry. But we should be running in a less long-lived - * working context. To avoid leaking cache memory if this routine fails partway - * through, we build in working memory and then copy the completed structure - * into cache memory. - */ -static List * -generate_partition_qual(Relation rel) -{ - HeapTuple tuple; - MemoryContext oldcxt; - Datum boundDatum; - bool isnull; - PartitionBoundSpec *bound; - List *my_qual = NIL, - *result = NIL; - Relation parent; - bool found_whole_row; - - /* Guard against stack overflow due to overly deep partition tree */ - check_stack_depth(); - - /* Quick copy */ - if (rel->rd_partcheck != NIL) - return copyObject(rel->rd_partcheck); - - /* Grab at least an AccessShareLock on the parent table */ - parent = heap_open(get_partition_parent(RelationGetRelid(rel)), - AccessShareLock); - - /* Get pg_class.relpartbound */ - tuple = SearchSysCache1(RELOID, RelationGetRelid(rel)); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "cache lookup failed for relation %u", - RelationGetRelid(rel)); - - boundDatum = SysCacheGetAttr(RELOID, tuple, - Anum_pg_class_relpartbound, - &isnull); - if (isnull) /* should not happen */ - elog(ERROR, "relation \"%s\" has relpartbound = null", - RelationGetRelationName(rel)); - bound = castNode(PartitionBoundSpec, - stringToNode(TextDatumGetCString(boundDatum))); - ReleaseSysCache(tuple); - - my_qual = get_qual_from_partbound(rel, parent, bound); - - /* Add the parent's quals to the list (if any) */ - if (parent->rd_rel->relispartition) - result = list_concat(generate_partition_qual(parent), my_qual); - else - result = my_qual; - - /* - * Change Vars to have partition's attnos instead of the parent's. We do - * this after we concatenate the parent's quals, because we want every Var - * in it to bear this relation's attnos. It's safe to assume varno = 1 - * here. - */ - result = map_partition_varattnos(result, 1, rel, parent, - &found_whole_row); - /* There can never be a whole-row reference here */ - if (found_whole_row) - elog(ERROR, "unexpected whole-row reference found in partition key"); - - /* Save a copy in the relcache */ - oldcxt = MemoryContextSwitchTo(CacheMemoryContext); - rel->rd_partcheck = copyObject(result); - MemoryContextSwitchTo(oldcxt); - - /* Keep the parent locked until commit */ - heap_close(parent, NoLock); - - return result; -} - -/* - * get_partition_for_tuple - * Finds partition of relation which accepts the partition key specified - * in values and isnull - * - * Return value is index of the partition (>= 0 and < partdesc->nparts) if one - * found or -1 if none found. - */ -int -get_partition_for_tuple(Relation relation, Datum *values, bool *isnull) -{ - int bound_offset; - int part_index = -1; - PartitionKey key = RelationGetPartitionKey(relation); - PartitionDesc partdesc = RelationGetPartitionDesc(relation); - - /* Route as appropriate based on partitioning strategy. */ - switch (key->strategy) - { - case PARTITION_STRATEGY_HASH: - { - PartitionBoundInfo boundinfo = partdesc->boundinfo; - int greatest_modulus = get_hash_partition_greatest_modulus(boundinfo); - uint64 rowHash = compute_hash_value(key->partnatts, - key->partsupfunc, - values, isnull); - - part_index = boundinfo->indexes[rowHash % greatest_modulus]; - } - break; - - case PARTITION_STRATEGY_LIST: - if (isnull[0]) - { - if (partition_bound_accepts_nulls(partdesc->boundinfo)) - part_index = partdesc->boundinfo->null_index; - } - else - { - bool equal = false; - - bound_offset = partition_list_bsearch(key->partsupfunc, - key->partcollation, - partdesc->boundinfo, - values[0], &equal); - if (bound_offset >= 0 && equal) - part_index = partdesc->boundinfo->indexes[bound_offset]; - } - break; - - case PARTITION_STRATEGY_RANGE: - { - bool equal = false, - range_partkey_has_null = false; - int i; - - /* - * No range includes NULL, so this will be accepted by the - * default partition if there is one, and otherwise rejected. - */ - for (i = 0; i < key->partnatts; i++) - { - if (isnull[i]) - { - range_partkey_has_null = true; - break; - } - } - - if (!range_partkey_has_null) - { - bound_offset = partition_range_datum_bsearch(key->partsupfunc, - key->partcollation, - partdesc->boundinfo, - key->partnatts, - values, - &equal); - - /* - * The bound at bound_offset is less than or equal to the - * tuple value, so the bound at offset+1 is the upper - * bound of the partition we're looking for, if there - * actually exists one. - */ - part_index = partdesc->boundinfo->indexes[bound_offset + 1]; - } - } - break; - - default: - elog(ERROR, "unexpected partition strategy: %d", - (int) key->strategy); - } - - /* - * part_index < 0 means we failed to find a partition of this parent. Use - * the default partition, if there is one. - */ - if (part_index < 0) - part_index = partdesc->boundinfo->default_index; - - return part_index; -} - -/* * Checks if any of the 'attnums' is a partition key attribute for rel * * Sets *used_in_expr if any of the 'attnums' is found to be referenced in some @@ -2629,7 +1644,7 @@ bool has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr) { - PartitionKey key; + PartitionKey key; int partnatts; List *partexprs; ListCell *partexprs_item; @@ -2679,27 +1694,13 @@ has_partition_attrs(Relation rel, Bitmapset *attnums, return false; } -/* - * 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); -} /* * partition_hbound_cmp * * Compares modulus first, then remainder if modulus are equal. */ -static int32 +int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2) { if (modulus1 < modulus2) @@ -2712,30 +1713,13 @@ partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2) } /* - * 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)); -} - -/* * make_one_range_bound * * Return a PartitionRangeBound given a list of PartitionRangeDatum elements * 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. */ -static PartitionRangeBound * +PartitionRangeBound * make_one_range_bound(PartitionKey key, int index, List *datums, bool lower) { PartitionRangeBound *bound; @@ -2774,19 +1758,6 @@ make_one_range_bound(PartitionKey key, int index, List *datums, bool lower) return bound; } -/* 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); -} - /* * partition_rbound_cmp * @@ -2804,8 +1775,9 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg) * structure, which only stores the upper bound of a common boundary between * two contiguous partitions. */ -static int32 -partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, +int32 +partition_rbound_cmp(int partnatts, FmgrInfo *partsupfuncinfo, + Oid *partcollation, Datum *datums1, PartitionRangeDatumKind *kind1, bool lower1, PartitionRangeBound *b2) { @@ -2836,7 +1808,7 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, */ break; - cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i], + cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfuncinfo[i], partcollation[i], datums1[i], datums2[i])); @@ -2868,7 +1840,7 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, * */ int32 -partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, +partition_rbound_datum_cmp(FmgrInfo *partsupfuncinfo, Oid *partcollation, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums, int n_tuple_datums) { @@ -2882,7 +1854,7 @@ partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE) return 1; - cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i], + cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfuncinfo[i], partcollation[i], rb_datums[i], tuple_datums[i])); @@ -2902,7 +1874,7 @@ partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, * to the input value. */ int -partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, +partition_list_bsearch(FmgrInfo *partsupfuncinfo, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal) { @@ -2917,7 +1889,7 @@ partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, int32 cmpval; mid = (lo + hi + 1) / 2; - cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0], + cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfuncinfo[0], partcollation[0], boundinfo->datums[mid][0], value)); @@ -2945,7 +1917,7 @@ partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, * to the input range bound */ int -partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc, +partition_range_bsearch(int partnatts, FmgrInfo *partsupfuncinfo, Oid *partcollation, PartitionBoundInfo boundinfo, PartitionRangeBound *probe, bool *is_equal) @@ -2961,7 +1933,8 @@ partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc, int32 cmpval; mid = (lo + hi + 1) / 2; - cmpval = partition_rbound_cmp(partnatts, partsupfunc, partcollation, + cmpval = partition_rbound_cmp(partnatts, partsupfuncinfo, + partcollation, boundinfo->datums[mid], boundinfo->kind[mid], (boundinfo->indexes[mid] == -1), @@ -2990,7 +1963,7 @@ partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc, * to the input tuple. */ int -partition_range_datum_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, +partition_range_datum_bsearch(FmgrInfo *partsupfuncinfo, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, Datum *values, bool *is_equal) { @@ -3005,7 +1978,7 @@ partition_range_datum_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, int32 cmpval; mid = (lo + hi + 1) / 2; - cmpval = partition_rbound_datum_cmp(partsupfunc, + cmpval = partition_rbound_datum_cmp(partsupfuncinfo, partcollation, boundinfo->datums[mid], boundinfo->kind[mid], @@ -3068,27 +2041,10 @@ partition_hash_bsearch(PartitionBoundInfo boundinfo, } /* - * get_default_oid_from_partdesc - * - * Given a partition descriptor, return the OID of the default partition, if - * one exists; else, return InvalidOid. - */ -Oid -get_default_oid_from_partdesc(PartitionDesc partdesc) -{ - if (partdesc && partdesc->boundinfo && - partition_bound_has_default(partdesc->boundinfo)) - return partdesc->oids[partdesc->boundinfo->default_index]; - - return InvalidOid; -} - -/* * get_default_partition_oid * * Given a relation OID, return the OID of the default partition, if one - * exists. Use get_default_oid_from_partdesc where possible, for - * efficiency. + * exists. Use get_default_partition_oid where possible, for efficiency. */ Oid get_default_partition_oid(Oid parentId) @@ -3171,46 +2127,6 @@ get_proposed_default_constraint(List *new_part_constraints) } /* - * get_partition_bound_num_indexes - * - * Returns the number of the entries in the partition bound indexes array. - */ -static int -get_partition_bound_num_indexes(PartitionBoundInfo bound) -{ - int num_indexes; - - Assert(bound); - - switch (bound->strategy) - { - case PARTITION_STRATEGY_HASH: - - /* - * The number of the entries in the indexes array is same as the - * greatest modulus. - */ - num_indexes = get_hash_partition_greatest_modulus(bound); - break; - - case PARTITION_STRATEGY_LIST: - num_indexes = bound->ndatums; - break; - - case PARTITION_STRATEGY_RANGE: - /* Range partitioned table has an extra index. */ - num_indexes = bound->ndatums + 1; - break; - - default: - elog(ERROR, "unexpected partition strategy: %d", - (int) bound->strategy); - } - - return num_indexes; -} - -/* * get_hash_partition_greatest_modulus * * Returns the greatest modulus of the hash partition bound. The greatest @@ -3233,7 +2149,7 @@ get_hash_partition_greatest_modulus(PartitionBoundInfo bound) * Compute the hash value for given not null partition key values. */ uint64 -compute_hash_value(int partnatts, FmgrInfo *partsupfunc, +compute_hash_value(int partnatts, FmgrInfo *partsupfuncinfo, Datum *values, bool *isnull) { int i; @@ -3246,14 +2162,14 @@ compute_hash_value(int partnatts, FmgrInfo *partsupfunc, { Datum hash; - Assert(OidIsValid(partsupfunc[i].fn_oid)); + Assert(OidIsValid(partsupfuncinfo[i].fn_oid)); /* * Compute hash for each datum value by calling respective * datatype-specific hash functions of each partition key * attribute. */ - hash = FunctionCall2(&partsupfunc[i], values[i], seed); + hash = FunctionCall2(&partsupfuncinfo[i], values[i], seed); /* Form a single 64-bit hash value */ rowHash = hash_combine64(rowHash, DatumGetUInt64(hash)); @@ -3288,7 +2204,7 @@ satisfies_hash_partition(PG_FUNCTION_ARGS) int16 variadic_typlen; bool variadic_typbyval; char variadic_typalign; - FmgrInfo partsupfunc[PARTITION_MAX_KEYS]; + FmgrInfo partsupfuncinfo[PARTITION_MAX_KEYS]; } ColumnsHashData; Oid parentId; int modulus; @@ -3325,23 +2241,39 @@ satisfies_hash_partition(PG_FUNCTION_ARGS) if (my_extra == NULL || my_extra->relid != parentId) { Relation parent; - PartitionKey key; + PartitionKey key; + FmgrInfo partsupfuncinfo[PARTITION_MAX_KEYS]; int j; /* Open parent relation and fetch partition keyinfo */ parent = try_relation_open(parentId, AccessShareLock); if (parent == NULL) PG_RETURN_NULL(); - key = RelationGetPartitionKey(parent); /* Reject parent table that is not hash-partitioned. */ - if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE || - key->strategy != PARTITION_STRATEGY_HASH) + if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("\"%s\" is not a hash partitioned table", get_rel_name(parentId)))); + key = RelationGetPartitionKey(parent); + Assert(key != NULL); + + if (key->strategy != PARTITION_STRATEGY_HASH) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("\"%s\" is not a hash partitioned table", + get_rel_name(parentId)))); + + /* Get partsupfunc FmgrInfo's. */ + for (j = 0; j < key->partnatts; j++) + { + fmgr_info_copy(&partsupfuncinfo[j], + partition_getprocinfo(parent, key, j), + CurrentMemoryContext); + } + if (!get_fn_expr_variadic(fcinfo->flinfo)) { int nargs = PG_NARGS() - 3; @@ -3356,7 +2288,8 @@ satisfies_hash_partition(PG_FUNCTION_ARGS) /* allocate space for our cache */ fcinfo->flinfo->fn_extra = MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt, - offsetof(ColumnsHashData, partsupfunc) + + offsetof(ColumnsHashData, + partsupfuncinfo) + sizeof(FmgrInfo) * nargs); my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra; my_extra->relid = parentId; @@ -3373,8 +2306,8 @@ satisfies_hash_partition(PG_FUNCTION_ARGS) errmsg("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"", j + 1, format_type_be(key->parttypid[j]), format_type_be(argtype)))); - fmgr_info_copy(&my_extra->partsupfunc[j], - &key->partsupfunc[j], + fmgr_info_copy(&my_extra->partsupfuncinfo[j], + &partsupfuncinfo[j], fcinfo->flinfo->fn_mcxt); } @@ -3386,7 +2319,8 @@ satisfies_hash_partition(PG_FUNCTION_ARGS) /* allocate space for our cache -- just one FmgrInfo in this case */ fcinfo->flinfo->fn_extra = MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt, - offsetof(ColumnsHashData, partsupfunc) + + offsetof(ColumnsHashData, + partsupfuncinfo) + sizeof(FmgrInfo)); my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra; my_extra->relid = parentId; @@ -3407,8 +2341,8 @@ satisfies_hash_partition(PG_FUNCTION_ARGS) format_type_be(key->parttypid[j]), format_type_be(my_extra->variadic_type)))); - fmgr_info_copy(&my_extra->partsupfunc[0], - &key->partsupfunc[0], + fmgr_info_copy(&my_extra->partsupfuncinfo[0], + &partsupfuncinfo[0], fcinfo->flinfo->fn_mcxt); } @@ -3437,9 +2371,9 @@ satisfies_hash_partition(PG_FUNCTION_ARGS) if (PG_ARGISNULL(argno)) continue; - Assert(OidIsValid(my_extra->partsupfunc[i].fn_oid)); + Assert(OidIsValid(my_extra->partsupfuncinfo[i].fn_oid)); - hash = FunctionCall2(&my_extra->partsupfunc[i], + hash = FunctionCall2(&my_extra->partsupfuncinfo[i], PG_GETARG_DATUM(argno), seed); @@ -3476,9 +2410,9 @@ satisfies_hash_partition(PG_FUNCTION_ARGS) if (isnull[i]) continue; - Assert(OidIsValid(my_extra->partsupfunc[0].fn_oid)); + Assert(OidIsValid(my_extra->partsupfuncinfo[0].fn_oid)); - hash = FunctionCall2(&my_extra->partsupfunc[0], + hash = FunctionCall2(&my_extra->partsupfuncinfo[0], datum[i], seed); diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 8ba9890ca6..32386b246f 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -623,12 +623,14 @@ CloneForeignKeyConstraints(Oid parentId, Oid relationId, List **cloned) if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { - PartitionDesc partdesc = RelationGetPartitionDesc(rel); + int nparts = RelationGetPartitionCount(rel); + Oid *partoids = RelationGetPartitionOids(rel); int i; - for (i = 0; i < partdesc->nparts; i++) + Assert(partoids != NULL || nparts == 0); + for (i = 0; i < nparts; i++) CloneForeignKeyConstraints(RelationGetRelid(rel), - partdesc->oids[i], + partoids[i], cloned); } diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 860a60d109..6b159718bb 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -876,15 +876,12 @@ DefineIndex(Oid relationId, */ if (!stmt->relation || stmt->relation->inh) { - PartitionDesc partdesc = RelationGetPartitionDesc(rel); - int nparts = partdesc->nparts; - Oid *part_oids = palloc(sizeof(Oid) * nparts); + int nparts = RelationGetPartitionCount(rel); + Oid *part_oids = RelationGetPartitionOids(rel); bool invalidate_parent = false; TupleDesc parentDesc; Oid *opfamOids; - memcpy(part_oids, partdesc->oids, sizeof(Oid) * nparts); - parentDesc = CreateTupleDescCopy(RelationGetDescr(rel)); opfamOids = palloc(sizeof(Oid) * numberOfAttributes); for (i = 0; i < numberOfAttributes; i++) @@ -900,6 +897,7 @@ DefineIndex(Oid relationId, * If none matches, build a new index by calling ourselves * recursively with the same options (except for the index name). */ + Assert(part_oids != NULL || nparts == 0); for (i = 0; i < nparts; i++) { Oid childRelid = part_oids[i]; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index f8108858ae..b9c9cb82c6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -825,8 +825,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, * the places such that lock parent, lock default partition and then * lock the partition so as to avoid a deadlock. */ - defaultPartOid = - get_default_oid_from_partdesc(RelationGetPartitionDesc(parent)); + defaultPartOid = RelationGetDefaultPartitionOid(parent); if (OidIsValid(defaultPartOid)) defaultRel = heap_open(defaultPartOid, AccessExclusiveLock); @@ -5860,18 +5859,14 @@ ATPrepDropNotNull(Relation rel, bool recurse, bool recursing) * If the parent is a partitioned table, like check constraints, we do not * support removing the NOT NULL while partitions exist. */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - { - PartitionDesc partdesc = RelationGetPartitionDesc(rel); - - Assert(partdesc != NULL); - if (partdesc->nparts > 0 && !recurse && !recursing) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("cannot remove constraint from only the partitioned table when partitions exist"), - errhint("Do not specify the ONLY keyword."))); - } + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && + RelationGetPartitionCount(rel) > 0 && !recurse && !recursing) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot remove constraint from only the partitioned table when partitions exist"), + errhint("Do not specify the ONLY keyword."))); } + static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode) { @@ -6005,16 +6000,12 @@ ATPrepSetNotNull(Relation rel, bool recurse, bool recursing) * constraints must be added to the child tables. Complain if requested * otherwise and partitions exist. */ - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - { - PartitionDesc partdesc = RelationGetPartitionDesc(rel); - - if (partdesc && partdesc->nparts > 0 && !recurse && !recursing) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("cannot add constraint to only the partitioned table when partitions exist"), - errhint("Do not specify the ONLY keyword."))); - } + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && + RelationGetPartitionCount(rel) > 0 && !recurse && !recursing) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot add constraint to only the partitioned table when partitions exist"), + errhint("Do not specify the ONLY keyword."))); } /* @@ -7695,13 +7686,13 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, */ if (recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { - PartitionDesc partdesc; + int nparts = RelationGetPartitionCount(rel); + Oid *partoids = RelationGetPartitionOids(rel); - partdesc = RelationGetPartitionDesc(rel); - - for (i = 0; i < partdesc->nparts; i++) + Assert(partoids != NULL || nparts == 0); + for (i = 0; i < nparts; i++) { - Oid partitionId = partdesc->oids[i]; + Oid partitionId = partoids[i]; Relation partition = heap_open(partitionId, lockmode); AlteredTableInfo *childtab; ObjectAddress childAddr; @@ -13985,10 +13976,12 @@ QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, } else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { - PartitionDesc partdesc = RelationGetPartitionDesc(scanrel); + int nparts = RelationGetPartitionCount(scanrel); + Oid *partoids = RelationGetPartitionOids(scanrel); int i; - for (i = 0; i < partdesc->nparts; i++) + Assert(partoids != NULL || nparts == 0); + for (i = 0; i < nparts; i++) { Relation part_rel; bool found_whole_row; @@ -13998,7 +13991,7 @@ QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, * This is the minimum lock we need to prevent concurrent data * additions. */ - part_rel = heap_open(partdesc->oids[i], ShareLock); + part_rel = heap_open(partoids[i], ShareLock); /* * Adjust the constraint for scanrel so that it matches this @@ -14048,8 +14041,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd) * We must lock the default partition if one exists, because attaching a * new partition will change its partition constraint. */ - defaultPartOid = - get_default_oid_from_partdesc(RelationGetPartitionDesc(rel)); + defaultPartOid = RelationGetDefaultPartitionOid(rel); if (OidIsValid(defaultPartOid)) LockRelationOid(defaultPartOid, AccessExclusiveLock); @@ -14623,8 +14615,7 @@ ATExecDetachPartition(Relation rel, RangeVar *name) * We must lock the default partition, because detaching this partition * will change its partition constraint. */ - defaultPartOid = - get_default_oid_from_partdesc(RelationGetPartitionDesc(rel)); + defaultPartOid = RelationGetDefaultPartitionOid(rel); if (OidIsValid(defaultPartOid)) LockRelationOid(defaultPartOid, AccessExclusiveLock); @@ -14824,8 +14815,9 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) AttrNumber *attmap; bool found; int i; - PartitionDesc partDesc; - Oid constraintOid, + int nparts = RelationGetPartitionCount(parentTbl); + Oid *partoids = RelationGetPartitionOids(parentTbl), + constraintOid, cldConstrId = InvalidOid; /* @@ -14843,11 +14835,11 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) RelationGetRelationName(partIdx)))); /* Make sure it indexes a partition of the other index's table */ - partDesc = RelationGetPartitionDesc(parentTbl); found = false; - for (i = 0; i < partDesc->nparts; i++) + Assert(partoids != NULL || nparts == 0); + for (i = 0; i < nparts; i++) { - if (partDesc->oids[i] == state.partitionOid) + if (partoids[i] == state.partitionOid) { found = true; break; @@ -14977,6 +14969,7 @@ validatePartitionedIndex(Relation partedIdx, Relation partedTbl) int tuples = 0; HeapTuple inhTup; bool updated = false; + int nparts = RelationGetPartitionCount(partedTbl); Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX); @@ -15016,7 +15009,7 @@ validatePartitionedIndex(Relation partedIdx, Relation partedTbl) * If we found as many inherited indexes as the partitioned table has * partitions, we're good; update pg_index to set indisvalid. */ - if (tuples == RelationGetPartitionDesc(partedTbl)->nparts) + if (tuples == nparts) { Relation idxRel; HeapTuple newtup; diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 0d57d46748..f225dc2cc3 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -1093,7 +1093,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, */ if (partition_recurse) { - PartitionDesc partdesc = RelationGetPartitionDesc(rel); + int nparts = RelationGetPartitionCount(rel); + Oid *partoids = RelationGetPartitionOids(rel); List *idxs = NIL; List *childTbls = NIL; ListCell *l; @@ -1125,7 +1126,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, oldcxt = MemoryContextSwitchTo(perChildCxt); /* Iterate to create the trigger on each existing partition */ - for (i = 0; i < partdesc->nparts; i++) + Assert(partoids != NULL || nparts == 0); + for (i = 0; i < nparts; i++) { Oid indexOnChild = InvalidOid; ListCell *l2; @@ -1134,14 +1136,14 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, Node *qual; bool found_whole_row; - childTbl = heap_open(partdesc->oids[i], ShareRowExclusiveLock); + childTbl = heap_open(partoids[i], ShareRowExclusiveLock); /* Find which of the child indexes is the one on this partition */ if (OidIsValid(indexOid)) { forboth(l, idxs, l2, childTbls) { - if (lfirst_oid(l2) == partdesc->oids[i]) + if (lfirst_oid(l2) == partoids[i]) { indexOnChild = lfirst_oid(l); break; @@ -1150,7 +1152,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, if (!OidIsValid(indexOnChild)) elog(ERROR, "failed to find index matching index \"%s\" in partition \"%s\"", get_rel_name(indexOid), - get_rel_name(partdesc->oids[i])); + get_rel_name(partoids[i])); } /* @@ -1178,7 +1180,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, elog(ERROR, "unexpected whole-row reference found in trigger WHEN clause"); CreateTrigger(childStmt, queryString, - partdesc->oids[i], refRelOid, + partoids[i], refRelOid, InvalidOid, indexOnChild, funcoid, trigoid, qual, isInternal, true); @@ -1861,14 +1863,16 @@ EnableDisableTrigger(Relation rel, const char *tgname, if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && (TRIGGER_FOR_ROW(oldtrig->tgtype))) { - PartitionDesc partdesc = RelationGetPartitionDesc(rel); + int nparts = RelationGetPartitionCount(rel); + Oid *partoids = RelationGetPartitionOids(rel); int i; - for (i = 0; i < partdesc->nparts; i++) + Assert(partoids != NULL || nparts == 0); + for (i = 0; i < nparts; i++) { Relation part; - part = relation_open(partdesc->oids[i], lockmode); + part = relation_open(partoids[i], lockmode); EnableDisableTrigger(part, NameStr(oldtrig->tgname), fires_when, skip_system, lockmode); heap_close(part, NoLock); /* keep lock till commit */ diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index d4d54e927a..8ba16ac56b 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -22,6 +22,7 @@ #include "mb/pg_wchar.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "partitioning/partbounds.h" #include "utils/lsyscache.h" #include "utils/rls.h" #include "utils/ruleutils.h" @@ -35,6 +36,8 @@ static void FormPartitionKeyDatum(PartitionDispatch pd, EState *estate, Datum *values, bool *isnull); +static int get_partition_for_tuple(PartitionDispatch pd, Datum *values, + bool *isnull); static char *ExecBuildSlotPartitionKeyDescription(Relation rel, Datum *values, bool *isnull, @@ -212,13 +215,11 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd, parent = pd[0]; while (true) { - PartitionDesc partdesc; TupleTableSlot *myslot = parent->tupslot; TupleConversionMap *map = parent->tupmap; int cur_index = -1; rel = parent->reldesc; - partdesc = RelationGetPartitionDesc(rel); /* * Convert the tuple to this parent's layout so that we can do certain @@ -249,13 +250,13 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd, * Nothing for get_partition_for_tuple() to do if there are no * partitions to begin with. */ - if (partdesc->nparts == 0) + if (parent->nparts == 0) { result = -1; break; } - cur_index = get_partition_for_tuple(rel, values, isnull); + cur_index = get_partition_for_tuple(parent, values, isnull); /* * cur_index < 0 means we failed to find a partition of this parent. @@ -995,8 +996,8 @@ get_partition_dispatch_recurse(Relation rel, Relation parent, List **pds, List **leaf_part_oids) { TupleDesc tupdesc = RelationGetDescr(rel); - PartitionDesc partdesc = RelationGetPartitionDesc(rel); - PartitionKey partkey = RelationGetPartitionKey(rel); + PartitionInfo *partinfo = RelationGetPartitionInfo(rel); + PartitionKey partkey = partinfo->key; PartitionDispatch pd; int i; @@ -1008,7 +1009,15 @@ get_partition_dispatch_recurse(Relation rel, Relation parent, pd->reldesc = rel; pd->key = partkey; pd->keystate = NIL; - pd->partdesc = partdesc; + /* Get partsupfunc FmgrInfo's. */ + for (i = 0; i < partkey->partnatts; i++) + { + fmgr_info_copy(&pd->partsupfuncinfo[i], + partition_getprocinfo(rel, partkey, i), + CurrentMemoryContext); + } + pd->nparts = partinfo->nparts; + pd->boundinfo = partinfo->boundinfo; if (parent != NULL) { /* @@ -1053,10 +1062,10 @@ get_partition_dispatch_recurse(Relation rel, Relation parent, * the tree. This value is used to continue the search in the next level * of the partition tree. */ - pd->indexes = (int *) palloc(partdesc->nparts * sizeof(int)); - for (i = 0; i < partdesc->nparts; i++) + pd->indexes = (int *) palloc(partinfo->nparts * sizeof(int)); + for (i = 0; i < partinfo->nparts; i++) { - Oid partrelid = partdesc->oids[i]; + Oid partrelid = partinfo->oids[i]; if (get_rel_relkind(partrelid) != RELKIND_PARTITIONED_TABLE) { @@ -1144,6 +1153,110 @@ FormPartitionKeyDatum(PartitionDispatch pd, } /* + * get_partition_for_tuple + * Finds partition of relation which accepts the partition key specified + * in values and isnull + * + * Return value is index of the partition (>= 0 and < partinfo->nparts) if one + * found or -1 if none found. + */ +static int +get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull) +{ + int bound_offset; + int part_index = -1; + PartitionKey key = pd->key; + FmgrInfo *partsupfuncinfo = pd->partsupfuncinfo; + PartitionBoundInfo boundinfo = pd->boundinfo; + + /* Route as appropriate based on partitioning strategy. */ + switch (key->strategy) + { + case PARTITION_STRATEGY_HASH: + { + int greatest_modulus = get_hash_partition_greatest_modulus(boundinfo); + uint64 rowHash = compute_hash_value(key->partnatts, + partsupfuncinfo, + values, isnull); + + part_index = boundinfo->indexes[rowHash % greatest_modulus]; + } + break; + + case PARTITION_STRATEGY_LIST: + if (isnull[0]) + { + if (partition_bound_accepts_nulls(boundinfo)) + part_index = boundinfo->null_index; + } + else + { + bool equal = false; + + bound_offset = partition_list_bsearch(partsupfuncinfo, + key->partcollation, + boundinfo, + values[0], &equal); + if (bound_offset >= 0 && equal) + part_index = boundinfo->indexes[bound_offset]; + } + break; + + case PARTITION_STRATEGY_RANGE: + { + bool equal = false, + range_partkey_has_null = false; + int i; + + /* + * No range includes NULL, so this will be accepted by the + * default partition if there is one, and otherwise rejected. + */ + for (i = 0; i < key->partnatts; i++) + { + if (isnull[i]) + { + range_partkey_has_null = true; + break; + } + } + + if (!range_partkey_has_null) + { + bound_offset = partition_range_datum_bsearch(partsupfuncinfo, + key->partcollation, + boundinfo, + key->partnatts, + values, + &equal); + + /* + * The bound at bound_offset is less than or equal to the + * tuple value, so the bound at offset+1 is the upper + * bound of the partition we're looking for, if there + * actually exists one. + */ + part_index = boundinfo->indexes[bound_offset + 1]; + } + } + break; + + default: + elog(ERROR, "unexpected partition strategy: %d", + (int) key->strategy); + } + + /* + * part_index < 0 means we failed to find a partition of this parent. Use + * the default partition, if there is one. + */ + if (part_index < 0) + part_index = boundinfo->default_index; + + return part_index; +} + +/* * ExecBuildSlotPartitionKeyDescription * * This works very much like BuildIndexValueDescription() and is currently @@ -1157,7 +1270,8 @@ ExecBuildSlotPartitionKeyDescription(Relation rel, int maxfieldlen) { StringInfoData buf; - PartitionKey key = RelationGetPartitionKey(rel); + PartitionInfo *partinfo = RelationGetPartitionInfo(rel); + PartitionKey key = partinfo->key; int partnatts = get_partition_natts(key); int i; Oid relid = RelationGetRelid(rel); @@ -1405,10 +1519,11 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo) PartitionPruneInfo *pinfo = (PartitionPruneInfo *) lfirst(lc); PartitionPruningData *pprune = &prunedata[i]; PartitionPruneContext *context = &pprune->context; - PartitionDesc partdesc; Relation rel; - PartitionKey partkey; + PartitionInfo *partinfo; + PartitionKey partkey; int partnatts; + int j; pprune->present_parts = bms_copy(pinfo->present_parts); pprune->subnode_map = palloc(sizeof(int) * pinfo->nparts); @@ -1429,17 +1544,23 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo) */ rel = relation_open(pinfo->reloid, NoLock); - partkey = RelationGetPartitionKey(rel); - partdesc = RelationGetPartitionDesc(rel); + partinfo = RelationGetPartitionInfo(rel); + partkey = partinfo->key; context->strategy = partkey->strategy; context->partnatts = partnatts = partkey->partnatts; context->partopfamily = partkey->partopfamily; context->partopcintype = partkey->partopcintype; context->partcollation = partkey->partcollation; - context->partsupfunc = partkey->partsupfunc; + /* Get partsupfunc FmgrInfo's. */ + for (j = 0; j < partkey->partnatts; j++) + { + fmgr_info_copy(&context->partsupfuncinfo[j], + partition_getprocinfo(rel, partkey, j), + CurrentMemoryContext); + } context->nparts = pinfo->nparts; - context->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey); + context->boundinfo = partinfo->boundinfo; context->planstate = planstate; context->safeparams = NULL; /* empty for now */ diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 8d86e98adc..23ce22a27e 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -1584,7 +1584,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) oldrelation = heap_open(parentOID, NoLock); /* Scan the inheritance set and expand it */ - if (RelationGetPartitionDesc(oldrelation) != NULL) + if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { Assert(rte->relkind == RELKIND_PARTITIONED_TABLE); @@ -1675,13 +1675,11 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, RangeTblEntry *childrte; Index childRTindex; bool has_child = false; - PartitionDesc partdesc = RelationGetPartitionDesc(parentrel); + int nparts = RelationGetPartitionCount(parentrel); + Oid *partoids = RelationGetPartitionOids(parentrel); check_stack_depth(); - /* A partitioned table should always have a partition descriptor. */ - Assert(partdesc); - Assert(parentrte->inh); /* @@ -1700,9 +1698,10 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, top_parentrc, parentrel, appinfos, &childrte, &childRTindex); - for (i = 0; i < partdesc->nparts; i++) + Assert(partoids != NULL || nparts == 0); + for (i = 0; i < nparts; i++) { - Oid childOID = partdesc->oids[i]; + Oid childOID = partoids[i]; Relation childrel; /* Open rel; we already have required locks */ diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 90bb0c2804..b77d8386da 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -70,8 +70,9 @@ static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index, static List *get_relation_statistics(RelOptInfo *rel, Relation relation); static void set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel, Relation relation); -static PartitionScheme find_partition_scheme(PlannerInfo *root, Relation rel); -static void set_baserel_partition_key_exprs(Relation relation, +static PartitionScheme find_partition_scheme(PlannerInfo *root, Relation rel, + PartitionKey partkey); +static void set_baserel_partition_key_exprs(PartitionKey partkey, RelOptInfo *rel); /* @@ -1873,18 +1874,17 @@ static void set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel, Relation relation) { - PartitionDesc partdesc; - PartitionKey partkey; + PartitionInfo *partinfo = RelationGetPartitionInfo(relation); + PartitionKey partkey = partinfo->key; Assert(relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); + Assert(partinfo != NULL && partkey != NULL); - partdesc = RelationGetPartitionDesc(relation); - partkey = RelationGetPartitionKey(relation); - rel->part_scheme = find_partition_scheme(root, relation); - Assert(partdesc != NULL && rel->part_scheme != NULL); - rel->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey); - rel->nparts = partdesc->nparts; - set_baserel_partition_key_exprs(relation, rel); + rel->part_scheme = find_partition_scheme(root, relation, partkey); + Assert(rel->part_scheme != NULL); + rel->boundinfo = partinfo->boundinfo; + rel->nparts = partinfo->nparts; + set_baserel_partition_key_exprs(partkey, rel); rel->partition_qual = RelationGetPartitionQual(relation); } @@ -1894,9 +1894,9 @@ set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel, * Find or create a PartitionScheme for this Relation. */ static PartitionScheme -find_partition_scheme(PlannerInfo *root, Relation relation) +find_partition_scheme(PlannerInfo *root, Relation rel, + PartitionKey partkey) { - PartitionKey partkey = RelationGetPartitionKey(relation); ListCell *lc; int partnatts, i; @@ -1937,15 +1937,12 @@ find_partition_scheme(PlannerInfo *root, Relation relation) /* * If partopfamily and partopcintype matched, must have the same - * partition comparison functions. Note that we cannot reliably - * Assert the equality of function structs themselves for they might - * be different across PartitionKey's, so just Assert for the function - * OIDs. + * partition comparison functions. */ #ifdef USE_ASSERT_CHECKING for (i = 0; i < partkey->partnatts; i++) - Assert(partkey->partsupfunc[i].fn_oid == - part_scheme->partsupfunc[i].fn_oid); + Assert(partkey->partsupfunc[i] == + part_scheme->partsupfuncinfo[i].fn_oid); #endif /* Found matching partition scheme. */ @@ -1953,39 +1950,22 @@ find_partition_scheme(PlannerInfo *root, Relation relation) } /* - * Did not find matching partition scheme. Create one copying relevant - * information from the relcache. We need to copy the contents of the - * array since the relcache entry may not survive after we have closed the - * relation. + * Did not find matching partition scheme. Create one usinng the + * relevant information copied from the relcache. */ part_scheme = (PartitionScheme) palloc0(sizeof(PartitionSchemeData)); part_scheme->strategy = partkey->strategy; part_scheme->partnatts = partkey->partnatts; - - part_scheme->partopfamily = (Oid *) palloc(sizeof(Oid) * partnatts); - memcpy(part_scheme->partopfamily, partkey->partopfamily, - sizeof(Oid) * partnatts); - - part_scheme->partopcintype = (Oid *) palloc(sizeof(Oid) * partnatts); - memcpy(part_scheme->partopcintype, partkey->partopcintype, - sizeof(Oid) * partnatts); - - part_scheme->partcollation = (Oid *) palloc(sizeof(Oid) * partnatts); - memcpy(part_scheme->partcollation, partkey->partcollation, - sizeof(Oid) * partnatts); - - part_scheme->parttyplen = (int16 *) palloc(sizeof(int16) * partnatts); - memcpy(part_scheme->parttyplen, partkey->parttyplen, - sizeof(int16) * partnatts); - - part_scheme->parttypbyval = (bool *) palloc(sizeof(bool) * partnatts); - memcpy(part_scheme->parttypbyval, partkey->parttypbyval, - sizeof(bool) * partnatts); - - part_scheme->partsupfunc = (FmgrInfo *) + part_scheme->partopfamily = partkey->partopfamily; + part_scheme->partopcintype = partkey->partopcintype; + part_scheme->partcollation = partkey->partcollation; + part_scheme->parttyplen = partkey->parttyplen; + part_scheme->parttypbyval = partkey->parttypbyval; + part_scheme->partsupfuncinfo = (FmgrInfo *) palloc(sizeof(FmgrInfo) * partnatts); for (i = 0; i < partnatts; i++) - fmgr_info_copy(&part_scheme->partsupfunc[i], &partkey->partsupfunc[i], + fmgr_info_copy(&part_scheme->partsupfuncinfo[i], + partition_getprocinfo(rel, partkey, i), CurrentMemoryContext); /* Add the partitioning scheme to PlannerInfo. */ @@ -2002,10 +1982,9 @@ find_partition_scheme(PlannerInfo *root, Relation relation) * nodes. All Var nodes are restamped with the relid of given relation. */ static void -set_baserel_partition_key_exprs(Relation relation, +set_baserel_partition_key_exprs(PartitionKey partkey, RelOptInfo *rel) { - PartitionKey partkey = RelationGetPartitionKey(relation); int partnatts; int cnt; List **partexprs; diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index f9f9904bad..246e57e1c6 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -3559,7 +3559,7 @@ transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd) { case RELKIND_PARTITIONED_TABLE: /* transform the partition bound, if any */ - Assert(RelationGetPartitionKey(parentRel) != NULL); + Assert(parentRel->rd_partkey != NULL); if (cmd->bound != NULL) cxt->partbound = transformPartitionBound(cxt->pstate, parentRel, cmd->bound); @@ -3600,7 +3600,7 @@ transformPartitionBound(ParseState *pstate, Relation parent, PartitionBoundSpec *spec) { PartitionBoundSpec *result_spec; - PartitionKey key = RelationGetPartitionKey(parent); + PartitionKey key = RelationGetPartitionKey(parent); char strategy = get_partition_strategy(key); int partnatts = get_partition_natts(key); List *partexprs = get_partition_exprs(key); diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 7666c6c412..e1744a68e8 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -153,13 +153,13 @@ static List *get_steps_using_prefix_recurse(GeneratePruningStepsContext *context List *step_cmpfns); static PruneStepResult *get_matching_hash_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, - FmgrInfo *partsupfunc, Bitmapset *nullkeys); + FmgrInfo *partsupfuncinfo, Bitmapset *nullkeys); static PruneStepResult *get_matching_list_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum value, int nvalues, - FmgrInfo *partsupfunc, Bitmapset *nullkeys); + FmgrInfo *partsupfuncinfo, Bitmapset *nullkeys); static PruneStepResult *get_matching_range_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, - FmgrInfo *partsupfunc, Bitmapset *nullkeys); + FmgrInfo *partsupfuncinfo, Bitmapset *nullkeys); static bool pull_partkey_params(PartitionPruneInfo *pinfo, List *steps); static PruneStepResult *perform_pruning_base_step(PartitionPruneContext *context, PartitionPruneStepOp *opstep); @@ -437,7 +437,10 @@ prune_append_rel_partitions(RelOptInfo *rel) context.partopfamily = rel->part_scheme->partopfamily; context.partopcintype = rel->part_scheme->partopcintype; context.partcollation = rel->part_scheme->partcollation; - context.partsupfunc = rel->part_scheme->partsupfunc; + for (i = 0; i < context.partnatts; i++) + fmgr_info_copy(&context.partsupfuncinfo[i], + &rel->part_scheme->partsupfuncinfo[i], + CurrentMemoryContext); context.nparts = rel->nparts; context.boundinfo = rel->boundinfo; @@ -1413,7 +1416,8 @@ match_clause_to_partition_key(RelOptInfo *rel, partclause->op_is_ne = false; partclause->expr = expr; /* We know that expr is of Boolean type. */ - partclause->cmpfn = rel->part_scheme->partsupfunc[partkeyidx].fn_oid; + partclause->cmpfn = + rel->part_scheme->partsupfuncinfo[partkeyidx].fn_oid; partclause->op_strategy = InvalidStrategy; *pc = partclause; @@ -1548,7 +1552,7 @@ match_clause_to_partition_key(RelOptInfo *rel, return PARTCLAUSE_UNSUPPORTED; } else - cmpfn = part_scheme->partsupfunc[partkeyidx].fn_oid; + cmpfn = part_scheme->partsupfuncinfo[partkeyidx].fn_oid; partclause = (PartClauseInfo *) palloc(sizeof(PartClauseInfo)); partclause->keyno = partkeyidx; @@ -1962,15 +1966,15 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, * * 'nvalues', the number of Datums in the 'values' array. * - * 'partsupfunc' contains partition hashing functions that can produce correct - * hash for the type of the values contained in 'values'. + * 'partsupfuncinfo' contains partition hashing functions that can produce + * correct hash for the type of the values contained in 'values'. * * 'nullkeys' is the set of partition keys that are null. */ static PruneStepResult * get_matching_hash_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, - FmgrInfo *partsupfunc, Bitmapset *nullkeys) + FmgrInfo *partsupfuncinfo, Bitmapset *nullkeys) { PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult)); PartitionBoundInfo boundinfo = context->boundinfo; @@ -2000,7 +2004,8 @@ get_matching_hash_bounds(PartitionPruneContext *context, isnull[i] = bms_is_member(i, nullkeys); greatest_modulus = get_hash_partition_greatest_modulus(boundinfo); - rowHash = compute_hash_value(partnatts, partsupfunc, values, isnull); + rowHash = compute_hash_value(partnatts, partsupfuncinfo, values, + isnull); if (partindices[rowHash % greatest_modulus] >= 0) result->bound_offsets = @@ -2029,15 +2034,15 @@ get_matching_hash_bounds(PartitionPruneContext *context, * * 'nvalues', if non-zero, should be exactly 1, because of list partitioning. * - * 'partsupfunc' contains the list partitioning comparison function to be used - * to perform partition_list_bsearch + * 'partsupfuncinfo' contains the list partitioning comparison function to be + * used to perform partition_list_bsearch * * 'nullkeys' is the set of partition keys that are null. */ static PruneStepResult * get_matching_list_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum value, int nvalues, - FmgrInfo *partsupfunc, Bitmapset *nullkeys) + FmgrInfo *partsupfuncinfo, Bitmapset *nullkeys) { PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult)); PartitionBoundInfo boundinfo = context->boundinfo; @@ -2102,8 +2107,8 @@ get_matching_list_bounds(PartitionPruneContext *context, result->bound_offsets = bms_add_range(NULL, 0, boundinfo->ndatums - 1); - off = partition_list_bsearch(partsupfunc, partcollation, boundinfo, - value, &is_equal); + off = partition_list_bsearch(partsupfuncinfo, partcollation, + boundinfo, value, &is_equal); if (off >= 0 && is_equal) { @@ -2133,7 +2138,7 @@ get_matching_list_bounds(PartitionPruneContext *context, switch (opstrategy) { case BTEqualStrategyNumber: - off = partition_list_bsearch(partsupfunc, + off = partition_list_bsearch(partsupfuncinfo, partcollation, boundinfo, value, &is_equal); @@ -2150,7 +2155,7 @@ get_matching_list_bounds(PartitionPruneContext *context, inclusive = true; /* fall through */ case BTGreaterStrategyNumber: - off = partition_list_bsearch(partsupfunc, + off = partition_list_bsearch(partsupfuncinfo, partcollation, boundinfo, value, &is_equal); @@ -2185,7 +2190,7 @@ get_matching_list_bounds(PartitionPruneContext *context, inclusive = true; /* fall through */ case BTLessStrategyNumber: - off = partition_list_bsearch(partsupfunc, + off = partition_list_bsearch(partsupfuncinfo, partcollation, boundinfo, value, &is_equal); @@ -2231,16 +2236,16 @@ get_matching_list_bounds(PartitionPruneContext *context, * * 'nvalues', number of Datums in 'values' array. Must be <= context->partnatts. * - * 'partsupfunc' contains the range partitioning comparison functions to be - * used to perform partition_range_datum_bsearch or partition_rbound_datum_cmp - * using. + * 'partsupfuncinfo' contains the range partitioning comparison functions to + * be used to perform partition_range_datum_bsearch or + * partition_rbound_datum_cmp using. * * 'nullkeys' is the set of partition keys that are null. */ static PruneStepResult * get_matching_range_bounds(PartitionPruneContext *context, StrategyNumber opstrategy, Datum *values, int nvalues, - FmgrInfo *partsupfunc, Bitmapset *nullkeys) + FmgrInfo *partsupfuncinfo, Bitmapset *nullkeys) { PruneStepResult *result = (PruneStepResult *) palloc0(sizeof(PruneStepResult)); PartitionBoundInfo boundinfo = context->boundinfo; @@ -2302,7 +2307,7 @@ get_matching_range_bounds(PartitionPruneContext *context, { case BTEqualStrategyNumber: /* Look for the smallest bound that is = lookup value. */ - off = partition_range_datum_bsearch(partsupfunc, + off = partition_range_datum_bsearch(partsupfuncinfo, partcollation, boundinfo, nvalues, values, @@ -2341,7 +2346,7 @@ get_matching_range_bounds(PartitionPruneContext *context, int32 cmpval; cmpval = - partition_rbound_datum_cmp(partsupfunc, + partition_rbound_datum_cmp(partsupfuncinfo, partcollation, boundinfo->datums[off - 1], boundinfo->kind[off - 1], @@ -2352,7 +2357,7 @@ get_matching_range_bounds(PartitionPruneContext *context, } Assert(0 == - partition_rbound_datum_cmp(partsupfunc, + partition_rbound_datum_cmp(partsupfuncinfo, partcollation, boundinfo->datums[off], boundinfo->kind[off], @@ -2381,7 +2386,7 @@ get_matching_range_bounds(PartitionPruneContext *context, { int32 cmpval; - cmpval = partition_rbound_datum_cmp(partsupfunc, + cmpval = partition_rbound_datum_cmp(partsupfuncinfo, partcollation, boundinfo->datums[off + 1], boundinfo->kind[off + 1], @@ -2392,7 +2397,7 @@ get_matching_range_bounds(PartitionPruneContext *context, } Assert(0 == - partition_rbound_datum_cmp(partsupfunc, + partition_rbound_datum_cmp(partsupfuncinfo, partcollation, boundinfo->datums[off], boundinfo->kind[off], @@ -2469,7 +2474,7 @@ get_matching_range_bounds(PartitionPruneContext *context, * Look for the smallest bound that is > or >= lookup value and * set minoff to its offset. */ - off = partition_range_datum_bsearch(partsupfunc, + off = partition_range_datum_bsearch(partsupfuncinfo, partcollation, boundinfo, nvalues, values, @@ -2506,7 +2511,7 @@ get_matching_range_bounds(PartitionPruneContext *context, nextoff = inclusive ? off - 1 : off + 1; cmpval = - partition_rbound_datum_cmp(partsupfunc, + partition_rbound_datum_cmp(partsupfuncinfo, partcollation, boundinfo->datums[nextoff], boundinfo->kind[nextoff], @@ -2518,7 +2523,7 @@ get_matching_range_bounds(PartitionPruneContext *context, } Assert(0 == - partition_rbound_datum_cmp(partsupfunc, + partition_rbound_datum_cmp(partsupfuncinfo, partcollation, boundinfo->datums[off], boundinfo->kind[off], @@ -2548,7 +2553,7 @@ get_matching_range_bounds(PartitionPruneContext *context, * Look for the greatest bound that is < or <= lookup value and * set minoff to its offset. */ - off = partition_range_datum_bsearch(partsupfunc, + off = partition_range_datum_bsearch(partsupfuncinfo, partcollation, boundinfo, nvalues, values, @@ -2575,7 +2580,7 @@ get_matching_range_bounds(PartitionPruneContext *context, int nextoff; nextoff = inclusive ? off + 1 : off - 1; - cmpval = partition_rbound_datum_cmp(partsupfunc, + cmpval = partition_rbound_datum_cmp(partsupfuncinfo, partcollation, boundinfo->datums[nextoff], boundinfo->kind[nextoff], @@ -2587,7 +2592,7 @@ get_matching_range_bounds(PartitionPruneContext *context, } Assert(0 == - partition_rbound_datum_cmp(partsupfunc, + partition_rbound_datum_cmp(partsupfuncinfo, partcollation, boundinfo->datums[off], boundinfo->kind[off], @@ -2747,7 +2752,7 @@ perform_pruning_base_step(PartitionPruneContext *context, int keyno, nvalues; Datum values[PARTITION_MAX_KEYS]; - FmgrInfo partsupfunc[PARTITION_MAX_KEYS]; + FmgrInfo partsupfuncinfo[PARTITION_MAX_KEYS]; /* * There better be the same number of expressions and compare functions. @@ -2796,11 +2801,12 @@ perform_pruning_base_step(PartitionPruneContext *context, */ cmpfn = lfirst_oid(lc2); Assert(OidIsValid(cmpfn)); - if (cmpfn != context->partsupfunc[keyno].fn_oid) - fmgr_info(cmpfn, &partsupfunc[keyno]); + if (cmpfn != context->partsupfuncinfo[keyno].fn_oid) + fmgr_info_cxt(cmpfn, &partsupfuncinfo[keyno], + CurrentMemoryContext); else - fmgr_info_copy(&partsupfunc[keyno], - &context->partsupfunc[keyno], + fmgr_info_copy(&partsupfuncinfo[keyno], + &context->partsupfuncinfo[keyno], CurrentMemoryContext); values[keyno] = datum; @@ -2818,21 +2824,21 @@ perform_pruning_base_step(PartitionPruneContext *context, return get_matching_hash_bounds(context, opstep->opstrategy, values, nvalues, - partsupfunc, + partsupfuncinfo, opstep->nullkeys); case PARTITION_STRATEGY_LIST: return get_matching_list_bounds(context, opstep->opstrategy, values[0], nvalues, - &partsupfunc[0], + &partsupfuncinfo[0], opstep->nullkeys); case PARTITION_STRATEGY_RANGE: return get_matching_range_bounds(context, opstep->opstrategy, values, nvalues, - partsupfunc, + partsupfuncinfo, opstep->nullkeys); default: diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index e81c4691ec..d37e124dba 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -51,6 +51,7 @@ #include "catalog/pg_auth_members.h" #include "catalog/pg_constraint.h" #include "catalog/pg_database.h" +#include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_partitioned_table.h" @@ -68,6 +69,7 @@ #include "commands/policy.h" #include "commands/trigger.h" #include "miscadmin.h" +#include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" @@ -265,8 +267,6 @@ static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK, bool force_non_hi static Relation AllocateRelationDesc(Form_pg_class relp); static void RelationParseRelOptions(Relation relation, HeapTuple tuple); static void RelationBuildTupleDesc(Relation relation); -static void RelationBuildPartitionKey(Relation relation); -static Relation RelationBuildDesc(Oid targetRelId, bool insertIt); static void RelationInitPhysicalAddr(Relation relation); static void load_critical_index(Oid indexoid, Oid heapoid); static TupleDesc GetPgClassDescriptor(void); @@ -286,9 +286,41 @@ static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid, StrategyNumber numSupport); static void RelationCacheInitFileRemoveInDir(const char *tblspcpath); static void unlink_initfile(const char *initfilename); -static bool equalPartitionDescs(PartitionKey key, PartitionDesc partdesc1, - PartitionDesc partdesc2); +/* + * PartitionBoundInfo encapsulates a set of partition bounds. It is usually + * associated with partitioned tables as part of its partition descriptor. + * + * The internal structure appears in partbounds.h. + */ +typedef struct PartitionBoundInfoData *PartitionBoundInfo; + +/* + * Information about partitions of a partitioned table. + */ +typedef struct PartitionDescData +{ + int nparts; /* Number of partitions */ + Oid *oids; /* OIDs of partitions */ + PartitionBoundInfo boundinfo; /* collection of partition bounds */ +} PartitionDescData; + +typedef struct PartitionDescData *PartitionDesc; + +typedef struct PartitionBoundSortInfo +{ + FmgrInfo *partsupfuncinfo; + PartitionKey key; +} PartitionBoundSortInfo; + +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 PartitionKey copy_partition_key(PartitionKey fromkey); +static int get_partition_bound_num_indexes(PartitionBoundInfo b); +static List *generate_partition_qual(Relation rel); /* * ScanPgRelation @@ -874,10 +906,10 @@ RelationBuildRuleLock(Relation relation) } /* - * RelationBuildPartitionKey - * Build and attach to relcache partition key data of relation + * RelationBuildPartitionInfo + * Build and attach to relcache partitionning related data of relation * - * Partitioning key data is a complex structure; to avoid complicated logic to + * Partitioning data is of complex structure; to avoid complicated logic to * free individual elements whenever the relcache entry is flushed, we give it * its own memory context, child of CacheMemoryContext, which can easily be * deleted on its own. To avoid leaking memory in that context in case of an @@ -889,7 +921,7 @@ RelationBuildRuleLock(Relation relation) * permanently. */ static void -RelationBuildPartitionKey(Relation relation) +RelationBuildPartitionInfo(Relation relation) { Form_pg_partitioned_table form; HeapTuple tuple; @@ -901,9 +933,29 @@ RelationBuildPartitionKey(Relation relation) oidvector *collation; ListCell *partexprs_item; Datum datum; - MemoryContext partkeycxt, + MemoryContext partcxt, oldcxt; int16 procnum; + int nparts; + List *inhoids, + *partoids; + Oid *oids = NULL; + List *boundspecs = NIL; + ListCell *cell; + PartitionDesc partdesc; + + 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; tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(RelationGetRelid(relation))); @@ -915,13 +967,13 @@ RelationBuildPartitionKey(Relation relation) if (!HeapTupleIsValid(tuple)) return; - partkeycxt = AllocSetContextCreate(CurTransactionContext, - "partition key", - ALLOCSET_SMALL_SIZES); - MemoryContextCopyAndSetIdentifier(partkeycxt, - RelationGetRelationName(relation)); + partcxt = AllocSetContextCreate(CurTransactionContext, + "partition info", + ALLOCSET_SMALL_SIZES); + MemoryContextCopyAndSetIdentifier(partcxt, + RelationGetRelationName(relation)); - key = (PartitionKey) MemoryContextAllocZero(partkeycxt, + key = (PartitionKey) MemoryContextAllocZero(partcxt, sizeof(PartitionKeyData)); /* Fixed-length attributes */ @@ -973,16 +1025,16 @@ RelationBuildPartitionKey(Relation relation) expr = eval_const_expressions(NULL, expr); fix_opfuncids(expr); - oldcxt = MemoryContextSwitchTo(partkeycxt); + oldcxt = MemoryContextSwitchTo(partcxt); key->partexprs = (List *) copyObject(expr); MemoryContextSwitchTo(oldcxt); } - oldcxt = MemoryContextSwitchTo(partkeycxt); + oldcxt = MemoryContextSwitchTo(partcxt); key->partattrs = (AttrNumber *) palloc0(key->partnatts * sizeof(AttrNumber)); key->partopfamily = (Oid *) palloc0(key->partnatts * sizeof(Oid)); key->partopcintype = (Oid *) palloc0(key->partnatts * sizeof(Oid)); - key->partsupfunc = (FmgrInfo *) palloc0(key->partnatts * sizeof(FmgrInfo)); + key->partsupfunc = (Oid *) palloc0(key->partnatts * sizeof(Oid)); key->partcollation = (Oid *) palloc0(key->partnatts * sizeof(Oid)); @@ -993,6 +1045,13 @@ RelationBuildPartitionKey(Relation relation) key->parttypbyval = (bool *) palloc0(key->partnatts * sizeof(bool)); key->parttypalign = (char *) palloc0(key->partnatts * sizeof(char)); key->parttypcoll = (Oid *) palloc0(key->partnatts * sizeof(Oid)); + + /* + * Also allocate space for partition support procedure FmgrInfo's, but + * they won't be filled until somebody calls partition_get_procinfo. + */ + relation->rd_partsupfuncinfo = (FmgrInfo *) + MemoryContextAllocZero(partcxt, key->partnatts * sizeof(FmgrInfo)); MemoryContextSwitchTo(oldcxt); /* determine support function number to search for */ @@ -1034,7 +1093,7 @@ RelationBuildPartitionKey(Relation relation) procnum, format_type_be(opclassform->opcintype)))); - fmgr_info(funcid, &key->partsupfunc[i]); + key->partsupfunc[i] = funcid; /* Collation */ key->partcollation[i] = collation->values[i]; @@ -1070,12 +1129,1127 @@ RelationBuildPartitionKey(Relation relation) ReleaseSysCache(tuple); /* + * Now build the PartitionDesc that contains information about partitions. + */ + + /* Get partition oids from pg_inherits */ + inhoids = find_inheritance_children(RelationGetRelid(relation), NoLock); + + /* Collect bound spec nodes in a list */ + i = 0; + partoids = NIL; + foreach(cell, inhoids) + { + Oid inhrelid = lfirst_oid(cell); + HeapTuple tuple; + Datum datum; + bool isnull; + Node *boundspec; + + tuple = SearchSysCache1(RELOID, inhrelid); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", inhrelid); + + /* + * It is possible that the pg_class tuple of a partition has not been + * updated yet to set its relpartbound field. The only case where + * this happens is when we open the parent relation to check using its + * partition descriptor that a new partition's bound does not overlap + * some existing partition. + */ + if (!((Form_pg_class) GETSTRUCT(tuple))->relispartition) + { + ReleaseSysCache(tuple); + continue; + } + + datum = SysCacheGetAttr(RELOID, tuple, + Anum_pg_class_relpartbound, + &isnull); + Assert(!isnull); + boundspec = (Node *) stringToNode(TextDatumGetCString(datum)); + + /* + * Sanity check: If the PartitionBoundSpec says this is the default + * partition, its OID should correspond to whatever's stored in + * pg_partitioned_table.partdefid; if not, the catalog is corrupt. + */ + if (castNode(PartitionBoundSpec, boundspec)->is_default) + { + Oid partdefid; + + partdefid = get_default_partition_oid(RelationGetRelid(relation)); + if (partdefid != inhrelid) + elog(ERROR, "expected partdefid %u, but got %u", + inhrelid, partdefid); + } + + boundspecs = lappend(boundspecs, boundspec); + partoids = lappend_oid(partoids, inhrelid); + ReleaseSysCache(tuple); + } + + nparts = list_length(partoids); + + if (nparts > 0) + { + FmgrInfo partsupfuncinfo[PARTITION_MAX_KEYS]; + PartitionBoundSortInfo sortinfo; + + /* Get partsupfunc FmgrInfo's. */ + for (i = 0; i < key->partnatts; i++) + { + fmgr_info_copy(&partsupfuncinfo[i], + partition_getprocinfo(relation, key, i), + CurrentMemoryContext); + } + + sortinfo.partsupfuncinfo = partsupfuncinfo; + sortinfo.key = key; + + 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 *) &sortinfo); + } + 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_range_bound(key, i, spec->lowerdatums, + true); + upper = make_one_range_bound(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 *) &sortinfo); + + /* 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(&partsupfuncinfo[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); + } + + /* Now build the actual relcache partition descriptor */ + oldcxt = MemoryContextSwitchTo(partcxt); + + partdesc = (PartitionDescData *) palloc0(sizeof(PartitionDescData)); + partdesc->nparts = nparts; + if (nparts > 0) + { + PartitionBoundInfo boundinfo; + int *mapping; + int next_index = 0; + + partdesc->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: + { + /* Modulus 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); + } + + partdesc->boundinfo = boundinfo; + + /* + * 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++) + partdesc->oids[mapping[i]] = oids[i]; + pfree(mapping); + } + + MemoryContextSwitchTo(oldcxt); + + /* * Success --- reparent our context and make the relcache point to the * newly constructed key */ - MemoryContextSetParent(partkeycxt, CacheMemoryContext); - relation->rd_partkeycxt = partkeycxt; + MemoryContextSetParent(partcxt, CacheMemoryContext); + relation->rd_partcxt = partcxt; relation->rd_partkey = key; + relation->rd_partdesc = partdesc; +} + +/* + * RelationGetPartitionInfo + * Returns a copy of the partitioning related data stored in relcache + * + * Note: Caller should be in a hopefully short-lived memory context + */ +PartitionInfo * +RelationGetPartitionInfo(Relation relation) +{ + PartitionDesc partdesc = relation->rd_partdesc; + PartitionInfo *pinfo = palloc0(sizeof(PartitionInfo)); + + pinfo->key = copy_partition_key(relation->rd_partkey); + pinfo->nparts = partdesc->nparts; + if (pinfo->nparts > 0) + { + Assert(partdesc->oids != NULL); + pinfo->oids = palloc(partdesc->nparts * sizeof(Oid)); + memcpy(pinfo->oids, partdesc->oids, partdesc->nparts * sizeof(Oid)); + Assert(partdesc->boundinfo != NULL); + pinfo->boundinfo = partition_bounds_copy(partdesc->boundinfo, + pinfo->key); + } + + return pinfo; +} + +/* + * Functions to call when calling RelationGetPartitionInfo might be too much. + */ + +PartitionKey +RelationGetPartitionKey(Relation relation) +{ + PartitionKey partkey = relation->rd_partkey; + + return copy_partition_key(partkey); +} + +int +RelationGetPartitionCount(Relation relation) +{ + PartitionDesc partdesc = relation->rd_partdesc; + + return partdesc->nparts; +} + +Oid * +RelationGetPartitionOids(Relation relation) +{ + PartitionDesc partdesc = relation->rd_partdesc; + Oid *result = NULL; + + Assert(partdesc != NULL); + if (partdesc->nparts > 0) + { + result = palloc(partdesc->nparts * sizeof(Oid)); + memcpy(result, partdesc->oids, partdesc->nparts * sizeof(Oid)); + } + + return result; +} + +/* + * RelationGetDefaultPartitionOid + * + * Return the OID of the default partition, if one exists; else InvalidOid. + */ +Oid +RelationGetDefaultPartitionOid(Relation rel) +{ + PartitionBoundInfo boundinfo; + + /* Shouldn't be here otherwise! */ + Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); + boundinfo = rel->rd_partdesc->boundinfo; + + if (boundinfo && partition_bound_has_default(boundinfo)) + return rel->rd_partdesc->oids[boundinfo->default_index]; + + return InvalidOid; +} + +/* + * partition_getprocinfo + * Return fmgr lookup info of partition support procs from relcache + * + * If it's not been built yet by calling fmgr.c, do that and add it to + * relcache. + */ +FmgrInfo * +partition_getprocinfo(Relation rel, PartitionKey key, int partattoff) +{ + FmgrInfo *info; + + info = rel->rd_partsupfuncinfo; + + Assert(info != NULL); + + info += partattoff; + + /* Initialize the lookup info if first time through */ + if (info->fn_oid == InvalidOid) + { + RegProcedure *func = key->partsupfunc; + RegProcedure procId; + + Assert(func != NULL); + + procId = func[partattoff]; + Assert(RegProcedureIsValid(procId)); + fmgr_info_cxt(procId, info, rel->rd_partcxt); + } + + return info; +} + +/* + * Are two partition bound collections logically equal? + * + * Used in the keep logic of relcache.c (ie, in RelationClearRelation()). + * This is also useful when b1 and b2 are bound collections of two separate + * relations, respectively, because PartitionBoundInfo is a canonical + * representation of partition bounds. + */ +bool +partition_bounds_equal(int partnatts, int16 *parttyplen, bool *parttypbyval, + PartitionBoundInfo b1, PartitionBoundInfo b2) +{ + int i; + + if (b1->strategy != b2->strategy) + return false; + + if (b1->ndatums != b2->ndatums) + return false; + + if (b1->null_index != b2->null_index) + return false; + + if (b1->default_index != b2->default_index) + return false; + + if (b1->strategy == PARTITION_STRATEGY_HASH) + { + int greatest_modulus = get_hash_partition_greatest_modulus(b1); + + /* + * If two hash partitioned tables have different greatest moduli, + * their partition schemes don't match. + */ + if (greatest_modulus != get_hash_partition_greatest_modulus(b2)) + return false; + + /* + * We arrange the partitions in the ascending order of their modulus + * and remainders. Also every modulus is factor of next larger + * modulus. Therefore we can safely store index of a given partition + * in indexes array at remainder of that partition. Also entries at + * (remainder + N * modulus) positions in indexes array are all same + * for (modulus, remainder) specification for any partition. Thus + * datums array from both the given bounds are same, if and only if + * their indexes array will be same. So, it suffices to compare + * indexes array. + */ + for (i = 0; i < greatest_modulus; i++) + if (b1->indexes[i] != b2->indexes[i]) + return false; + +#ifdef USE_ASSERT_CHECKING + + /* + * Nonetheless make sure that the bounds are indeed same when the + * indexes match. Hash partition bound stores modulus and remainder + * at b1->datums[i][0] and b1->datums[i][1] position respectively. + */ + for (i = 0; i < b1->ndatums; i++) + Assert((b1->datums[i][0] == b2->datums[i][0] && + b1->datums[i][1] == b2->datums[i][1])); +#endif + } + else + { + for (i = 0; i < b1->ndatums; i++) + { + int j; + + for (j = 0; j < partnatts; j++) + { + /* For range partitions, the bounds might not be finite. */ + if (b1->kind != NULL) + { + /* The different kinds of bound all differ from each other */ + if (b1->kind[i][j] != b2->kind[i][j]) + return false; + + /* + * Non-finite bounds are equal without further + * examination. + */ + if (b1->kind[i][j] != PARTITION_RANGE_DATUM_VALUE) + continue; + } + + /* + * Compare the actual values. Note that it would be both + * incorrect and unsafe to invoke the comparison operator + * derived from the partitioning specification here. It would + * be incorrect because we want the relcache entry to be + * updated for ANY change to the partition bounds, not just + * those that the partitioning operator thinks are + * significant. It would be unsafe because we might reach + * this code in the context of an aborted transaction, and an + * arbitrary partitioning operator might not be safe in that + * context. datumIsEqual() should be simple enough to be + * safe. + */ + if (!datumIsEqual(b1->datums[i][j], b2->datums[i][j], + parttypbyval[j], parttyplen[j])) + return false; + } + + if (b1->indexes[i] != b2->indexes[i]) + return false; + } + + /* There are ndatums+1 indexes in case of range partitions */ + if (b1->strategy == PARTITION_STRATEGY_RANGE && + b1->indexes[i] != b2->indexes[i]) + return false; + } + return true; +} + +/* + * Return a copy of given PartitionBoundInfo structure. The data types of bounds + * are described by given partition key specification. + */ +PartitionBoundInfo +partition_bounds_copy(PartitionBoundInfo src, + PartitionKey key) +{ + PartitionBoundInfo dest; + int i; + int ndatums; + int partnatts; + int num_indexes; + + dest = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData)); + + dest->strategy = src->strategy; + ndatums = dest->ndatums = src->ndatums; + partnatts = key->partnatts; + + num_indexes = get_partition_bound_num_indexes(src); + + /* List partitioned tables have only a single partition key. */ + Assert(key->strategy != PARTITION_STRATEGY_LIST || partnatts == 1); + + dest->datums = (Datum **) palloc(sizeof(Datum *) * ndatums); + + if (src->kind != NULL) + { + dest->kind = (PartitionRangeDatumKind **) palloc(ndatums * + sizeof(PartitionRangeDatumKind *)); + for (i = 0; i < ndatums; i++) + { + dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts * + sizeof(PartitionRangeDatumKind)); + + memcpy(dest->kind[i], src->kind[i], + sizeof(PartitionRangeDatumKind) * key->partnatts); + } + } + else + dest->kind = NULL; + + for (i = 0; i < ndatums; i++) + { + int j; + + /* + * For a corresponding to hash partition, datums array will have two + * elements - modulus and remainder. + */ + bool hash_part = (key->strategy == PARTITION_STRATEGY_HASH); + int natts = hash_part ? 2 : partnatts; + + dest->datums[i] = (Datum *) palloc(sizeof(Datum) * natts); + + for (j = 0; j < natts; j++) + { + bool byval; + int typlen; + + if (hash_part) + { + typlen = sizeof(int32); /* Always int4 */ + byval = true; /* int4 is pass-by-value */ + } + else + { + byval = key->parttypbyval[j]; + typlen = key->parttyplen[j]; + } + + if (dest->kind == NULL || + dest->kind[i][j] == PARTITION_RANGE_DATUM_VALUE) + dest->datums[i][j] = datumCopy(src->datums[i][j], + byval, typlen); + } + } + + dest->indexes = (int *) palloc(sizeof(int) * num_indexes); + memcpy(dest->indexes, src->indexes, sizeof(int) * num_indexes); + + dest->null_index = src->null_index; + dest->default_index = src->default_index; + + return dest; +} + +/* + * RelationGetPartitionQual + * + * Returns a list of partition quals + */ +List * +RelationGetPartitionQual(Relation rel) +{ + /* Quick exit */ + if (!rel->rd_rel->relispartition) + return NIL; + + return generate_partition_qual(rel); +} + +/* + * get_partition_qual_relid + * + * Returns an expression tree describing the passed-in relation's partition + * constraint. If there is no partition constraint returns NULL; this can + * happen if the default partition is the only partition. + */ +Expr * +get_partition_qual_relid(Oid relid) +{ + Relation rel = heap_open(relid, AccessShareLock); + Expr *result = NULL; + List *and_args; + + /* Do the work only if this relation is a partition. */ + if (rel->rd_rel->relispartition) + { + and_args = generate_partition_qual(rel); + + if (and_args == NIL) + result = NULL; + else if (list_length(and_args) > 1) + result = makeBoolExpr(AND_EXPR, and_args, -1); + else + result = linitial(and_args); + } + + /* Keep the lock. */ + heap_close(rel, NoLock); + + 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; + PartitionBoundSortInfo *sortinfo = (PartitionBoundSortInfo *) arg; + PartitionKey key = sortinfo->key; + + return DatumGetInt32(FunctionCall2Coll(&sortinfo->partsupfuncinfo[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); + PartitionBoundSortInfo *sortinfo = (PartitionBoundSortInfo *) arg; + PartitionKey key = sortinfo->key; + + return partition_rbound_cmp(key->partnatts, sortinfo->partsupfuncinfo, + key->partcollation, b1->datums, b1->kind, + b1->lower, b2); +} + +/* + * copy_partition_key + * + * The copy is allocated in the current memory context. + */ +static PartitionKey +copy_partition_key(PartitionKey fromkey) +{ + PartitionKey newkey; + int n; + + Assert(fromkey != NULL); + + newkey = (PartitionKey) palloc(sizeof(PartitionKeyData)); + + newkey->strategy = fromkey->strategy; + newkey->partnatts = n = fromkey->partnatts; + + newkey->partattrs = (AttrNumber *) palloc(n * sizeof(AttrNumber)); + memcpy(newkey->partattrs, fromkey->partattrs, n * sizeof(AttrNumber)); + + newkey->partexprs = copyObject(fromkey->partexprs); + + newkey->partopfamily = (Oid *) palloc(n * sizeof(Oid)); + memcpy(newkey->partopfamily, fromkey->partopfamily, n * sizeof(Oid)); + + newkey->partopcintype = (Oid *) palloc(n * sizeof(Oid)); + memcpy(newkey->partopcintype, fromkey->partopcintype, n * sizeof(Oid)); + + newkey->partsupfunc = (Oid *) palloc(n * sizeof(Oid)); + memcpy(newkey->partsupfunc, fromkey->partsupfunc, n * sizeof(Oid)); + + newkey->partcollation = (Oid *) palloc(n * sizeof(Oid)); + memcpy(newkey->partcollation, fromkey->partcollation, n * sizeof(Oid)); + + newkey->parttypid = (Oid *) palloc(n * sizeof(Oid)); + memcpy(newkey->parttypid, fromkey->parttypid, n * sizeof(Oid)); + + newkey->parttypmod = (int32 *) palloc(n * sizeof(int32)); + memcpy(newkey->parttypmod, fromkey->parttypmod, n * sizeof(int32)); + + newkey->parttyplen = (int16 *) palloc(n * sizeof(int16)); + memcpy(newkey->parttyplen, fromkey->parttyplen, n * sizeof(int16)); + + newkey->parttypbyval = (bool *) palloc(n * sizeof(bool)); + memcpy(newkey->parttypbyval, fromkey->parttypbyval, n * sizeof(bool)); + + newkey->parttypalign = (char *) palloc(n * sizeof(bool)); + memcpy(newkey->parttypalign, fromkey->parttypalign, n * sizeof(char)); + + newkey->parttypcoll = (Oid *) palloc(n * sizeof(Oid)); + memcpy(newkey->parttypcoll, fromkey->parttypcoll, n * sizeof(Oid)); + + return newkey; +} +/* + * get_partition_bound_num_indexes + * + * Returns the number of the entries in the partition bound indexes array. + */ +static int +get_partition_bound_num_indexes(PartitionBoundInfo bound) +{ + int num_indexes; + + Assert(bound); + + switch (bound->strategy) + { + case PARTITION_STRATEGY_HASH: + + /* + * The number of the entries in the indexes array is same as the + * greatest modulus. + */ + num_indexes = get_hash_partition_greatest_modulus(bound); + break; + + case PARTITION_STRATEGY_LIST: + num_indexes = bound->ndatums; + break; + + case PARTITION_STRATEGY_RANGE: + /* Range partitioned table has an extra index. */ + num_indexes = bound->ndatums + 1; + break; + + default: + elog(ERROR, "unexpected partition strategy: %d", + (int) bound->strategy); + } + + return num_indexes; +} + +/* + * generate_partition_qual + * + * Generate partition predicate from rel's partition bound expression. The + * function returns a NIL list if there is no predicate. + * + * Result expression tree is stored CacheMemoryContext to ensure it survives + * as long as the relcache entry. But we should be running in a less long-lived + * working context. To avoid leaking cache memory if this routine fails partway + * through, we build in working memory and then copy the completed structure + * into cache memory. + */ +static List * +generate_partition_qual(Relation rel) +{ + HeapTuple tuple; + MemoryContext oldcxt; + Datum boundDatum; + bool isnull; + PartitionBoundSpec *bound; + List *my_qual = NIL, + *result = NIL; + Relation parent; + bool found_whole_row; + + /* Guard against stack overflow due to overly deep partition tree */ + check_stack_depth(); + + /* Quick copy */ + if (rel->rd_partcheck != NIL) + return copyObject(rel->rd_partcheck); + + /* Grab at least an AccessShareLock on the parent table */ + parent = heap_open(get_partition_parent(RelationGetRelid(rel)), + AccessShareLock); + + /* Get pg_class.relpartbound */ + tuple = SearchSysCache1(RELOID, RelationGetRelid(rel)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", + RelationGetRelid(rel)); + + boundDatum = SysCacheGetAttr(RELOID, tuple, + Anum_pg_class_relpartbound, + &isnull); + if (isnull) /* should not happen */ + elog(ERROR, "relation \"%s\" has relpartbound = null", + RelationGetRelationName(rel)); + bound = castNode(PartitionBoundSpec, + stringToNode(TextDatumGetCString(boundDatum))); + ReleaseSysCache(tuple); + + my_qual = get_qual_from_partbound(rel, parent, bound); + + /* Add the parent's quals to the list (if any) */ + if (parent->rd_rel->relispartition) + result = list_concat(generate_partition_qual(parent), my_qual); + else + result = my_qual; + + /* + * Change Vars to have partition's attnos instead of the parent's. We do + * this after we concatenate the parent's quals, because we want every Var + * in it to bear this relation's attnos. It's safe to assume varno = 1 + * here. + */ + result = map_partition_varattnos(result, 1, rel, parent, + &found_whole_row); + /* There can never be a whole-row reference here */ + if (found_whole_row) + elog(ERROR, "unexpected whole-row reference found in partition key"); + + /* Save a copy in the relcache */ + oldcxt = MemoryContextSwitchTo(rel->rd_partcxt); + rel->rd_partcheck = copyObject(result); + MemoryContextSwitchTo(oldcxt); + + /* Keep the parent locked until commit */ + heap_close(parent, NoLock); + + return result; } /* @@ -1206,60 +2380,6 @@ equalRSDesc(RowSecurityDesc *rsdesc1, RowSecurityDesc *rsdesc2) } /* - * equalPartitionDescs - * Compare two partition descriptors for logical equality - */ -static bool -equalPartitionDescs(PartitionKey key, PartitionDesc partdesc1, - PartitionDesc partdesc2) -{ - int i; - - if (partdesc1 != NULL) - { - if (partdesc2 == NULL) - return false; - if (partdesc1->nparts != partdesc2->nparts) - return false; - - Assert(key != NULL || partdesc1->nparts == 0); - - /* - * Same oids? If the partitioning structure did not change, that is, - * no partitions were added or removed to the relation, the oids array - * should still match element-by-element. - */ - for (i = 0; i < partdesc1->nparts; i++) - { - if (partdesc1->oids[i] != partdesc2->oids[i]) - return false; - } - - /* - * Now compare partition bound collections. The logic to iterate over - * the collections is private to partition.c. - */ - if (partdesc1->boundinfo != NULL) - { - if (partdesc2->boundinfo == NULL) - return false; - - if (!partition_bounds_equal(key->partnatts, key->parttyplen, - key->parttypbyval, - partdesc1->boundinfo, - partdesc2->boundinfo)) - return false; - } - else if (partdesc2->boundinfo != NULL) - return false; - } - else if (partdesc2 != NULL) - return false; - - return true; -} - -/* * RelationBuildDesc * * Build a relation descriptor. The caller must hold at least @@ -1389,16 +2509,26 @@ RelationBuildDesc(Oid targetRelId, bool insertIt) /* if a partitioned table, initialize key and partition descriptor info */ if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + RelationBuildPartitionInfo(relation); + else if (relation->rd_rel->relispartition) { - RelationBuildPartitionKey(relation); - RelationBuildPartitionDesc(relation); + /* + * Create a cache memory context to allocate memory for partition + * constraint expression tree. Partition constraint itself is built + * only when needed. + */ + relation->rd_partcxt = AllocSetContextCreate(CacheMemoryContext, + "partition info", + ALLOCSET_SMALL_SIZES); + MemoryContextCopyAndSetIdentifier(relation->rd_partcxt, + RelationGetRelationName(relation)); } else { - relation->rd_partkeycxt = NULL; + relation->rd_partcxt = NULL; relation->rd_partkey = NULL; relation->rd_partdesc = NULL; - relation->rd_pdcxt = NULL; + relation->rd_partcheck = NIL; } /* @@ -2401,12 +3531,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc) MemoryContextDelete(relation->rd_rulescxt); if (relation->rd_rsdesc) MemoryContextDelete(relation->rd_rsdesc->rscxt); - if (relation->rd_partkeycxt) - MemoryContextDelete(relation->rd_partkeycxt); - if (relation->rd_pdcxt) - MemoryContextDelete(relation->rd_pdcxt); - if (relation->rd_partcheck) - pfree(relation->rd_partcheck); + if (relation->rd_partcxt) + MemoryContextDelete(relation->rd_partcxt); if (relation->rd_fdwroutine) pfree(relation->rd_fdwroutine); pfree(relation); @@ -2573,8 +3699,6 @@ RelationClearRelation(Relation relation, bool rebuild) bool keep_tupdesc; bool keep_rules; bool keep_policies; - bool keep_partkey; - bool keep_partdesc; /* Build temporary entry, but don't link it into hashtable */ newrel = RelationBuildDesc(save_relid, false); @@ -2605,10 +3729,6 @@ RelationClearRelation(Relation relation, bool rebuild) keep_tupdesc = equalTupleDescs(relation->rd_att, newrel->rd_att); keep_rules = equalRuleLocks(relation->rd_rules, newrel->rd_rules); keep_policies = equalRSDesc(relation->rd_rsdesc, newrel->rd_rsdesc); - keep_partkey = (relation->rd_partkey != NULL); - keep_partdesc = equalPartitionDescs(relation->rd_partkey, - relation->rd_partdesc, - newrel->rd_partdesc); /* * Perform swapping of the relcache entry contents. Within this @@ -2663,18 +3783,6 @@ RelationClearRelation(Relation relation, bool rebuild) SWAPFIELD(Oid, rd_toastoid); /* pgstat_info must be preserved */ SWAPFIELD(struct PgStat_TableStatus *, pgstat_info); - /* partition key must be preserved, if we have one */ - if (keep_partkey) - { - SWAPFIELD(PartitionKey, rd_partkey); - SWAPFIELD(MemoryContext, rd_partkeycxt); - } - /* preserve old partdesc if no logical change */ - if (keep_partdesc) - { - SWAPFIELD(PartitionDesc, rd_partdesc); - SWAPFIELD(MemoryContext, rd_pdcxt); - } #undef SWAPFIELD @@ -3912,19 +5020,10 @@ RelationCacheInitializePhase3(void) /* * Reload the partition key and descriptor for a partitioned table. */ - if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && - relation->rd_partkey == NULL) + if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { - RelationBuildPartitionKey(relation); + RelationBuildPartitionInfo(relation); Assert(relation->rd_partkey != NULL); - - restart = true; - } - - if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && - relation->rd_partdesc == NULL) - { - RelationBuildPartitionDesc(relation); Assert(relation->rd_partdesc != NULL); restart = true; @@ -5777,9 +6876,9 @@ load_relcache_init_file(bool shared) rel->rd_rulescxt = NULL; rel->trigdesc = NULL; rel->rd_rsdesc = NULL; - rel->rd_partkeycxt = NULL; + rel->rd_partcxt = NULL; rel->rd_partkey = NULL; - rel->rd_pdcxt = NULL; + rel->rd_partsupfuncinfo = NULL; rel->rd_partdesc = NULL; rel->rd_partcheck = NIL; rel->rd_indexprs = NIL; diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index b25e25bf9d..6683b34c42 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -22,27 +22,22 @@ /* Seed for the extended hash function */ #define HASH_PARTITION_SEED UINT64CONST(0x7A5B22367996DCFD) -/* - * PartitionBoundInfo encapsulates a set of partition bounds. It is usually - * associated with partitioned tables as part of its partition descriptor. - * - * The internal structure appears in partbounds.h. - */ -typedef struct PartitionBoundInfoData *PartitionBoundInfo; +extern PartitionInfo *RelationGetPartitionInfo(Relation relation); +extern PartitionKey RelationGetPartitionKey(Relation relation); +extern int RelationGetPartitionCount(Relation relation); +extern Oid *RelationGetPartitionOids(Relation relation); +extern Oid RelationGetDefaultPartitionOid(Relation rel); +extern List *RelationGetPartitionQual(Relation rel); +extern Expr *get_partition_qual_relid(Oid relid); -/* - * Information about partitions of a partitioned table. - */ -typedef struct PartitionDescData -{ - int nparts; /* Number of partitions */ - Oid *oids; /* OIDs of partitions */ - PartitionBoundInfo boundinfo; /* collection of partition bounds */ -} PartitionDescData; - -typedef struct PartitionDescData *PartitionDesc; - -extern void RelationBuildPartitionDesc(Relation relation); +extern PartitionRangeBound *make_one_range_bound(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 *partsupfuncinfo, + Oid *partcollation, Datum *datums1, + PartitionRangeDatumKind *kind1, bool lower1, + PartitionRangeBound *b2); extern bool partition_bounds_equal(int partnatts, int16 *parttyplen, bool *parttypbyval, PartitionBoundInfo b1, PartitionBoundInfo b2); @@ -58,19 +53,13 @@ extern List *get_qual_from_partbound(Relation rel, Relation parent, extern List *map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel, bool *found_whole_row); -extern List *RelationGetPartitionQual(Relation rel); -extern Expr *get_partition_qual_relid(Oid relid); extern bool has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr); -extern Oid get_default_oid_from_partdesc(PartitionDesc partdesc); extern Oid get_default_partition_oid(Oid parentId); extern void update_default_partition_oid(Oid parentId, Oid defaultPartId); extern void check_default_allows_bound(Relation parent, Relation defaultRel, PartitionBoundSpec *new_spec); extern List *get_proposed_default_constraint(List *new_part_constaints); -extern int get_partition_for_tuple(Relation relation, Datum *values, - bool *isnull); - #endif /* PARTITION_H */ diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h index b2daf24c41..e83e42577e 100644 --- a/src/include/executor/execPartition.h +++ b/src/include/executor/execPartition.h @@ -26,14 +26,16 @@ * reldesc Relation descriptor of the table * key Partition key information of the table * keystate Execution state required for expressions in the partition key - * partdesc Partition descriptor of the table + * partsupfuncinfo fmgr lookup info of partition support functions + * nparts Number of partitions of the table + * boundinfo PartitionBoundInfo of the table * tupslot A standalone TupleTableSlot initialized with this table's tuple * descriptor * tupmap TupleConversionMap to convert from the parent's rowtype to * this table's rowtype (when extracting the partition key of a * tuple just before routing it through this table) - * indexes Array with partdesc->nparts members (for details on what - * individual members represent, see how they are set in + * indexes Array with nparts members (for details on what individual + * members represent, see how they are set in * get_partition_dispatch_recurse()) *----------------------- */ @@ -42,7 +44,9 @@ typedef struct PartitionDispatchData Relation reldesc; PartitionKey key; List *keystate; /* list of ExprState */ - PartitionDesc partdesc; + FmgrInfo partsupfuncinfo[PARTITION_MAX_KEYS]; + int nparts; + PartitionBoundInfo boundinfo; TupleTableSlot *tupslot; TupleConversionMap *tupmap; int *indexes; diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 73a41c5475..84f3e6a9c6 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -360,7 +360,7 @@ typedef struct PartitionSchemeData bool *parttypbyval; /* Cached information about partition comparison functions. */ - FmgrInfo *partsupfunc; + FmgrInfo *partsupfuncinfo; } PartitionSchemeData; typedef struct PartitionSchemeData *PartitionScheme; diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h index c76014d4a8..bd239c2250 100644 --- a/src/include/partitioning/partbounds.h +++ b/src/include/partitioning/partbounds.h @@ -11,9 +11,6 @@ #ifndef PARTBOUNDS_H #define PARTBOUNDS_H -#include "catalog/partition.h" - - /* * PartitionBoundInfoData encapsulates a set of partition bounds. It is * usually associated with partitioned tables as part of its partition @@ -68,6 +65,8 @@ typedef struct PartitionBoundInfoData * isn't one */ } PartitionBoundInfoData; +typedef struct PartitionBoundInfoData *PartitionBoundInfo; + #define partition_bound_accepts_nulls(bi) ((bi)->null_index != -1) #define partition_bound_has_default(bi) ((bi)->default_index != -1) @@ -101,22 +100,23 @@ typedef struct PartitionRangeBound } PartitionRangeBound; extern int get_hash_partition_greatest_modulus(PartitionBoundInfo b); -extern int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, +extern int partition_list_bsearch(FmgrInfo *partsupfuncinfo, + Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal); -extern int partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc, +extern int partition_range_bsearch(int partnatts, FmgrInfo *partsupfuncinfo, Oid *partcollation, PartitionBoundInfo boundinfo, PartitionRangeBound *probe, bool *is_equal); -extern int partition_range_datum_bsearch(FmgrInfo *partsupfunc, +extern int partition_range_datum_bsearch(FmgrInfo *partsupfuncinfo, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, Datum *values, bool *is_equal); extern int partition_hash_bsearch(PartitionBoundInfo boundinfo, int modulus, int remainder); -extern uint64 compute_hash_value(int partnatts, FmgrInfo *partsupfunc, +extern uint64 compute_hash_value(int partnatts, FmgrInfo *partsupfuncinfo, Datum *values, bool *isnull); -extern int32 partition_rbound_datum_cmp(FmgrInfo *partsupfunc, +extern int32 partition_rbound_datum_cmp(FmgrInfo *partsupfuncinfo, Oid *partcollation, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums, int n_tuple_datums); diff --git a/src/include/partitioning/partprune.h b/src/include/partitioning/partprune.h index 2ae2fd16ed..f078dbdc82 100644 --- a/src/include/partitioning/partprune.h +++ b/src/include/partitioning/partprune.h @@ -30,7 +30,7 @@ typedef struct PartitionPruneContext Oid *partopfamily; Oid *partopcintype; Oid *partcollation; - FmgrInfo *partsupfunc; + FmgrInfo partsupfuncinfo[PARTITION_MAX_KEYS]; /* Number of partitions */ int nparts; diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index ffffde01da..6c81029c68 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -21,6 +21,7 @@ #include "catalog/pg_publication.h" #include "fmgr.h" #include "nodes/bitmapset.h" +#include "partitioning/partbounds.h" #include "rewrite/prs2lock.h" #include "storage/block.h" #include "storage/relfilenode.h" @@ -60,7 +61,7 @@ typedef struct PartitionKeyData Oid *partopfamily; /* OIDs of operator families */ Oid *partopcintype; /* OIDs of opclass declared input data types */ - FmgrInfo *partsupfunc; /* lookup info for support funcs */ + Oid *partsupfunc; /* partition support func OIDs */ /* Partitioning collation per attribute */ Oid *partcollation; @@ -125,9 +126,11 @@ typedef struct RelationData List *rd_fkeylist; /* list of ForeignKeyCacheInfo (see below) */ bool rd_fkeyvalid; /* true if list has been computed */ - MemoryContext rd_partkeycxt; /* private memory cxt for the below */ + MemoryContext rd_partcxt; /* private memory cxt for the values contained + * in the fields related to partitioning */ struct PartitionKeyData *rd_partkey; /* partition key, or NULL */ - MemoryContext rd_pdcxt; /* private context for partdesc */ + FmgrInfo *rd_partsupfuncinfo; /* lookup info for partition support + * procs */ struct PartitionDescData *rd_partdesc; /* partitions, or NULL */ List *rd_partcheck; /* partition CHECK quals */ @@ -248,6 +251,21 @@ typedef struct ForeignKeyCacheInfo } ForeignKeyCacheInfo; /* + * PartitionInfo + * Information related to partitioning for a partitioned table + * + * This consists of the information from PartitionKey and PartitionDesc + * structs that are cached in the table's RelationData. + */ +typedef struct PartitionInfo +{ + PartitionKey key; + int nparts; + Oid *oids; + struct PartitionBoundInfoData *boundinfo; +} PartitionInfo; + +/* * Options common for all all indexes */ typedef struct GenericIndexOpts @@ -612,11 +630,8 @@ typedef struct ViewOptions RelationNeedsWAL(relation) && \ !IsCatalogRelation(relation)) -/* - * RelationGetPartitionKey - * Returns the PartitionKey of a relation - */ -#define RelationGetPartitionKey(relation) ((relation)->rd_partkey) +extern FmgrInfo *partition_getprocinfo(Relation rel, PartitionKey key, + int partattoff); /* * PartitionKey inquiry functions @@ -660,12 +675,6 @@ get_partition_col_typmod(PartitionKey key, int col) return key->parttypmod[col]; } -/* - * RelationGetPartitionDesc - * Returns partition descriptor for a relation. - */ -#define RelationGetPartitionDesc(relation) ((relation)->rd_partdesc) - /* routines in utils/cache/relcache.c */ extern void RelationIncrementReferenceCount(Relation rel); extern void RelationDecrementReferenceCount(Relation rel); -- 2.11.0