v4-0001-Reorganize-partitioning-code.patch
text/plain
Filename: v4-0001-Reorganize-partitioning-code.patch
Type: text/plain
Part: 0
Message:
Re: reorganizing partitioning code
Patch
Format: format-patch
Series: patch v4-0001
Subject: Reorganize partitioning code
| File | + | − |
|---|---|---|
| src/backend/catalog/partition.c | 1 | 1933 |
| src/backend/executor/execMain.c | 0 | 1 |
| src/backend/executor/execPartition.c | 1 | 0 |
| src/backend/optimizer/path/joinrels.c | 1 | 1 |
| src/backend/optimizer/prep/prepunion.c | 0 | 1 |
| src/backend/optimizer/util/plancat.c | 0 | 1 |
| src/backend/optimizer/util/relnode.c | 1 | 1 |
| src/backend/utils/adt/ruleutils.c | 0 | 1 |
| src/backend/utils/cache/Makefile | 3 | 3 |
| src/backend/utils/cache/partcache.c | 2116 | 0 |
| src/backend/utils/cache/relcache.c | 1 | 205 |
| src/include/catalog/partition.h | 1 | 41 |
| src/include/executor/execPartition.h | 1 | 1 |
| src/include/utils/partcache.h | 187 | 0 |
| src/include/utils/rel.h | 1 | 74 |
From 7e59c27070ae358ed37fbd1d0e5b37faf286ac3d Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Tue, 13 Feb 2018 15:59:30 +0900
Subject: [PATCH v4] Reorganize partitioning code
---
src/backend/catalog/partition.c | 1934 +----------------------------
src/backend/executor/execMain.c | 1 -
src/backend/executor/execPartition.c | 1 +
src/backend/optimizer/path/joinrels.c | 2 +-
src/backend/optimizer/prep/prepunion.c | 1 -
src/backend/optimizer/util/plancat.c | 1 -
src/backend/optimizer/util/relnode.c | 2 +-
src/backend/utils/adt/ruleutils.c | 1 -
src/backend/utils/cache/Makefile | 6 +-
src/backend/utils/cache/partcache.c | 2116 ++++++++++++++++++++++++++++++++
src/backend/utils/cache/relcache.c | 206 +---
src/include/catalog/partition.h | 42 +-
src/include/executor/execPartition.h | 2 +-
src/include/utils/partcache.h | 187 +++
src/include/utils/rel.h | 75 +-
15 files changed, 2314 insertions(+), 2263 deletions(-)
create mode 100644 src/backend/utils/cache/partcache.c
create mode 100644 src/include/utils/partcache.h
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 31c80c7f1a..01198bea9e 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -15,11 +15,7 @@
#include "postgres.h"
-#include "access/hash.h"
-#include "access/heapam.h"
#include "access/htup_details.h"
-#include "access/nbtree.h"
-#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaddress.h"
@@ -35,7 +31,6 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
-#include "nodes/parsenodes.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
@@ -48,102 +43,12 @@
#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/hashutils.h"
-#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
-#include "utils/ruleutils.h"
+#include "utils/snapmgr.h"
#include "utils/syscache.h"
-/*
- * Information about bounds of a partitioned relation
- *
- * A list partition datum that is known to be NULL is never put into the
- * datums array. Instead, it is tracked using the null_index field.
- *
- * In the case of range partitioning, ndatums will typically be far less than
- * 2 * nparts, because a partition's upper bound and the next partition's lower
- * bound are the same in most common cases, and we only store one of them (the
- * upper bound). In case of hash partitioning, ndatums will be same as the
- * number of partitions.
- *
- * For range and list partitioned tables, datums is an array of datum-tuples
- * with key->partnatts datums each. For hash partitioned tables, it is an array
- * of datum-tuples with 2 datums, modulus and remainder, corresponding to a
- * given partition.
- *
- * The datums in datums array are arranged in increasing order as defined by
- * functions qsort_partition_rbound_cmp(), qsort_partition_list_value_cmp() and
- * qsort_partition_hbound_cmp() for range, list and hash partitioned tables
- * respectively. For range and list partitions this simply means that the
- * datums in the datums array are arranged in increasing order as defined by
- * the partition key's operator classes and collations.
- *
- * In the case of list partitioning, the indexes array stores one entry for
- * every datum, which is the index of the partition that accepts a given datum.
- * In case of range partitioning, it stores one entry per distinct range
- * datum, which is the index of the partition for which a given datum
- * is an upper bound. In the case of hash partitioning, the number of the
- * entries in the indexes array is same as the greatest modulus amongst all
- * partitions. For a given partition key datum-tuple, the index of the
- * partition which would accept that datum-tuple would be given by the entry
- * pointed by remainder produced when hash value of the datum-tuple is divided
- * by the greatest modulus.
- */
-
-typedef struct PartitionBoundInfoData
-{
- char strategy; /* hash, list or range? */
- int ndatums; /* Length of the datums following array */
- Datum **datums;
- PartitionRangeDatumKind **kind; /* The kind of each range bound datum;
- * NULL for hash and list partitioned
- * tables */
- int *indexes; /* Partition indexes */
- int null_index; /* Index of the null-accepting partition; -1
- * if there isn't one */
- int default_index; /* Index of the default partition; -1 if there
- * isn't one */
-} PartitionBoundInfoData;
-
-#define partition_bound_accepts_nulls(bi) ((bi)->null_index != -1)
-#define partition_bound_has_default(bi) ((bi)->default_index != -1)
-
-/*
- * When qsort'ing partition bounds after reading from the catalog, each bound
- * is represented with one of the following structs.
- */
-
-/* One bound of a hash partition */
-typedef struct PartitionHashBound
-{
- int modulus;
- int remainder;
- int index;
-} PartitionHashBound;
-
-/* One value coming from some (index'th) list partition */
-typedef struct PartitionListValue
-{
- int index;
- Datum value;
-} PartitionListValue;
-
-/* One bound of a range partition */
-typedef struct PartitionRangeBound
-{
- int index;
- Datum *datums; /* range bound datums */
- PartitionRangeDatumKind *kind; /* the kind of each datum */
- bool lower; /* this is the lower (vs upper) bound */
-} PartitionRangeBound;
-
-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);
static Expr *make_partition_op_expr(PartitionKey key, int keynum,
@@ -159,1069 +64,11 @@ 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(PartitionKey key,
- Datum *datums1, PartitionRangeDatumKind *kind1,
- bool lower1, PartitionRangeBound *b2);
-static int32 partition_rbound_datum_cmp(PartitionKey key,
- Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
- Datum *tuple_datums, int n_tuple_datums);
-
-static int partition_list_bsearch(PartitionKey key,
- PartitionBoundInfo boundinfo,
- Datum value, bool *is_equal);
-static int partition_range_bsearch(PartitionKey key,
- PartitionBoundInfo boundinfo,
- PartitionRangeBound *probe, bool *is_equal);
-static int partition_range_datum_bsearch(PartitionKey key,
- PartitionBoundInfo boundinfo,
- int nvalues, Datum *values, bool *is_equal);
-static int partition_hash_bsearch(PartitionKey key,
- PartitionBoundInfo boundinfo,
- int modulus, int remainder);
-
-static int get_partition_bound_num_indexes(PartitionBoundInfo b);
-static int get_greatest_modulus(PartitionBoundInfo b);
-static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull);
/* SQL-callable function for use in hash partition CHECK constraints */
PG_FUNCTION_INFO_V1(satisfies_hash_partition);
/*
- * 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;
-
- /*
- * The following could happen in situations where rel has a pg_class entry
- * but not the pg_partitioned_table entry yet.
- */
- if (key == NULL)
- return;
-
- /* 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 = AllocSetContextCreateExtended(CacheMemoryContext,
- RelationGetRelationName(rel),
- MEMCONTEXT_COPY_NAME,
- ALLOCSET_DEFAULT_SIZES);
- 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_greatest_modulus(b1);
-
- /*
- * If two hash partitioned tables have different greatest moduli,
- * their partition schemes don't match.
- */
- if (greatest_modulus != get_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.
- */
-extern 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
- *
- * Checks if the new partition's bound overlaps any of the existing partitions
- * of parent. Also performs additional checks as necessary per strategy.
- */
-void
-check_new_partition_bound(char *relname, Relation parent,
- PartitionBoundSpec *spec)
-{
- PartitionKey key = RelationGetPartitionKey(parent);
- PartitionDesc partdesc = RelationGetPartitionDesc(parent);
- PartitionBoundInfo boundinfo = partdesc->boundinfo;
- ParseState *pstate = make_parsestate(NULL);
- int with = -1;
- bool overlap = false;
-
- if (spec->is_default)
- {
- if (boundinfo == NULL || !partition_bound_has_default(boundinfo))
- return;
-
- /* Default partition already exists, error out. */
- 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])),
- parser_errposition(pstate, spec->location)));
- }
-
- switch (key->strategy)
- {
- case PARTITION_STRATEGY_HASH:
- {
- Assert(spec->strategy == PARTITION_STRATEGY_HASH);
- Assert(spec->remainder >= 0 && spec->remainder < spec->modulus);
-
- if (partdesc->nparts > 0)
- {
- PartitionBoundInfo boundinfo = partdesc->boundinfo;
- Datum **datums = boundinfo->datums;
- int ndatums = boundinfo->ndatums;
- int greatest_modulus;
- int remainder;
- int offset;
- bool valid_modulus = true;
- int prev_modulus, /* Previous largest modulus */
- next_modulus; /* Next largest modulus */
-
- /*
- * Check rule that every modulus must be a factor of the
- * next larger modulus. For example, if you have a bunch
- * of partitions that all have modulus 5, you can add a
- * new partition with modulus 10 or a new partition with
- * modulus 15, but you cannot add both a partition with
- * modulus 10 and a partition with modulus 15, because 10
- * is not a factor of 15.
- *
- * Get the greatest (modulus, remainder) pair contained in
- * boundinfo->datums that is less than or equal to the
- * (spec->modulus, spec->remainder) pair.
- */
- offset = partition_hash_bsearch(key, boundinfo,
- spec->modulus,
- spec->remainder);
- if (offset < 0)
- {
- next_modulus = DatumGetInt32(datums[0][0]);
- valid_modulus = (next_modulus % spec->modulus) == 0;
- }
- else
- {
- prev_modulus = DatumGetInt32(datums[offset][0]);
- valid_modulus = (spec->modulus % prev_modulus) == 0;
-
- if (valid_modulus && (offset + 1) < ndatums)
- {
- next_modulus = DatumGetInt32(datums[offset + 1][0]);
- valid_modulus = (next_modulus % spec->modulus) == 0;
- }
- }
-
- if (!valid_modulus)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("every hash partition modulus must be a factor of the next larger modulus")));
-
- greatest_modulus = get_greatest_modulus(boundinfo);
- remainder = spec->remainder;
-
- /*
- * Normally, the lowest remainder that could conflict with
- * the new partition is equal to the remainder specified
- * for the new partition, but when the new partition has a
- * modulus higher than any used so far, we need to adjust.
- */
- if (remainder >= greatest_modulus)
- remainder = remainder % greatest_modulus;
-
- /* Check every potentially-conflicting remainder. */
- do
- {
- if (boundinfo->indexes[remainder] != -1)
- {
- overlap = true;
- with = boundinfo->indexes[remainder];
- break;
- }
- remainder += spec->modulus;
- } while (remainder < greatest_modulus);
- }
-
- break;
- }
-
- case PARTITION_STRATEGY_LIST:
- {
- Assert(spec->strategy == PARTITION_STRATEGY_LIST);
-
- if (partdesc->nparts > 0)
- {
- ListCell *cell;
-
- Assert(boundinfo &&
- boundinfo->strategy == PARTITION_STRATEGY_LIST &&
- (boundinfo->ndatums > 0 ||
- partition_bound_accepts_nulls(boundinfo) ||
- partition_bound_has_default(boundinfo)));
-
- foreach(cell, spec->listdatums)
- {
- Const *val = castNode(Const, lfirst(cell));
-
- if (!val->constisnull)
- {
- int offset;
- bool equal;
-
- offset = partition_list_bsearch(key, boundinfo,
- val->constvalue,
- &equal);
- if (offset >= 0 && equal)
- {
- overlap = true;
- with = boundinfo->indexes[offset];
- break;
- }
- }
- else if (partition_bound_accepts_nulls(boundinfo))
- {
- overlap = true;
- with = boundinfo->null_index;
- break;
- }
- }
- }
-
- break;
- }
-
- case PARTITION_STRATEGY_RANGE:
- {
- PartitionRangeBound *lower,
- *upper;
-
- Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
- lower = make_one_range_bound(key, -1, spec->lowerdatums, true);
- upper = make_one_range_bound(key, -1, spec->upperdatums, false);
-
- /*
- * First check if the resulting range would be empty with
- * specified lower and upper bounds
- */
- if (partition_rbound_cmp(key, lower->datums, lower->kind, true,
- upper) >= 0)
- {
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("empty range bound specified for partition \"%s\"",
- relname),
- errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
- get_range_partbound_string(spec->lowerdatums),
- get_range_partbound_string(spec->upperdatums)),
- parser_errposition(pstate, spec->location)));
- }
-
- if (partdesc->nparts > 0)
- {
- PartitionBoundInfo boundinfo = partdesc->boundinfo;
- int offset;
- bool equal;
-
- Assert(boundinfo &&
- boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
- (boundinfo->ndatums > 0 ||
- partition_bound_has_default(boundinfo)));
-
- /*
- * Test whether the new lower bound (which is treated
- * inclusively as part of the new partition) lies inside
- * an existing partition, or in a gap.
- *
- * If it's inside an existing partition, the bound at
- * offset + 1 will be the upper bound of that partition,
- * and its index will be >= 0.
- *
- * If it's in a gap, the bound at offset + 1 will be the
- * lower bound of the next partition, and its index will
- * be -1. This is also true if there is no next partition,
- * since the index array is initialised with an extra -1
- * at the end.
- */
- offset = partition_range_bsearch(key, boundinfo, lower,
- &equal);
-
- if (boundinfo->indexes[offset + 1] < 0)
- {
- /*
- * Check that the new partition will fit in the gap.
- * For it to fit, the new upper bound must be less
- * than or equal to the lower bound of the next
- * partition, if there is one.
- */
- if (offset + 1 < boundinfo->ndatums)
- {
- int32 cmpval;
- Datum *datums;
- PartitionRangeDatumKind *kind;
- bool is_lower;
-
- datums = boundinfo->datums[offset + 1];
- kind = boundinfo->kind[offset + 1];
- is_lower = (boundinfo->indexes[offset + 1] == -1);
-
- cmpval = partition_rbound_cmp(key, datums, kind,
- is_lower, upper);
- if (cmpval < 0)
- {
- /*
- * The new partition overlaps with the
- * existing partition between offset + 1 and
- * offset + 2.
- */
- overlap = true;
- with = boundinfo->indexes[offset + 2];
- }
- }
- }
- else
- {
- /*
- * The new partition overlaps with the existing
- * partition between offset and offset + 1.
- */
- overlap = true;
- with = boundinfo->indexes[offset + 1];
- }
- }
-
- break;
- }
-
- default:
- elog(ERROR, "unexpected partition strategy: %d",
- (int) key->strategy);
- }
-
- if (overlap)
- {
- Assert(with >= 0);
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("partition \"%s\" would overlap partition \"%s\"",
- relname, get_rel_name(partdesc->oids[with])),
- parser_errposition(pstate, spec->location)));
- }
-}
-
-/*
* check_default_allows_bound
*
* This function checks if there exists a row in the default partition that
@@ -1502,54 +349,6 @@ map_partition_varattnos(List *expr, int fromrel_varno,
return expr;
}
-/*
- * 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;
-}
-
/* Module-local functions */
/*
@@ -2466,644 +1265,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_greatest_modulus(boundinfo);
- uint64 rowHash = compute_hash_value(key, 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,
- 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,
- 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
- * partition key expression. It's possible for a column to be both used
- * directly and as part of an expression; if that happens, *used_in_expr may
- * end up as either true or false. That's OK for current uses of this
- * function, because *used_in_expr is only used to tailor the error message
- * text.
- */
-bool
-has_partition_attrs(Relation rel, Bitmapset *attnums,
- bool *used_in_expr)
-{
- PartitionKey key;
- int partnatts;
- List *partexprs;
- ListCell *partexprs_item;
- int i;
-
- if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
- return false;
-
- key = RelationGetPartitionKey(rel);
- partnatts = get_partition_natts(key);
- partexprs = get_partition_exprs(key);
-
- partexprs_item = list_head(partexprs);
- for (i = 0; i < partnatts; i++)
- {
- AttrNumber partattno = get_partition_col_attnum(key, i);
-
- if (partattno != 0)
- {
- if (bms_is_member(partattno - FirstLowInvalidHeapAttributeNumber,
- attnums))
- {
- if (used_in_expr)
- *used_in_expr = false;
- return true;
- }
- }
- else
- {
- /* Arbitrary expression */
- Node *expr = (Node *) lfirst(partexprs_item);
- Bitmapset *expr_attrs = NULL;
-
- /* Find all attributes referenced */
- pull_varattnos(expr, 1, &expr_attrs);
- partexprs_item = lnext(partexprs_item);
-
- if (bms_overlap(attnums, expr_attrs))
- {
- if (used_in_expr)
- *used_in_expr = true;
- return true;
- }
- }
- }
-
- 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
-partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
-{
- if (modulus1 < modulus2)
- return -1;
- if (modulus1 > modulus2)
- return 1;
- if (modulus1 == modulus2 && remainder1 != remainder2)
- return (remainder1 > remainder2) ? 1 : -1;
- return 0;
-}
-
-/*
- * 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 *
-make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
-{
- PartitionRangeBound *bound;
- ListCell *lc;
- int i;
-
- Assert(datums != NIL);
-
- bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
- bound->index = index;
- bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
- bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
- sizeof(PartitionRangeDatumKind));
- bound->lower = lower;
-
- i = 0;
- foreach(lc, datums)
- {
- PartitionRangeDatum *datum = castNode(PartitionRangeDatum, lfirst(lc));
-
- /* What's contained in this range datum? */
- bound->kind[i] = datum->kind;
-
- if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
- {
- Const *val = castNode(Const, datum->value);
-
- if (val->constisnull)
- elog(ERROR, "invalid range bound datum");
- bound->datums[i] = val->constvalue;
- }
-
- i++;
- }
-
- 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, b1->datums, b1->kind, b1->lower, b2);
-}
-
-/*
- * partition_rbound_cmp
- *
- * Return for two range bounds whether the 1st one (specified in datums1,
- * kind1, and lower1) is <, =, or > the bound specified in *b2.
- *
- * Note that if the values of the two range bounds compare equal, then we take
- * into account whether they are upper or lower bounds, and an upper bound is
- * considered to be smaller than a lower bound. This is important to the way
- * that RelationBuildPartitionDesc() builds the PartitionBoundInfoData
- * structure, which only stores the upper bound of a common boundary between
- * two contiguous partitions.
- */
-static int32
-partition_rbound_cmp(PartitionKey key,
- Datum *datums1, PartitionRangeDatumKind *kind1,
- bool lower1, PartitionRangeBound *b2)
-{
- int32 cmpval = 0; /* placate compiler */
- int i;
- Datum *datums2 = b2->datums;
- PartitionRangeDatumKind *kind2 = b2->kind;
- bool lower2 = b2->lower;
-
- for (i = 0; i < key->partnatts; i++)
- {
- /*
- * First, handle cases where the column is unbounded, which should not
- * invoke the comparison procedure, and should not consider any later
- * columns. Note that the PartitionRangeDatumKind enum elements
- * compare the same way as the values they represent.
- */
- if (kind1[i] < kind2[i])
- return -1;
- else if (kind1[i] > kind2[i])
- return 1;
- else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
-
- /*
- * The column bounds are both MINVALUE or both MAXVALUE. No later
- * columns should be considered, but we still need to compare
- * whether they are upper or lower bounds.
- */
- break;
-
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
- key->partcollation[i],
- datums1[i],
- datums2[i]));
- if (cmpval != 0)
- break;
- }
-
- /*
- * If the comparison is anything other than equal, we're done. If they
- * compare equal though, we still have to consider whether the boundaries
- * are inclusive or exclusive. Exclusive one is considered smaller of the
- * two.
- */
- if (cmpval == 0 && lower1 != lower2)
- cmpval = lower1 ? 1 : -1;
-
- return cmpval;
-}
-
-/*
- * partition_rbound_datum_cmp
- *
- * Return whether range bound (specified in rb_datums, rb_kind, and rb_lower)
- * is <, =, or > partition key of tuple (tuple_datums)
- */
-static int32
-partition_rbound_datum_cmp(PartitionKey key,
- Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
- Datum *tuple_datums, int n_tuple_datums)
-{
- int i;
- int32 cmpval = -1;
-
- for (i = 0; i < n_tuple_datums; i++)
- {
- if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
- return -1;
- else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
- return 1;
-
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
- key->partcollation[i],
- rb_datums[i],
- tuple_datums[i]));
- if (cmpval != 0)
- break;
- }
-
- return cmpval;
-}
-
-/*
- * partition_list_bsearch
- * Returns the index of the greatest bound datum that is less than equal
- * to the given value or -1 if all of the bound datums are greater
- *
- * *is_equal is set to true if the bound datum at the returned index is equal
- * to the input value.
- */
-static int
-partition_list_bsearch(PartitionKey key,
- PartitionBoundInfo boundinfo,
- Datum value, bool *is_equal)
-{
- int lo,
- hi,
- mid;
-
- lo = -1;
- hi = boundinfo->ndatums - 1;
- while (lo < hi)
- {
- int32 cmpval;
-
- mid = (lo + hi + 1) / 2;
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
- key->partcollation[0],
- boundinfo->datums[mid][0],
- value));
- if (cmpval <= 0)
- {
- lo = mid;
- *is_equal = (cmpval == 0);
- if (*is_equal)
- break;
- }
- else
- hi = mid - 1;
- }
-
- return lo;
-}
-
-/*
- * partition_range_bsearch
- * Returns the index of the greatest range bound that is less than or
- * equal to the given range bound or -1 if all of the range bounds are
- * greater
- *
- * *is_equal is set to true if the range bound at the returned index is equal
- * to the input range bound
- */
-static int
-partition_range_bsearch(PartitionKey key,
- PartitionBoundInfo boundinfo,
- PartitionRangeBound *probe, bool *is_equal)
-{
- int lo,
- hi,
- mid;
-
- lo = -1;
- hi = boundinfo->ndatums - 1;
- while (lo < hi)
- {
- int32 cmpval;
-
- mid = (lo + hi + 1) / 2;
- cmpval = partition_rbound_cmp(key,
- boundinfo->datums[mid],
- boundinfo->kind[mid],
- (boundinfo->indexes[mid] == -1),
- probe);
- if (cmpval <= 0)
- {
- lo = mid;
- *is_equal = (cmpval == 0);
-
- if (*is_equal)
- break;
- }
- else
- hi = mid - 1;
- }
-
- return lo;
-}
-
-/*
- * partition_range_bsearch
- * Returns the index of the greatest range bound that is less than or
- * equal to the given tuple or -1 if all of the range bounds are greater
- *
- * *is_equal is set to true if the range bound at the returned index is equal
- * to the input tuple.
- */
-static int
-partition_range_datum_bsearch(PartitionKey key,
- PartitionBoundInfo boundinfo,
- int nvalues, Datum *values, bool *is_equal)
-{
- int lo,
- hi,
- mid;
-
- lo = -1;
- hi = boundinfo->ndatums - 1;
- while (lo < hi)
- {
- int32 cmpval;
-
- mid = (lo + hi + 1) / 2;
- cmpval = partition_rbound_datum_cmp(key,
- boundinfo->datums[mid],
- boundinfo->kind[mid],
- values,
- nvalues);
- if (cmpval <= 0)
- {
- lo = mid;
- *is_equal = (cmpval == 0);
-
- if (*is_equal)
- break;
- }
- else
- hi = mid - 1;
- }
-
- return lo;
-}
-
-/*
- * partition_hash_bsearch
- * Returns the index of the greatest (modulus, remainder) pair that is
- * less than or equal to the given (modulus, remainder) pair or -1 if
- * all of them are greater
- */
-static int
-partition_hash_bsearch(PartitionKey key,
- PartitionBoundInfo boundinfo,
- int modulus, int remainder)
-{
- int lo,
- hi,
- mid;
-
- lo = -1;
- hi = boundinfo->ndatums - 1;
- while (lo < hi)
- {
- int32 cmpval,
- bound_modulus,
- bound_remainder;
-
- mid = (lo + hi + 1) / 2;
- bound_modulus = DatumGetInt32(boundinfo->datums[mid][0]);
- bound_remainder = DatumGetInt32(boundinfo->datums[mid][1]);
- cmpval = partition_hbound_cmp(bound_modulus, bound_remainder,
- modulus, remainder);
- if (cmpval <= 0)
- {
- lo = mid;
-
- if (cmpval == 0)
- break;
- }
- else
- hi = mid - 1;
- }
-
- return lo;
-}
-
-/*
- * 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
@@ -3189,99 +1350,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_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_greatest_modulus
- *
- * Returns the greatest modulus of the hash partition bound. The greatest
- * modulus will be at the end of the datums array because hash partitions are
- * arranged in the ascending order of their modulus and remainders.
- */
-static int
-get_greatest_modulus(PartitionBoundInfo bound)
-{
- Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
- Assert(bound->datums && bound->ndatums > 0);
- Assert(DatumGetInt32(bound->datums[bound->ndatums - 1][0]) > 0);
-
- return DatumGetInt32(bound->datums[bound->ndatums - 1][0]);
-}
-
-/*
- * compute_hash_value
- *
- * Compute the hash value for given not null partition key values.
- */
-static uint64
-compute_hash_value(PartitionKey key, Datum *values, bool *isnull)
-{
- int i;
- int nkeys = key->partnatts;
- uint64 rowHash = 0;
- Datum seed = UInt64GetDatum(HASH_PARTITION_SEED);
-
- for (i = 0; i < nkeys; i++)
- {
- if (!isnull[i])
- {
- Datum hash;
-
- Assert(OidIsValid(key->partsupfunc[i].fn_oid));
-
- /*
- * Compute hash for each datum value by calling respective
- * datatype-specific hash functions of each partition key
- * attribute.
- */
- hash = FunctionCall2(&key->partsupfunc[i], values[i], seed);
-
- /* Form a single 64-bit hash value */
- rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
- }
- }
-
- return rowHash;
-}
-
-/*
* satisfies_hash_partition
*
* This is an SQL-callable function for use in hash partition constraints.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 5d3e923cca..b17abb5c7d 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -42,7 +42,6 @@
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/namespace.h"
-#include "catalog/partition.h"
#include "catalog/pg_publication.h"
#include "commands/matview.h"
#include "commands/trigger.h"
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 4048c3ebc6..cc77ba3701 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -20,6 +20,7 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "utils/lsyscache.h"
+#include "utils/rel.h"
#include "utils/rls.h"
#include "utils/ruleutils.h"
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index f74afdb4dd..7164a09995 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -15,13 +15,13 @@
#include "postgres.h"
#include "miscadmin.h"
-#include "catalog/partition.h"
#include "optimizer/clauses.h"
#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/prep.h"
#include "utils/lsyscache.h"
+#include "utils/partcache.h"
#include "utils/memutils.h"
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index b586f941a8..ad2d750bd4 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -33,7 +33,6 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
-#include "catalog/partition.h"
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 60f21711f4..605493d04a 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -27,7 +27,6 @@
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
-#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_statistic_ext.h"
#include "foreign/fdwapi.h"
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 5c368321e6..74f8e0a04d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -17,7 +17,6 @@
#include <limits.h>
#include "miscadmin.h"
-#include "catalog/partition.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
@@ -28,6 +27,7 @@
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "utils/hsearch.h"
+#include "utils/partcache.h"
typedef struct JoinHashEntry
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index ba9fab4582..0797e258b3 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -24,7 +24,6 @@
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
-#include "catalog/partition.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
diff --git a/src/backend/utils/cache/Makefile b/src/backend/utils/cache/Makefile
index a943f8ea4b..94511eaf54 100644
--- a/src/backend/utils/cache/Makefile
+++ b/src/backend/utils/cache/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/utils/cache
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = attoptcache.o catcache.o evtcache.o inval.o plancache.o relcache.o \
- relmapper.o relfilenodemap.o spccache.o syscache.o lsyscache.o \
- typcache.o ts_cache.o
+OBJS = attoptcache.o catcache.o evtcache.o inval.o plancache.o partcache.o \
+ relcache.o relmapper.o relfilenodemap.o spccache.o syscache.o \
+ lsyscache.o typcache.o ts_cache.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c
new file mode 100644
index 0000000000..8d40f3b2ba
--- /dev/null
+++ b/src/backend/utils/cache/partcache.c
@@ -0,0 +1,2116 @@
+/*-------------------------------------------------------------------------
+ *
+ * partcache.c
+ * Partitioning related cache data structures and manipulation functions
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/cache/partcache.c
+ *
+ *-------------------------------------------------------------------------
+*/
+
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/sysattr.h"
+#include "catalog/partition.h"
+#include "catalog/pg_inherits_fn.h"
+#include "catalog/pg_opclass.h"
+#include "catalog/pg_partitioned_table.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/clauses.h"
+#include "optimizer/var.h"
+#include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/hashutils.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/ruleutils.h"
+#include "utils/syscache.h"
+
+/*
+ * Partition bounds are put into using one of the following structs when they
+ * are first read into the backend memory from the catalog wherein they are
+ * stored in in their parser represenation. Especially, the bound comparison
+ * and sort functions expect the bounds passed to them to be of this form.
+ */
+
+/* One bound of a hash partition */
+typedef struct PartitionHashBound
+{
+ int modulus;
+ int remainder;
+ int index;
+} PartitionHashBound;
+
+/* One value coming from some (index'th) list partition */
+typedef struct PartitionListValue
+{
+ int index;
+ Datum value;
+} PartitionListValue;
+
+/* One bound of a range partition */
+typedef struct PartitionRangeBound
+{
+ int index;
+ Datum *datums; /* range bound datums */
+ PartitionRangeDatumKind *kind; /* the kind of each datum */
+ bool lower; /* this is the lower (vs upper) bound */
+} PartitionRangeBound;
+
+static List *generate_partition_qual(Relation rel);
+
+static int32 partition_hbound_cmp(int modulus1, int remainder1, int modulus2,
+ int remainder2);
+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 PartitionRangeBound *make_one_range_bound(PartitionKey key, int index,
+ List *datums, bool lower);
+static int32 partition_rbound_cmp(PartitionKey key,
+ Datum *datums1, PartitionRangeDatumKind *kind1,
+ bool lower1, PartitionRangeBound *b2);
+static int32 qsort_partition_rbound_cmp(const void *a, const void *b,
+ void *arg);
+
+static int partition_list_bsearch(PartitionKey key,
+ PartitionBoundInfo boundinfo,
+ Datum value, bool *is_equal);
+static int partition_range_bsearch(PartitionKey key,
+ PartitionBoundInfo boundinfo,
+ PartitionRangeBound *probe, bool *is_equal);
+static int32 partition_rbound_datum_cmp(PartitionKey key,
+ Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
+ Datum *tuple_datums, int n_tuple_datums);
+static int partition_range_datum_bsearch(PartitionKey key,
+ PartitionBoundInfo boundinfo,
+ int nvalues, Datum *values, bool *is_equal);
+static int partition_hash_bsearch(PartitionKey key,
+ PartitionBoundInfo boundinfo,
+ int modulus, int remainder);
+
+static int get_partition_bound_num_indexes(PartitionBoundInfo b);
+
+/*
+ * RelationBuildPartitionKey
+ * Build and attach to relcache partition key data of relation
+ *
+ * Partitioning key data is a 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
+ * error partway through this function, the context is initially created as a
+ * child of CurTransactionContext and only re-parented to CacheMemoryContext
+ * at the end, when no further errors are possible. Also, we don't make this
+ * context the current context except in very brief code sections, out of fear
+ * that some of our callees allocate memory on their own which would be leaked
+ * permanently.
+ */
+void
+RelationBuildPartitionKey(Relation relation)
+{
+ Form_pg_partitioned_table form;
+ HeapTuple tuple;
+ bool isnull;
+ int i;
+ PartitionKey key;
+ AttrNumber *attrs;
+ oidvector *opclass;
+ oidvector *collation;
+ ListCell *partexprs_item;
+ Datum datum;
+ MemoryContext partkeycxt,
+ oldcxt;
+ int16 procnum;
+
+ tuple = SearchSysCache1(PARTRELID,
+ ObjectIdGetDatum(RelationGetRelid(relation)));
+
+ /*
+ * The following happens when we have created our pg_class entry but not
+ * the pg_partitioned_table entry yet.
+ */
+ if (!HeapTupleIsValid(tuple))
+ return;
+
+ partkeycxt = AllocSetContextCreateExtended(CurTransactionContext,
+ RelationGetRelationName(relation),
+ MEMCONTEXT_COPY_NAME,
+ ALLOCSET_SMALL_SIZES);
+
+ key = (PartitionKey) MemoryContextAllocZero(partkeycxt,
+ sizeof(PartitionKeyData));
+
+ /* Fixed-length attributes */
+ form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
+ key->strategy = form->partstrat;
+ key->partnatts = form->partnatts;
+
+ /*
+ * We can rely on the first variable-length attribute being mapped to the
+ * relevant field of the catalog's C struct, because all previous
+ * attributes are non-nullable and fixed-length.
+ */
+ attrs = form->partattrs.values;
+
+ /* But use the hard way to retrieve further variable-length attributes */
+ /* Operator class */
+ datum = SysCacheGetAttr(PARTRELID, tuple,
+ Anum_pg_partitioned_table_partclass, &isnull);
+ Assert(!isnull);
+ opclass = (oidvector *) DatumGetPointer(datum);
+
+ /* Collation */
+ datum = SysCacheGetAttr(PARTRELID, tuple,
+ Anum_pg_partitioned_table_partcollation, &isnull);
+ Assert(!isnull);
+ collation = (oidvector *) DatumGetPointer(datum);
+
+ /* Expressions */
+ datum = SysCacheGetAttr(PARTRELID, tuple,
+ Anum_pg_partitioned_table_partexprs, &isnull);
+ if (!isnull)
+ {
+ char *exprString;
+ Node *expr;
+
+ exprString = TextDatumGetCString(datum);
+ expr = stringToNode(exprString);
+ pfree(exprString);
+
+ /*
+ * Run the expressions through const-simplification since the planner
+ * will be comparing them to similarly-processed qual clause operands,
+ * and may fail to detect valid matches without this step; fix
+ * opfuncids while at it. We don't need to bother with
+ * canonicalize_qual() though, because partition expressions are not
+ * full-fledged qualification clauses.
+ */
+ expr = eval_const_expressions(NULL, expr);
+ fix_opfuncids(expr);
+
+ oldcxt = MemoryContextSwitchTo(partkeycxt);
+ key->partexprs = (List *) copyObject(expr);
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ oldcxt = MemoryContextSwitchTo(partkeycxt);
+ 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->partcollation = (Oid *) palloc0(key->partnatts * sizeof(Oid));
+
+ /* Gather type and collation info as well */
+ key->parttypid = (Oid *) palloc0(key->partnatts * sizeof(Oid));
+ key->parttypmod = (int32 *) palloc0(key->partnatts * sizeof(int32));
+ key->parttyplen = (int16 *) palloc0(key->partnatts * sizeof(int16));
+ key->parttypbyval = (bool *) palloc0(key->partnatts * sizeof(bool));
+ key->parttypalign = (char *) palloc0(key->partnatts * sizeof(char));
+ key->parttypcoll = (Oid *) palloc0(key->partnatts * sizeof(Oid));
+ MemoryContextSwitchTo(oldcxt);
+
+ /* determine support function number to search for */
+ procnum = (key->strategy == PARTITION_STRATEGY_HASH) ?
+ HASHEXTENDED_PROC : BTORDER_PROC;
+
+ /* Copy partattrs and fill other per-attribute info */
+ memcpy(key->partattrs, attrs, key->partnatts * sizeof(int16));
+ partexprs_item = list_head(key->partexprs);
+ for (i = 0; i < key->partnatts; i++)
+ {
+ AttrNumber attno = key->partattrs[i];
+ HeapTuple opclasstup;
+ Form_pg_opclass opclassform;
+ Oid funcid;
+
+ /* Collect opfamily information */
+ opclasstup = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(opclass->values[i]));
+ if (!HeapTupleIsValid(opclasstup))
+ elog(ERROR, "cache lookup failed for opclass %u", opclass->values[i]);
+
+ opclassform = (Form_pg_opclass) GETSTRUCT(opclasstup);
+ key->partopfamily[i] = opclassform->opcfamily;
+ key->partopcintype[i] = opclassform->opcintype;
+
+ /* Get a support function for the specified opfamily and datatypes */
+ funcid = get_opfamily_proc(opclassform->opcfamily,
+ opclassform->opcintype,
+ opclassform->opcintype,
+ procnum);
+ if (!OidIsValid(funcid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("operator class \"%s\" of access method %s is missing support function %d for type %s",
+ NameStr(opclassform->opcname),
+ (key->strategy == PARTITION_STRATEGY_HASH) ?
+ "hash" : "btree",
+ procnum,
+ format_type_be(opclassform->opcintype))));
+
+ fmgr_info(funcid, &key->partsupfunc[i]);
+
+ /* Collation */
+ key->partcollation[i] = collation->values[i];
+
+ /* Collect type information */
+ if (attno != 0)
+ {
+ Form_pg_attribute att = TupleDescAttr(relation->rd_att, attno - 1);
+
+ key->parttypid[i] = att->atttypid;
+ key->parttypmod[i] = att->atttypmod;
+ key->parttypcoll[i] = att->attcollation;
+ }
+ else
+ {
+ if (partexprs_item == NULL)
+ elog(ERROR, "wrong number of partition key expressions");
+
+ key->parttypid[i] = exprType(lfirst(partexprs_item));
+ key->parttypmod[i] = exprTypmod(lfirst(partexprs_item));
+ key->parttypcoll[i] = exprCollation(lfirst(partexprs_item));
+
+ partexprs_item = lnext(partexprs_item);
+ }
+ get_typlenbyvalalign(key->parttypid[i],
+ &key->parttyplen[i],
+ &key->parttypbyval[i],
+ &key->parttypalign[i]);
+
+ ReleaseSysCache(opclasstup);
+ }
+
+ ReleaseSysCache(tuple);
+
+ /*
+ * Success --- reparent our context and make the relcache point to the
+ * newly constructed key
+ */
+ MemoryContextSetParent(partkeycxt, CacheMemoryContext);
+ relation->rd_partkeycxt = partkeycxt;
+ relation->rd_partkey = key;
+}
+
+/*
+ * 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;
+
+ /*
+ * The following could happen in situations where rel has a pg_class entry
+ * but not the pg_partitioned_table entry yet.
+ */
+ if (key == NULL)
+ return;
+
+ /* 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 = AllocSetContextCreateExtended(CacheMemoryContext,
+ RelationGetRelationName(rel),
+ MEMCONTEXT_COPY_NAME,
+ ALLOCSET_DEFAULT_SIZES);
+ 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_greatest_modulus(b1);
+
+ /*
+ * If two hash partitioned tables have different greatest moduli,
+ * their partition schemes don't match.
+ */
+ if (greatest_modulus != get_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
+ *
+ * Checks if the new partition's bound overlaps any of the existing partitions
+ * of parent. Also performs additional checks as necessary per strategy.
+ */
+void
+check_new_partition_bound(char *relname, Relation parent,
+ PartitionBoundSpec *spec)
+{
+ PartitionKey key = RelationGetPartitionKey(parent);
+ PartitionDesc partdesc = RelationGetPartitionDesc(parent);
+ PartitionBoundInfo boundinfo = partdesc->boundinfo;
+ ParseState *pstate = make_parsestate(NULL);
+ int with = -1;
+ bool overlap = false;
+
+ if (spec->is_default)
+ {
+ if (boundinfo == NULL || !partition_bound_has_default(boundinfo))
+ return;
+
+ /* Default partition already exists, error out. */
+ 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])),
+ parser_errposition(pstate, spec->location)));
+ }
+
+ switch (key->strategy)
+ {
+ case PARTITION_STRATEGY_HASH:
+ {
+ Assert(spec->strategy == PARTITION_STRATEGY_HASH);
+ Assert(spec->remainder >= 0 && spec->remainder < spec->modulus);
+
+ if (partdesc->nparts > 0)
+ {
+ PartitionBoundInfo boundinfo = partdesc->boundinfo;
+ Datum **datums = boundinfo->datums;
+ int ndatums = boundinfo->ndatums;
+ int greatest_modulus;
+ int remainder;
+ int offset;
+ bool valid_modulus = true;
+ int prev_modulus, /* Previous largest modulus */
+ next_modulus; /* Next largest modulus */
+
+ /*
+ * Check rule that every modulus must be a factor of the
+ * next larger modulus. For example, if you have a bunch
+ * of partitions that all have modulus 5, you can add a
+ * new partition with modulus 10 or a new partition with
+ * modulus 15, but you cannot add both a partition with
+ * modulus 10 and a partition with modulus 15, because 10
+ * is not a factor of 15.
+ *
+ * Get the greatest (modulus, remainder) pair contained in
+ * boundinfo->datums that is less than or equal to the
+ * (spec->modulus, spec->remainder) pair.
+ */
+ offset = partition_hash_bsearch(key, boundinfo,
+ spec->modulus,
+ spec->remainder);
+ if (offset < 0)
+ {
+ next_modulus = DatumGetInt32(datums[0][0]);
+ valid_modulus = (next_modulus % spec->modulus) == 0;
+ }
+ else
+ {
+ prev_modulus = DatumGetInt32(datums[offset][0]);
+ valid_modulus = (spec->modulus % prev_modulus) == 0;
+
+ if (valid_modulus && (offset + 1) < ndatums)
+ {
+ next_modulus = DatumGetInt32(datums[offset + 1][0]);
+ valid_modulus = (next_modulus % spec->modulus) == 0;
+ }
+ }
+
+ if (!valid_modulus)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("every hash partition modulus must be a factor of the next larger modulus")));
+
+ greatest_modulus = get_greatest_modulus(boundinfo);
+ remainder = spec->remainder;
+
+ /*
+ * Normally, the lowest remainder that could conflict with
+ * the new partition is equal to the remainder specified
+ * for the new partition, but when the new partition has a
+ * modulus higher than any used so far, we need to adjust.
+ */
+ if (remainder >= greatest_modulus)
+ remainder = remainder % greatest_modulus;
+
+ /* Check every potentially-conflicting remainder. */
+ do
+ {
+ if (boundinfo->indexes[remainder] != -1)
+ {
+ overlap = true;
+ with = boundinfo->indexes[remainder];
+ break;
+ }
+ remainder += spec->modulus;
+ } while (remainder < greatest_modulus);
+ }
+
+ break;
+ }
+
+ case PARTITION_STRATEGY_LIST:
+ {
+ Assert(spec->strategy == PARTITION_STRATEGY_LIST);
+
+ if (partdesc->nparts > 0)
+ {
+ ListCell *cell;
+
+ Assert(boundinfo &&
+ boundinfo->strategy == PARTITION_STRATEGY_LIST &&
+ (boundinfo->ndatums > 0 ||
+ partition_bound_accepts_nulls(boundinfo) ||
+ partition_bound_has_default(boundinfo)));
+
+ foreach(cell, spec->listdatums)
+ {
+ Const *val = castNode(Const, lfirst(cell));
+
+ if (!val->constisnull)
+ {
+ int offset;
+ bool equal;
+
+ offset = partition_list_bsearch(key, boundinfo,
+ val->constvalue,
+ &equal);
+ if (offset >= 0 && equal)
+ {
+ overlap = true;
+ with = boundinfo->indexes[offset];
+ break;
+ }
+ }
+ else if (partition_bound_accepts_nulls(boundinfo))
+ {
+ overlap = true;
+ with = boundinfo->null_index;
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+
+ case PARTITION_STRATEGY_RANGE:
+ {
+ PartitionRangeBound *lower,
+ *upper;
+
+ Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
+ lower = make_one_range_bound(key, -1, spec->lowerdatums, true);
+ upper = make_one_range_bound(key, -1, spec->upperdatums, false);
+
+ /*
+ * First check if the resulting range would be empty with
+ * specified lower and upper bounds
+ */
+ if (partition_rbound_cmp(key, lower->datums, lower->kind, true,
+ upper) >= 0)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("empty range bound specified for partition \"%s\"",
+ relname),
+ errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
+ get_range_partbound_string(spec->lowerdatums),
+ get_range_partbound_string(spec->upperdatums)),
+ parser_errposition(pstate, spec->location)));
+ }
+
+ if (partdesc->nparts > 0)
+ {
+ PartitionBoundInfo boundinfo = partdesc->boundinfo;
+ int offset;
+ bool equal;
+
+ Assert(boundinfo &&
+ boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
+ (boundinfo->ndatums > 0 ||
+ partition_bound_has_default(boundinfo)));
+
+ /*
+ * Test whether the new lower bound (which is treated
+ * inclusively as part of the new partition) lies inside
+ * an existing partition, or in a gap.
+ *
+ * If it's inside an existing partition, the bound at
+ * offset + 1 will be the upper bound of that partition,
+ * and its index will be >= 0.
+ *
+ * If it's in a gap, the bound at offset + 1 will be the
+ * lower bound of the next partition, and its index will
+ * be -1. This is also true if there is no next partition,
+ * since the index array is initialised with an extra -1
+ * at the end.
+ */
+ offset = partition_range_bsearch(key, boundinfo, lower,
+ &equal);
+
+ if (boundinfo->indexes[offset + 1] < 0)
+ {
+ /*
+ * Check that the new partition will fit in the gap.
+ * For it to fit, the new upper bound must be less
+ * than or equal to the lower bound of the next
+ * partition, if there is one.
+ */
+ if (offset + 1 < boundinfo->ndatums)
+ {
+ int32 cmpval;
+ Datum *datums;
+ PartitionRangeDatumKind *kind;
+ bool is_lower;
+
+ datums = boundinfo->datums[offset + 1];
+ kind = boundinfo->kind[offset + 1];
+ is_lower = (boundinfo->indexes[offset + 1] == -1);
+
+ cmpval = partition_rbound_cmp(key, datums, kind,
+ is_lower, upper);
+ if (cmpval < 0)
+ {
+ /*
+ * The new partition overlaps with the
+ * existing partition between offset + 1 and
+ * offset + 2.
+ */
+ overlap = true;
+ with = boundinfo->indexes[offset + 2];
+ }
+ }
+ }
+ else
+ {
+ /*
+ * The new partition overlaps with the existing
+ * partition between offset and offset + 1.
+ */
+ overlap = true;
+ with = boundinfo->indexes[offset + 1];
+ }
+ }
+
+ break;
+ }
+
+ default:
+ elog(ERROR, "unexpected partition strategy: %d",
+ (int) key->strategy);
+ }
+
+ if (overlap)
+ {
+ Assert(with >= 0);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("partition \"%s\" would overlap partition \"%s\"",
+ relname, get_rel_name(partdesc->oids[with])),
+ parser_errposition(pstate, spec->location)));
+ }
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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
+ * partition key expression. It's possible for a column to be both used
+ * directly and as part of an expression; if that happens, *used_in_expr may
+ * end up as either true or false. That's OK for current uses of this
+ * function, because *used_in_expr is only used to tailor the error message
+ * text.
+ */
+bool
+has_partition_attrs(Relation rel, Bitmapset *attnums,
+ bool *used_in_expr)
+{
+ PartitionKey key;
+ int partnatts;
+ List *partexprs;
+ ListCell *partexprs_item;
+ int i;
+
+ if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+ return false;
+
+ key = RelationGetPartitionKey(rel);
+ partnatts = get_partition_natts(key);
+ partexprs = get_partition_exprs(key);
+
+ partexprs_item = list_head(partexprs);
+ for (i = 0; i < partnatts; i++)
+ {
+ AttrNumber partattno = get_partition_col_attnum(key, i);
+
+ if (partattno != 0)
+ {
+ if (bms_is_member(partattno - FirstLowInvalidHeapAttributeNumber,
+ attnums))
+ {
+ if (used_in_expr)
+ *used_in_expr = false;
+ return true;
+ }
+ }
+ else
+ {
+ /* Arbitrary expression */
+ Node *expr = (Node *) lfirst(partexprs_item);
+ Bitmapset *expr_attrs = NULL;
+
+ /* Find all attributes referenced */
+ pull_varattnos(expr, 1, &expr_attrs);
+ partexprs_item = lnext(partexprs_item);
+
+ if (bms_overlap(attnums, expr_attrs))
+ {
+ if (used_in_expr)
+ *used_in_expr = true;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/*
+ * 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_greatest_modulus(boundinfo);
+ uint64 rowHash = compute_hash_value(key, 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,
+ 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,
+ 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;
+}
+
+/*
+ * get_greatest_modulus
+ *
+ * Returns the greatest modulus of the hash partition bound. The greatest
+ * modulus will be at the end of the datums array because hash partitions are
+ * arranged in the ascending order of their modulus and remainders.
+ */
+int
+get_greatest_modulus(PartitionBoundInfo bound)
+{
+ Assert(bound && bound->strategy == PARTITION_STRATEGY_HASH);
+ Assert(bound->datums && bound->ndatums > 0);
+ Assert(DatumGetInt32(bound->datums[bound->ndatums - 1][0]) > 0);
+
+ return DatumGetInt32(bound->datums[bound->ndatums - 1][0]);
+}
+
+/*
+ * compute_hash_value
+ *
+ * Compute the hash value for given not null partition key values.
+ */
+uint64
+compute_hash_value(PartitionKey key, Datum *values, bool *isnull)
+{
+ int i;
+ int nkeys = key->partnatts;
+ uint64 rowHash = 0;
+ Datum seed = UInt64GetDatum(HASH_PARTITION_SEED);
+
+ for (i = 0; i < nkeys; i++)
+ {
+ if (!isnull[i])
+ {
+ Datum hash;
+
+ Assert(OidIsValid(key->partsupfunc[i].fn_oid));
+
+ /*
+ * Compute hash for each datum value by calling respective
+ * datatype-specific hash functions of each partition key
+ * attribute.
+ */
+ hash = FunctionCall2(&key->partsupfunc[i], values[i], seed);
+
+ /* Form a single 64-bit hash value */
+ rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
+ }
+ }
+
+ return rowHash;
+}
+
+/*
+ * 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;
+}
+
+/* Module-local functions. */
+
+/*
+ * 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;
+}
+
+/*
+ * partition_hbound_cmp
+ *
+ * Compares modulus first, then remainder if modulus are equal.
+ */
+static int32
+partition_hbound_cmp(int modulus1, int remainder1, int modulus2, int remainder2)
+{
+ if (modulus1 < modulus2)
+ return -1;
+ if (modulus1 > modulus2)
+ return 1;
+ if (modulus1 == modulus2 && remainder1 != remainder2)
+ return (remainder1 > remainder2) ? 1 : -1;
+ return 0;
+}
+
+/*
+ * qsort_partition_hbound_cmp
+ *
+ * We sort hash bounds by modulus, then by remainder.
+ */
+static int32
+qsort_partition_hbound_cmp(const void *a, const void *b)
+{
+ PartitionHashBound *h1 = (*(PartitionHashBound *const *) a);
+ PartitionHashBound *h2 = (*(PartitionHashBound *const *) b);
+
+ return partition_hbound_cmp(h1->modulus, h1->remainder,
+ h2->modulus, h2->remainder);
+}
+
+/*
+ * qsort_partition_list_value_cmp
+ *
+ * Compare two list partition bound datums
+ */
+static int32
+qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
+{
+ Datum val1 = (*(const PartitionListValue **) a)->value,
+ val2 = (*(const PartitionListValue **) b)->value;
+ PartitionKey key = (PartitionKey) arg;
+
+ return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
+ key->partcollation[0],
+ val1, val2));
+}
+
+/*
+ * 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 *
+make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
+{
+ PartitionRangeBound *bound;
+ ListCell *lc;
+ int i;
+
+ Assert(datums != NIL);
+
+ bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
+ bound->index = index;
+ bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
+ bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
+ sizeof(PartitionRangeDatumKind));
+ bound->lower = lower;
+
+ i = 0;
+ foreach(lc, datums)
+ {
+ PartitionRangeDatum *datum = castNode(PartitionRangeDatum, lfirst(lc));
+
+ /* What's contained in this range datum? */
+ bound->kind[i] = datum->kind;
+
+ if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
+ {
+ Const *val = castNode(Const, datum->value);
+
+ if (val->constisnull)
+ elog(ERROR, "invalid range bound datum");
+ bound->datums[i] = val->constvalue;
+ }
+
+ i++;
+ }
+
+ return bound;
+}
+
+/*
+ * partition_rbound_cmp
+ *
+ * Return for two range bounds whether the 1st one (specified in datums1,
+ * kind1, and lower1) is <, =, or > the bound specified in *b2.
+ *
+ * Note that if the values of the two range bounds compare equal, then we take
+ * into account whether they are upper or lower bounds, and an upper bound is
+ * considered to be smaller than a lower bound. This is important to the way
+ * that RelationBuildPartitionDesc() builds the PartitionBoundInfoData
+ * structure, which only stores the upper bound of a common boundary between
+ * two contiguous partitions.
+ */
+static int32
+partition_rbound_cmp(PartitionKey key,
+ Datum *datums1, PartitionRangeDatumKind *kind1,
+ bool lower1, PartitionRangeBound *b2)
+{
+ int32 cmpval = 0; /* placate compiler */
+ int i;
+ Datum *datums2 = b2->datums;
+ PartitionRangeDatumKind *kind2 = b2->kind;
+ bool lower2 = b2->lower;
+
+ for (i = 0; i < key->partnatts; i++)
+ {
+ /*
+ * First, handle cases where the column is unbounded, which should not
+ * invoke the comparison procedure, and should not consider any later
+ * columns. Note that the PartitionRangeDatumKind enum elements
+ * compare the same way as the values they represent.
+ */
+ if (kind1[i] < kind2[i])
+ return -1;
+ else if (kind1[i] > kind2[i])
+ return 1;
+ else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
+
+ /*
+ * The column bounds are both MINVALUE or both MAXVALUE. No later
+ * columns should be considered, but we still need to compare
+ * whether they are upper or lower bounds.
+ */
+ break;
+
+ cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
+ key->partcollation[i],
+ datums1[i],
+ datums2[i]));
+ if (cmpval != 0)
+ break;
+ }
+
+ /*
+ * If the comparison is anything other than equal, we're done. If they
+ * compare equal though, we still have to consider whether the boundaries
+ * are inclusive or exclusive. Exclusive one is considered smaller of the
+ * two.
+ */
+ if (cmpval == 0 && lower1 != lower2)
+ cmpval = lower1 ? 1 : -1;
+
+ return cmpval;
+}
+
+/* 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, b1->datums, b1->kind, b1->lower, b2);
+}
+
+/*
+ * partition_list_bsearch
+ * Returns the index of the greatest bound datum that is less than equal
+ * to the given value or -1 if all of the bound datums are greater
+ *
+ * *is_equal is set to true if the bound datum at the returned index is equal
+ * to the input value.
+ */
+static int
+partition_list_bsearch(PartitionKey key,
+ PartitionBoundInfo boundinfo,
+ Datum value, bool *is_equal)
+{
+ int lo,
+ hi,
+ mid;
+
+ lo = -1;
+ hi = boundinfo->ndatums - 1;
+ while (lo < hi)
+ {
+ int32 cmpval;
+
+ mid = (lo + hi + 1) / 2;
+ cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
+ key->partcollation[0],
+ boundinfo->datums[mid][0],
+ value));
+ if (cmpval <= 0)
+ {
+ lo = mid;
+ *is_equal = (cmpval == 0);
+ if (*is_equal)
+ break;
+ }
+ else
+ hi = mid - 1;
+ }
+
+ return lo;
+}
+
+/*
+ * partition_rbound_datum_cmp
+ *
+ * Return whether range bound (specified in rb_datums, rb_kind, and rb_lower)
+ * is <, =, or > partition key of tuple (tuple_datums)
+ */
+static int32
+partition_rbound_datum_cmp(PartitionKey key,
+ Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
+ Datum *tuple_datums, int n_tuple_datums)
+{
+ int i;
+ int32 cmpval = -1;
+
+ for (i = 0; i < n_tuple_datums; i++)
+ {
+ if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
+ return -1;
+ else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
+ return 1;
+
+ cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
+ key->partcollation[i],
+ rb_datums[i],
+ tuple_datums[i]));
+ if (cmpval != 0)
+ break;
+ }
+
+ return cmpval;
+}
+
+/*
+ * partition_range_bsearch
+ * Returns the index of the greatest range bound that is less than or
+ * equal to the given range bound or -1 if all of the range bounds are
+ * greater
+ *
+ * *is_equal is set to true if the range bound at the returned index is equal
+ * to the input range bound
+ */
+static int
+partition_range_bsearch(PartitionKey key,
+ PartitionBoundInfo boundinfo,
+ PartitionRangeBound *probe, bool *is_equal)
+{
+ int lo,
+ hi,
+ mid;
+
+ lo = -1;
+ hi = boundinfo->ndatums - 1;
+ while (lo < hi)
+ {
+ int32 cmpval;
+
+ mid = (lo + hi + 1) / 2;
+ cmpval = partition_rbound_cmp(key,
+ boundinfo->datums[mid],
+ boundinfo->kind[mid],
+ (boundinfo->indexes[mid] == -1),
+ probe);
+ if (cmpval <= 0)
+ {
+ lo = mid;
+ *is_equal = (cmpval == 0);
+
+ if (*is_equal)
+ break;
+ }
+ else
+ hi = mid - 1;
+ }
+
+ return lo;
+}
+
+/*
+ * partition_range_datum_bsearch
+ * Returns the index of the greatest range bound that is less than or
+ * equal to the given tuple or -1 if all of the range bounds are greater
+ *
+ * *is_equal is set to true if the range bound at the returned index is equal
+ * to the input tuple.
+ */
+static int
+partition_range_datum_bsearch(PartitionKey key,
+ PartitionBoundInfo boundinfo,
+ int nvalues, Datum *values, bool *is_equal)
+{
+ int lo,
+ hi,
+ mid;
+
+ lo = -1;
+ hi = boundinfo->ndatums - 1;
+ while (lo < hi)
+ {
+ int32 cmpval;
+
+ mid = (lo + hi + 1) / 2;
+ cmpval = partition_rbound_datum_cmp(key,
+ boundinfo->datums[mid],
+ boundinfo->kind[mid],
+ values,
+ nvalues);
+ if (cmpval <= 0)
+ {
+ lo = mid;
+ *is_equal = (cmpval == 0);
+
+ if (*is_equal)
+ break;
+ }
+ else
+ hi = mid - 1;
+ }
+
+ return lo;
+}
+
+/*
+ * partition_hash_bsearch
+ * Returns the index of the greatest (modulus, remainder) pair that is
+ * less than or equal to the given (modulus, remainder) pair or -1 if
+ * all of them are greater
+ */
+static int
+partition_hash_bsearch(PartitionKey key,
+ PartitionBoundInfo boundinfo,
+ int modulus, int remainder)
+{
+ int lo,
+ hi,
+ mid;
+
+ lo = -1;
+ hi = boundinfo->ndatums - 1;
+ while (lo < hi)
+ {
+ int32 cmpval,
+ bound_modulus,
+ bound_remainder;
+
+ mid = (lo + hi + 1) / 2;
+ bound_modulus = DatumGetInt32(boundinfo->datums[mid][0]);
+ bound_remainder = DatumGetInt32(boundinfo->datums[mid][1]);
+ cmpval = partition_hbound_cmp(bound_modulus, bound_remainder,
+ modulus, remainder);
+ if (cmpval <= 0)
+ {
+ lo = mid;
+
+ if (cmpval == 0)
+ break;
+ }
+ else
+ hi = mid - 1;
+ }
+
+ return lo;
+}
+
+/*
+ * 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_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;
+}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1ebf9c4ed2..32fba90be7 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -42,7 +42,6 @@
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
-#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
@@ -81,6 +80,7 @@
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
+#include "utils/partcache.h"
#include "utils/relmapper.h"
#include "utils/resowner_private.h"
#include "utils/snapmgr.h"
@@ -261,7 +261,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);
@@ -809,209 +808,6 @@ RelationBuildRuleLock(Relation relation)
}
/*
- * RelationBuildPartitionKey
- * Build and attach to relcache partition key data of relation
- *
- * Partitioning key data is a 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
- * error partway through this function, the context is initially created as a
- * child of CurTransactionContext and only re-parented to CacheMemoryContext
- * at the end, when no further errors are possible. Also, we don't make this
- * context the current context except in very brief code sections, out of fear
- * that some of our callees allocate memory on their own which would be leaked
- * permanently.
- */
-static void
-RelationBuildPartitionKey(Relation relation)
-{
- Form_pg_partitioned_table form;
- HeapTuple tuple;
- bool isnull;
- int i;
- PartitionKey key;
- AttrNumber *attrs;
- oidvector *opclass;
- oidvector *collation;
- ListCell *partexprs_item;
- Datum datum;
- MemoryContext partkeycxt,
- oldcxt;
- int16 procnum;
-
- tuple = SearchSysCache1(PARTRELID,
- ObjectIdGetDatum(RelationGetRelid(relation)));
-
- /*
- * The following happens when we have created our pg_class entry but not
- * the pg_partitioned_table entry yet.
- */
- if (!HeapTupleIsValid(tuple))
- return;
-
- partkeycxt = AllocSetContextCreateExtended(CurTransactionContext,
- RelationGetRelationName(relation),
- MEMCONTEXT_COPY_NAME,
- ALLOCSET_SMALL_SIZES);
-
- key = (PartitionKey) MemoryContextAllocZero(partkeycxt,
- sizeof(PartitionKeyData));
-
- /* Fixed-length attributes */
- form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
- key->strategy = form->partstrat;
- key->partnatts = form->partnatts;
-
- /*
- * We can rely on the first variable-length attribute being mapped to the
- * relevant field of the catalog's C struct, because all previous
- * attributes are non-nullable and fixed-length.
- */
- attrs = form->partattrs.values;
-
- /* But use the hard way to retrieve further variable-length attributes */
- /* Operator class */
- datum = SysCacheGetAttr(PARTRELID, tuple,
- Anum_pg_partitioned_table_partclass, &isnull);
- Assert(!isnull);
- opclass = (oidvector *) DatumGetPointer(datum);
-
- /* Collation */
- datum = SysCacheGetAttr(PARTRELID, tuple,
- Anum_pg_partitioned_table_partcollation, &isnull);
- Assert(!isnull);
- collation = (oidvector *) DatumGetPointer(datum);
-
- /* Expressions */
- datum = SysCacheGetAttr(PARTRELID, tuple,
- Anum_pg_partitioned_table_partexprs, &isnull);
- if (!isnull)
- {
- char *exprString;
- Node *expr;
-
- exprString = TextDatumGetCString(datum);
- expr = stringToNode(exprString);
- pfree(exprString);
-
- /*
- * Run the expressions through const-simplification since the planner
- * will be comparing them to similarly-processed qual clause operands,
- * and may fail to detect valid matches without this step; fix
- * opfuncids while at it. We don't need to bother with
- * canonicalize_qual() though, because partition expressions are not
- * full-fledged qualification clauses.
- */
- expr = eval_const_expressions(NULL, expr);
- fix_opfuncids(expr);
-
- oldcxt = MemoryContextSwitchTo(partkeycxt);
- key->partexprs = (List *) copyObject(expr);
- MemoryContextSwitchTo(oldcxt);
- }
-
- oldcxt = MemoryContextSwitchTo(partkeycxt);
- 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->partcollation = (Oid *) palloc0(key->partnatts * sizeof(Oid));
-
- /* Gather type and collation info as well */
- key->parttypid = (Oid *) palloc0(key->partnatts * sizeof(Oid));
- key->parttypmod = (int32 *) palloc0(key->partnatts * sizeof(int32));
- key->parttyplen = (int16 *) palloc0(key->partnatts * sizeof(int16));
- key->parttypbyval = (bool *) palloc0(key->partnatts * sizeof(bool));
- key->parttypalign = (char *) palloc0(key->partnatts * sizeof(char));
- key->parttypcoll = (Oid *) palloc0(key->partnatts * sizeof(Oid));
- MemoryContextSwitchTo(oldcxt);
-
- /* determine support function number to search for */
- procnum = (key->strategy == PARTITION_STRATEGY_HASH) ?
- HASHEXTENDED_PROC : BTORDER_PROC;
-
- /* Copy partattrs and fill other per-attribute info */
- memcpy(key->partattrs, attrs, key->partnatts * sizeof(int16));
- partexprs_item = list_head(key->partexprs);
- for (i = 0; i < key->partnatts; i++)
- {
- AttrNumber attno = key->partattrs[i];
- HeapTuple opclasstup;
- Form_pg_opclass opclassform;
- Oid funcid;
-
- /* Collect opfamily information */
- opclasstup = SearchSysCache1(CLAOID,
- ObjectIdGetDatum(opclass->values[i]));
- if (!HeapTupleIsValid(opclasstup))
- elog(ERROR, "cache lookup failed for opclass %u", opclass->values[i]);
-
- opclassform = (Form_pg_opclass) GETSTRUCT(opclasstup);
- key->partopfamily[i] = opclassform->opcfamily;
- key->partopcintype[i] = opclassform->opcintype;
-
- /* Get a support function for the specified opfamily and datatypes */
- funcid = get_opfamily_proc(opclassform->opcfamily,
- opclassform->opcintype,
- opclassform->opcintype,
- procnum);
- if (!OidIsValid(funcid))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("operator class \"%s\" of access method %s is missing support function %d for type %s",
- NameStr(opclassform->opcname),
- (key->strategy == PARTITION_STRATEGY_HASH) ?
- "hash" : "btree",
- procnum,
- format_type_be(opclassform->opcintype))));
-
- fmgr_info(funcid, &key->partsupfunc[i]);
-
- /* Collation */
- key->partcollation[i] = collation->values[i];
-
- /* Collect type information */
- if (attno != 0)
- {
- Form_pg_attribute att = TupleDescAttr(relation->rd_att, attno - 1);
-
- key->parttypid[i] = att->atttypid;
- key->parttypmod[i] = att->atttypmod;
- key->parttypcoll[i] = att->attcollation;
- }
- else
- {
- if (partexprs_item == NULL)
- elog(ERROR, "wrong number of partition key expressions");
-
- key->parttypid[i] = exprType(lfirst(partexprs_item));
- key->parttypmod[i] = exprTypmod(lfirst(partexprs_item));
- key->parttypcoll[i] = exprCollation(lfirst(partexprs_item));
-
- partexprs_item = lnext(partexprs_item);
- }
- get_typlenbyvalalign(key->parttypid[i],
- &key->parttyplen[i],
- &key->parttypbyval[i],
- &key->parttypalign[i]);
-
- ReleaseSysCache(opclasstup);
- }
-
- ReleaseSysCache(tuple);
-
- /*
- * Success --- reparent our context and make the relcache point to the
- * newly constructed key
- */
- MemoryContextSetParent(partkeycxt, CacheMemoryContext);
- relation->rd_partkeycxt = partkeycxt;
- relation->rd_partkey = key;
-}
-
-/*
* equalRuleLocks
*
* Determine whether two RuleLocks are equivalent
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 2faf0ca26e..21abc8babd 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -14,63 +14,23 @@
#define PARTITION_H
#include "fmgr.h"
-#include "executor/tuptable.h"
-#include "nodes/execnodes.h"
-#include "parser/parse_node.h"
+#include "nodes/parsenodes.h"
#include "utils/rel.h"
/* 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 is opaque outside partition.c.
- */
-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;
-
-extern void RelationBuildPartitionDesc(Relation relation);
-extern bool partition_bounds_equal(int partnatts, int16 *parttyplen,
- bool *parttypbyval, PartitionBoundInfo b1,
- PartitionBoundInfo b2);
-extern PartitionBoundInfo partition_bounds_copy(PartitionBoundInfo src,
- PartitionKey key);
-
-extern void check_new_partition_bound(char *relname, Relation parent,
- PartitionBoundSpec *spec);
extern Oid get_partition_parent(Oid relid);
extern List *get_qual_from_partbound(Relation rel, Relation parent,
PartitionBoundSpec *spec);
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);
-/* For tuple routing */
-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 3df9c498bb..c53dfcc265 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -13,10 +13,10 @@
#ifndef EXECPARTITION_H
#define EXECPARTITION_H
-#include "catalog/partition.h"
#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
#include "nodes/plannodes.h"
+#include "utils/partcache.h"
/*-----------------------
* PartitionDispatch - information about one partitioned table in a partition
diff --git a/src/include/utils/partcache.h b/src/include/utils/partcache.h
new file mode 100644
index 0000000000..4bbd2962a9
--- /dev/null
+++ b/src/include/utils/partcache.h
@@ -0,0 +1,187 @@
+/*-------------------------------------------------------------------------
+ *
+ * partcache.h
+ * Header file for partitioning related cached data structures and
+ * manipulation functions
+ *
+ * Copyright (c) 2007-2018, PostgreSQL Global Development Group
+ *
+ * src/include/utils/partcache.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARTCACHE_H
+#define PARTCACHE_H
+
+#include "fmgr.h"
+#include "utils/relcache.h"
+
+/*
+ * Information about the partition key of a relation
+ */
+typedef struct PartitionKeyData
+{
+ char strategy; /* partitioning strategy */
+ int16 partnatts; /* number of columns in the partition key */
+ AttrNumber *partattrs; /* attribute numbers of columns in the
+ * partition key */
+ List *partexprs; /* list of expressions in the partitioning
+ * key, or NIL */
+
+ Oid *partopfamily; /* OIDs of operator families */
+ Oid *partopcintype; /* OIDs of opclass declared input data types */
+ FmgrInfo *partsupfunc; /* lookup info for support funcs */
+
+ /* Partitioning collation per attribute */
+ Oid *partcollation;
+
+ /* Type information per attribute */
+ Oid *parttypid;
+ int32 *parttypmod;
+ int16 *parttyplen;
+ bool *parttypbyval;
+ char *parttypalign;
+ Oid *parttypcoll;
+} PartitionKeyData;
+
+typedef struct PartitionKeyData *PartitionKey;
+
+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;
+
+/*
+ * Information about bounds of a partitioned relation
+ *
+ * A list partition datum that is known to be NULL is never put into the
+ * datums array. Instead, it is tracked using the null_index field.
+ *
+ * In the case of range partitioning, ndatums will typically be far less than
+ * 2 * nparts, because a partition's upper bound and the next partition's lower
+ * bound are the same in most common cases, and we only store one of them (the
+ * upper bound). In case of hash partitioning, ndatums will be same as the
+ * number of partitions.
+ *
+ * For range and list partitioned tables, datums is an array of datum-tuples
+ * with key->partnatts datums each. For hash partitioned tables, it is an array
+ * of datum-tuples with 2 datums, modulus and remainder, corresponding to a
+ * given partition.
+ *
+ * The datums in datums array are arranged in increasing order as defined by
+ * functions qsort_partition_rbound_cmp(), qsort_partition_list_value_cmp() and
+ * qsort_partition_hbound_cmp() for range, list and hash partitioned tables
+ * respectively. For range and list partitions this simply means that the
+ * datums in the datums array are arranged in increasing order as defined by
+ * the partition key's operator classes and collations.
+ *
+ * In the case of list partitioning, the indexes array stores one entry for
+ * every datum, which is the index of the partition that accepts a given datum.
+ * In case of range partitioning, it stores one entry per distinct range
+ * datum, which is the index of the partition for which a given datum
+ * is an upper bound. In the case of hash partitioning, the number of the
+ * entries in the indexes array is same as the greatest modulus amongst all
+ * partitions. For a given partition key datum-tuple, the index of the
+ * partition which would accept that datum-tuple would be given by the entry
+ * pointed by remainder produced when hash value of the datum-tuple is divided
+ * by the greatest modulus.
+ */
+
+typedef struct PartitionBoundInfoData
+{
+ char strategy; /* hash, list or range? */
+ int ndatums; /* Length of the datums following array */
+ Datum **datums;
+ PartitionRangeDatumKind **kind; /* The kind of each range bound datum;
+ * NULL for hash and list partitioned
+ * tables */
+ int *indexes; /* Partition indexes */
+ int null_index; /* Index of the null-accepting partition; -1
+ * if there isn't one */
+ int default_index; /* Index of the default partition; -1 if there
+ * isn't one */
+} PartitionBoundInfoData;
+
+#define partition_bound_accepts_nulls(bi) ((bi)->null_index != -1)
+#define partition_bound_has_default(bi) ((bi)->default_index != -1)
+
+/*
+ * PartitionKey inquiry functions
+ */
+static inline int
+get_partition_strategy(PartitionKey key)
+{
+ return key->strategy;
+}
+
+static inline int
+get_partition_natts(PartitionKey key)
+{
+ return key->partnatts;
+}
+
+static inline List *
+get_partition_exprs(PartitionKey key)
+{
+ return key->partexprs;
+}
+
+/*
+ * PartitionKey inquiry functions - one column
+ */
+static inline int16
+get_partition_col_attnum(PartitionKey key, int col)
+{
+ return key->partattrs[col];
+}
+
+static inline Oid
+get_partition_col_typid(PartitionKey key, int col)
+{
+ return key->parttypid[col];
+}
+
+static inline int32
+get_partition_col_typmod(PartitionKey key, int col)
+{
+ return key->parttypmod[col];
+}
+
+extern void RelationBuildPartitionKey(Relation relation);
+extern void RelationBuildPartitionDesc(Relation relation);
+extern bool partition_bounds_equal(int partnatts, int16 *parttyplen,
+ bool *parttypbyval, PartitionBoundInfo b1,
+ PartitionBoundInfo b2);
+
+extern PartitionBoundInfo partition_bounds_copy(PartitionBoundInfo src,
+ PartitionKey key);
+
+extern void check_new_partition_bound(char *relname, Relation parent,
+ PartitionBoundSpec *spec);
+
+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 int get_greatest_modulus(PartitionBoundInfo b);
+extern uint64 compute_hash_value(PartitionKey key, Datum *values,
+ bool *isnull);
+
+/* For tuple routing */
+extern int get_partition_for_tuple(Relation relation, Datum *values,
+ bool *isnull);
+
+#endif /* PARTCACHE_H */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index aa8add544a..db3545ec7f 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -19,12 +19,11 @@
#include "catalog/pg_class.h"
#include "catalog/pg_index.h"
#include "catalog/pg_publication.h"
-#include "fmgr.h"
#include "nodes/bitmapset.h"
#include "rewrite/prs2lock.h"
#include "storage/block.h"
#include "storage/relfilenode.h"
-#include "utils/relcache.h"
+#include "utils/partcache.h"
#include "utils/reltrigger.h"
@@ -47,36 +46,6 @@ typedef struct LockInfoData
typedef LockInfoData *LockInfo;
/*
- * Information about the partition key of a relation
- */
-typedef struct PartitionKeyData
-{
- char strategy; /* partitioning strategy */
- int16 partnatts; /* number of columns in the partition key */
- AttrNumber *partattrs; /* attribute numbers of columns in the
- * partition key */
- List *partexprs; /* list of expressions in the partitioning
- * key, or NIL */
-
- Oid *partopfamily; /* OIDs of operator families */
- Oid *partopcintype; /* OIDs of opclass declared input data types */
- FmgrInfo *partsupfunc; /* lookup info for support funcs */
-
- /* Partitioning collation per attribute */
- Oid *partcollation;
-
- /* Type information per attribute */
- Oid *parttypid;
- int32 *parttypmod;
- int16 *parttyplen;
- bool *parttypbyval;
- char *parttypalign;
- Oid *parttypcoll;
-} PartitionKeyData;
-
-typedef struct PartitionKeyData *PartitionKey;
-
-/*
* Here are the contents of a relation cache entry.
*/
@@ -593,48 +562,6 @@ typedef struct ViewOptions
#define RelationGetPartitionKey(relation) ((relation)->rd_partkey)
/*
- * PartitionKey inquiry functions
- */
-static inline int
-get_partition_strategy(PartitionKey key)
-{
- return key->strategy;
-}
-
-static inline int
-get_partition_natts(PartitionKey key)
-{
- return key->partnatts;
-}
-
-static inline List *
-get_partition_exprs(PartitionKey key)
-{
- return key->partexprs;
-}
-
-/*
- * PartitionKey inquiry functions - one column
- */
-static inline int16
-get_partition_col_attnum(PartitionKey key, int col)
-{
- return key->partattrs[col];
-}
-
-static inline Oid
-get_partition_col_typid(PartitionKey key, int col)
-{
- return key->parttypid[col];
-}
-
-static inline int32
-get_partition_col_typmod(PartitionKey key, int col)
-{
- return key->parttypmod[col];
-}
-
-/*
* RelationGetPartitionDesc
* Returns partition descriptor for a relation.
*/
--
2.11.0