PostgreSQL Source Code git master
execPartition.c File Reference
#include "postgres.h"
#include "access/table.h"
#include "access/tableam.h"
#include "catalog/partition.h"
#include "executor/execPartition.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
#include "foreign/fdwapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "partitioning/partbounds.h"
#include "partitioning/partdesc.h"
#include "partitioning/partprune.h"
#include "rewrite/rewriteManip.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
#include "utils/partcache.h"
#include "utils/rls.h"
#include "utils/ruleutils.h"
Include dependency graph for execPartition.c:

Go to the source code of this file.

Data Structures

struct  PartitionTupleRouting
 
struct  PartitionDispatchData
 

Macros

#define PARTITION_CACHED_FIND_THRESHOLD   16
 

Typedefs

typedef struct PartitionDispatchData PartitionDispatchData
 

Functions

static ResultRelInfoExecInitPartitionInfo (ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, PartitionDispatch dispatch, ResultRelInfo *rootResultRelInfo, int partidx)
 
static void ExecInitRoutingInfo (ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, PartitionDispatch dispatch, ResultRelInfo *partRelInfo, int partidx, bool is_borrowed_rel)
 
static PartitionDispatch ExecInitPartitionDispatchInfo (EState *estate, PartitionTupleRouting *proute, Oid partoid, PartitionDispatch parent_pd, int partidx, ResultRelInfo *rootResultRelInfo)
 
static void FormPartitionKeyDatum (PartitionDispatch pd, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
 
static int get_partition_for_tuple (PartitionDispatch pd, Datum *values, bool *isnull)
 
static char * ExecBuildSlotPartitionKeyDescription (Relation rel, Datum *values, bool *isnull, int maxfieldlen)
 
static Listadjust_partition_colnos (List *colnos, ResultRelInfo *leaf_part_rri)
 
static Listadjust_partition_colnos_using_map (List *colnos, AttrMap *attrMap)
 
static PartitionPruneStateCreatePartitionPruneState (EState *estate, PartitionPruneInfo *pruneinfo, Bitmapset **all_leafpart_rtis)
 
static void InitPartitionPruneContext (PartitionPruneContext *context, List *pruning_steps, PartitionDesc partdesc, PartitionKey partkey, PlanState *planstate, ExprContext *econtext)
 
static void InitExecPartitionPruneContexts (PartitionPruneState *prunestate, PlanState *parent_plan, Bitmapset *initially_valid_subplans, int n_total_subplans)
 
static void find_matching_subplans_recurse (PartitionPruningData *prunedata, PartitionedRelPruningData *pprune, bool initial_prune, Bitmapset **validsubplans, Bitmapset **validsubplan_rtis)
 
PartitionTupleRoutingExecSetupPartitionTupleRouting (EState *estate, Relation rel)
 
ResultRelInfoExecFindPartition (ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, TupleTableSlot *slot, EState *estate)
 
void ExecCleanupTupleRouting (ModifyTableState *mtstate, PartitionTupleRouting *proute)
 
void ExecDoInitialPruning (EState *estate)
 
PartitionPruneStateExecInitPartitionExecPruning (PlanState *planstate, int n_total_subplans, int part_prune_index, Bitmapset *relids, Bitmapset **initially_valid_subplans)
 
BitmapsetExecFindMatchingSubPlans (PartitionPruneState *prunestate, bool initial_prune, Bitmapset **validsubplan_rtis)
 

Macro Definition Documentation

◆ PARTITION_CACHED_FIND_THRESHOLD

#define PARTITION_CACHED_FIND_THRESHOLD   16

Definition at line 1351 of file execPartition.c.

Typedef Documentation

◆ PartitionDispatchData

Function Documentation

◆ adjust_partition_colnos()

static List * adjust_partition_colnos ( List colnos,
ResultRelInfo leaf_part_rri 
)
static

Definition at line 1702 of file execPartition.c.

1703{
1704 TupleConversionMap *map = ExecGetChildToRootMap(leaf_part_rri);
1705
1706 Assert(map != NULL);
1707
1708 return adjust_partition_colnos_using_map(colnos, map->attrMap);
1709}
static List * adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap)
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1300
Assert(PointerIsAligned(start, uint64))
AttrMap * attrMap
Definition: tupconvert.h:28

References adjust_partition_colnos_using_map(), Assert(), TupleConversionMap::attrMap, and ExecGetChildToRootMap().

Referenced by ExecInitPartitionInfo().

◆ adjust_partition_colnos_using_map()

static List * adjust_partition_colnos_using_map ( List colnos,
AttrMap attrMap 
)
static

Definition at line 1719 of file execPartition.c.

1720{
1721 List *new_colnos = NIL;
1722 ListCell *lc;
1723
1724 Assert(attrMap != NULL); /* else we shouldn't be here */
1725
1726 foreach(lc, colnos)
1727 {
1728 AttrNumber parentattrno = lfirst_int(lc);
1729
1730 if (parentattrno <= 0 ||
1731 parentattrno > attrMap->maplen ||
1732 attrMap->attnums[parentattrno - 1] == 0)
1733 elog(ERROR, "unexpected attno %d in target column list",
1734 parentattrno);
1735 new_colnos = lappend_int(new_colnos,
1736 attrMap->attnums[parentattrno - 1]);
1737 }
1738
1739 return new_colnos;
1740}
int16 AttrNumber
Definition: attnum.h:21
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
List * lappend_int(List *list, int datum)
Definition: list.c:357
#define NIL
Definition: pg_list.h:68
#define lfirst_int(lc)
Definition: pg_list.h:173
int maplen
Definition: attmap.h:37
AttrNumber * attnums
Definition: attmap.h:36
Definition: pg_list.h:54

References Assert(), AttrMap::attnums, elog, ERROR, lappend_int(), lfirst_int, AttrMap::maplen, and NIL.

Referenced by adjust_partition_colnos(), and ExecInitPartitionInfo().

◆ CreatePartitionPruneState()

static PartitionPruneState * CreatePartitionPruneState ( EState estate,
PartitionPruneInfo pruneinfo,
Bitmapset **  all_leafpart_rtis 
)
static

Definition at line 1969 of file execPartition.c.

1971{
1972 PartitionPruneState *prunestate;
1973 int n_part_hierarchies;
1974 ListCell *lc;
1975 int i;
1976
1977 /*
1978 * Expression context that will be used by partkey_datum_from_expr() to
1979 * evaluate expressions for comparison against partition bounds.
1980 */
1981 ExprContext *econtext = CreateExprContext(estate);
1982
1983 /* For data reading, executor always includes detached partitions */
1984 if (estate->es_partition_directory == NULL)
1985 estate->es_partition_directory =
1986 CreatePartitionDirectory(estate->es_query_cxt, false);
1987
1988 n_part_hierarchies = list_length(pruneinfo->prune_infos);
1989 Assert(n_part_hierarchies > 0);
1990
1991 /*
1992 * Allocate the data structure
1993 */
1994 prunestate = (PartitionPruneState *)
1995 palloc(offsetof(PartitionPruneState, partprunedata) +
1996 sizeof(PartitionPruningData *) * n_part_hierarchies);
1997
1998 /* Save ExprContext for use during InitExecPartitionPruneContexts(). */
1999 prunestate->econtext = econtext;
2000 prunestate->execparamids = NULL;
2001 /* other_subplans can change at runtime, so we need our own copy */
2002 prunestate->other_subplans = bms_copy(pruneinfo->other_subplans);
2003 prunestate->do_initial_prune = false; /* may be set below */
2004 prunestate->do_exec_prune = false; /* may be set below */
2005 prunestate->num_partprunedata = n_part_hierarchies;
2006
2007 /*
2008 * Create a short-term memory context which we'll use when making calls to
2009 * the partition pruning functions. This avoids possible memory leaks,
2010 * since the pruning functions call comparison functions that aren't under
2011 * our control.
2012 */
2013 prunestate->prune_context =
2015 "Partition Prune",
2017
2018 i = 0;
2019 foreach(lc, pruneinfo->prune_infos)
2020 {
2021 List *partrelpruneinfos = lfirst_node(List, lc);
2022 int npartrelpruneinfos = list_length(partrelpruneinfos);
2023 PartitionPruningData *prunedata;
2024 ListCell *lc2;
2025 int j;
2026
2027 prunedata = (PartitionPruningData *)
2028 palloc(offsetof(PartitionPruningData, partrelprunedata) +
2029 npartrelpruneinfos * sizeof(PartitionedRelPruningData));
2030 prunestate->partprunedata[i] = prunedata;
2031 prunedata->num_partrelprunedata = npartrelpruneinfos;
2032
2033 j = 0;
2034 foreach(lc2, partrelpruneinfos)
2035 {
2037 PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
2038 Relation partrel;
2039 PartitionDesc partdesc;
2040 PartitionKey partkey;
2041
2042 /*
2043 * We can rely on the copies of the partitioned table's partition
2044 * key and partition descriptor appearing in its relcache entry,
2045 * because that entry will be held open and locked for the
2046 * duration of this executor run.
2047 */
2048 partrel = ExecGetRangeTableRelation(estate, pinfo->rtindex, false);
2049
2050 /* Remember for InitExecPartitionPruneContexts(). */
2051 pprune->partrel = partrel;
2052
2053 partkey = RelationGetPartitionKey(partrel);
2055 partrel);
2056
2057 /*
2058 * Initialize the subplan_map and subpart_map.
2059 *
2060 * The set of partitions that exist now might not be the same that
2061 * existed when the plan was made. The normal case is that it is;
2062 * optimize for that case with a quick comparison, and just copy
2063 * the subplan_map and make subpart_map, leafpart_rti_map point to
2064 * the ones in PruneInfo.
2065 *
2066 * For the case where they aren't identical, we could have more
2067 * partitions on either side; or even exactly the same number of
2068 * them on both but the set of OIDs doesn't match fully. Handle
2069 * this by creating new subplan_map and subpart_map arrays that
2070 * corresponds to the ones in the PruneInfo where the new
2071 * partition descriptor's OIDs match. Any that don't match can be
2072 * set to -1, as if they were pruned. By construction, both
2073 * arrays are in partition bounds order.
2074 */
2075 pprune->nparts = partdesc->nparts;
2076 pprune->subplan_map = palloc(sizeof(int) * partdesc->nparts);
2077
2078 if (partdesc->nparts == pinfo->nparts &&
2079 memcmp(partdesc->oids, pinfo->relid_map,
2080 sizeof(int) * partdesc->nparts) == 0)
2081 {
2082 pprune->subpart_map = pinfo->subpart_map;
2083 pprune->leafpart_rti_map = pinfo->leafpart_rti_map;
2084 memcpy(pprune->subplan_map, pinfo->subplan_map,
2085 sizeof(int) * pinfo->nparts);
2086 }
2087 else
2088 {
2089 int pd_idx = 0;
2090 int pp_idx;
2091
2092 /*
2093 * When the partition arrays are not identical, there could be
2094 * some new ones but it's also possible that one was removed;
2095 * we cope with both situations by walking the arrays and
2096 * discarding those that don't match.
2097 *
2098 * If the number of partitions on both sides match, it's still
2099 * possible that one partition has been detached and another
2100 * attached. Cope with that by creating a map that skips any
2101 * mismatches.
2102 */
2103 pprune->subpart_map = palloc(sizeof(int) * partdesc->nparts);
2104 pprune->leafpart_rti_map = palloc(sizeof(int) * partdesc->nparts);
2105
2106 for (pp_idx = 0; pp_idx < partdesc->nparts; pp_idx++)
2107 {
2108 /* Skip any InvalidOid relid_map entries */
2109 while (pd_idx < pinfo->nparts &&
2110 !OidIsValid(pinfo->relid_map[pd_idx]))
2111 pd_idx++;
2112
2113 recheck:
2114 if (pd_idx < pinfo->nparts &&
2115 pinfo->relid_map[pd_idx] == partdesc->oids[pp_idx])
2116 {
2117 /* match... */
2118 pprune->subplan_map[pp_idx] =
2119 pinfo->subplan_map[pd_idx];
2120 pprune->subpart_map[pp_idx] =
2121 pinfo->subpart_map[pd_idx];
2122 pprune->leafpart_rti_map[pp_idx] =
2123 pinfo->leafpart_rti_map[pd_idx];
2124 pd_idx++;
2125 continue;
2126 }
2127
2128 /*
2129 * There isn't an exact match in the corresponding
2130 * positions of both arrays. Peek ahead in
2131 * pinfo->relid_map to see if we have a match for the
2132 * current partition in partdesc. Normally if a match
2133 * exists it's just one element ahead, and it means the
2134 * planner saw one extra partition that we no longer see
2135 * now (its concurrent detach finished just in between);
2136 * so we skip that one by updating pd_idx to the new
2137 * location and jumping above. We can then continue to
2138 * match the rest of the elements after skipping the OID
2139 * with no match; no future matches are tried for the
2140 * element that was skipped, because we know the arrays to
2141 * be in the same order.
2142 *
2143 * If we don't see a match anywhere in the rest of the
2144 * pinfo->relid_map array, that means we see an element
2145 * now that the planner didn't see, so mark that one as
2146 * pruned and move on.
2147 */
2148 for (int pd_idx2 = pd_idx + 1; pd_idx2 < pinfo->nparts; pd_idx2++)
2149 {
2150 if (pd_idx2 >= pinfo->nparts)
2151 break;
2152 if (pinfo->relid_map[pd_idx2] == partdesc->oids[pp_idx])
2153 {
2154 pd_idx = pd_idx2;
2155 goto recheck;
2156 }
2157 }
2158
2159 pprune->subpart_map[pp_idx] = -1;
2160 pprune->subplan_map[pp_idx] = -1;
2161 pprune->leafpart_rti_map[pp_idx] = 0;
2162 }
2163 }
2164
2165 /* present_parts is also subject to later modification */
2166 pprune->present_parts = bms_copy(pinfo->present_parts);
2167
2168 /*
2169 * Only initial_context is initialized here. exec_context is
2170 * initialized during ExecInitPartitionExecPruning() when the
2171 * parent plan's PlanState is available.
2172 *
2173 * Note that we must skip execution-time (both "init" and "exec")
2174 * partition pruning in EXPLAIN (GENERIC_PLAN), since parameter
2175 * values may be missing.
2176 */
2178 if (pinfo->initial_pruning_steps &&
2180 {
2182 pprune->initial_pruning_steps,
2183 partdesc, partkey, NULL,
2184 econtext);
2185 /* Record whether initial pruning is needed at any level */
2186 prunestate->do_initial_prune = true;
2187 }
2188 pprune->exec_pruning_steps = pinfo->exec_pruning_steps;
2189 if (pinfo->exec_pruning_steps &&
2191 {
2192 /* Record whether exec pruning is needed at any level */
2193 prunestate->do_exec_prune = true;
2194 }
2195
2196 /*
2197 * Accumulate the IDs of all PARAM_EXEC Params affecting the
2198 * partitioning decisions at this plan node.
2199 */
2200 prunestate->execparamids = bms_add_members(prunestate->execparamids,
2201 pinfo->execparamids);
2202
2203 /*
2204 * Return all leaf partition indexes if we're skipping pruning in
2205 * the EXPLAIN (GENERIC_PLAN) case.
2206 */
2207 if (pinfo->initial_pruning_steps && !prunestate->do_initial_prune)
2208 {
2209 int part_index = -1;
2210
2211 while ((part_index = bms_next_member(pprune->present_parts,
2212 part_index)) >= 0)
2213 {
2214 Index rtindex = pprune->leafpart_rti_map[part_index];
2215
2216 if (rtindex)
2217 *all_leafpart_rtis = bms_add_member(*all_leafpart_rtis,
2218 rtindex);
2219 }
2220 }
2221
2222 j++;
2223 }
2224 i++;
2225 }
2226
2227 return prunestate;
2228}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:917
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:122
unsigned int Index
Definition: c.h:585
#define OidIsValid(objectId)
Definition: c.h:746
static void InitPartitionPruneContext(PartitionPruneContext *context, List *pruning_steps, PartitionDesc partdesc, PartitionKey partkey, PlanState *planstate, ExprContext *econtext)
struct PartitionedRelPruningData PartitionedRelPruningData
Relation ExecGetRangeTableRelation(EState *estate, Index rti, bool isResultRel)
Definition: execUtils.c:825
ExprContext * CreateExprContext(EState *estate)
Definition: execUtils.c:307
#define EXEC_FLAG_EXPLAIN_GENERIC
Definition: executor.h:66
int j
Definition: isn.c:78
int i
Definition: isn.c:77
void * palloc(Size size)
Definition: mcxt.c:1945
MemoryContext CurrentMemoryContext
Definition: mcxt.c:159
#define AllocSetContextCreate
Definition: memutils.h:149
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:180
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:51
PartitionDirectory CreatePartitionDirectory(MemoryContext mcxt, bool omit_detached)
Definition: partdesc.c:423
PartitionDesc PartitionDirectoryLookup(PartitionDirectory pdir, Relation rel)
Definition: partdesc.c:456
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
int es_top_eflags
Definition: execnodes.h:715
MemoryContext es_query_cxt
Definition: execnodes.h:706
PartitionDirectory es_partition_directory
Definition: execnodes.h:688
struct EState * ecxt_estate
Definition: execnodes.h:309
Bitmapset * other_subplans
Definition: plannodes.h:1592
PartitionPruningData * partprunedata[FLEXIBLE_ARRAY_MEMBER]
Bitmapset * execparamids
ExprContext * econtext
Bitmapset * other_subplans
MemoryContext prune_context
PartitionedRelPruningData partrelprunedata[FLEXIBLE_ARRAY_MEMBER]
Definition: execPartition.h:87
Bitmapset * present_parts
Definition: plannodes.h:1626
Bitmapset * execparamids
Definition: plannodes.h:1655
PartitionPruneContext initial_context
Definition: execPartition.h:73

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), bms_add_member(), bms_add_members(), bms_copy(), bms_next_member(), CreateExprContext(), CreatePartitionDirectory(), CurrentMemoryContext, PartitionPruneState::do_exec_prune, PartitionPruneState::do_initial_prune, PartitionPruneState::econtext, ExprContext::ecxt_estate, EState::es_partition_directory, EState::es_query_cxt, EState::es_top_eflags, EXEC_FLAG_EXPLAIN_GENERIC, PartitionedRelPruningData::exec_pruning_steps, PartitionedRelPruneInfo::exec_pruning_steps, ExecGetRangeTableRelation(), PartitionPruneState::execparamids, PartitionedRelPruneInfo::execparamids, i, PartitionedRelPruningData::initial_context, PartitionedRelPruningData::initial_pruning_steps, PartitionedRelPruneInfo::initial_pruning_steps, InitPartitionPruneContext(), j, PartitionedRelPruningData::leafpart_rti_map, lfirst_node, list_length(), PartitionedRelPruningData::nparts, PartitionedRelPruneInfo::nparts, PartitionDescData::nparts, PartitionPruneState::num_partprunedata, PartitionPruningData::num_partrelprunedata, OidIsValid, PartitionDescData::oids, PartitionPruneState::other_subplans, PartitionPruneInfo::other_subplans, palloc(), PartitionDirectoryLookup(), PartitionPruneState::partprunedata, PartitionedRelPruningData::partrel, PartitionPruningData::partrelprunedata, PartitionedRelPruningData::present_parts, PartitionedRelPruneInfo::present_parts, PartitionPruneState::prune_context, PartitionPruneInfo::prune_infos, RelationGetPartitionKey(), PartitionedRelPruneInfo::rtindex, PartitionedRelPruningData::subpart_map, and PartitionedRelPruningData::subplan_map.

