Commit 4e5fe9ad authored by Robert Haas's avatar Robert Haas

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
parent cd8ce3a2
This diff is collapsed.
...@@ -27,6 +27,7 @@ ...@@ -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 ...@@ -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 @@ ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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;
}
This diff is collapsed.
...@@ -40,6 +40,7 @@ ...@@ -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 ...@@ -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, ...@@ -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, ...@@ -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 */
/*--------------------------------------------------------------------
* 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); ...@@ -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, ...@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment