mirror of
https://github.com/postgres/postgres.git
synced 2025-06-26 12:21:12 +03:00
Centralize executor-related partitioning code.
Some code is moved from partition.c, which has grown very quickly lately; splitting the executor parts out might help to keep it from getting totally out of control. Other code is moved from execMain.c. All is moved to a new file execPartition.c. get_partition_for_tuple now has a new interface that more clearly separates executor concerns from generic concerns. Amit Langote. A slight comment tweak by me. Discussion: http://postgr.es/m/1f0985f8-3b61-8bc4-4350-baa6d804cb6d@lab.ntt.co.jp
This commit is contained in:
@ -170,8 +170,6 @@ static int32 partition_bound_cmp(PartitionKey key,
|
|||||||
static int partition_bound_bsearch(PartitionKey key,
|
static int partition_bound_bsearch(PartitionKey key,
|
||||||
PartitionBoundInfo boundinfo,
|
PartitionBoundInfo boundinfo,
|
||||||
void *probe, bool probe_is_bound, bool *is_equal);
|
void *probe, bool probe_is_bound, bool *is_equal);
|
||||||
static void get_partition_dispatch_recurse(Relation rel, Relation parent,
|
|
||||||
List **pds, List **leaf_part_oids);
|
|
||||||
static int get_partition_bound_num_indexes(PartitionBoundInfo b);
|
static int get_partition_bound_num_indexes(PartitionBoundInfo b);
|
||||||
static int get_greatest_modulus(PartitionBoundInfo b);
|
static int get_greatest_modulus(PartitionBoundInfo b);
|
||||||
static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull);
|
static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull);
|
||||||
@ -1530,148 +1528,6 @@ get_partition_qual_relid(Oid relid)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* RelationGetPartitionDispatchInfo
|
|
||||||
* Returns information necessary to route tuples down a partition tree
|
|
||||||
*
|
|
||||||
* The number of elements in the returned array (that is, the number of
|
|
||||||
* PartitionDispatch objects for the partitioned tables in the partition tree)
|
|
||||||
* is returned in *num_parted and a list of the OIDs of all the leaf
|
|
||||||
* partitions of rel is returned in *leaf_part_oids.
|
|
||||||
*
|
|
||||||
* All the relations in the partition tree (including 'rel') must have been
|
|
||||||
* locked (using at least the AccessShareLock) by the caller.
|
|
||||||
*/
|
|
||||||
PartitionDispatch *
|
|
||||||
RelationGetPartitionDispatchInfo(Relation rel,
|
|
||||||
int *num_parted, List **leaf_part_oids)
|
|
||||||
{
|
|
||||||
List *pdlist = NIL;
|
|
||||||
PartitionDispatchData **pd;
|
|
||||||
ListCell *lc;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
|
|
||||||
|
|
||||||
*num_parted = 0;
|
|
||||||
*leaf_part_oids = NIL;
|
|
||||||
|
|
||||||
get_partition_dispatch_recurse(rel, NULL, &pdlist, leaf_part_oids);
|
|
||||||
*num_parted = list_length(pdlist);
|
|
||||||
pd = (PartitionDispatchData **) palloc(*num_parted *
|
|
||||||
sizeof(PartitionDispatchData *));
|
|
||||||
i = 0;
|
|
||||||
foreach(lc, pdlist)
|
|
||||||
{
|
|
||||||
pd[i++] = lfirst(lc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* get_partition_dispatch_recurse
|
|
||||||
* Recursively expand partition tree rooted at rel
|
|
||||||
*
|
|
||||||
* As the partition tree is expanded in a depth-first manner, we maintain two
|
|
||||||
* global lists: of PartitionDispatch objects corresponding to partitioned
|
|
||||||
* tables in *pds and of the leaf partition OIDs in *leaf_part_oids.
|
|
||||||
*
|
|
||||||
* Note that the order of OIDs of leaf partitions in leaf_part_oids matches
|
|
||||||
* the order in which the planner's expand_partitioned_rtentry() processes
|
|
||||||
* them. It's not necessarily the case that the offsets match up exactly,
|
|
||||||
* because constraint exclusion might prune away some partitions on the
|
|
||||||
* planner side, whereas we'll always have the complete list; but unpruned
|
|
||||||
* partitions will appear in the same order in the plan as they are returned
|
|
||||||
* here.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_partition_dispatch_recurse(Relation rel, Relation parent,
|
|
||||||
List **pds, List **leaf_part_oids)
|
|
||||||
{
|
|
||||||
TupleDesc tupdesc = RelationGetDescr(rel);
|
|
||||||
PartitionDesc partdesc = RelationGetPartitionDesc(rel);
|
|
||||||
PartitionKey partkey = RelationGetPartitionKey(rel);
|
|
||||||
PartitionDispatch pd;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
check_stack_depth();
|
|
||||||
|
|
||||||
/* Build a PartitionDispatch for this table and add it to *pds. */
|
|
||||||
pd = (PartitionDispatch) palloc(sizeof(PartitionDispatchData));
|
|
||||||
*pds = lappend(*pds, pd);
|
|
||||||
pd->reldesc = rel;
|
|
||||||
pd->key = partkey;
|
|
||||||
pd->keystate = NIL;
|
|
||||||
pd->partdesc = partdesc;
|
|
||||||
if (parent != NULL)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* For every partitioned table other than the root, we must store a
|
|
||||||
* tuple table slot initialized with its tuple descriptor and a tuple
|
|
||||||
* conversion map to convert a tuple from its parent's rowtype to its
|
|
||||||
* own. That is to make sure that we are looking at the correct row
|
|
||||||
* using the correct tuple descriptor when computing its partition key
|
|
||||||
* for tuple routing.
|
|
||||||
*/
|
|
||||||
pd->tupslot = MakeSingleTupleTableSlot(tupdesc);
|
|
||||||
pd->tupmap = convert_tuples_by_name(RelationGetDescr(parent),
|
|
||||||
tupdesc,
|
|
||||||
gettext_noop("could not convert row type"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Not required for the root partitioned table */
|
|
||||||
pd->tupslot = NULL;
|
|
||||||
pd->tupmap = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Go look at each partition of this table. If it's a leaf partition,
|
|
||||||
* simply add its OID to *leaf_part_oids. If it's a partitioned table,
|
|
||||||
* recursively call get_partition_dispatch_recurse(), so that its
|
|
||||||
* partitions are processed as well and a corresponding PartitionDispatch
|
|
||||||
* object gets added to *pds.
|
|
||||||
*
|
|
||||||
* About the values in pd->indexes: for a leaf partition, it contains the
|
|
||||||
* leaf partition's position in the global list *leaf_part_oids minus 1,
|
|
||||||
* whereas for a partitioned table partition, it contains the partition's
|
|
||||||
* position in the global list *pds multiplied by -1. The latter is
|
|
||||||
* multiplied by -1 to distinguish partitioned tables from leaf partitions
|
|
||||||
* when going through the values in pd->indexes. So, for example, when
|
|
||||||
* using it during tuple-routing, encountering a value >= 0 means we found
|
|
||||||
* a leaf partition. It is immediately returned as the index in the array
|
|
||||||
* of ResultRelInfos of all the leaf partitions, using which we insert the
|
|
||||||
* tuple into that leaf partition. A negative value means we found a
|
|
||||||
* partitioned table. The value multiplied by -1 is returned as the index
|
|
||||||
* in the array of PartitionDispatch objects of all partitioned tables in
|
|
||||||
* the tree. This value is used to continue the search in the next level
|
|
||||||
* of the partition tree.
|
|
||||||
*/
|
|
||||||
pd->indexes = (int *) palloc(partdesc->nparts * sizeof(int));
|
|
||||||
for (i = 0; i < partdesc->nparts; i++)
|
|
||||||
{
|
|
||||||
Oid partrelid = partdesc->oids[i];
|
|
||||||
|
|
||||||
if (get_rel_relkind(partrelid) != RELKIND_PARTITIONED_TABLE)
|
|
||||||
{
|
|
||||||
*leaf_part_oids = lappend_oid(*leaf_part_oids, partrelid);
|
|
||||||
pd->indexes[i] = list_length(*leaf_part_oids) - 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* We assume all tables in the partition tree were already locked
|
|
||||||
* by the caller.
|
|
||||||
*/
|
|
||||||
Relation partrel = heap_open(partrelid, NoLock);
|
|
||||||
|
|
||||||
pd->indexes[i] = -list_length(*pds);
|
|
||||||
get_partition_dispatch_recurse(partrel, rel, pds, leaf_part_oids);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Module-local functions */
|
/* Module-local functions */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2617,259 +2473,108 @@ generate_partition_qual(Relation rel)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* FormPartitionKeyDatum
|
|
||||||
* Construct values[] and isnull[] arrays for the partition key
|
|
||||||
* of a tuple.
|
|
||||||
*
|
|
||||||
* pd Partition dispatch object of the partitioned table
|
|
||||||
* slot Heap tuple from which to extract partition key
|
|
||||||
* estate executor state for evaluating any partition key
|
|
||||||
* expressions (must be non-NULL)
|
|
||||||
* values Array of partition key Datums (output area)
|
|
||||||
* isnull Array of is-null indicators (output area)
|
|
||||||
*
|
|
||||||
* the ecxt_scantuple slot of estate's per-tuple expr context must point to
|
|
||||||
* the heap tuple passed in.
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
FormPartitionKeyDatum(PartitionDispatch pd,
|
|
||||||
TupleTableSlot *slot,
|
|
||||||
EState *estate,
|
|
||||||
Datum *values,
|
|
||||||
bool *isnull)
|
|
||||||
{
|
|
||||||
ListCell *partexpr_item;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (pd->key->partexprs != NIL && pd->keystate == NIL)
|
|
||||||
{
|
|
||||||
/* Check caller has set up context correctly */
|
|
||||||
Assert(estate != NULL &&
|
|
||||||
GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
|
|
||||||
|
|
||||||
/* First time through, set up expression evaluation state */
|
|
||||||
pd->keystate = ExecPrepareExprList(pd->key->partexprs, estate);
|
|
||||||
}
|
|
||||||
|
|
||||||
partexpr_item = list_head(pd->keystate);
|
|
||||||
for (i = 0; i < pd->key->partnatts; i++)
|
|
||||||
{
|
|
||||||
AttrNumber keycol = pd->key->partattrs[i];
|
|
||||||
Datum datum;
|
|
||||||
bool isNull;
|
|
||||||
|
|
||||||
if (keycol != 0)
|
|
||||||
{
|
|
||||||
/* Plain column; get the value directly from the heap tuple */
|
|
||||||
datum = slot_getattr(slot, keycol, &isNull);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Expression; need to evaluate it */
|
|
||||||
if (partexpr_item == NULL)
|
|
||||||
elog(ERROR, "wrong number of partition key expressions");
|
|
||||||
datum = ExecEvalExprSwitchContext((ExprState *) lfirst(partexpr_item),
|
|
||||||
GetPerTupleExprContext(estate),
|
|
||||||
&isNull);
|
|
||||||
partexpr_item = lnext(partexpr_item);
|
|
||||||
}
|
|
||||||
values[i] = datum;
|
|
||||||
isnull[i] = isNull;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (partexpr_item != NULL)
|
|
||||||
elog(ERROR, "wrong number of partition key expressions");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_partition_for_tuple
|
* get_partition_for_tuple
|
||||||
* Finds a leaf partition for tuple contained in *slot
|
* Finds partition of relation which accepts the partition key specified
|
||||||
|
* in values and isnull
|
||||||
*
|
*
|
||||||
* Returned value is the sequence number of the leaf partition thus found,
|
* Return value is index of the partition (>= 0 and < partdesc->nparts) if one
|
||||||
* or -1 if no leaf partition is found for the tuple. *failed_at is set
|
* found or -1 if none found.
|
||||||
* to the OID of the partitioned table whose partition was not found in
|
|
||||||
* the latter case.
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
get_partition_for_tuple(PartitionDispatch *pd,
|
get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
|
||||||
TupleTableSlot *slot,
|
|
||||||
EState *estate,
|
|
||||||
PartitionDispatchData **failed_at,
|
|
||||||
TupleTableSlot **failed_slot)
|
|
||||||
{
|
{
|
||||||
PartitionDispatch parent;
|
int bound_offset;
|
||||||
Datum values[PARTITION_MAX_KEYS];
|
int part_index = -1;
|
||||||
bool isnull[PARTITION_MAX_KEYS];
|
PartitionKey key = RelationGetPartitionKey(relation);
|
||||||
int result;
|
PartitionDesc partdesc = RelationGetPartitionDesc(relation);
|
||||||
ExprContext *ecxt = GetPerTupleExprContext(estate);
|
|
||||||
TupleTableSlot *ecxt_scantuple_old = ecxt->ecxt_scantuple;
|
|
||||||
|
|
||||||
/* start with the root partitioned table */
|
/* Route as appropriate based on partitioning strategy. */
|
||||||
parent = pd[0];
|
switch (key->strategy)
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
PartitionKey key = parent->key;
|
case PARTITION_STRATEGY_HASH:
|
||||||
PartitionDesc partdesc = parent->partdesc;
|
{
|
||||||
TupleTableSlot *myslot = parent->tupslot;
|
PartitionBoundInfo boundinfo = partdesc->boundinfo;
|
||||||
TupleConversionMap *map = parent->tupmap;
|
int greatest_modulus = get_greatest_modulus(boundinfo);
|
||||||
int cur_index = -1;
|
uint64 rowHash = compute_hash_value(key, values, isnull);
|
||||||
|
|
||||||
if (myslot != NULL && map != NULL)
|
part_index = boundinfo->indexes[rowHash % greatest_modulus];
|
||||||
{
|
}
|
||||||
HeapTuple tuple = ExecFetchSlotTuple(slot);
|
break;
|
||||||
|
|
||||||
ExecClearTuple(myslot);
|
case PARTITION_STRATEGY_LIST:
|
||||||
tuple = do_convert_tuple(tuple, map);
|
if (isnull[0])
|
||||||
ExecStoreTuple(tuple, myslot, InvalidBuffer, true);
|
{
|
||||||
slot = myslot;
|
if (partition_bound_accepts_nulls(partdesc->boundinfo))
|
||||||
}
|
part_index = partdesc->boundinfo->null_index;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool equal = false;
|
||||||
|
|
||||||
/* Quick exit */
|
bound_offset = partition_bound_bsearch(key,
|
||||||
if (partdesc->nparts == 0)
|
partdesc->boundinfo,
|
||||||
{
|
values,
|
||||||
*failed_at = parent;
|
false,
|
||||||
*failed_slot = slot;
|
&equal);
|
||||||
result = -1;
|
if (bound_offset >= 0 && equal)
|
||||||
goto error_exit;
|
part_index = partdesc->boundinfo->indexes[bound_offset];
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
/*
|
case PARTITION_STRATEGY_RANGE:
|
||||||
* Extract partition key from tuple. Expression evaluation machinery
|
{
|
||||||
* that FormPartitionKeyDatum() invokes expects ecxt_scantuple to
|
bool equal = false,
|
||||||
* point to the correct tuple slot. The slot might have changed from
|
range_partkey_has_null = false;
|
||||||
* what was used for the parent table if the table of the current
|
int i;
|
||||||
* partitioning level has different tuple descriptor from the parent.
|
|
||||||
* So update ecxt_scantuple accordingly.
|
|
||||||
*/
|
|
||||||
ecxt->ecxt_scantuple = slot;
|
|
||||||
FormPartitionKeyDatum(parent, slot, estate, values, isnull);
|
|
||||||
|
|
||||||
/* Route as appropriate based on partitioning strategy. */
|
/*
|
||||||
switch (key->strategy)
|
* No range includes NULL, so this will be accepted by the
|
||||||
{
|
* default partition if there is one, and otherwise
|
||||||
case PARTITION_STRATEGY_HASH:
|
* rejected.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < key->partnatts; i++)
|
||||||
{
|
{
|
||||||
PartitionBoundInfo boundinfo = partdesc->boundinfo;
|
if (isnull[i] &&
|
||||||
int greatest_modulus = get_greatest_modulus(boundinfo);
|
partition_bound_has_default(partdesc->boundinfo))
|
||||||
uint64 rowHash = compute_hash_value(key, values,
|
|
||||||
isnull);
|
|
||||||
|
|
||||||
cur_index = boundinfo->indexes[rowHash % greatest_modulus];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PARTITION_STRATEGY_LIST:
|
|
||||||
|
|
||||||
if (isnull[0])
|
|
||||||
{
|
|
||||||
if (partition_bound_accepts_nulls(partdesc->boundinfo))
|
|
||||||
cur_index = partdesc->boundinfo->null_index;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bool equal = false;
|
|
||||||
int cur_offset;
|
|
||||||
|
|
||||||
cur_offset = partition_bound_bsearch(key,
|
|
||||||
partdesc->boundinfo,
|
|
||||||
values,
|
|
||||||
false,
|
|
||||||
&equal);
|
|
||||||
if (cur_offset >= 0 && equal)
|
|
||||||
cur_index = partdesc->boundinfo->indexes[cur_offset];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PARTITION_STRATEGY_RANGE:
|
|
||||||
{
|
|
||||||
bool equal = false,
|
|
||||||
range_partkey_has_null = false;
|
|
||||||
int cur_offset;
|
|
||||||
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;
|
||||||
partition_bound_has_default(partdesc->boundinfo))
|
part_index = partdesc->boundinfo->default_index;
|
||||||
{
|
|
||||||
range_partkey_has_null = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (isnull[i])
|
|
||||||
{
|
|
||||||
*failed_at = parent;
|
|
||||||
*failed_slot = slot;
|
|
||||||
result = -1;
|
|
||||||
goto error_exit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* No need to search for partition, as the null key will
|
|
||||||
* be routed to the default partition.
|
|
||||||
*/
|
|
||||||
if (range_partkey_has_null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
cur_offset = partition_bound_bsearch(key,
|
|
||||||
partdesc->boundinfo,
|
|
||||||
values,
|
|
||||||
false,
|
|
||||||
&equal);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The offset returned is such that the bound at
|
|
||||||
* cur_offset is less than or equal to the tuple value, so
|
|
||||||
* the bound at offset+1 is the upper bound.
|
|
||||||
*/
|
|
||||||
cur_index = partdesc->boundinfo->indexes[cur_offset + 1];
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
if (!range_partkey_has_null)
|
||||||
elog(ERROR, "unexpected partition strategy: %d",
|
{
|
||||||
(int) key->strategy);
|
bound_offset = partition_bound_bsearch(key,
|
||||||
}
|
partdesc->boundinfo,
|
||||||
|
values,
|
||||||
|
false,
|
||||||
|
&equal);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cur_index < 0 means we failed to find a partition of this parent.
|
* The bound at bound_offset is less than or equal to the
|
||||||
* Use the default partition, if there is one.
|
* tuple value, so the bound at offset+1 is the upper
|
||||||
*/
|
* bound of the partition we're looking for, if there
|
||||||
if (cur_index < 0)
|
* actually exists one.
|
||||||
cur_index = partdesc->boundinfo->default_index;
|
*/
|
||||||
|
part_index = partdesc->boundinfo->indexes[bound_offset + 1];
|
||||||
/*
|
}
|
||||||
* If cur_index is still less than 0 at this point, there's no
|
}
|
||||||
* partition for this tuple. Otherwise, we either found the leaf
|
|
||||||
* partition, or a child partitioned table through which we have to
|
|
||||||
* route the tuple.
|
|
||||||
*/
|
|
||||||
if (cur_index < 0)
|
|
||||||
{
|
|
||||||
result = -1;
|
|
||||||
*failed_at = parent;
|
|
||||||
*failed_slot = slot;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
else if (parent->indexes[cur_index] >= 0)
|
default:
|
||||||
{
|
elog(ERROR, "unexpected partition strategy: %d",
|
||||||
result = parent->indexes[cur_index];
|
(int) key->strategy);
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
parent = pd[-parent->indexes[cur_index]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error_exit:
|
/*
|
||||||
ecxt->ecxt_scantuple = ecxt_scantuple_old;
|
* part_index < 0 means we failed to find a partition of this parent.
|
||||||
return result;
|
* Use the default partition, if there is one.
|
||||||
|
*/
|
||||||
|
if (part_index < 0)
|
||||||
|
part_index = partdesc->boundinfo->default_index;
|
||||||
|
|
||||||
|
return part_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "commands/copy.h"
|
#include "commands/copy.h"
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
|
#include "executor/execPartition.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "libpq/libpq.h"
|
#include "libpq/libpq.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
|
@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global
|
|||||||
|
|
||||||
OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
|
OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
|
||||||
execGrouping.o execIndexing.o execJunk.o \
|
execGrouping.o execIndexing.o execJunk.o \
|
||||||
execMain.o execParallel.o execProcnode.o \
|
execMain.o execParallel.o execPartition.o execProcnode.o \
|
||||||
execReplication.o execScan.o execSRF.o execTuples.o \
|
execReplication.o execScan.o execSRF.o execTuples.o \
|
||||||
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
|
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
|
||||||
nodeBitmapAnd.o nodeBitmapOr.o \
|
nodeBitmapAnd.o nodeBitmapOr.o \
|
||||||
|
@ -43,7 +43,6 @@
|
|||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/partition.h"
|
#include "catalog/partition.h"
|
||||||
#include "catalog/pg_inherits_fn.h"
|
|
||||||
#include "catalog/pg_publication.h"
|
#include "catalog/pg_publication.h"
|
||||||
#include "commands/matview.h"
|
#include "commands/matview.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
@ -98,14 +97,8 @@ static char *ExecBuildSlotValueDescription(Oid reloid,
|
|||||||
TupleDesc tupdesc,
|
TupleDesc tupdesc,
|
||||||
Bitmapset *modifiedCols,
|
Bitmapset *modifiedCols,
|
||||||
int maxfieldlen);
|
int maxfieldlen);
|
||||||
static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
|
|
||||||
Datum *values,
|
|
||||||
bool *isnull,
|
|
||||||
int maxfieldlen);
|
|
||||||
static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
|
static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
|
||||||
Plan *planTree);
|
Plan *planTree);
|
||||||
static void ExecPartitionCheck(ResultRelInfo *resultRelInfo,
|
|
||||||
TupleTableSlot *slot, EState *estate);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note that GetUpdatedColumns() also exists in commands/trigger.c. There does
|
* Note that GetUpdatedColumns() also exists in commands/trigger.c. There does
|
||||||
@ -1854,8 +1847,10 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* ExecPartitionCheck --- check that tuple meets the partition constraint.
|
* ExecPartitionCheck --- check that tuple meets the partition constraint.
|
||||||
|
*
|
||||||
|
* Exported in executor.h for outside use.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
|
ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
|
||||||
EState *estate)
|
EState *estate)
|
||||||
{
|
{
|
||||||
@ -3245,258 +3240,3 @@ EvalPlanQualEnd(EPQState *epqstate)
|
|||||||
epqstate->planstate = NULL;
|
epqstate->planstate = NULL;
|
||||||
epqstate->origslot = NULL;
|
epqstate->origslot = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* ExecSetupPartitionTupleRouting - set up information needed during
|
|
||||||
* tuple routing for partitioned tables
|
|
||||||
*
|
|
||||||
* Output arguments:
|
|
||||||
* 'pd' receives an array of PartitionDispatch objects with one entry for
|
|
||||||
* every partitioned table in the partition tree
|
|
||||||
* 'partitions' receives an array of ResultRelInfo* objects with one entry for
|
|
||||||
* every leaf partition in the partition tree
|
|
||||||
* 'tup_conv_maps' receives an array of TupleConversionMap objects with one
|
|
||||||
* entry for every leaf partition (required to convert input tuple based
|
|
||||||
* on the root table's rowtype to a leaf partition's rowtype after tuple
|
|
||||||
* routing is done)
|
|
||||||
* 'partition_tuple_slot' receives a standalone TupleTableSlot to be used
|
|
||||||
* to manipulate any given leaf partition's rowtype after that partition
|
|
||||||
* is chosen by tuple-routing.
|
|
||||||
* 'num_parted' receives the number of partitioned tables in the partition
|
|
||||||
* tree (= the number of entries in the 'pd' output array)
|
|
||||||
* 'num_partitions' receives the number of leaf partitions in the partition
|
|
||||||
* tree (= the number of entries in the 'partitions' and 'tup_conv_maps'
|
|
||||||
* output arrays
|
|
||||||
*
|
|
||||||
* Note that all the relations in the partition tree are locked using the
|
|
||||||
* RowExclusiveLock mode upon return from this function.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ExecSetupPartitionTupleRouting(Relation rel,
|
|
||||||
Index resultRTindex,
|
|
||||||
EState *estate,
|
|
||||||
PartitionDispatch **pd,
|
|
||||||
ResultRelInfo ***partitions,
|
|
||||||
TupleConversionMap ***tup_conv_maps,
|
|
||||||
TupleTableSlot **partition_tuple_slot,
|
|
||||||
int *num_parted, int *num_partitions)
|
|
||||||
{
|
|
||||||
TupleDesc tupDesc = RelationGetDescr(rel);
|
|
||||||
List *leaf_parts;
|
|
||||||
ListCell *cell;
|
|
||||||
int i;
|
|
||||||
ResultRelInfo *leaf_part_rri;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the information about the partition tree after locking all the
|
|
||||||
* partitions.
|
|
||||||
*/
|
|
||||||
(void) find_all_inheritors(RelationGetRelid(rel), RowExclusiveLock, NULL);
|
|
||||||
*pd = RelationGetPartitionDispatchInfo(rel, num_parted, &leaf_parts);
|
|
||||||
*num_partitions = list_length(leaf_parts);
|
|
||||||
*partitions = (ResultRelInfo **) palloc(*num_partitions *
|
|
||||||
sizeof(ResultRelInfo *));
|
|
||||||
*tup_conv_maps = (TupleConversionMap **) palloc0(*num_partitions *
|
|
||||||
sizeof(TupleConversionMap *));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize an empty slot that will be used to manipulate tuples of any
|
|
||||||
* given partition's rowtype. It is attached to the caller-specified node
|
|
||||||
* (such as ModifyTableState) and released when the node finishes
|
|
||||||
* processing.
|
|
||||||
*/
|
|
||||||
*partition_tuple_slot = MakeTupleTableSlot();
|
|
||||||
|
|
||||||
leaf_part_rri = (ResultRelInfo *) palloc0(*num_partitions *
|
|
||||||
sizeof(ResultRelInfo));
|
|
||||||
i = 0;
|
|
||||||
foreach(cell, leaf_parts)
|
|
||||||
{
|
|
||||||
Relation partrel;
|
|
||||||
TupleDesc part_tupdesc;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We locked all the partitions above including the leaf partitions.
|
|
||||||
* Note that each of the relations in *partitions are eventually
|
|
||||||
* closed by the caller.
|
|
||||||
*/
|
|
||||||
partrel = heap_open(lfirst_oid(cell), NoLock);
|
|
||||||
part_tupdesc = RelationGetDescr(partrel);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Save a tuple conversion map to convert a tuple routed to this
|
|
||||||
* partition from the parent's type to the partition's.
|
|
||||||
*/
|
|
||||||
(*tup_conv_maps)[i] = convert_tuples_by_name(tupDesc, part_tupdesc,
|
|
||||||
gettext_noop("could not convert row type"));
|
|
||||||
|
|
||||||
InitResultRelInfo(leaf_part_rri,
|
|
||||||
partrel,
|
|
||||||
resultRTindex,
|
|
||||||
rel,
|
|
||||||
estate->es_instrument);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Verify result relation is a valid target for INSERT.
|
|
||||||
*/
|
|
||||||
CheckValidResultRel(leaf_part_rri, CMD_INSERT);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Open partition indices (remember we do not support ON CONFLICT in
|
|
||||||
* case of partitioned tables, so we do not need support information
|
|
||||||
* for speculative insertion)
|
|
||||||
*/
|
|
||||||
if (leaf_part_rri->ri_RelationDesc->rd_rel->relhasindex &&
|
|
||||||
leaf_part_rri->ri_IndexRelationDescs == NULL)
|
|
||||||
ExecOpenIndices(leaf_part_rri, false);
|
|
||||||
|
|
||||||
estate->es_leaf_result_relations =
|
|
||||||
lappend(estate->es_leaf_result_relations, leaf_part_rri);
|
|
||||||
|
|
||||||
(*partitions)[i] = leaf_part_rri++;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ExecFindPartition -- Find a leaf partition in the partition tree rooted
|
|
||||||
* at parent, for the heap tuple contained in *slot
|
|
||||||
*
|
|
||||||
* estate must be non-NULL; we'll need it to compute any expressions in the
|
|
||||||
* partition key(s)
|
|
||||||
*
|
|
||||||
* If no leaf partition is found, this routine errors out with the appropriate
|
|
||||||
* error message, else it returns the leaf partition sequence number returned
|
|
||||||
* by get_partition_for_tuple() unchanged.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
|
|
||||||
TupleTableSlot *slot, EState *estate)
|
|
||||||
{
|
|
||||||
int result;
|
|
||||||
PartitionDispatchData *failed_at;
|
|
||||||
TupleTableSlot *failed_slot;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* First check the root table's partition constraint, if any. No point in
|
|
||||||
* routing the tuple if it doesn't belong in the root table itself.
|
|
||||||
*/
|
|
||||||
if (resultRelInfo->ri_PartitionCheck)
|
|
||||||
ExecPartitionCheck(resultRelInfo, slot, estate);
|
|
||||||
|
|
||||||
result = get_partition_for_tuple(pd, slot, estate,
|
|
||||||
&failed_at, &failed_slot);
|
|
||||||
if (result < 0)
|
|
||||||
{
|
|
||||||
Relation failed_rel;
|
|
||||||
Datum key_values[PARTITION_MAX_KEYS];
|
|
||||||
bool key_isnull[PARTITION_MAX_KEYS];
|
|
||||||
char *val_desc;
|
|
||||||
ExprContext *ecxt = GetPerTupleExprContext(estate);
|
|
||||||
|
|
||||||
failed_rel = failed_at->reldesc;
|
|
||||||
ecxt->ecxt_scantuple = failed_slot;
|
|
||||||
FormPartitionKeyDatum(failed_at, failed_slot, estate,
|
|
||||||
key_values, key_isnull);
|
|
||||||
val_desc = ExecBuildSlotPartitionKeyDescription(failed_rel,
|
|
||||||
key_values,
|
|
||||||
key_isnull,
|
|
||||||
64);
|
|
||||||
Assert(OidIsValid(RelationGetRelid(failed_rel)));
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_CHECK_VIOLATION),
|
|
||||||
errmsg("no partition of relation \"%s\" found for row",
|
|
||||||
RelationGetRelationName(failed_rel)),
|
|
||||||
val_desc ? errdetail("Partition key of the failing row contains %s.", val_desc) : 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* BuildSlotPartitionKeyDescription
|
|
||||||
*
|
|
||||||
* This works very much like BuildIndexValueDescription() and is currently
|
|
||||||
* used for building error messages when ExecFindPartition() fails to find
|
|
||||||
* partition for a row.
|
|
||||||
*/
|
|
||||||
static char *
|
|
||||||
ExecBuildSlotPartitionKeyDescription(Relation rel,
|
|
||||||
Datum *values,
|
|
||||||
bool *isnull,
|
|
||||||
int maxfieldlen)
|
|
||||||
{
|
|
||||||
StringInfoData buf;
|
|
||||||
PartitionKey key = RelationGetPartitionKey(rel);
|
|
||||||
int partnatts = get_partition_natts(key);
|
|
||||||
int i;
|
|
||||||
Oid relid = RelationGetRelid(rel);
|
|
||||||
AclResult aclresult;
|
|
||||||
|
|
||||||
if (check_enable_rls(relid, InvalidOid, true) == RLS_ENABLED)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* If the user has table-level access, just go build the description. */
|
|
||||||
aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT);
|
|
||||||
if (aclresult != ACLCHECK_OK)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Step through the columns of the partition key and make sure the
|
|
||||||
* user has SELECT rights on all of them.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < partnatts; i++)
|
|
||||||
{
|
|
||||||
AttrNumber attnum = get_partition_col_attnum(key, i);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If this partition key column is an expression, we return no
|
|
||||||
* detail rather than try to figure out what column(s) the
|
|
||||||
* expression includes and if the user has SELECT rights on them.
|
|
||||||
*/
|
|
||||||
if (attnum == InvalidAttrNumber ||
|
|
||||||
pg_attribute_aclcheck(relid, attnum, GetUserId(),
|
|
||||||
ACL_SELECT) != ACLCHECK_OK)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initStringInfo(&buf);
|
|
||||||
appendStringInfo(&buf, "(%s) = (",
|
|
||||||
pg_get_partkeydef_columns(relid, true));
|
|
||||||
|
|
||||||
for (i = 0; i < partnatts; i++)
|
|
||||||
{
|
|
||||||
char *val;
|
|
||||||
int vallen;
|
|
||||||
|
|
||||||
if (isnull[i])
|
|
||||||
val = "null";
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Oid foutoid;
|
|
||||||
bool typisvarlena;
|
|
||||||
|
|
||||||
getTypeOutputInfo(get_partition_col_typid(key, i),
|
|
||||||
&foutoid, &typisvarlena);
|
|
||||||
val = OidOutputFunctionCall(foutoid, values[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i > 0)
|
|
||||||
appendStringInfoString(&buf, ", ");
|
|
||||||
|
|
||||||
/* truncate if needed */
|
|
||||||
vallen = strlen(val);
|
|
||||||
if (vallen <= maxfieldlen)
|
|
||||||
appendStringInfoString(&buf, val);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vallen = pg_mbcliplen(val, vallen, maxfieldlen);
|
|
||||||
appendBinaryStringInfo(&buf, val, vallen);
|
|
||||||
appendStringInfoString(&buf, "...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
appendStringInfoChar(&buf, ')');
|
|
||||||
|
|
||||||
return buf.data;
|
|
||||||
}
|
|
||||||
|
560
src/backend/executor/execPartition.c
Normal file
560
src/backend/executor/execPartition.c
Normal file
@ -0,0 +1,560 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* execPartition.c
|
||||||
|
* Support routines for partitioning.
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* src/backend/executor/execPartition.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "catalog/pg_inherits_fn.h"
|
||||||
|
#include "executor/execPartition.h"
|
||||||
|
#include "executor/executor.h"
|
||||||
|
#include "mb/pg_wchar.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/rls.h"
|
||||||
|
#include "utils/ruleutils.h"
|
||||||
|
|
||||||
|
static PartitionDispatch *RelationGetPartitionDispatchInfo(Relation rel,
|
||||||
|
int *num_parted, List **leaf_part_oids);
|
||||||
|
static void get_partition_dispatch_recurse(Relation rel, Relation parent,
|
||||||
|
List **pds, List **leaf_part_oids);
|
||||||
|
static void FormPartitionKeyDatum(PartitionDispatch pd,
|
||||||
|
TupleTableSlot *slot,
|
||||||
|
EState *estate,
|
||||||
|
Datum *values,
|
||||||
|
bool *isnull);
|
||||||
|
static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
|
||||||
|
Datum *values,
|
||||||
|
bool *isnull,
|
||||||
|
int maxfieldlen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ExecSetupPartitionTupleRouting - set up information needed during
|
||||||
|
* tuple routing for partitioned tables
|
||||||
|
*
|
||||||
|
* Output arguments:
|
||||||
|
* 'pd' receives an array of PartitionDispatch objects with one entry for
|
||||||
|
* every partitioned table in the partition tree
|
||||||
|
* 'partitions' receives an array of ResultRelInfo* objects with one entry for
|
||||||
|
* every leaf partition in the partition tree
|
||||||
|
* 'tup_conv_maps' receives an array of TupleConversionMap objects with one
|
||||||
|
* entry for every leaf partition (required to convert input tuple based
|
||||||
|
* on the root table's rowtype to a leaf partition's rowtype after tuple
|
||||||
|
* routing is done)
|
||||||
|
* 'partition_tuple_slot' receives a standalone TupleTableSlot to be used
|
||||||
|
* to manipulate any given leaf partition's rowtype after that partition
|
||||||
|
* is chosen by tuple-routing.
|
||||||
|
* 'num_parted' receives the number of partitioned tables in the partition
|
||||||
|
* tree (= the number of entries in the 'pd' output array)
|
||||||
|
* 'num_partitions' receives the number of leaf partitions in the partition
|
||||||
|
* tree (= the number of entries in the 'partitions' and 'tup_conv_maps'
|
||||||
|
* output arrays
|
||||||
|
*
|
||||||
|
* Note that all the relations in the partition tree are locked using the
|
||||||
|
* RowExclusiveLock mode upon return from this function.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ExecSetupPartitionTupleRouting(Relation rel,
|
||||||
|
Index resultRTindex,
|
||||||
|
EState *estate,
|
||||||
|
PartitionDispatch **pd,
|
||||||
|
ResultRelInfo ***partitions,
|
||||||
|
TupleConversionMap ***tup_conv_maps,
|
||||||
|
TupleTableSlot **partition_tuple_slot,
|
||||||
|
int *num_parted, int *num_partitions)
|
||||||
|
{
|
||||||
|
TupleDesc tupDesc = RelationGetDescr(rel);
|
||||||
|
List *leaf_parts;
|
||||||
|
ListCell *cell;
|
||||||
|
int i;
|
||||||
|
ResultRelInfo *leaf_part_rri;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the information about the partition tree after locking all the
|
||||||
|
* partitions.
|
||||||
|
*/
|
||||||
|
(void) find_all_inheritors(RelationGetRelid(rel), RowExclusiveLock, NULL);
|
||||||
|
*pd = RelationGetPartitionDispatchInfo(rel, num_parted, &leaf_parts);
|
||||||
|
*num_partitions = list_length(leaf_parts);
|
||||||
|
*partitions = (ResultRelInfo **) palloc(*num_partitions *
|
||||||
|
sizeof(ResultRelInfo *));
|
||||||
|
*tup_conv_maps = (TupleConversionMap **) palloc0(*num_partitions *
|
||||||
|
sizeof(TupleConversionMap *));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize an empty slot that will be used to manipulate tuples of any
|
||||||
|
* given partition's rowtype. It is attached to the caller-specified node
|
||||||
|
* (such as ModifyTableState) and released when the node finishes
|
||||||
|
* processing.
|
||||||
|
*/
|
||||||
|
*partition_tuple_slot = MakeTupleTableSlot();
|
||||||
|
|
||||||
|
leaf_part_rri = (ResultRelInfo *) palloc0(*num_partitions *
|
||||||
|
sizeof(ResultRelInfo));
|
||||||
|
i = 0;
|
||||||
|
foreach(cell, leaf_parts)
|
||||||
|
{
|
||||||
|
Relation partrel;
|
||||||
|
TupleDesc part_tupdesc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We locked all the partitions above including the leaf partitions.
|
||||||
|
* Note that each of the relations in *partitions are eventually
|
||||||
|
* closed by the caller.
|
||||||
|
*/
|
||||||
|
partrel = heap_open(lfirst_oid(cell), NoLock);
|
||||||
|
part_tupdesc = RelationGetDescr(partrel);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save a tuple conversion map to convert a tuple routed to this
|
||||||
|
* partition from the parent's type to the partition's.
|
||||||
|
*/
|
||||||
|
(*tup_conv_maps)[i] = convert_tuples_by_name(tupDesc, part_tupdesc,
|
||||||
|
gettext_noop("could not convert row type"));
|
||||||
|
|
||||||
|
InitResultRelInfo(leaf_part_rri,
|
||||||
|
partrel,
|
||||||
|
resultRTindex,
|
||||||
|
rel,
|
||||||
|
estate->es_instrument);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verify result relation is a valid target for INSERT.
|
||||||
|
*/
|
||||||
|
CheckValidResultRel(leaf_part_rri, CMD_INSERT);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open partition indices (remember we do not support ON CONFLICT in
|
||||||
|
* case of partitioned tables, so we do not need support information
|
||||||
|
* for speculative insertion)
|
||||||
|
*/
|
||||||
|
if (leaf_part_rri->ri_RelationDesc->rd_rel->relhasindex &&
|
||||||
|
leaf_part_rri->ri_IndexRelationDescs == NULL)
|
||||||
|
ExecOpenIndices(leaf_part_rri, false);
|
||||||
|
|
||||||
|
estate->es_leaf_result_relations =
|
||||||
|
lappend(estate->es_leaf_result_relations, leaf_part_rri);
|
||||||
|
|
||||||
|
(*partitions)[i] = leaf_part_rri++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ExecFindPartition -- Find a leaf partition in the partition tree rooted
|
||||||
|
* at parent, for the heap tuple contained in *slot
|
||||||
|
*
|
||||||
|
* estate must be non-NULL; we'll need it to compute any expressions in the
|
||||||
|
* partition key(s)
|
||||||
|
*
|
||||||
|
* If no leaf partition is found, this routine errors out with the appropriate
|
||||||
|
* error message, else it returns the leaf partition sequence number
|
||||||
|
* as an index into the array of (ResultRelInfos of) all leaf partitions in
|
||||||
|
* the partition tree.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
|
||||||
|
TupleTableSlot *slot, EState *estate)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
Datum values[PARTITION_MAX_KEYS];
|
||||||
|
bool isnull[PARTITION_MAX_KEYS];
|
||||||
|
Relation rel;
|
||||||
|
PartitionDispatch parent;
|
||||||
|
ExprContext *ecxt = GetPerTupleExprContext(estate);
|
||||||
|
TupleTableSlot *ecxt_scantuple_old = ecxt->ecxt_scantuple;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First check the root table's partition constraint, if any. No point in
|
||||||
|
* routing the tuple if it doesn't belong in the root table itself.
|
||||||
|
*/
|
||||||
|
if (resultRelInfo->ri_PartitionCheck)
|
||||||
|
ExecPartitionCheck(resultRelInfo, slot, estate);
|
||||||
|
|
||||||
|
/* start with the root partitioned table */
|
||||||
|
parent = pd[0];
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
PartitionDesc partdesc;
|
||||||
|
TupleTableSlot *myslot = parent->tupslot;
|
||||||
|
TupleConversionMap *map = parent->tupmap;
|
||||||
|
int cur_index = -1;
|
||||||
|
|
||||||
|
rel = parent->reldesc;
|
||||||
|
partdesc = RelationGetPartitionDesc(rel);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert the tuple to this parent's layout so that we can do certain
|
||||||
|
* things we do below.
|
||||||
|
*/
|
||||||
|
if (myslot != NULL && map != NULL)
|
||||||
|
{
|
||||||
|
HeapTuple tuple = ExecFetchSlotTuple(slot);
|
||||||
|
|
||||||
|
ExecClearTuple(myslot);
|
||||||
|
tuple = do_convert_tuple(tuple, map);
|
||||||
|
ExecStoreTuple(tuple, myslot, InvalidBuffer, true);
|
||||||
|
slot = myslot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quick exit */
|
||||||
|
if (partdesc->nparts == 0)
|
||||||
|
{
|
||||||
|
result = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract partition key from tuple. Expression evaluation machinery
|
||||||
|
* that FormPartitionKeyDatum() invokes expects ecxt_scantuple to
|
||||||
|
* point to the correct tuple slot. The slot might have changed from
|
||||||
|
* what was used for the parent table if the table of the current
|
||||||
|
* partitioning level has different tuple descriptor from the parent.
|
||||||
|
* So update ecxt_scantuple accordingly.
|
||||||
|
*/
|
||||||
|
ecxt->ecxt_scantuple = slot;
|
||||||
|
FormPartitionKeyDatum(parent, slot, estate, values, isnull);
|
||||||
|
cur_index = get_partition_for_tuple(rel, values, isnull);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cur_index < 0 means we failed to find a partition of this parent.
|
||||||
|
* cur_index >= 0 means we either found the leaf partition, or the
|
||||||
|
* next parent to find a partition of.
|
||||||
|
*/
|
||||||
|
if (cur_index < 0)
|
||||||
|
{
|
||||||
|
result = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (parent->indexes[cur_index] >= 0)
|
||||||
|
{
|
||||||
|
result = parent->indexes[cur_index];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
parent = pd[-parent->indexes[cur_index]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A partition was not found. */
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
char *val_desc;
|
||||||
|
|
||||||
|
val_desc = ExecBuildSlotPartitionKeyDescription(rel,
|
||||||
|
values, isnull, 64);
|
||||||
|
Assert(OidIsValid(RelationGetRelid(rel)));
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_CHECK_VIOLATION),
|
||||||
|
errmsg("no partition of relation \"%s\" found for row",
|
||||||
|
RelationGetRelationName(rel)),
|
||||||
|
val_desc ? errdetail("Partition key of the failing row contains %s.", val_desc) : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
ecxt->ecxt_scantuple = ecxt_scantuple_old;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RelationGetPartitionDispatchInfo
|
||||||
|
* Returns information necessary to route tuples down a partition tree
|
||||||
|
*
|
||||||
|
* The number of elements in the returned array (that is, the number of
|
||||||
|
* PartitionDispatch objects for the partitioned tables in the partition tree)
|
||||||
|
* is returned in *num_parted and a list of the OIDs of all the leaf
|
||||||
|
* partitions of rel is returned in *leaf_part_oids.
|
||||||
|
*
|
||||||
|
* All the relations in the partition tree (including 'rel') must have been
|
||||||
|
* locked (using at least the AccessShareLock) by the caller.
|
||||||
|
*/
|
||||||
|
static PartitionDispatch *
|
||||||
|
RelationGetPartitionDispatchInfo(Relation rel,
|
||||||
|
int *num_parted, List **leaf_part_oids)
|
||||||
|
{
|
||||||
|
List *pdlist = NIL;
|
||||||
|
PartitionDispatchData **pd;
|
||||||
|
ListCell *lc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
|
||||||
|
|
||||||
|
*num_parted = 0;
|
||||||
|
*leaf_part_oids = NIL;
|
||||||
|
|
||||||
|
get_partition_dispatch_recurse(rel, NULL, &pdlist, leaf_part_oids);
|
||||||
|
*num_parted = list_length(pdlist);
|
||||||
|
pd = (PartitionDispatchData **) palloc(*num_parted *
|
||||||
|
sizeof(PartitionDispatchData *));
|
||||||
|
i = 0;
|
||||||
|
foreach(lc, pdlist)
|
||||||
|
{
|
||||||
|
pd[i++] = lfirst(lc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_partition_dispatch_recurse
|
||||||
|
* Recursively expand partition tree rooted at rel
|
||||||
|
*
|
||||||
|
* As the partition tree is expanded in a depth-first manner, we maintain two
|
||||||
|
* global lists: of PartitionDispatch objects corresponding to partitioned
|
||||||
|
* tables in *pds and of the leaf partition OIDs in *leaf_part_oids.
|
||||||
|
*
|
||||||
|
* Note that the order of OIDs of leaf partitions in leaf_part_oids matches
|
||||||
|
* the order in which the planner's expand_partitioned_rtentry() processes
|
||||||
|
* them. It's not necessarily the case that the offsets match up exactly,
|
||||||
|
* because constraint exclusion might prune away some partitions on the
|
||||||
|
* planner side, whereas we'll always have the complete list; but unpruned
|
||||||
|
* partitions will appear in the same order in the plan as they are returned
|
||||||
|
* here.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
get_partition_dispatch_recurse(Relation rel, Relation parent,
|
||||||
|
List **pds, List **leaf_part_oids)
|
||||||
|
{
|
||||||
|
TupleDesc tupdesc = RelationGetDescr(rel);
|
||||||
|
PartitionDesc partdesc = RelationGetPartitionDesc(rel);
|
||||||
|
PartitionKey partkey = RelationGetPartitionKey(rel);
|
||||||
|
PartitionDispatch pd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
check_stack_depth();
|
||||||
|
|
||||||
|
/* Build a PartitionDispatch for this table and add it to *pds. */
|
||||||
|
pd = (PartitionDispatch) palloc(sizeof(PartitionDispatchData));
|
||||||
|
*pds = lappend(*pds, pd);
|
||||||
|
pd->reldesc = rel;
|
||||||
|
pd->key = partkey;
|
||||||
|
pd->keystate = NIL;
|
||||||
|
pd->partdesc = partdesc;
|
||||||
|
if (parent != NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* For every partitioned table other than the root, we must store a
|
||||||
|
* tuple table slot initialized with its tuple descriptor and a tuple
|
||||||
|
* conversion map to convert a tuple from its parent's rowtype to its
|
||||||
|
* own. That is to make sure that we are looking at the correct row
|
||||||
|
* using the correct tuple descriptor when computing its partition key
|
||||||
|
* for tuple routing.
|
||||||
|
*/
|
||||||
|
pd->tupslot = MakeSingleTupleTableSlot(tupdesc);
|
||||||
|
pd->tupmap = convert_tuples_by_name(RelationGetDescr(parent),
|
||||||
|
tupdesc,
|
||||||
|
gettext_noop("could not convert row type"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Not required for the root partitioned table */
|
||||||
|
pd->tupslot = NULL;
|
||||||
|
pd->tupmap = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Go look at each partition of this table. If it's a leaf partition,
|
||||||
|
* simply add its OID to *leaf_part_oids. If it's a partitioned table,
|
||||||
|
* recursively call get_partition_dispatch_recurse(), so that its
|
||||||
|
* partitions are processed as well and a corresponding PartitionDispatch
|
||||||
|
* object gets added to *pds.
|
||||||
|
*
|
||||||
|
* About the values in pd->indexes: for a leaf partition, it contains the
|
||||||
|
* leaf partition's position in the global list *leaf_part_oids minus 1,
|
||||||
|
* whereas for a partitioned table partition, it contains the partition's
|
||||||
|
* position in the global list *pds multiplied by -1. The latter is
|
||||||
|
* multiplied by -1 to distinguish partitioned tables from leaf partitions
|
||||||
|
* when going through the values in pd->indexes. So, for example, when
|
||||||
|
* using it during tuple-routing, encountering a value >= 0 means we found
|
||||||
|
* a leaf partition. It is immediately returned as the index in the array
|
||||||
|
* of ResultRelInfos of all the leaf partitions, using which we insert the
|
||||||
|
* tuple into that leaf partition. A negative value means we found a
|
||||||
|
* partitioned table. The value multiplied by -1 is returned as the index
|
||||||
|
* in the array of PartitionDispatch objects of all partitioned tables in
|
||||||
|
* the tree. This value is used to continue the search in the next level
|
||||||
|
* of the partition tree.
|
||||||
|
*/
|
||||||
|
pd->indexes = (int *) palloc(partdesc->nparts * sizeof(int));
|
||||||
|
for (i = 0; i < partdesc->nparts; i++)
|
||||||
|
{
|
||||||
|
Oid partrelid = partdesc->oids[i];
|
||||||
|
|
||||||
|
if (get_rel_relkind(partrelid) != RELKIND_PARTITIONED_TABLE)
|
||||||
|
{
|
||||||
|
*leaf_part_oids = lappend_oid(*leaf_part_oids, partrelid);
|
||||||
|
pd->indexes[i] = list_length(*leaf_part_oids) - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We assume all tables in the partition tree were already locked
|
||||||
|
* by the caller.
|
||||||
|
*/
|
||||||
|
Relation partrel = heap_open(partrelid, NoLock);
|
||||||
|
|
||||||
|
pd->indexes[i] = -list_length(*pds);
|
||||||
|
get_partition_dispatch_recurse(partrel, rel, pds, leaf_part_oids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* FormPartitionKeyDatum
|
||||||
|
* Construct values[] and isnull[] arrays for the partition key
|
||||||
|
* of a tuple.
|
||||||
|
*
|
||||||
|
* pd Partition dispatch object of the partitioned table
|
||||||
|
* slot Heap tuple from which to extract partition key
|
||||||
|
* estate executor state for evaluating any partition key
|
||||||
|
* expressions (must be non-NULL)
|
||||||
|
* values Array of partition key Datums (output area)
|
||||||
|
* isnull Array of is-null indicators (output area)
|
||||||
|
*
|
||||||
|
* the ecxt_scantuple slot of estate's per-tuple expr context must point to
|
||||||
|
* the heap tuple passed in.
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
FormPartitionKeyDatum(PartitionDispatch pd,
|
||||||
|
TupleTableSlot *slot,
|
||||||
|
EState *estate,
|
||||||
|
Datum *values,
|
||||||
|
bool *isnull)
|
||||||
|
{
|
||||||
|
ListCell *partexpr_item;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (pd->key->partexprs != NIL && pd->keystate == NIL)
|
||||||
|
{
|
||||||
|
/* Check caller has set up context correctly */
|
||||||
|
Assert(estate != NULL &&
|
||||||
|
GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
|
||||||
|
|
||||||
|
/* First time through, set up expression evaluation state */
|
||||||
|
pd->keystate = ExecPrepareExprList(pd->key->partexprs, estate);
|
||||||
|
}
|
||||||
|
|
||||||
|
partexpr_item = list_head(pd->keystate);
|
||||||
|
for (i = 0; i < pd->key->partnatts; i++)
|
||||||
|
{
|
||||||
|
AttrNumber keycol = pd->key->partattrs[i];
|
||||||
|
Datum datum;
|
||||||
|
bool isNull;
|
||||||
|
|
||||||
|
if (keycol != 0)
|
||||||
|
{
|
||||||
|
/* Plain column; get the value directly from the heap tuple */
|
||||||
|
datum = slot_getattr(slot, keycol, &isNull);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Expression; need to evaluate it */
|
||||||
|
if (partexpr_item == NULL)
|
||||||
|
elog(ERROR, "wrong number of partition key expressions");
|
||||||
|
datum = ExecEvalExprSwitchContext((ExprState *) lfirst(partexpr_item),
|
||||||
|
GetPerTupleExprContext(estate),
|
||||||
|
&isNull);
|
||||||
|
partexpr_item = lnext(partexpr_item);
|
||||||
|
}
|
||||||
|
values[i] = datum;
|
||||||
|
isnull[i] = isNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partexpr_item != NULL)
|
||||||
|
elog(ERROR, "wrong number of partition key expressions");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BuildSlotPartitionKeyDescription
|
||||||
|
*
|
||||||
|
* This works very much like BuildIndexValueDescription() and is currently
|
||||||
|
* used for building error messages when ExecFindPartition() fails to find
|
||||||
|
* partition for a row.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
ExecBuildSlotPartitionKeyDescription(Relation rel,
|
||||||
|
Datum *values,
|
||||||
|
bool *isnull,
|
||||||
|
int maxfieldlen)
|
||||||
|
{
|
||||||
|
StringInfoData buf;
|
||||||
|
PartitionKey key = RelationGetPartitionKey(rel);
|
||||||
|
int partnatts = get_partition_natts(key);
|
||||||
|
int i;
|
||||||
|
Oid relid = RelationGetRelid(rel);
|
||||||
|
AclResult aclresult;
|
||||||
|
|
||||||
|
if (check_enable_rls(relid, InvalidOid, true) == RLS_ENABLED)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* If the user has table-level access, just go build the description. */
|
||||||
|
aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT);
|
||||||
|
if (aclresult != ACLCHECK_OK)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Step through the columns of the partition key and make sure the
|
||||||
|
* user has SELECT rights on all of them.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < partnatts; i++)
|
||||||
|
{
|
||||||
|
AttrNumber attnum = get_partition_col_attnum(key, i);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this partition key column is an expression, we return no
|
||||||
|
* detail rather than try to figure out what column(s) the
|
||||||
|
* expression includes and if the user has SELECT rights on them.
|
||||||
|
*/
|
||||||
|
if (attnum == InvalidAttrNumber ||
|
||||||
|
pg_attribute_aclcheck(relid, attnum, GetUserId(),
|
||||||
|
ACL_SELECT) != ACLCHECK_OK)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initStringInfo(&buf);
|
||||||
|
appendStringInfo(&buf, "(%s) = (",
|
||||||
|
pg_get_partkeydef_columns(relid, true));
|
||||||
|
|
||||||
|
for (i = 0; i < partnatts; i++)
|
||||||
|
{
|
||||||
|
char *val;
|
||||||
|
int vallen;
|
||||||
|
|
||||||
|
if (isnull[i])
|
||||||
|
val = "null";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Oid foutoid;
|
||||||
|
bool typisvarlena;
|
||||||
|
|
||||||
|
getTypeOutputInfo(get_partition_col_typid(key, i),
|
||||||
|
&foutoid, &typisvarlena);
|
||||||
|
val = OidOutputFunctionCall(foutoid, values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > 0)
|
||||||
|
appendStringInfoString(&buf, ", ");
|
||||||
|
|
||||||
|
/* truncate if needed */
|
||||||
|
vallen = strlen(val);
|
||||||
|
if (vallen <= maxfieldlen)
|
||||||
|
appendStringInfoString(&buf, val);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vallen = pg_mbcliplen(val, vallen, maxfieldlen);
|
||||||
|
appendBinaryStringInfo(&buf, val, vallen);
|
||||||
|
appendStringInfoString(&buf, "...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendStringInfoChar(&buf, ')');
|
||||||
|
|
||||||
|
return buf.data;
|
||||||
|
}
|
@ -40,6 +40,7 @@
|
|||||||
#include "access/htup_details.h"
|
#include "access/htup_details.h"
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
|
#include "executor/execPartition.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "executor/nodeModifyTable.h"
|
#include "executor/nodeModifyTable.h"
|
||||||
#include "foreign/fdwapi.h"
|
#include "foreign/fdwapi.h"
|
||||||
|
@ -42,37 +42,6 @@ typedef struct PartitionDescData
|
|||||||
|
|
||||||
typedef struct PartitionDescData *PartitionDesc;
|
typedef struct PartitionDescData *PartitionDesc;
|
||||||
|
|
||||||
/*-----------------------
|
|
||||||
* PartitionDispatch - information about one partitioned table in a partition
|
|
||||||
* hierarchy required to route a tuple to one of its partitions
|
|
||||||
*
|
|
||||||
* reldesc Relation descriptor of the table
|
|
||||||
* key Partition key information of the table
|
|
||||||
* keystate Execution state required for expressions in the partition key
|
|
||||||
* partdesc Partition descriptor of the table
|
|
||||||
* tupslot A standalone TupleTableSlot initialized with this table's tuple
|
|
||||||
* descriptor
|
|
||||||
* tupmap TupleConversionMap to convert from the parent's rowtype to
|
|
||||||
* this table's rowtype (when extracting the partition key of a
|
|
||||||
* tuple just before routing it through this table)
|
|
||||||
* indexes Array with partdesc->nparts members (for details on what
|
|
||||||
* individual members represent, see how they are set in
|
|
||||||
* RelationGetPartitionDispatchInfo())
|
|
||||||
*-----------------------
|
|
||||||
*/
|
|
||||||
typedef struct PartitionDispatchData
|
|
||||||
{
|
|
||||||
Relation reldesc;
|
|
||||||
PartitionKey key;
|
|
||||||
List *keystate; /* list of ExprState */
|
|
||||||
PartitionDesc partdesc;
|
|
||||||
TupleTableSlot *tupslot;
|
|
||||||
TupleConversionMap *tupmap;
|
|
||||||
int *indexes;
|
|
||||||
} PartitionDispatchData;
|
|
||||||
|
|
||||||
typedef struct PartitionDispatchData *PartitionDispatch;
|
|
||||||
|
|
||||||
extern void RelationBuildPartitionDesc(Relation relation);
|
extern void RelationBuildPartitionDesc(Relation relation);
|
||||||
extern bool partition_bounds_equal(int partnatts, int16 *parttyplen,
|
extern bool partition_bounds_equal(int partnatts, int16 *parttyplen,
|
||||||
bool *parttypbyval, PartitionBoundInfo b1,
|
bool *parttypbyval, PartitionBoundInfo b1,
|
||||||
@ -91,19 +60,6 @@ extern List *map_partition_varattnos(List *expr, int target_varno,
|
|||||||
extern List *RelationGetPartitionQual(Relation rel);
|
extern List *RelationGetPartitionQual(Relation rel);
|
||||||
extern Expr *get_partition_qual_relid(Oid relid);
|
extern Expr *get_partition_qual_relid(Oid relid);
|
||||||
|
|
||||||
/* For tuple routing */
|
|
||||||
extern PartitionDispatch *RelationGetPartitionDispatchInfo(Relation rel,
|
|
||||||
int *num_parted, List **leaf_part_oids);
|
|
||||||
extern void FormPartitionKeyDatum(PartitionDispatch pd,
|
|
||||||
TupleTableSlot *slot,
|
|
||||||
EState *estate,
|
|
||||||
Datum *values,
|
|
||||||
bool *isnull);
|
|
||||||
extern int get_partition_for_tuple(PartitionDispatch *pd,
|
|
||||||
TupleTableSlot *slot,
|
|
||||||
EState *estate,
|
|
||||||
PartitionDispatchData **failed_at,
|
|
||||||
TupleTableSlot **failed_slot);
|
|
||||||
extern Oid get_default_oid_from_partdesc(PartitionDesc partdesc);
|
extern Oid get_default_oid_from_partdesc(PartitionDesc partdesc);
|
||||||
extern Oid get_default_partition_oid(Oid parentId);
|
extern Oid get_default_partition_oid(Oid parentId);
|
||||||
extern void update_default_partition_oid(Oid parentId, Oid defaultPartId);
|
extern void update_default_partition_oid(Oid parentId, Oid defaultPartId);
|
||||||
@ -111,4 +67,8 @@ extern void check_default_allows_bound(Relation parent, Relation defaultRel,
|
|||||||
PartitionBoundSpec *new_spec);
|
PartitionBoundSpec *new_spec);
|
||||||
extern List *get_proposed_default_constraint(List *new_part_constaints);
|
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 */
|
#endif /* PARTITION_H */
|
||||||
|
65
src/include/executor/execPartition.h
Normal file
65
src/include/executor/execPartition.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*--------------------------------------------------------------------
|
||||||
|
* execPartition.h
|
||||||
|
* POSTGRES partitioning executor interface
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* src/include/executor/execPartition.h
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EXECPARTITION_H
|
||||||
|
#define EXECPARTITION_H
|
||||||
|
|
||||||
|
#include "catalog/partition.h"
|
||||||
|
#include "nodes/execnodes.h"
|
||||||
|
#include "nodes/parsenodes.h"
|
||||||
|
#include "nodes/plannodes.h"
|
||||||
|
|
||||||
|
/*-----------------------
|
||||||
|
* PartitionDispatch - information about one partitioned table in a partition
|
||||||
|
* hierarchy required to route a tuple to one of its partitions
|
||||||
|
*
|
||||||
|
* reldesc Relation descriptor of the table
|
||||||
|
* key Partition key information of the table
|
||||||
|
* keystate Execution state required for expressions in the partition key
|
||||||
|
* partdesc Partition descriptor of the table
|
||||||
|
* tupslot A standalone TupleTableSlot initialized with this table's tuple
|
||||||
|
* descriptor
|
||||||
|
* tupmap TupleConversionMap to convert from the parent's rowtype to
|
||||||
|
* this table's rowtype (when extracting the partition key of a
|
||||||
|
* tuple just before routing it through this table)
|
||||||
|
* indexes Array with partdesc->nparts members (for details on what
|
||||||
|
* individual members represent, see how they are set in
|
||||||
|
* get_partition_dispatch_recurse())
|
||||||
|
*-----------------------
|
||||||
|
*/
|
||||||
|
typedef struct PartitionDispatchData
|
||||||
|
{
|
||||||
|
Relation reldesc;
|
||||||
|
PartitionKey key;
|
||||||
|
List *keystate; /* list of ExprState */
|
||||||
|
PartitionDesc partdesc;
|
||||||
|
TupleTableSlot *tupslot;
|
||||||
|
TupleConversionMap *tupmap;
|
||||||
|
int *indexes;
|
||||||
|
} PartitionDispatchData;
|
||||||
|
|
||||||
|
typedef struct PartitionDispatchData *PartitionDispatch;
|
||||||
|
|
||||||
|
extern void ExecSetupPartitionTupleRouting(Relation rel,
|
||||||
|
Index resultRTindex,
|
||||||
|
EState *estate,
|
||||||
|
PartitionDispatch **pd,
|
||||||
|
ResultRelInfo ***partitions,
|
||||||
|
TupleConversionMap ***tup_conv_maps,
|
||||||
|
TupleTableSlot **partition_tuple_slot,
|
||||||
|
int *num_parted, int *num_partitions);
|
||||||
|
extern int ExecFindPartition(ResultRelInfo *resultRelInfo,
|
||||||
|
PartitionDispatch *pd,
|
||||||
|
TupleTableSlot *slot,
|
||||||
|
EState *estate);
|
||||||
|
|
||||||
|
#endif /* EXECPARTITION_H */
|
@ -188,6 +188,8 @@ extern void ExecCleanUpTriggerState(EState *estate);
|
|||||||
extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
|
extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
|
||||||
extern void ExecConstraints(ResultRelInfo *resultRelInfo,
|
extern void ExecConstraints(ResultRelInfo *resultRelInfo,
|
||||||
TupleTableSlot *slot, EState *estate);
|
TupleTableSlot *slot, EState *estate);
|
||||||
|
extern void ExecPartitionCheck(ResultRelInfo *resultRelInfo,
|
||||||
|
TupleTableSlot *slot, EState *estate);
|
||||||
extern void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
|
extern void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
|
||||||
TupleTableSlot *slot, EState *estate);
|
TupleTableSlot *slot, EState *estate);
|
||||||
extern LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo);
|
extern LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo);
|
||||||
@ -206,18 +208,6 @@ extern void EvalPlanQualSetPlan(EPQState *epqstate,
|
|||||||
extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
|
extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
|
||||||
HeapTuple tuple);
|
HeapTuple tuple);
|
||||||
extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
|
extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
|
||||||
extern void ExecSetupPartitionTupleRouting(Relation rel,
|
|
||||||
Index resultRTindex,
|
|
||||||
EState *estate,
|
|
||||||
PartitionDispatch **pd,
|
|
||||||
ResultRelInfo ***partitions,
|
|
||||||
TupleConversionMap ***tup_conv_maps,
|
|
||||||
TupleTableSlot **partition_tuple_slot,
|
|
||||||
int *num_parted, int *num_partitions);
|
|
||||||
extern int ExecFindPartition(ResultRelInfo *resultRelInfo,
|
|
||||||
PartitionDispatch *pd,
|
|
||||||
TupleTableSlot *slot,
|
|
||||||
EState *estate);
|
|
||||||
|
|
||||||
#define EvalPlanQualSetSlot(epqstate, slot) ((epqstate)->origslot = (slot))
|
#define EvalPlanQualSetSlot(epqstate, slot) ((epqstate)->origslot = (slot))
|
||||||
extern void EvalPlanQualFetchRowMarks(EPQState *epqstate);
|
extern void EvalPlanQualFetchRowMarks(EPQState *epqstate);
|
||||||
|
Reference in New Issue
Block a user