Referenced by ExecDoInitialPruning().

◆ ExecBuildSlotPartitionKeyDescription()

static char * ExecBuildSlotPartitionKeyDescription ( Relation  rel,
Datum values,
bool *  isnull,
int  maxfieldlen 
)
static

Definition at line 1614 of file execPartition.c.

1618{
1621 int partnatts = get_partition_natts(key);
1622 int i;
1623 Oid relid = RelationGetRelid(rel);
1624 AclResult aclresult;
1625
1626 if (check_enable_rls(relid, InvalidOid, true) == RLS_ENABLED)
1627 return NULL;
1628
1629 /* If the user has table-level access, just go build the description. */
1630 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT);
1631 if (aclresult != ACLCHECK_OK)
1632 {
1633 /*
1634 * Step through the columns of the partition key and make sure the
1635 * user has SELECT rights on all of them.
1636 */
1637 for (i = 0; i < partnatts; i++)
1638 {
1640
1641 /*
1642 * If this partition key column is an expression, we return no
1643 * detail rather than try to figure out what column(s) the
1644 * expression includes and if the user has SELECT rights on them.
1645 */
1646 if (attnum == InvalidAttrNumber ||
1649 return NULL;
1650 }
1651 }
1652
1654 appendStringInfo(&buf, "(%s) = (",
1655 pg_get_partkeydef_columns(relid, true));
1656
1657 for (i = 0; i < partnatts; i++)
1658 {
1659 char *val;
1660 int vallen;
1661
1662 if (isnull[i])
1663 val = "null";
1664 else
1665 {
1666 Oid foutoid;
1667 bool typisvarlena;
1668
1670 &foutoid, &typisvarlena);
1671 val = OidOutputFunctionCall(foutoid, values[i]);
1672 }
1673
1674 if (i > 0)
1676
1677 /* truncate if needed */
1678 vallen = strlen(val);
1679 if (vallen <= maxfieldlen)
1680 appendBinaryStringInfo(&buf, val, vallen);
1681 else
1682 {
1683 vallen = pg_mbcliplen(val, vallen, maxfieldlen);
1684 appendBinaryStringInfo(&buf, val, vallen);
1685 appendStringInfoString(&buf, "...");
1686 }
1687 }
1688
1690
1691 return buf.data;
1692}
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3853
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4024
#define InvalidAttrNumber
Definition: attnum.h:23
static Datum values[MAXATTR]
Definition: bootstrap.c:151
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1763
long val
Definition: informix.c:689
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:3047
int pg_mbcliplen(const char *mbstr, int len, int limit)
Definition: mbutils.c:1083
Oid GetUserId(void)
Definition: miscinit.c:520
#define ACL_SELECT
Definition: parsenodes.h:77
static int16 get_partition_col_attnum(PartitionKey key, int col)
Definition: partcache.h:80
static int get_partition_natts(PartitionKey key)
Definition: partcache.h:65
static Oid get_partition_col_typid(PartitionKey key, int col)
Definition: partcache.h:86
int16 attnum
Definition: pg_attribute.h:74
static char * buf
Definition: pg_test_fsync.c:72
#define InvalidOid
Definition: postgres_ext.h:35
unsigned int Oid
Definition: postgres_ext.h:30
#define RelationGetRelid(relation)
Definition: rel.h:516
int check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
Definition: rls.c:52
@ RLS_ENABLED
Definition: rls.h:45
char * pg_get_partkeydef_columns(Oid relid, bool pretty)
Definition: ruleutils.c:1924
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:145
void appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
Definition: stringinfo.c:281
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:230
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:242
void initStringInfo(StringInfo str)
Definition: stringinfo.c:97

References ACL_SELECT, ACLCHECK_OK, appendBinaryStringInfo(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), attnum, buf, check_enable_rls(), get_partition_col_attnum(), get_partition_col_typid(), get_partition_natts(), getTypeOutputInfo(), GetUserId(), i, initStringInfo(), InvalidAttrNumber, InvalidOid, sort-test::key, OidOutputFunctionCall(), pg_attribute_aclcheck(), pg_class_aclcheck(), pg_get_partkeydef_columns(), pg_mbcliplen(), RelationGetPartitionKey(), RelationGetRelid, RLS_ENABLED, val, and values.

Referenced by ExecFindPartition().

◆ ExecCleanupTupleRouting()

void ExecCleanupTupleRouting ( ModifyTableState mtstate,
PartitionTupleRouting proute 
)

Definition at line 1236 of file execPartition.c.

1238{
1239 int i;
1240
1241 /*
1242 * Remember, proute->partition_dispatch_info[0] corresponds to the root
1243 * partitioned table, which we must not try to close, because it is the
1244 * main target table of the query that will be closed by callers such as
1245 * ExecEndPlan() or DoCopy(). Also, tupslot is NULL for the root
1246 * partitioned table.
1247 */
1248 for (i = 1; i < proute->num_dispatch; i++)
1249 {
1251
1253
1254 if (pd->tupslot)
1256 }
1257
1258 for (i = 0; i < proute->num_partitions; i++)
1259 {
1260 ResultRelInfo *resultRelInfo = proute->partitions[i];
1261
1262 /* Allow any FDWs to shut down */
1263 if (resultRelInfo->ri_FdwRoutine != NULL &&
1264 resultRelInfo->ri_FdwRoutine->EndForeignInsert != NULL)
1265 resultRelInfo->ri_FdwRoutine->EndForeignInsert(mtstate->ps.state,
1266 resultRelInfo);
1267
1268 /*
1269 * Close it if it's not one of the result relations borrowed from the
1270 * owning ModifyTableState; those will be closed by ExecEndPlan().
1271 */
1272 if (proute->is_borrowed_rel[i])
1273 continue;
1274
1275 ExecCloseIndices(resultRelInfo);
1276 table_close(resultRelInfo->ri_RelationDesc, NoLock);
1277 }
1278}
void ExecCloseIndices(ResultRelInfo *resultRelInfo)
Definition: execIndexing.c:238
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1443
#define NoLock
Definition: lockdefs.h:34
EndForeignInsert_function EndForeignInsert
Definition: fdwapi.h:239
PlanState ps
Definition: execnodes.h:1394
TupleTableSlot * tupslot
PartitionDispatch * partition_dispatch_info
Definition: execPartition.c:94
ResultRelInfo ** partitions
Definition: execPartition.c:98
EState * state
Definition: execnodes.h:1158
Relation ri_RelationDesc
Definition: execnodes.h:474
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:527
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126

References FdwRoutine::EndForeignInsert, ExecCloseIndices(), ExecDropSingleTupleTableSlot(), i, PartitionTupleRouting::is_borrowed_rel, NoLock, PartitionTupleRouting::num_dispatch, PartitionTupleRouting::num_partitions, PartitionTupleRouting::partition_dispatch_info, PartitionTupleRouting::partitions, ModifyTableState::ps, PartitionDispatchData::reldesc, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_RelationDesc, PlanState::state, table_close(), and PartitionDispatchData::tupslot.

Referenced by CopyFrom(), ExecEndModifyTable(), and finish_edata().

◆ ExecDoInitialPruning()

void ExecDoInitialPruning ( EState estate)

Definition at line 1819 of file execPartition.c.

1820{
1821 ListCell *lc;
1822
1823 foreach(lc, estate->es_part_prune_infos)
1824 {
1826 PartitionPruneState *prunestate;
1827 Bitmapset *validsubplans = NULL;
1828 Bitmapset *all_leafpart_rtis = NULL;
1829 Bitmapset *validsubplan_rtis = NULL;
1830
1831 /* Create and save the PartitionPruneState. */
1832 prunestate = CreatePartitionPruneState(estate, pruneinfo,
1833 &all_leafpart_rtis);
1835 prunestate);
1836
1837 /*
1838 * Perform initial pruning steps, if any, and save the result
1839 * bitmapset or NULL as described in the header comment.
1840 */
1841 if (prunestate->do_initial_prune)
1842 validsubplans = ExecFindMatchingSubPlans(prunestate, true,
1843 &validsubplan_rtis);
1844 else
1845 validsubplan_rtis = all_leafpart_rtis;
1846
1848 validsubplan_rtis);
1850 validsubplans);
1851 }
1852}
Bitmapset * ExecFindMatchingSubPlans(PartitionPruneState *prunestate, bool initial_prune, Bitmapset **validsubplan_rtis)
static PartitionPruneState * CreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo, Bitmapset **all_leafpart_rtis)
List * lappend(List *list, void *datum)
Definition: list.c:339
List * es_part_prune_infos
Definition: execnodes.h:666
Bitmapset * es_unpruned_relids
Definition: execnodes.h:669
List * es_part_prune_states
Definition: execnodes.h:667
List * es_part_prune_results
Definition: execnodes.h:668

References bms_add_members(), CreatePartitionPruneState(), PartitionPruneState::do_initial_prune, EState::es_part_prune_infos, EState::es_part_prune_results, EState::es_part_prune_states, EState::es_unpruned_relids, ExecFindMatchingSubPlans(), lappend(), and lfirst_node.

Referenced by InitPlan().

◆ ExecFindMatchingSubPlans()

Bitmapset * ExecFindMatchingSubPlans ( PartitionPruneState prunestate,
bool  initial_prune,
Bitmapset **  validsubplan_rtis 
)

Definition at line 2493 of file execPartition.c.

2496{
2497 Bitmapset *result = NULL;
2498 MemoryContext oldcontext;
2499 int i;
2500
2501 /*
2502 * Either we're here on the initial prune done during pruning
2503 * initialization, or we're at a point where PARAM_EXEC Params can be
2504 * evaluated *and* there are steps in which to do so.
2505 */
2506 Assert(initial_prune || prunestate->do_exec_prune);
2507 Assert(validsubplan_rtis != NULL || !initial_prune);
2508
2509 /*
2510 * Switch to a temp context to avoid leaking memory in the executor's
2511 * query-lifespan memory context.
2512 */
2513 oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
2514
2515 /*
2516 * For each hierarchy, do the pruning tests, and add nondeletable
2517 * subplans' indexes to "result".
2518 */
2519 for (i = 0; i < prunestate->num_partprunedata; i++)
2520 {
2521 PartitionPruningData *prunedata = prunestate->partprunedata[i];
2523
2524 /*
2525 * We pass the zeroth item, belonging to the root table of the
2526 * hierarchy, and find_matching_subplans_recurse() takes care of
2527 * recursing to other (lower-level) parents as needed.
2528 */
2529 pprune = &prunedata->partrelprunedata[0];
2530 find_matching_subplans_recurse(prunedata, pprune, initial_prune,
2531 &result, validsubplan_rtis);
2532
2533 /*
2534 * Expression eval may have used space in ExprContext too. Avoid
2535 * accessing exec_context during initial pruning, as it is not valid
2536 * at that stage.
2537 */
2538 if (!initial_prune && pprune->exec_pruning_steps)
2540 }
2541
2542 /* Add in any subplans that partition pruning didn't account for */
2543 result = bms_add_members(result, prunestate->other_subplans);
2544
2545 MemoryContextSwitchTo(oldcontext);
2546
2547 /* Copy result out of the temp context before we reset it */
2548 result = bms_copy(result);
2549 if (validsubplan_rtis)
2550 *validsubplan_rtis = bms_copy(*validsubplan_rtis);
2551
2552 MemoryContextReset(prunestate->prune_context);
2553
2554 return result;
2555}
static void find_matching_subplans_recurse(PartitionPruningData *prunedata, PartitionedRelPruningData *pprune, bool initial_prune, Bitmapset **validsubplans, Bitmapset **validsubplan_rtis)
#define ResetExprContext(econtext)
Definition: executor.h:644
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:414
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
ExprContext * exprcontext
Definition: partprune.h:60
PartitionPruneContext exec_context
Definition: execPartition.h:74

References Assert(), bms_add_members(), bms_copy(), PartitionPruneState::do_exec_prune, PartitionedRelPruningData::exec_context, PartitionedRelPruningData::exec_pruning_steps, PartitionPruneContext::exprcontext, find_matching_subplans_recurse(), i, MemoryContextReset(), MemoryContextSwitchTo(), PartitionPruneState::num_partprunedata, PartitionPruneState::other_subplans, PartitionPruneState::partprunedata, PartitionPruningData::partrelprunedata, PartitionPruneState::prune_context, and ResetExprContext.

Referenced by choose_next_subplan_for_leader(), choose_next_subplan_for_worker(), choose_next_subplan_locally(), ExecAppendAsyncBegin(), ExecDoInitialPruning(), and ExecMergeAppend().

◆ ExecFindPartition()

ResultRelInfo * ExecFindPartition ( ModifyTableState mtstate,
ResultRelInfo rootResultRelInfo,
PartitionTupleRouting proute,
TupleTableSlot slot,
EState estate 
)

Definition at line 265 of file execPartition.c.

269{
272 bool isnull[PARTITION_MAX_KEYS];
273 Relation rel;
274 PartitionDispatch dispatch;
275 PartitionDesc partdesc;
276 ExprContext *ecxt = GetPerTupleExprContext(estate);
277 TupleTableSlot *ecxt_scantuple_saved = ecxt->ecxt_scantuple;
278 TupleTableSlot *rootslot = slot;
279 TupleTableSlot *myslot = NULL;
280 MemoryContext oldcxt;
281 ResultRelInfo *rri = NULL;
282
283 /* use per-tuple context here to avoid leaking memory */
285
286 /*
287 * First check the root table's partition constraint, if any. No point in
288 * routing the tuple if it doesn't belong in the root table itself.
289 */
290 if (rootResultRelInfo->ri_RelationDesc->rd_rel->relispartition)
291 ExecPartitionCheck(rootResultRelInfo, slot, estate, true);
292
293 /* start with the root partitioned table */
294 dispatch = pd[0];
295 while (dispatch != NULL)
296 {
297 int partidx = -1;
298 bool is_leaf;
299
301
302 rel = dispatch->reldesc;
303 partdesc = dispatch->partdesc;
304
305 /*
306 * Extract partition key from tuple. Expression evaluation machinery
307 * that FormPartitionKeyDatum() invokes expects ecxt_scantuple to
308 * point to the correct tuple slot. The slot might have changed from
309 * what was used for the parent table if the table of the current
310 * partitioning level has different tuple descriptor from the parent.
311 * So update ecxt_scantuple accordingly.
312 */
313 ecxt->ecxt_scantuple = slot;
314 FormPartitionKeyDatum(dispatch, slot, estate, values, isnull);
315
316 /*
317 * If this partitioned table has no partitions or no partition for
318 * these values, error out.
319 */
320 if (partdesc->nparts == 0 ||
321 (partidx = get_partition_for_tuple(dispatch, values, isnull)) < 0)
322 {
323 char *val_desc;
324
326 values, isnull, 64);
329 (errcode(ERRCODE_CHECK_VIOLATION),
330 errmsg("no partition of relation \"%s\" found for row",
332 val_desc ?
333 errdetail("Partition key of the failing row contains %s.",
334 val_desc) : 0,
335 errtable(rel)));
336 }
337
338 is_leaf = partdesc->is_leaf[partidx];
339 if (is_leaf)
340 {
341 /*
342 * We've reached the leaf -- hurray, we're done. Look to see if
343 * we've already got a ResultRelInfo for this partition.
344 */
345 if (likely(dispatch->indexes[partidx] >= 0))
346 {
347 /* ResultRelInfo already built */
348 Assert(dispatch->indexes[partidx] < proute->num_partitions);
349 rri = proute->partitions[dispatch->indexes[partidx]];
350 }
351 else
352 {
353 /*
354 * If the partition is known in the owning ModifyTableState
355 * node, we can re-use that ResultRelInfo instead of creating
356 * a new one with ExecInitPartitionInfo().
357 */
358 rri = ExecLookupResultRelByOid(mtstate,
359 partdesc->oids[partidx],
360 true, false);
361 if (rri)
362 {
363 /* Verify this ResultRelInfo allows INSERTs */
365
366 /*
367 * Initialize information needed to insert this and
368 * subsequent tuples routed to this partition.
369 */
370 ExecInitRoutingInfo(mtstate, estate, proute, dispatch,
371 rri, partidx, true);
372 }
373 else
374 {
375 /* We need to create a new one. */
376 rri = ExecInitPartitionInfo(mtstate, estate, proute,
377 dispatch,
378 rootResultRelInfo, partidx);
379 }
380 }
381 Assert(rri != NULL);
382
383 /* Signal to terminate the loop */
384 dispatch = NULL;
385 }
386 else
387 {
388 /*
389 * Partition is a sub-partitioned table; get the PartitionDispatch
390 */
391 if (likely(dispatch->indexes[partidx] >= 0))
392 {
393 /* Already built. */
394 Assert(dispatch->indexes[partidx] < proute->num_dispatch);
395
396 rri = proute->nonleaf_partitions[dispatch->indexes[partidx]];
397
398 /*
399 * Move down to the next partition level and search again
400 * until we find a leaf partition that matches this tuple
401 */
402 dispatch = pd[dispatch->indexes[partidx]];
403 }
404 else
405 {
406 /* Not yet built. Do that now. */
407 PartitionDispatch subdispatch;
408
409 /*
410 * Create the new PartitionDispatch. We pass the current one
411 * in as the parent PartitionDispatch
412 */
413 subdispatch = ExecInitPartitionDispatchInfo(estate,
414 proute,
415 partdesc->oids[partidx],
416 dispatch, partidx,
417 mtstate->rootResultRelInfo);
418 Assert(dispatch->indexes[partidx] >= 0 &&
419 dispatch->indexes[partidx] < proute->num_dispatch);
420
421 rri = proute->nonleaf_partitions[dispatch->indexes[partidx]];
422 dispatch = subdispatch;
423 }
424
425 /*
426 * Convert the tuple to the new parent's layout, if different from
427 * the previous parent.
428 */
429 if (dispatch->tupslot)
430 {
431 AttrMap *map = dispatch->tupmap;
432 TupleTableSlot *tempslot = myslot;
433
434 myslot = dispatch->tupslot;
435 slot = execute_attr_map_slot(map, slot, myslot);
436
437 if (tempslot != NULL)
438 ExecClearTuple(tempslot);
439 }
440 }
441
442 /*
443 * If this partition is the default one, we must check its partition
444 * constraint now, which may have changed concurrently due to
445 * partitions being added to the parent.
446 *
447 * (We do this here, and do not rely on ExecInsert doing it, because
448 * we don't want to miss doing it for non-leaf partitions.)
449 */
450 if (partidx == partdesc->boundinfo->default_index)
451 {
452 /*
453 * The tuple must match the partition's layout for the constraint
454 * expression to be evaluated successfully. If the partition is
455 * sub-partitioned, that would already be the case due to the code
456 * above, but for a leaf partition the tuple still matches the
457 * parent's layout.
458 *
459 * Note that we have a map to convert from root to current
460 * partition, but not from immediate parent to current partition.
461 * So if we have to convert, do it from the root slot; if not, use
462 * the root slot as-is.
463 */
464 if (is_leaf)
465 {
466 TupleConversionMap *map = ExecGetRootToChildMap(rri, estate);
467
468 if (map)
469 slot = execute_attr_map_slot(map->attrMap, rootslot,
471 else
472 slot = rootslot;
473 }
474
475 ExecPartitionCheck(rri, slot, estate, true);
476 }
477 }
478
479 /* Release the tuple in the lowest parent's dedicated slot. */
480 if (myslot != NULL)
481 ExecClearTuple(myslot);
482 /* and restore ecxt's scantuple */
483 ecxt->ecxt_scantuple = ecxt_scantuple_saved;
484 MemoryContextSwitchTo(oldcxt);
485
486 return rri;
487}
#define likely(x)
Definition: c.h:346
int errdetail(const char *fmt,...)
Definition: elog.c:1204
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ereport(elevel,...)
Definition: elog.h:149
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation, List *mergeActions)
Definition: execMain.c:1048
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1828
static PartitionDispatch ExecInitPartitionDispatchInfo(EState *estate, PartitionTupleRouting *proute, Oid partoid, PartitionDispatch parent_pd, int partidx, ResultRelInfo *rootResultRelInfo)
static ResultRelInfo * ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, PartitionDispatch dispatch, ResultRelInfo *rootResultRelInfo, int partidx)
static void ExecInitRoutingInfo(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, PartitionDispatch dispatch, ResultRelInfo *partRelInfo, int partidx, bool is_borrowed_rel)
static char * ExecBuildSlotPartitionKeyDescription(Relation rel, Datum *values, bool *isnull, int maxfieldlen)
static void FormPartitionKeyDatum(PartitionDispatch pd, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
static int get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
TupleConversionMap * ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, EState *estate)
Definition: execUtils.c:1326
#define GetPerTupleExprContext(estate)
Definition: executor.h:650
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:655
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
ResultRelInfo * ExecLookupResultRelByOid(ModifyTableState *node, Oid resultoid, bool missing_ok, bool update_cache)
@ CMD_INSERT
Definition: nodes.h:273
#define PARTITION_MAX_KEYS
uintptr_t Datum
Definition: postgres.h:69
#define RelationGetRelationName(relation)
Definition: rel.h:550
int errtable(Relation rel)
Definition: relcache.c:6049
Definition: attmap.h:35
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:267
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1407
PartitionBoundInfo boundinfo
Definition: partdesc.h:38
bool * is_leaf
Definition: partdesc.h:35
PartitionDesc partdesc
int indexes[FLEXIBLE_ARRAY_MEMBER]
ResultRelInfo ** nonleaf_partitions
Definition: execPartition.c:95
Form_pg_class rd_rel
Definition: rel.h:111
TupleTableSlot * ri_PartitionTupleSlot
Definition: execnodes.h:615
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:192
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:458

References Assert(), TupleConversionMap::attrMap, PartitionDescData::boundinfo, CHECK_FOR_INTERRUPTS, CheckValidResultRel(), CMD_INSERT, PartitionBoundInfoData::default_index, ExprContext::ecxt_scantuple, ereport, errcode(), errdetail(), errmsg(), ERROR, errtable(), ExecBuildSlotPartitionKeyDescription(), ExecClearTuple(), ExecGetRootToChildMap(), ExecInitPartitionDispatchInfo(), ExecInitPartitionInfo(), ExecInitRoutingInfo(), ExecLookupResultRelByOid(), ExecPartitionCheck(), execute_attr_map_slot(), FormPartitionKeyDatum(), get_partition_for_tuple(), GetPerTupleExprContext, GetPerTupleMemoryContext, PartitionDispatchData::indexes, PartitionDescData::is_leaf, likely, MemoryContextSwitchTo(), NIL, PartitionTupleRouting::nonleaf_partitions, PartitionDescData::nparts, PartitionTupleRouting::num_dispatch, PartitionTupleRouting::num_partitions, OidIsValid, PartitionDescData::oids, PartitionDispatchData::partdesc, PartitionTupleRouting::partition_dispatch_info, PARTITION_MAX_KEYS, PartitionTupleRouting::partitions, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, PartitionDispatchData::reldesc, ResultRelInfo::ri_PartitionTupleSlot, ResultRelInfo::ri_RelationDesc, ModifyTableState::rootResultRelInfo, PartitionDispatchData::tupmap, PartitionDispatchData::tupslot, and values.

Referenced by apply_handle_tuple_routing(), CopyFrom(), and ExecPrepareTupleRouting().

◆ ExecInitPartitionDispatchInfo()

static PartitionDispatch ExecInitPartitionDispatchInfo ( EState estate,
PartitionTupleRouting proute,
Oid  partoid,
PartitionDispatch  parent_pd,
int  partidx,
ResultRelInfo rootResultRelInfo 
)
static

Definition at line 1097 of file execPartition.c.

1101{
1102 Relation rel;
1103 PartitionDesc partdesc;
1105 int dispatchidx;
1106 MemoryContext oldcxt;
1107
1108 /*
1109 * For data modification, it is better that executor does not include
1110 * partitions being detached, except when running in snapshot-isolation
1111 * mode. This means that a read-committed transaction immediately gets a
1112 * "no partition for tuple" error when a tuple is inserted into a
1113 * partition that's being detached concurrently, but a transaction in
1114 * repeatable-read mode can still use such a partition.
1115 */
1116 if (estate->es_partition_directory == NULL)
1117 estate->es_partition_directory =
1120
1121 oldcxt = MemoryContextSwitchTo(proute->memcxt);
1122
1123 /*
1124 * Only sub-partitioned tables need to be locked here. The root
1125 * partitioned table will already have been locked as it's referenced in
1126 * the query's rtable.
1127 */
1128 if (partoid != RelationGetRelid(proute->partition_root))
1129 rel = table_open(partoid, RowExclusiveLock);
1130 else
1131 rel = proute->partition_root;
1132 partdesc = PartitionDirectoryLookup(estate->es_partition_directory, rel);
1133
1134 pd = (PartitionDispatch) palloc(offsetof(PartitionDispatchData, indexes) +
1135 partdesc->nparts * sizeof(int));
1136 pd->reldesc = rel;
1137 pd->key = RelationGetPartitionKey(rel);
1138 pd->keystate = NIL;
1139 pd->partdesc = partdesc;
1140 if (parent_pd != NULL)
1141 {
1142 TupleDesc tupdesc = RelationGetDescr(rel);
1143
1144 /*
1145 * For sub-partitioned tables where the column order differs from its
1146 * direct parent partitioned table, we must store a tuple table slot
1147 * initialized with its tuple descriptor and a tuple conversion map to
1148 * convert a tuple from its parent's rowtype to its own. This is to
1149 * make sure that we are looking at the correct row using the correct
1150 * tuple descriptor when computing its partition key for tuple
1151 * routing.
1152 */
1154 tupdesc,
1155 false);
1156 pd->tupslot = pd->tupmap ?
1157 MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL;
1158 }
1159 else
1160 {
1161 /* Not required for the root partitioned table */
1162 pd->tupmap = NULL;
1163 pd->tupslot = NULL;
1164 }
1165
1166 /*
1167 * Initialize with -1 to signify that the corresponding partition's
1168 * ResultRelInfo or PartitionDispatch has not been created yet.
1169 */
1170 memset(pd->indexes, -1, sizeof(int) * partdesc->nparts);
1171
1172 /* Track in PartitionTupleRouting for later use */
1173 dispatchidx = proute->num_dispatch++;
1174
1175 /* Allocate or enlarge the array, as needed */
1176 if (proute->num_dispatch >= proute->max_dispatch)
1177 {
1178 if (proute->max_dispatch == 0)
1179 {
1180 proute->max_dispatch = 4;
1182 palloc(sizeof(PartitionDispatch) * proute->max_dispatch);
1183 proute->nonleaf_partitions = (ResultRelInfo **)
1184 palloc(sizeof(ResultRelInfo *) * proute->max_dispatch);
1185 }
1186 else
1187 {
1188 proute->max_dispatch *= 2;
1191 sizeof(PartitionDispatch) * proute->max_dispatch);
1192 proute->nonleaf_partitions = (ResultRelInfo **)
1194 sizeof(ResultRelInfo *) * proute->max_dispatch);
1195 }
1196 }
1197 proute->partition_dispatch_info[dispatchidx] = pd;
1198
1199 /*
1200 * If setting up a PartitionDispatch for a sub-partitioned table, we may
1201 * also need a minimally valid ResultRelInfo for checking the partition
1202 * constraint later; set that up now.
1203 */
1204 if (parent_pd)
1205 {
1207
1208 InitResultRelInfo(rri, rel, 0, rootResultRelInfo, 0);
1209 proute->nonleaf_partitions[dispatchidx] = rri;
1210 }
1211 else
1212 proute->nonleaf_partitions[dispatchidx] = NULL;
1213
1214 /*
1215 * Finally, if setting up a PartitionDispatch for a sub-partitioned table,
1216 * install a downlink in the parent to allow quick descent.
1217 */
1218 if (parent_pd)
1219 {
1220 Assert(parent_pd->indexes[partidx] == -1);
1221 parent_pd->indexes[partidx] = dispatchidx;
1222 }
1223
1224 MemoryContextSwitchTo(oldcxt);
1225
1226 return pd;
1227}
AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:261
void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, ResultRelInfo *partition_root_rri, int instrument_options)
Definition: execMain.c:1225
struct PartitionDispatchData * PartitionDispatch
Definition: execPartition.h:22
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1427
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
#define RowExclusiveLock
Definition: lockdefs.h:38
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:2172
#define makeNode(_type_)
Definition: nodes.h:161
#define RelationGetDescr(relation)
Definition: rel.h:542
MemoryContext memcxt
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
#define IsolationUsesXactSnapshot()
Definition: xact.h:51

References Assert(), build_attrmap_by_name_if_req(), CreatePartitionDirectory(), EState::es_partition_directory, EState::es_query_cxt, PartitionDispatchData::indexes, InitResultRelInfo(), IsolationUsesXactSnapshot, PartitionDispatchData::key, PartitionDispatchData::keystate, makeNode, MakeSingleTupleTableSlot(), PartitionTupleRouting::max_dispatch, PartitionTupleRouting::memcxt, MemoryContextSwitchTo(), NIL, PartitionTupleRouting::nonleaf_partitions, PartitionDescData::nparts, PartitionTupleRouting::num_dispatch, palloc(), PartitionDispatchData::partdesc, PartitionTupleRouting::partition_dispatch_info, PartitionTupleRouting::partition_root, PartitionDirectoryLookup(), RelationGetDescr, RelationGetPartitionKey(), RelationGetRelid, PartitionDispatchData::reldesc, repalloc(), RowExclusiveLock, table_open(), TTSOpsVirtual, PartitionDispatchData::tupmap, and PartitionDispatchData::tupslot.

Referenced by ExecFindPartition(), and ExecSetupPartitionTupleRouting().

◆ ExecInitPartitionExecPruning()

PartitionPruneState * ExecInitPartitionExecPruning ( PlanState planstate,
int  n_total_subplans,
int  part_prune_index,
Bitmapset relids,
Bitmapset **  initially_valid_subplans 
)

Definition at line 1875 of file execPartition.c.

1880{
1881 PartitionPruneState *prunestate;
1882 EState *estate = planstate->state;
1883 PartitionPruneInfo *pruneinfo;
1884
1885 /* Obtain the pruneinfo we need. */
1887 part_prune_index);
1888
1889 /* Its relids better match the plan node's or the planner messed up. */
1890 if (!bms_equal(relids, pruneinfo->relids))
1891 elog(ERROR, "wrong pruneinfo with relids=%s found at part_prune_index=%d contained in plan node with relids=%s",
1892 bmsToString(pruneinfo->relids), part_prune_index,
1893 bmsToString(relids));
1894
1895 /*
1896 * The PartitionPruneState would have been created by
1897 * ExecDoInitialPruning() and stored as the part_prune_index'th element of
1898 * EState.es_part_prune_states.
1899 */
1900 prunestate = list_nth(estate->es_part_prune_states, part_prune_index);
1901 Assert(prunestate != NULL);
1902
1903 /* Use the result of initial pruning done by ExecDoInitialPruning(). */
1904 if (prunestate->do_initial_prune)
1905 *initially_valid_subplans = list_nth_node(Bitmapset,
1906 estate->es_part_prune_results,
1907 part_prune_index);
1908 else
1909 {
1910 /* No pruning, so we'll need to initialize all subplans */
1911 Assert(n_total_subplans > 0);
1912 *initially_valid_subplans = bms_add_range(NULL, 0,
1913 n_total_subplans - 1);
1914 }
1915
1916 /*
1917 * The exec pruning state must also be initialized, if needed, before it
1918 * can be used for pruning during execution.
1919 *
1920 * This also re-sequences subplan indexes contained in prunestate to
1921 * account for any that were removed due to initial pruning; refer to the
1922 * condition in InitExecPartitionPruneContexts() that is used to determine
1923 * whether to do this. If no exec pruning needs to be done, we would thus
1924 * leave the maps to be in an invalid state, but that's ok since that data
1925 * won't be consulted again (cf initial Assert in
1926 * ExecFindMatchingSubPlans).
1927 */
1928 if (prunestate->do_exec_prune)
1929 InitExecPartitionPruneContexts(prunestate, planstate,
1930 *initially_valid_subplans,
1931 n_total_subplans);
1932
1933 return prunestate;
1934}
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:142
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:1019
static void InitExecPartitionPruneContexts(PartitionPruneState *prunestate, PlanState *parent_plan, Bitmapset *initially_valid_subplans, int n_total_subplans)
char * bmsToString(const Bitmapset *bms)
Definition: outfuncs.c:814
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define list_nth_node(type, list, n)
Definition: pg_list.h:327
Bitmapset * relids
Definition: plannodes.h:1590

References Assert(), bms_add_range(), bms_equal(), bmsToString(), PartitionPruneState::do_exec_prune, PartitionPruneState::do_initial_prune, elog, ERROR, EState::es_part_prune_infos, EState::es_part_prune_results, EState::es_part_prune_states, InitExecPartitionPruneContexts(), list_nth(), list_nth_node, PartitionPruneInfo::relids, and PlanState::state.

Referenced by ExecInitAppend(), and ExecInitMergeAppend().

◆ ExecInitPartitionInfo()

static ResultRelInfo * ExecInitPartitionInfo ( ModifyTableState mtstate,
EState estate,
PartitionTupleRouting proute,
PartitionDispatch  dispatch,
ResultRelInfo rootResultRelInfo,
int  partidx 
)
static

Definition at line 498 of file execPartition.c.

503{
504 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
505 Oid partOid = dispatch->partdesc->oids[partidx];
506 Relation partrel;
507 int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
508 Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
509 ResultRelInfo *leaf_part_rri;
510 MemoryContext oldcxt;
511 AttrMap *part_attmap = NULL;
512 bool found_whole_row;
513
514 oldcxt = MemoryContextSwitchTo(proute->memcxt);
515
516 partrel = table_open(partOid, RowExclusiveLock);
517
518 leaf_part_rri = makeNode(ResultRelInfo);
519 InitResultRelInfo(leaf_part_rri,
520 partrel,
521 0,
522 rootResultRelInfo,
523 estate->es_instrument);
524
525 /*
526 * Verify result relation is a valid target for an INSERT. An UPDATE of a
527 * partition-key becomes a DELETE+INSERT operation, so this check is still
528 * required when the operation is CMD_UPDATE.
529 */
530 CheckValidResultRel(leaf_part_rri, CMD_INSERT, NIL);
531
532 /*
533 * Open partition indices. The user may have asked to check for conflicts
534 * within this leaf partition and do "nothing" instead of throwing an
535 * error. Be prepared in that case by initializing the index information
536 * needed by ExecInsert() to perform speculative insertions.
537 */
538 if (partrel->rd_rel->relhasindex &&
539 leaf_part_rri->ri_IndexRelationDescs == NULL)
540 ExecOpenIndices(leaf_part_rri,
541 (node != NULL &&
543
544 /*
545 * Build WITH CHECK OPTION constraints for the partition. Note that we
546 * didn't build the withCheckOptionList for partitions within the planner,
547 * but simple translation of varattnos will suffice. This only occurs for
548 * the INSERT case or in the case of UPDATE/MERGE tuple routing where we
549 * didn't find a result rel to reuse.
550 */
551 if (node && node->withCheckOptionLists != NIL)
552 {
553 List *wcoList;
554 List *wcoExprs = NIL;
555 ListCell *ll;
556
557 /*
558 * In the case of INSERT on a partitioned table, there is only one
559 * plan. Likewise, there is only one WCO list, not one per partition.
560 * For UPDATE/MERGE, there are as many WCO lists as there are plans.
561 */
562 Assert((node->operation == CMD_INSERT &&
563 list_length(node->withCheckOptionLists) == 1 &&
564 list_length(node->resultRelations) == 1) ||
565 (node->operation == CMD_UPDATE &&
568 (node->operation == CMD_MERGE &&
571
572 /*
573 * Use the WCO list of the first plan as a reference to calculate
574 * attno's for the WCO list of this partition. In the INSERT case,
575 * that refers to the root partitioned table, whereas in the UPDATE
576 * tuple routing case, that refers to the first partition in the
577 * mtstate->resultRelInfo array. In any case, both that relation and
578 * this partition should have the same columns, so we should be able
579 * to map attributes successfully.
580 */
581 wcoList = linitial(node->withCheckOptionLists);
582
583 /*
584 * Convert Vars in it to contain this partition's attribute numbers.
585 */
586 part_attmap =
588 RelationGetDescr(firstResultRel),
589 false);
590 wcoList = (List *)
591 map_variable_attnos((Node *) wcoList,
592 firstVarno, 0,
593 part_attmap,
594 RelationGetForm(partrel)->reltype,
595 &found_whole_row);
596 /* We ignore the value of found_whole_row. */
597
598 foreach(ll, wcoList)
599 {
601 ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual),
602 &mtstate->ps);
603
604 wcoExprs = lappend(wcoExprs, wcoExpr);
605 }
606
607 leaf_part_rri->ri_WithCheckOptions = wcoList;
608 leaf_part_rri->ri_WithCheckOptionExprs = wcoExprs;
609 }
610
611 /*
612 * Build the RETURNING projection for the partition. Note that we didn't
613 * build the returningList for partitions within the planner, but simple
614 * translation of varattnos will suffice. This only occurs for the INSERT
615 * case or in the case of UPDATE/MERGE tuple routing where we didn't find
616 * a result rel to reuse.
617 */
618 if (node && node->returningLists != NIL)
619 {
620 TupleTableSlot *slot;
621 ExprContext *econtext;
622 List *returningList;
623
624 /* See the comment above for WCO lists. */
625 Assert((node->operation == CMD_INSERT &&
626 list_length(node->returningLists) == 1 &&
627 list_length(node->resultRelations) == 1) ||
628 (node->operation == CMD_UPDATE &&
631 (node->operation == CMD_MERGE &&
634
635 /*
636 * Use the RETURNING list of the first plan as a reference to
637 * calculate attno's for the RETURNING list of this partition. See
638 * the comment above for WCO lists for more details on why this is
639 * okay.
640 */
641 returningList = linitial(node->returningLists);
642
643 /*
644 * Convert Vars in it to contain this partition's attribute numbers.
645 */
646 if (part_attmap == NULL)
647 part_attmap =
649 RelationGetDescr(firstResultRel),
650 false);
651 returningList = (List *)
652 map_variable_attnos((Node *) returningList,
653 firstVarno, 0,
654 part_attmap,
655 RelationGetForm(partrel)->reltype,
656 &found_whole_row);
657 /* We ignore the value of found_whole_row. */
658
659 leaf_part_rri->ri_returningList = returningList;
660
661 /*
662 * Initialize the projection itself.
663 *
664 * Use the slot and the expression context that would have been set up
665 * in ExecInitModifyTable() for projection's output.
666 */
667 Assert(mtstate->ps.ps_ResultTupleSlot != NULL);
668 slot = mtstate->ps.ps_ResultTupleSlot;
669 Assert(mtstate->ps.ps_ExprContext != NULL);
670 econtext = mtstate->ps.ps_ExprContext;
671 leaf_part_rri->ri_projectReturning =
672 ExecBuildProjectionInfo(returningList, econtext, slot,
673 &mtstate->ps, RelationGetDescr(partrel));
674 }
675
676 /* Set up information needed for routing tuples to the partition. */
677 ExecInitRoutingInfo(mtstate, estate, proute, dispatch,
678 leaf_part_rri, partidx, false);
679
680 /*
681 * If there is an ON CONFLICT clause, initialize state for it.
682 */
683 if (node && node->onConflictAction != ONCONFLICT_NONE)
684 {
685 TupleDesc partrelDesc = RelationGetDescr(partrel);
686 ExprContext *econtext = mtstate->ps.ps_ExprContext;
687 ListCell *lc;
688 List *arbiterIndexes = NIL;
689
690 /*
691 * If there is a list of arbiter indexes, map it to a list of indexes
692 * in the partition. We do that by scanning the partition's index
693 * list and searching for ancestry relationships to each index in the
694 * ancestor table.
695 */
696 if (rootResultRelInfo->ri_onConflictArbiterIndexes != NIL)
697 {
698 List *childIdxs;
699
700 childIdxs = RelationGetIndexList(leaf_part_rri->ri_RelationDesc);
701
702 foreach(lc, childIdxs)
703 {
704 Oid childIdx = lfirst_oid(lc);
705 List *ancestors;
706 ListCell *lc2;
707
708 ancestors = get_partition_ancestors(childIdx);
709 foreach(lc2, rootResultRelInfo->ri_onConflictArbiterIndexes)
710 {
711 if (list_member_oid(ancestors, lfirst_oid(lc2)))
712 arbiterIndexes = lappend_oid(arbiterIndexes, childIdx);
713 }
714 list_free(ancestors);
715 }
716 }
717
718 /*
719 * If the resulting lists are of inequal length, something is wrong.
720 * (This shouldn't happen, since arbiter index selection should not
721 * pick up an invalid index.)
722 */
723 if (list_length(rootResultRelInfo->ri_onConflictArbiterIndexes) !=
724 list_length(arbiterIndexes))
725 elog(ERROR, "invalid arbiter index list");
726 leaf_part_rri->ri_onConflictArbiterIndexes = arbiterIndexes;
727
728 /*
729 * In the DO UPDATE case, we have some more state to initialize.
730 */
732 {
735
736 map = ExecGetRootToChildMap(leaf_part_rri, estate);
737
738 Assert(node->onConflictSet != NIL);
739 Assert(rootResultRelInfo->ri_onConflict != NULL);
740
741 leaf_part_rri->ri_onConflict = onconfl;
742
743 /*
744 * Need a separate existing slot for each partition, as the
745 * partition could be of a different AM, even if the tuple
746 * descriptors match.
747 */
748 onconfl->oc_Existing =
749 table_slot_create(leaf_part_rri->ri_RelationDesc,
750 &mtstate->ps.state->es_tupleTable);
751
752 /*
753 * If the partition's tuple descriptor matches exactly the root
754 * parent (the common case), we can re-use most of the parent's ON
755 * CONFLICT SET state, skipping a bunch of work. Otherwise, we
756 * need to create state specific to this partition.
757 */
758 if (map == NULL)
759 {
760 /*
761 * It's safe to reuse these from the partition root, as we
762 * only process one tuple at a time (therefore we won't
763 * overwrite needed data in slots), and the results of
764 * projections are independent of the underlying storage.
765 * Projections and where clauses themselves don't store state
766 * / are independent of the underlying storage.
767 */
768 onconfl->oc_ProjSlot =
769 rootResultRelInfo->ri_onConflict->oc_ProjSlot;
770 onconfl->oc_ProjInfo =
771 rootResultRelInfo->ri_onConflict->oc_ProjInfo;
772 onconfl->oc_WhereClause =
773 rootResultRelInfo->ri_onConflict->oc_WhereClause;
774 }
775 else
776 {
777 List *onconflset;
778 List *onconflcols;
779
780 /*
781 * Translate expressions in onConflictSet to account for
782 * different attribute numbers. For that, map partition
783 * varattnos twice: first to catch the EXCLUDED
784 * pseudo-relation (INNER_VAR), and second to handle the main
785 * target relation (firstVarno).
786 */
787 onconflset = copyObject(node->onConflictSet);
788 if (part_attmap == NULL)
789 part_attmap =
791 RelationGetDescr(firstResultRel),
792 false);
793 onconflset = (List *)
794 map_variable_attnos((Node *) onconflset,
795 INNER_VAR, 0,
796 part_attmap,
797 RelationGetForm(partrel)->reltype,
798 &found_whole_row);
799 /* We ignore the value of found_whole_row. */
800 onconflset = (List *)
801 map_variable_attnos((Node *) onconflset,
802 firstVarno, 0,
803 part_attmap,
804 RelationGetForm(partrel)->reltype,
805 &found_whole_row);
806 /* We ignore the value of found_whole_row. */
807
808 /* Finally, adjust the target colnos to match the partition. */
809 onconflcols = adjust_partition_colnos(node->onConflictCols,
810 leaf_part_rri);
811
812 /* create the tuple slot for the UPDATE SET projection */
813 onconfl->oc_ProjSlot =
814 table_slot_create(partrel,
815 &mtstate->ps.state->es_tupleTable);
816
817 /* build UPDATE SET projection state */
818 onconfl->oc_ProjInfo =
819 ExecBuildUpdateProjection(onconflset,
820 true,
821 onconflcols,
822 partrelDesc,
823 econtext,
824 onconfl->oc_ProjSlot,
825 &mtstate->ps);
826
827 /*
828 * If there is a WHERE clause, initialize state where it will
829 * be evaluated, mapping the attribute numbers appropriately.
830 * As with onConflictSet, we need to map partition varattnos
831 * to the partition's tupdesc.
832 */
833 if (node->onConflictWhere)
834 {
835 List *clause;
836
837 clause = copyObject((List *) node->onConflictWhere);
838 clause = (List *)
839 map_variable_attnos((Node *) clause,
840 INNER_VAR, 0,
841 part_attmap,
842 RelationGetForm(partrel)->reltype,
843 &found_whole_row);
844 /* We ignore the value of found_whole_row. */
845 clause = (List *)
846 map_variable_attnos((Node *) clause,
847 firstVarno, 0,
848 part_attmap,
849 RelationGetForm(partrel)->reltype,
850 &found_whole_row);
851 /* We ignore the value of found_whole_row. */
852 onconfl->oc_WhereClause =
853 ExecInitQual((List *) clause, &mtstate->ps);
854 }
855 }
856 }
857 }
858
859 /*
860 * Since we've just initialized this ResultRelInfo, it's not in any list
861 * attached to the estate as yet. Add it, so that it can be found later.
862 *
863 * Note that the entries in this list appear in no predetermined order,
864 * because partition result rels are initialized as and when they're
865 * needed.
866 */
870 leaf_part_rri);
871
872 /*
873 * Initialize information about this partition that's needed to handle
874 * MERGE. We take the "first" result relation's mergeActionList as
875 * reference and make copy for this relation, converting stuff that
876 * references attribute numbers to match this relation's.
877 *
878 * This duplicates much of the logic in ExecInitMerge(), so if something
879 * changes there, look here too.
880 */
881 if (node && node->operation == CMD_MERGE)
882 {
883 List *firstMergeActionList = linitial(node->mergeActionLists);
884 ListCell *lc;
885 ExprContext *econtext = mtstate->ps.ps_ExprContext;
886 Node *joinCondition;
887
888 if (part_attmap == NULL)
889 part_attmap =
891 RelationGetDescr(firstResultRel),
892 false);
893
894 if (unlikely(!leaf_part_rri->ri_projectNewInfoValid))
895 ExecInitMergeTupleSlots(mtstate, leaf_part_rri);
896
897 /* Initialize state for join condition checking. */
898 joinCondition =
900 firstVarno, 0,
901 part_attmap,
902 RelationGetForm(partrel)->reltype,
903 &found_whole_row);
904 /* We ignore the value of found_whole_row. */
905 leaf_part_rri->ri_MergeJoinCondition =
906 ExecInitQual((List *) joinCondition, &mtstate->ps);
907
908 foreach(lc, firstMergeActionList)
909 {
910 /* Make a copy for this relation to be safe. */
912 MergeActionState *action_state;
913
914 /* Generate the action's state for this relation */
915 action_state = makeNode(MergeActionState);
916 action_state->mas_action = action;
917
918 /* And put the action in the appropriate list */
919 leaf_part_rri->ri_MergeActions[action->matchKind] =
920 lappend(leaf_part_rri->ri_MergeActions[action->matchKind],
921 action_state);
922
923 switch (action->commandType)
924 {
925 case CMD_INSERT:
926
927 /*
928 * ExecCheckPlanOutput() already done on the targetlist
929 * when "first" result relation initialized and it is same
930 * for all result relations.
931 */
932 action_state->mas_proj =
933 ExecBuildProjectionInfo(action->targetList, econtext,
934 leaf_part_rri->ri_newTupleSlot,
935 &mtstate->ps,
936 RelationGetDescr(partrel));
937 break;
938 case CMD_UPDATE:
939
940 /*
941 * Convert updateColnos from "first" result relation
942 * attribute numbers to this result rel's.
943 */
944 if (part_attmap)
945 action->updateColnos =
947 part_attmap);
948 action_state->mas_proj =
950 true,
951 action->updateColnos,
952 RelationGetDescr(leaf_part_rri->ri_RelationDesc),
953 econtext,
954 leaf_part_rri->ri_newTupleSlot,
955 NULL);
956 break;
957 case CMD_DELETE:
958 case CMD_NOTHING:
959 /* Nothing to do */
960 break;
961
962 default:
963 elog(ERROR, "unknown action in MERGE WHEN clause");
964 }
965
966 /* found_whole_row intentionally ignored. */
967 action->qual =
969 firstVarno, 0,
970 part_attmap,
971 RelationGetForm(partrel)->reltype,
972 &found_whole_row);
973 action_state->mas_whenqual =
974 ExecInitQual((List *) action->qual, &mtstate->ps);
975 }
976 }
977 MemoryContextSwitchTo(oldcxt);
978
979 return leaf_part_rri;
980}
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:175
#define unlikely(x)
Definition: c.h:347
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:370
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:229
ProjectionInfo * ExecBuildUpdateProjection(List *targetList, bool evalTargetList, List *targetColnos, TupleDesc relDesc, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent)
Definition: execExpr.c:547
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
Definition: execIndexing.c:160
static List * adjust_partition_colnos(List *colnos, ResultRelInfo *leaf_part_rri)
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
void list_free(List *list)
Definition: list.c:1546
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:722
void ExecInitMergeTupleSlots(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
#define copyObject(obj)
Definition: nodes.h:230
@ ONCONFLICT_NONE
Definition: nodes.h:424
@ ONCONFLICT_UPDATE
Definition: nodes.h:426
@ CMD_MERGE
Definition: nodes.h:275
@ CMD_DELETE
Definition: nodes.h:274
@ CMD_UPDATE
Definition: nodes.h:272
@ CMD_NOTHING
Definition: nodes.h:278
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
List * get_partition_ancestors(Oid relid)
Definition: partition.c:134
#define lfirst(lc)
Definition: pg_list.h:172
#define linitial(l)
Definition: pg_list.h:178
#define lfirst_oid(lc)
Definition: pg_list.h:174
#define INNER_VAR
Definition: primnodes.h:242
#define RelationGetForm(relation)
Definition: rel.h:510
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4836
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
List * es_tuple_routing_result_relations
Definition: execnodes.h:694
int es_instrument
Definition: execnodes.h:716
List * es_tupleTable
Definition: execnodes.h:708
MergeAction * mas_action
Definition: execnodes.h:443
ProjectionInfo * mas_proj
Definition: execnodes.h:444
ExprState * mas_whenqual
Definition: execnodes.h:446
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1399
List * onConflictCols
Definition: plannodes.h:321
List * mergeJoinConditions
Definition: plannodes.h:331
CmdType operation
Definition: plannodes.h:285
List * resultRelations
Definition: plannodes.h:295
List * onConflictSet
Definition: plannodes.h:319
List * mergeActionLists
Definition: plannodes.h:329
List * returningLists
Definition: plannodes.h:305
List * withCheckOptionLists
Definition: plannodes.h:299
Node * onConflictWhere
Definition: plannodes.h:323
OnConflictAction onConflictAction
Definition: plannodes.h:315
Definition: nodes.h:135
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:428
TupleTableSlot * oc_Existing
Definition: execnodes.h:427
ExprState * oc_WhereClause
Definition: execnodes.h:430
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:429
Plan * plan
Definition: execnodes.h:1156
ExprContext * ps_ExprContext
Definition: execnodes.h:1195
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1194
OnConflictSetState * ri_onConflict
Definition: execnodes.h:577
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:574
Index ri_RangeTableIndex
Definition: execnodes.h:471
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:92

References generate_unaccent_rules::action, adjust_partition_colnos(), adjust_partition_colnos_using_map(), Assert(), build_attrmap_by_name(), castNode, CheckValidResultRel(), CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_NOTHING, CMD_UPDATE, copyObject, elog, ERROR, EState::es_instrument, EState::es_query_cxt, EState::es_tuple_routing_result_relations, EState::es_tupleTable, ExecBuildProjectionInfo(), ExecBuildUpdateProjection(), ExecGetRootToChildMap(), ExecInitMergeTupleSlots(), ExecInitQual(), ExecInitRoutingInfo(), ExecOpenIndices(), get_partition_ancestors(), InitResultRelInfo(), INNER_VAR, lappend(), lappend_oid(), lfirst, lfirst_node, lfirst_oid, linitial, list_free(), list_length(), list_member_oid(), makeNode, map_variable_attnos(), MergeActionState::mas_action, MergeActionState::mas_proj, MergeActionState::mas_whenqual, PartitionTupleRouting::memcxt, MemoryContextSwitchTo(), ModifyTable::mergeActionLists, ModifyTable::mergeJoinConditions, NIL, OnConflictSetState::oc_Existing, OnConflictSetState::oc_ProjInfo, OnConflictSetState::oc_ProjSlot, OnConflictSetState::oc_WhereClause, PartitionDescData::oids, ONCONFLICT_NONE, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictCols, ModifyTable::onConflictSet, ModifyTable::onConflictWhere, ModifyTable::operation, PartitionDispatchData::partdesc, PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, PlanState::ps_ResultTupleSlot, WithCheckOption::qual, RelationGetDescr, RelationGetForm, RelationGetIndexList(), ModifyTable::resultRelations, ModifyTableState::resultRelInfo, ModifyTable::returningLists, ResultRelInfo::ri_onConflict, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, RowExclusiveLock, PlanState::state, table_open(), table_slot_create(), unlikely, and ModifyTable::withCheckOptionLists.

Referenced by ExecFindPartition().

◆ ExecInitRoutingInfo()

static void ExecInitRoutingInfo ( ModifyTableState mtstate,
EState estate,
PartitionTupleRouting proute,
PartitionDispatch  dispatch,
ResultRelInfo partRelInfo,
int  partidx,
bool  is_borrowed_rel 
)
static

Definition at line 989 of file execPartition.c.

996{
997 MemoryContext oldcxt;
998 int rri_index;
999
1000 oldcxt = MemoryContextSwitchTo(proute->memcxt);
1001
1002 /*
1003 * Set up tuple conversion between root parent and the partition if the
1004 * two have different rowtypes. If conversion is indeed required, also
1005 * initialize a slot dedicated to storing this partition's converted
1006 * tuples. Various operations that are applied to tuples after routing,
1007 * such as checking constraints, will refer to this slot.
1008 */
1009 if (ExecGetRootToChildMap(partRelInfo, estate) != NULL)
1010 {
1011 Relation partrel = partRelInfo->ri_RelationDesc;
1012
1013 /*
1014 * This pins the partition's TupleDesc, which will be released at the
1015 * end of the command.
1016 */
1017 partRelInfo->ri_PartitionTupleSlot =
1018 table_slot_create(partrel, &estate->es_tupleTable);
1019 }
1020 else
1021 partRelInfo->ri_PartitionTupleSlot = NULL;
1022
1023 /*
1024 * If the partition is a foreign table, let the FDW init itself for
1025 * routing tuples to the partition.
1026 */
1027 if (partRelInfo->ri_FdwRoutine != NULL &&
1028 partRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL)
1029 partRelInfo->ri_FdwRoutine->BeginForeignInsert(mtstate, partRelInfo);
1030
1031 /*
1032 * Determine if the FDW supports batch insert and determine the batch size
1033 * (a FDW may support batching, but it may be disabled for the
1034 * server/table or for this particular query).
1035 *
1036 * If the FDW does not support batching, we set the batch size to 1.
1037 */
1038 if (partRelInfo->ri_FdwRoutine != NULL &&
1041 partRelInfo->ri_BatchSize =
1042 partRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(partRelInfo);
1043 else
1044 partRelInfo->ri_BatchSize = 1;
1045
1046 Assert(partRelInfo->ri_BatchSize >= 1);
1047
1048 partRelInfo->ri_CopyMultiInsertBuffer = NULL;
1049
1050 /*
1051 * Keep track of it in the PartitionTupleRouting->partitions array.
1052 */
1053 Assert(dispatch->indexes[partidx] == -1);
1054
1055 rri_index = proute->num_partitions++;
1056
1057 /* Allocate or enlarge the array, as needed */
1058 if (proute->num_partitions >= proute->max_partitions)
1059 {
1060 if (proute->max_partitions == 0)
1061 {
1062 proute->max_partitions = 8;
1063 proute->partitions = (ResultRelInfo **)
1064 palloc(sizeof(ResultRelInfo *) * proute->max_partitions);
1065 proute->is_borrowed_rel = (bool *)
1066 palloc(sizeof(bool) * proute->max_partitions);
1067 }
1068 else
1069 {
1070 proute->max_partitions *= 2;
1071 proute->partitions = (ResultRelInfo **)
1072 repalloc(proute->partitions, sizeof(ResultRelInfo *) *
1073 proute->max_partitions);
1074 proute->is_borrowed_rel = (bool *)
1075 repalloc(proute->is_borrowed_rel, sizeof(bool) *
1076 proute->max_partitions);
1077 }
1078 }
1079
1080 proute->partitions[rri_index] = partRelInfo;
1081 proute->is_borrowed_rel[rri_index] = is_borrowed_rel;
1082 dispatch->indexes[partidx] = rri_index;
1083
1084 MemoryContextSwitchTo(oldcxt);
1085}
BeginForeignInsert_function BeginForeignInsert
Definition: fdwapi.h:238
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:233
GetForeignModifyBatchSize_function GetForeignModifyBatchSize
Definition: fdwapi.h:234
struct CopyMultiInsertBuffer * ri_CopyMultiInsertBuffer
Definition: execnodes.h:618
int ri_BatchSize
Definition: execnodes.h:538

References Assert(), FdwRoutine::BeginForeignInsert, EState::es_tupleTable, FdwRoutine::ExecForeignBatchInsert, ExecGetRootToChildMap(), FdwRoutine::GetForeignModifyBatchSize, PartitionDispatchData::indexes, PartitionTupleRouting::is_borrowed_rel, PartitionTupleRouting::max_partitions, PartitionTupleRouting::memcxt, MemoryContextSwitchTo(), PartitionTupleRouting::num_partitions, palloc(), PartitionTupleRouting::partitions, repalloc(), ResultRelInfo::ri_BatchSize, ResultRelInfo::ri_CopyMultiInsertBuffer, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_PartitionTupleSlot, ResultRelInfo::ri_RelationDesc, and table_slot_create().

Referenced by ExecFindPartition(), and ExecInitPartitionInfo().

◆ ExecSetupPartitionTupleRouting()

PartitionTupleRouting * ExecSetupPartitionTupleRouting ( EState estate,
Relation  rel 
)

Definition at line 218 of file execPartition.c.

219{
220 PartitionTupleRouting *proute;
221
222 /*
223 * Here we attempt to expend as little effort as possible in setting up
224 * the PartitionTupleRouting. Each partition's ResultRelInfo is built on
225 * demand, only when we actually need to route a tuple to that partition.
226 * The reason for this is that a common case is for INSERT to insert a
227 * single tuple into a partitioned table and this must be fast.
228 */
230 proute->partition_root = rel;
232 /* Rest of members initialized by zeroing */
233
234 /*
235 * Initialize this table's PartitionDispatch object. Here we pass in the
236 * parent as NULL as we don't need to care about any parent of the target
237 * partitioned table.
238 */
240 NULL, 0, NULL);
241
242 return proute;
243}
void * palloc0(Size size)
Definition: mcxt.c:1975

References CurrentMemoryContext, ExecInitPartitionDispatchInfo(), PartitionTupleRouting::memcxt, palloc0(), PartitionTupleRouting::partition_root, and RelationGetRelid.

Referenced by apply_handle_tuple_routing(), CopyFrom(), ExecCrossPartitionUpdate(), ExecInitMerge(), and ExecInitModifyTable().

◆ find_matching_subplans_recurse()

static void find_matching_subplans_recurse ( PartitionPruningData prunedata,
PartitionedRelPruningData pprune,
bool  initial_prune,
Bitmapset **  validsubplans,
Bitmapset **  validsubplan_rtis 
)
static

Definition at line 2566 of file execPartition.c.

2571{
2572 Bitmapset *partset;
2573 int i;
2574
2575 /* Guard against stack overflow due to overly deep partition hierarchy. */
2577
2578 /*
2579 * Prune as appropriate, if we have pruning steps matching the current
2580 * execution context. Otherwise just include all partitions at this
2581 * level.
2582 */
2583 if (initial_prune && pprune->initial_pruning_steps)
2584 partset = get_matching_partitions(&pprune->initial_context,
2585 pprune->initial_pruning_steps);
2586 else if (!initial_prune && pprune->exec_pruning_steps)
2587 partset = get_matching_partitions(&pprune->exec_context,
2588 pprune->exec_pruning_steps);
2589 else
2590 partset = pprune->present_parts;
2591
2592 /* Translate partset into subplan indexes */
2593 i = -1;
2594 while ((i = bms_next_member(partset, i)) >= 0)
2595 {
2596 if (pprune->subplan_map[i] >= 0)
2597 {
2598 *validsubplans = bms_add_member(*validsubplans,
2599 pprune->subplan_map[i]);
2600
2601 /*
2602 * Only report leaf partitions. Non-leaf partitions may appear
2603 * here when they use an unflattened Append or MergeAppend.
2604 */
2605 if (validsubplan_rtis && pprune->leafpart_rti_map[i])
2606 *validsubplan_rtis = bms_add_member(*validsubplan_rtis,
2607 pprune->leafpart_rti_map[i]);
2608 }
2609 else
2610 {
2611 int partidx = pprune->subpart_map[i];
2612
2613 if (partidx >= 0)
2615 &prunedata->partrelprunedata[partidx],
2616 initial_prune, validsubplans,
2617 validsubplan_rtis);
2618 else
2619 {
2620 /*
2621 * We get here if the planner already pruned all the sub-
2622 * partitions for this partition. Silently ignore this
2623 * partition in this case. The end result is the same: we
2624 * would have pruned all partitions just the same, but we
2625 * don't have any pruning steps to execute to verify this.
2626 */
2627 }
2628 }
2629 }
2630}
Bitmapset * get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
Definition: partprune.c:846
void check_stack_depth(void)
Definition: stack_depth.c:95

References bms_add_member(), bms_next_member(), check_stack_depth(), PartitionedRelPruningData::exec_context, PartitionedRelPruningData::exec_pruning_steps, find_matching_subplans_recurse(), get_matching_partitions(), i, PartitionedRelPruningData::initial_context, PartitionedRelPruningData::initial_pruning_steps, PartitionedRelPruningData::leafpart_rti_map, PartitionPruningData::partrelprunedata, PartitionedRelPruningData::present_parts, PartitionedRelPruningData::subpart_map, and PartitionedRelPruningData::subplan_map.

Referenced by ExecFindMatchingSubPlans(), and find_matching_subplans_recurse().

◆ FormPartitionKeyDatum()

static void FormPartitionKeyDatum ( PartitionDispatch  pd,
TupleTableSlot slot,
EState estate,
Datum values,
bool *  isnull 
)
static

Definition at line 1297 of file execPartition.c.

1302{
1303 ListCell *partexpr_item;
1304 int i;
1305
1306 if (pd->key->partexprs != NIL && pd->keystate == NIL)
1307 {
1308 /* Check caller has set up context correctly */
1309 Assert(estate != NULL &&
1310 GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
1311
1312 /* First time through, set up expression evaluation state */
1313 pd->keystate = ExecPrepareExprList(pd->key->partexprs, estate);
1314 }
1315
1316 partexpr_item = list_head(pd->keystate);
1317 for (i = 0; i < pd->key->partnatts; i++)
1318 {
1319 AttrNumber keycol = pd->key->partattrs[i];
1320 Datum datum;
1321 bool isNull;
1322
1323 if (keycol != 0)
1324 {
1325 /* Plain column; get the value directly from the heap tuple */
1326 datum = slot_getattr(slot, keycol, &isNull);
1327 }
1328 else
1329 {
1330 /* Expression; need to evaluate it */
1331 if (partexpr_item == NULL)
1332 elog(ERROR, "wrong number of partition key expressions");
1333 datum = ExecEvalExprSwitchContext((ExprState *) lfirst(partexpr_item),
1334 GetPerTupleExprContext(estate),
1335 &isNull);
1336 partexpr_item = lnext(pd->keystate, partexpr_item);
1337 }
1338 values[i] = datum;
1339 isnull[i] = isNull;
1340 }
1341
1342 if (partexpr_item != NULL)
1343 elog(ERROR, "wrong number of partition key expressions");
1344}
List * ExecPrepareExprList(List *nodes, EState *estate)
Definition: execExpr.c:839
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:430
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
List * partexprs
Definition: partcache.h:31
AttrNumber * partattrs
Definition: partcache.h:29
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:399

References Assert(), elog, ERROR, ExecEvalExprSwitchContext(), ExecPrepareExprList(), GetPerTupleExprContext, i, PartitionDispatchData::key, PartitionDispatchData::keystate, lfirst, list_head(), lnext(), NIL, PartitionKeyData::partattrs, PartitionKeyData::partexprs, PartitionKeyData::partnatts, slot_getattr(), and values.

Referenced by ExecFindPartition().

◆ get_partition_for_tuple()

static int get_partition_for_tuple ( PartitionDispatch  pd,
Datum values,
bool *  isnull 
)
static

Definition at line 1394 of file execPartition.c.

1395{
1396 int bound_offset = -1;
1397 int part_index = -1;
1398 PartitionKey key = pd->key;
1399 PartitionDesc partdesc = pd->partdesc;
1400 PartitionBoundInfo boundinfo = partdesc->boundinfo;
1401
1402 /*
1403 * In the switch statement below, when we perform a cached lookup for
1404 * RANGE and LIST partitioned tables, if we find that the last found
1405 * partition matches the 'values', we return the partition index right
1406 * away. We do this instead of breaking out of the switch as we don't
1407 * want to execute the code about the DEFAULT partition or do any updates
1408 * for any of the cache-related fields. That would be a waste of effort
1409 * as we already know it's not the DEFAULT partition and have no need to
1410 * increment the number of times we found the same partition any higher
1411 * than PARTITION_CACHED_FIND_THRESHOLD.
1412 */
1413
1414 /* Route as appropriate based on partitioning strategy. */
1415 switch (key->strategy)
1416 {
1418 {
1419 uint64 rowHash;
1420
1421 /* hash partitioning is too cheap to bother caching */
1422 rowHash = compute_partition_hash_value(key->partnatts,
1423 key->partsupfunc,
1424 key->partcollation,
1425 values, isnull);
1426
1427 /*
1428 * HASH partitions can't have a DEFAULT partition and we don't
1429 * do any caching work for them, so just return the part index
1430 */
1431 return boundinfo->indexes[rowHash % boundinfo->nindexes];
1432 }
1433
1435 if (isnull[0])
1436 {
1437 /* this is far too cheap to bother doing any caching */
1438 if (partition_bound_accepts_nulls(boundinfo))
1439 {
1440 /*
1441 * When there is a NULL partition we just return that
1442 * directly. We don't have a bound_offset so it's not
1443 * valid to drop into the code after the switch which
1444 * checks and updates the cache fields. We perhaps should
1445 * be invalidating the details of the last cached
1446 * partition but there's no real need to. Keeping those
1447 * fields set gives a chance at matching to the cached
1448 * partition on the next lookup.
1449 */
1450 return boundinfo->null_index;
1451 }
1452 }
1453 else
1454 {
1455 bool equal;
1456
1458 {
1459 int last_datum_offset = partdesc->last_found_datum_index;
1460 Datum lastDatum = boundinfo->datums[last_datum_offset][0];
1461 int32 cmpval;
1462
1463 /* does the last found datum index match this datum? */
1464 cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
1465 key->partcollation[0],
1466 lastDatum,
1467 values[0]));
1468
1469 if (cmpval == 0)
1470 return boundinfo->indexes[last_datum_offset];
1471
1472 /* fall-through and do a manual lookup */
1473 }
1474
1475 bound_offset = partition_list_bsearch(key->partsupfunc,
1476 key->partcollation,
1477 boundinfo,
1478 values[0], &equal);
1479 if (bound_offset >= 0 && equal)
1480 part_index = boundinfo->indexes[bound_offset];
1481 }
1482 break;
1483
1485 {
1486 bool equal = false,
1487 range_partkey_has_null = false;
1488 int i;
1489
1490 /*
1491 * No range includes NULL, so this will be accepted by the
1492 * default partition if there is one, and otherwise rejected.
1493 */
1494 for (i = 0; i < key->partnatts; i++)
1495 {
1496 if (isnull[i])
1497 {
1498 range_partkey_has_null = true;
1499 break;
1500 }
1501 }
1502
1503 /* NULLs belong in the DEFAULT partition */
1504 if (range_partkey_has_null)
1505 break;
1506
1508 {
1509 int last_datum_offset = partdesc->last_found_datum_index;
1510 Datum *lastDatums = boundinfo->datums[last_datum_offset];
1511 PartitionRangeDatumKind *kind = boundinfo->kind[last_datum_offset];
1512 int32 cmpval;
1513
1514 /* check if the value is >= to the lower bound */
1515 cmpval = partition_rbound_datum_cmp(key->partsupfunc,
1516 key->partcollation,
1517 lastDatums,
1518 kind,
1519 values,
1520 key->partnatts);
1521
1522 /*
1523 * If it's equal to the lower bound then no need to check
1524 * the upper bound.
1525 */
1526 if (cmpval == 0)
1527 return boundinfo->indexes[last_datum_offset + 1];
1528
1529 if (cmpval < 0 && last_datum_offset + 1 < boundinfo->ndatums)
1530 {
1531 /* check if the value is below the upper bound */
1532 lastDatums = boundinfo->datums[last_datum_offset + 1];
1533 kind = boundinfo->kind[last_datum_offset + 1];
1534 cmpval = partition_rbound_datum_cmp(key->partsupfunc,
1535 key->partcollation,
1536 lastDatums,
1537 kind,
1538 values,
1539 key->partnatts);
1540
1541 if (cmpval > 0)
1542 return boundinfo->indexes[last_datum_offset + 1];
1543 }
1544 /* fall-through and do a manual lookup */
1545 }
1546
1547 bound_offset = partition_range_datum_bsearch(key->partsupfunc,
1548 key->partcollation,
1549 boundinfo,
1550 key->partnatts,
1551 values,
1552 &equal);
1553
1554 /*
1555 * The bound at bound_offset is less than or equal to the
1556 * tuple value, so the bound at offset+1 is the upper bound of
1557 * the partition we're looking for, if there actually exists
1558 * one.
1559 */
1560 part_index = boundinfo->indexes[bound_offset + 1];
1561 }
1562 break;
1563
1564 default:
1565 elog(ERROR, "unexpected partition strategy: %d",
1566 (int) key->strategy);
1567 }
1568
1569 /*
1570 * part_index < 0 means we failed to find a partition of this parent. Use
1571 * the default partition, if there is one.
1572 */
1573 if (part_index < 0)
1574 {
1575 /*
1576 * No need to reset the cache fields here. The next set of values
1577 * might end up belonging to the cached partition, so leaving the
1578 * cache alone improves the chances of a cache hit on the next lookup.
1579 */
1580 return boundinfo->default_index;
1581 }
1582
1583 /* we should only make it here when the code above set bound_offset */
1584 Assert(bound_offset >= 0);
1585
1586 /*
1587 * Attend to the cache fields. If the bound_offset matches the last
1588 * cached bound offset then we've found the same partition as last time,
1589 * so bump the count by one. If all goes well, we'll eventually reach
1590 * PARTITION_CACHED_FIND_THRESHOLD and try the cache path next time
1591 * around. Otherwise, we'll reset the cache count back to 1 to mark that
1592 * we've found this partition for the first time.
1593 */
1594 if (bound_offset == partdesc->last_found_datum_index)
1595 partdesc->last_found_count++;
1596 else
1597 {
1598 partdesc->last_found_count = 1;
1599 partdesc->last_found_part_index = part_index;
1600 partdesc->last_found_datum_index = bound_offset;
1601 }
1602
1603 return part_index;
1604}
int32_t int32
Definition: c.h:498
uint64_t uint64
Definition: c.h:503
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
#define PARTITION_CACHED_FIND_THRESHOLD
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1149
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:885
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:883
@ PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:884
PartitionRangeDatumKind
Definition: parsenodes.h:934
int32 partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums, int n_tuple_datums)
Definition: partbounds.c:3556
uint64 compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc, const Oid *partcollation, const Datum *values, const bool *isnull)
Definition: partbounds.c:4722
int partition_range_datum_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, Datum *values, bool *is_equal)
Definition: partbounds.c:3695
int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
Definition: partbounds.c:3607
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:98
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:207
PartitionRangeDatumKind ** kind
Definition: partbounds.h:84
int last_found_datum_index
Definition: partdesc.h:46
int last_found_count
Definition: partdesc.h:63
int last_found_part_index
Definition: partdesc.h:52

References Assert(), PartitionDescData::boundinfo, compute_partition_hash_value(), DatumGetInt32(), PartitionBoundInfoData::datums, PartitionBoundInfoData::default_index, elog, equal(), ERROR, FunctionCall2Coll(), i, PartitionBoundInfoData::indexes, PartitionDispatchData::key, sort-test::key, PartitionBoundInfoData::kind, PartitionDescData::last_found_count, PartitionDescData::last_found_datum_index, PartitionDescData::last_found_part_index, PartitionBoundInfoData::ndatums, PartitionBoundInfoData::nindexes, PartitionBoundInfoData::null_index, PartitionDispatchData::partdesc, partition_bound_accepts_nulls, PARTITION_CACHED_FIND_THRESHOLD, partition_list_bsearch(), partition_range_datum_bsearch(), partition_rbound_datum_cmp(), PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, and values.

Referenced by ExecFindPartition().

◆ InitExecPartitionPruneContexts()

static void InitExecPartitionPruneContexts ( PartitionPruneState prunestate,
PlanState parent_plan,
Bitmapset initially_valid_subplans,
int  n_total_subplans 
)
static

Definition at line 2336 of file execPartition.c.

2340{
2341 EState *estate;
2342 int *new_subplan_indexes = NULL;
2343 Bitmapset *new_other_subplans;
2344 int i;
2345 int newidx;
2346 bool fix_subplan_map = false;
2347
2348 Assert(prunestate->do_exec_prune);
2349 Assert(parent_plan != NULL);
2350 estate = parent_plan->state;
2351
2352 /*
2353 * No need to fix subplans maps if initial pruning didn't eliminate any
2354 * subplans.
2355 */
2356 if (bms_num_members(initially_valid_subplans) < n_total_subplans)
2357 {
2358 fix_subplan_map = true;
2359
2360 /*
2361 * First we must build a temporary array which maps old subplan
2362 * indexes to new ones. For convenience of initialization, we use
2363 * 1-based indexes in this array and leave pruned items as 0.
2364 */
2365 new_subplan_indexes = (int *) palloc0(sizeof(int) * n_total_subplans);
2366 newidx = 1;
2367 i = -1;
2368 while ((i = bms_next_member(initially_valid_subplans, i)) >= 0)
2369 {
2370 Assert(i < n_total_subplans);
2371 new_subplan_indexes[i] = newidx++;
2372 }
2373 }
2374
2375 /*
2376 * Now we can update each PartitionedRelPruneInfo's subplan_map with new
2377 * subplan indexes. We must also recompute its present_parts bitmap.
2378 */
2379 for (i = 0; i < prunestate->num_partprunedata; i++)
2380 {
2381 PartitionPruningData *prunedata = prunestate->partprunedata[i];
2382 int j;
2383
2384 /*
2385 * Within each hierarchy, we perform this loop in back-to-front order
2386 * so that we determine present_parts for the lowest-level partitioned
2387 * tables first. This way we can tell whether a sub-partitioned
2388 * table's partitions were entirely pruned so we can exclude it from
2389 * the current level's present_parts.
2390 */
2391 for (j = prunedata->num_partrelprunedata - 1; j >= 0; j--)
2392 {
2393 PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
2394 int nparts = pprune->nparts;
2395 int k;
2396
2397 /* Initialize PartitionPruneContext for exec pruning, if needed. */
2398 if (pprune->exec_pruning_steps != NIL)
2399 {
2400 PartitionKey partkey;
2401 PartitionDesc partdesc;
2402
2403 /*
2404 * See the comment in CreatePartitionPruneState() regarding
2405 * the usage of partdesc and partkey.
2406 */
2407 partkey = RelationGetPartitionKey(pprune->partrel);
2409 pprune->partrel);
2410
2412 pprune->exec_pruning_steps,
2413 partdesc, partkey, parent_plan,
2414 prunestate->econtext);
2415 }
2416
2417 if (!fix_subplan_map)
2418 continue;
2419
2420 /* We just rebuild present_parts from scratch */
2421 bms_free(pprune->present_parts);
2422 pprune->present_parts = NULL;
2423
2424 for (k = 0; k < nparts; k++)
2425 {
2426 int oldidx = pprune->subplan_map[k];
2427 int subidx;
2428
2429 /*
2430 * If this partition existed as a subplan then change the old
2431 * subplan index to the new subplan index. The new index may
2432 * become -1 if the partition was pruned above, or it may just
2433 * come earlier in the subplan list due to some subplans being
2434 * removed earlier in the list. If it's a subpartition, add
2435 * it to present_parts unless it's entirely pruned.
2436 */
2437 if (oldidx >= 0)
2438 {
2439 Assert(oldidx < n_total_subplans);
2440 pprune->subplan_map[k] = new_subplan_indexes[oldidx] - 1;
2441
2442 if (new_subplan_indexes[oldidx] > 0)
2443 pprune->present_parts =
2444 bms_add_member(pprune->present_parts, k);
2445 }
2446 else if ((subidx = pprune->subpart_map[k]) >= 0)
2447 {
2448 PartitionedRelPruningData *subprune;
2449
2450 subprune = &prunedata->partrelprunedata[subidx];
2451
2452 if (!bms_is_empty(subprune->present_parts))
2453 pprune->present_parts =
2454 bms_add_member(pprune->present_parts, k);
2455 }
2456 }
2457 }
2458 }
2459
2460 /*
2461 * If we fixed subplan maps, we must also recompute the other_subplans
2462 * set, since indexes in it may change.
2463 */
2464 if (fix_subplan_map)
2465 {
2466 new_other_subplans = NULL;
2467 i = -1;
2468 while ((i = bms_next_member(prunestate->other_subplans, i)) >= 0)
2469 new_other_subplans = bms_add_member(new_other_subplans,
2470 new_subplan_indexes[i] - 1);
2471
2472 bms_free(prunestate->other_subplans);
2473 prunestate->other_subplans = new_other_subplans;
2474
2475 pfree(new_subplan_indexes);
2476 }
2477}
void bms_free(Bitmapset *a)
Definition: bitmapset.c:239
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:751
#define bms_is_empty(a)
Definition: bitmapset.h:118
void pfree(void *pointer)
Definition: mcxt.c:2152

References Assert(), bms_add_member(), bms_free(), bms_is_empty, bms_next_member(), bms_num_members(), PartitionPruneState::do_exec_prune, PartitionPruneState::econtext, EState::es_partition_directory, PartitionedRelPruningData::exec_context, PartitionedRelPruningData::exec_pruning_steps, i, InitPartitionPruneContext(), j, NIL, PartitionedRelPruningData::nparts, PartitionPruneState::num_partprunedata, PartitionPruningData::num_partrelprunedata, PartitionPruneState::other_subplans, palloc0(), PartitionDirectoryLookup(), PartitionPruneState::partprunedata, PartitionedRelPruningData::partrel, PartitionPruningData::partrelprunedata, pfree(), PartitionedRelPruningData::present_parts, RelationGetPartitionKey(), PlanState::state, PartitionedRelPruningData::subpart_map, and PartitionedRelPruningData::subplan_map.

Referenced by ExecInitPartitionExecPruning().

◆ InitPartitionPruneContext()

static void InitPartitionPruneContext ( PartitionPruneContext context,
List pruning_steps,
PartitionDesc  partdesc,
PartitionKey  partkey,
PlanState planstate,
ExprContext econtext 
)
static

Definition at line 2234 of file execPartition.c.

2240{
2241 int n_steps;
2242 int partnatts;
2243 ListCell *lc;
2244
2245 n_steps = list_length(pruning_steps);
2246
2247 context->strategy = partkey->strategy;
2248 context->partnatts = partnatts = partkey->partnatts;
2249 context->nparts = partdesc->nparts;
2250 context->boundinfo = partdesc->boundinfo;
2251 context->partcollation = partkey->partcollation;
2252 context->partsupfunc = partkey->partsupfunc;
2253
2254 /* We'll look up type-specific support functions as needed */
2255 context->stepcmpfuncs = (FmgrInfo *)
2256 palloc0(sizeof(FmgrInfo) * n_steps * partnatts);
2257
2259 context->planstate = planstate;
2260 context->exprcontext = econtext;
2261
2262 /* Initialize expression state for each expression we need */
2263 context->exprstates = (ExprState **)
2264 palloc0(sizeof(ExprState *) * n_steps * partnatts);
2265 foreach(lc, pruning_steps)
2266 {
2268 ListCell *lc2 = list_head(step->exprs);
2269 int keyno;
2270
2271 /* not needed for other step kinds */
2272 if (!IsA(step, PartitionPruneStepOp))
2273 continue;
2274
2275 Assert(list_length(step->exprs) <= partnatts);
2276
2277 for (keyno = 0; keyno < partnatts; keyno++)
2278 {
2279 if (bms_is_member(keyno, step->nullkeys))
2280 continue;
2281
2282 if (lc2 != NULL)
2283 {
2284 Expr *expr = lfirst(lc2);
2285
2286 /* not needed for Consts */
2287 if (!IsA(expr, Const))
2288 {
2289 int stateidx = PruneCxtStateIdx(partnatts,
2290 step->step.step_id,
2291 keyno);
2292
2293 /*
2294 * When planstate is NULL, pruning_steps is known not to
2295 * contain any expressions that depend on the parent plan.
2296 * Information of any available EXTERN parameters must be
2297 * passed explicitly in that case, which the caller must
2298 * have made available via econtext.
2299 */
2300 if (planstate == NULL)
2301 context->exprstates[stateidx] =
2303 econtext->ecxt_param_list_info);
2304 else
2305 context->exprstates[stateidx] =
2306 ExecInitExpr(expr, context->planstate);
2307 }
2308 lc2 = lnext(step->exprs, lc2);
2309 }
2310 }
2311 }
2312}
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:143
ExprState * ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
Definition: execExpr.c:180
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
#define PruneCxtStateIdx(partnatts, step_id, keyno)
Definition: partprune.h:70
ParamListInfo ecxt_param_list_info
Definition: execnodes.h:279
Definition: fmgr.h:57
Oid * partcollation
Definition: partcache.h:39
PartitionStrategy strategy
Definition: partcache.h:27
FmgrInfo * partsupfunc
Definition: partcache.h:36
FmgrInfo * partsupfunc
Definition: partprune.h:56
MemoryContext ppccontext
Definition: partprune.h:58
PartitionBoundInfo boundinfo
Definition: partprune.h:54
PlanState * planstate
Definition: partprune.h:59
FmgrInfo * stepcmpfuncs
Definition: partprune.h:57
ExprState ** exprstates
Definition: partprune.h:61
PartitionPruneStep step
Definition: plannodes.h:1701
Bitmapset * nullkeys
Definition: plannodes.h:1706

References Assert(), bms_is_member(), PartitionDescData::boundinfo, PartitionPruneContext::boundinfo, CurrentMemoryContext, ExprContext::ecxt_param_list_info, ExecInitExpr(), ExecInitExprWithParams(), PartitionPruneContext::exprcontext, PartitionPruneStepOp::exprs, PartitionPruneContext::exprstates, IsA, lfirst, list_head(), list_length(), lnext(), PartitionDescData::nparts, PartitionPruneContext::nparts, PartitionPruneStepOp::nullkeys, palloc0(), PartitionPruneContext::partcollation, PartitionKeyData::partcollation, PartitionPruneContext::partnatts, PartitionKeyData::partnatts, PartitionPruneContext::partsupfunc, PartitionKeyData::partsupfunc, PartitionPruneContext::planstate, PartitionPruneContext::ppccontext, PruneCxtStateIdx, PartitionPruneStepOp::step, PartitionPruneStep::step_id, PartitionPruneContext::stepcmpfuncs, PartitionPruneContext::strategy, and PartitionKeyData::strategy.

Referenced by CreatePartitionPruneState(), and InitExecPartitionPruneContexts().