PostgreSQL Source Code git master
Loading...
Searching...
No Matches
execPartition.c File Reference
#include "postgres.h"
#include "access/table.h"
#include "access/tableam.h"
#include "catalog/index.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/injection_point.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, const Datum *values, const bool *isnull)
 
static charExecBuildSlotPartitionKeyDescription (Relation rel, const Datum *values, const 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)
 
static bool IsIndexCompatibleAsArbiter (Relation arbiterIndexRelation, IndexInfo *arbiterIndexInfo, Relation indexRelation, IndexInfo *indexInfo)
 
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 1504 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 1855 of file execPartition.c.

1856{
1858
1859 Assert(map != NULL);
1860
1862}
#define Assert(condition)
Definition c.h:873
static List * adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap)
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition execUtils.c:1300
static int fb(int x)
AttrMap * attrMap
Definition tupconvert.h:28

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

Referenced by ExecInitPartitionInfo().

◆ adjust_partition_colnos_using_map()

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

Definition at line 1872 of file execPartition.c.

1873{
1874 List *new_colnos = NIL;
1875 ListCell *lc;
1876
1877 Assert(attrMap != NULL); /* else we shouldn't be here */
1878
1879 foreach(lc, colnos)
1880 {
1882
1883 if (parentattrno <= 0 ||
1884 parentattrno > attrMap->maplen ||
1885 attrMap->attnums[parentattrno - 1] == 0)
1886 elog(ERROR, "unexpected attno %d in target column list",
1887 parentattrno);
1889 attrMap->attnums[parentattrno - 1]);
1890 }
1891
1892 return new_colnos;
1893}
int16 AttrNumber
Definition attnum.h:21
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
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, fb(), 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 2122 of file execPartition.c.

2124{
2127 ListCell *lc;
2128 int i;
2129
2130 /*
2131 * Expression context that will be used by partkey_datum_from_expr() to
2132 * evaluate expressions for comparison against partition bounds.
2133 */
2134 ExprContext *econtext = CreateExprContext(estate);
2135
2136 /* For data reading, executor always includes detached partitions */
2137 if (estate->es_partition_directory == NULL)
2138 estate->es_partition_directory =
2139 CreatePartitionDirectory(estate->es_query_cxt, false);
2140
2143
2144 /*
2145 * Allocate the data structure
2146 */
2148 palloc(offsetof(PartitionPruneState, partprunedata) +
2150
2151 /* Save ExprContext for use during InitExecPartitionPruneContexts(). */
2152 prunestate->econtext = econtext;
2153 prunestate->execparamids = NULL;
2154 /* other_subplans can change at runtime, so we need our own copy */
2155 prunestate->other_subplans = bms_copy(pruneinfo->other_subplans);
2156 prunestate->do_initial_prune = false; /* may be set below */
2157 prunestate->do_exec_prune = false; /* may be set below */
2158 prunestate->num_partprunedata = n_part_hierarchies;
2159
2160 /*
2161 * Create a short-term memory context which we'll use when making calls to
2162 * the partition pruning functions. This avoids possible memory leaks,
2163 * since the pruning functions call comparison functions that aren't under
2164 * our control.
2165 */
2166 prunestate->prune_context =
2168 "Partition Prune",
2170
2171 i = 0;
2172 foreach(lc, pruneinfo->prune_infos)
2173 {
2177 ListCell *lc2;
2178 int j;
2179
2181 palloc(offsetof(PartitionPruningData, partrelprunedata) +
2183 prunestate->partprunedata[i] = prunedata;
2184 prunedata->num_partrelprunedata = npartrelpruneinfos;
2185
2186 j = 0;
2187 foreach(lc2, partrelpruneinfos)
2188 {
2190 PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
2191 Relation partrel;
2192 PartitionDesc partdesc;
2194
2195 /*
2196 * We can rely on the copies of the partitioned table's partition
2197 * key and partition descriptor appearing in its relcache entry,
2198 * because that entry will be held open and locked for the
2199 * duration of this executor run.
2200 */
2201 partrel = ExecGetRangeTableRelation(estate, pinfo->rtindex, false);
2202
2203 /* Remember for InitExecPartitionPruneContexts(). */
2204 pprune->partrel = partrel;
2205
2208 partrel);
2209
2210 /*
2211 * Initialize the subplan_map and subpart_map.
2212 *
2213 * The set of partitions that exist now might not be the same that
2214 * existed when the plan was made. The normal case is that it is;
2215 * optimize for that case with a quick comparison, and just copy
2216 * the subplan_map and make subpart_map, leafpart_rti_map point to
2217 * the ones in PruneInfo.
2218 *
2219 * For the case where they aren't identical, we could have more
2220 * partitions on either side; or even exactly the same number of
2221 * them on both but the set of OIDs doesn't match fully. Handle
2222 * this by creating new subplan_map and subpart_map arrays that
2223 * corresponds to the ones in the PruneInfo where the new
2224 * partition descriptor's OIDs match. Any that don't match can be
2225 * set to -1, as if they were pruned. By construction, both
2226 * arrays are in partition bounds order.
2227 */
2228 pprune->nparts = partdesc->nparts;
2229 pprune->subplan_map = palloc_array(int, partdesc->nparts);
2230
2231 if (partdesc->nparts == pinfo->nparts &&
2232 memcmp(partdesc->oids, pinfo->relid_map,
2233 sizeof(int) * partdesc->nparts) == 0)
2234 {
2235 pprune->subpart_map = pinfo->subpart_map;
2236 pprune->leafpart_rti_map = pinfo->leafpart_rti_map;
2237 memcpy(pprune->subplan_map, pinfo->subplan_map,
2238 sizeof(int) * pinfo->nparts);
2239 }
2240 else
2241 {
2242 int pd_idx = 0;
2243 int pp_idx;
2244
2245 /*
2246 * When the partition arrays are not identical, there could be
2247 * some new ones but it's also possible that one was removed;
2248 * we cope with both situations by walking the arrays and
2249 * discarding those that don't match.
2250 *
2251 * If the number of partitions on both sides match, it's still
2252 * possible that one partition has been detached and another
2253 * attached. Cope with that by creating a map that skips any
2254 * mismatches.
2255 */
2256 pprune->subpart_map = palloc_array(int, partdesc->nparts);
2257 pprune->leafpart_rti_map = palloc_array(int, partdesc->nparts);
2258
2259 for (pp_idx = 0; pp_idx < partdesc->nparts; pp_idx++)
2260 {
2261 /* Skip any InvalidOid relid_map entries */
2262 while (pd_idx < pinfo->nparts &&
2263 !OidIsValid(pinfo->relid_map[pd_idx]))
2264 pd_idx++;
2265
2266 recheck:
2267 if (pd_idx < pinfo->nparts &&
2268 pinfo->relid_map[pd_idx] == partdesc->oids[pp_idx])
2269 {
2270 /* match... */
2271 pprune->subplan_map[pp_idx] =
2272 pinfo->subplan_map[pd_idx];
2273 pprune->subpart_map[pp_idx] =
2274 pinfo->subpart_map[pd_idx];
2275 pprune->leafpart_rti_map[pp_idx] =
2276 pinfo->leafpart_rti_map[pd_idx];
2277 pd_idx++;
2278 continue;
2279 }
2280
2281 /*
2282 * There isn't an exact match in the corresponding
2283 * positions of both arrays. Peek ahead in
2284 * pinfo->relid_map to see if we have a match for the
2285 * current partition in partdesc. Normally if a match
2286 * exists it's just one element ahead, and it means the
2287 * planner saw one extra partition that we no longer see
2288 * now (its concurrent detach finished just in between);
2289 * so we skip that one by updating pd_idx to the new
2290 * location and jumping above. We can then continue to
2291 * match the rest of the elements after skipping the OID
2292 * with no match; no future matches are tried for the
2293 * element that was skipped, because we know the arrays to
2294 * be in the same order.
2295 *
2296 * If we don't see a match anywhere in the rest of the
2297 * pinfo->relid_map array, that means we see an element
2298 * now that the planner didn't see, so mark that one as
2299 * pruned and move on.
2300 */
2301 for (int pd_idx2 = pd_idx + 1; pd_idx2 < pinfo->nparts; pd_idx2++)
2302 {
2303 if (pd_idx2 >= pinfo->nparts)
2304 break;
2305 if (pinfo->relid_map[pd_idx2] == partdesc->oids[pp_idx])
2306 {
2307 pd_idx = pd_idx2;
2308 goto recheck;
2309 }
2310 }
2311
2312 pprune->subpart_map[pp_idx] = -1;
2313 pprune->subplan_map[pp_idx] = -1;
2314 pprune->leafpart_rti_map[pp_idx] = 0;
2315 }
2316 }
2317
2318 /* present_parts is also subject to later modification */
2320
2321 /*
2322 * Only initial_context is initialized here. exec_context is
2323 * initialized during ExecInitPartitionExecPruning() when the
2324 * parent plan's PlanState is available.
2325 *
2326 * Note that we must skip execution-time (both "init" and "exec")
2327 * partition pruning in EXPLAIN (GENERIC_PLAN), since parameter
2328 * values may be missing.
2329 */
2330 pprune->initial_pruning_steps = pinfo->initial_pruning_steps;
2331 if (pinfo->initial_pruning_steps &&
2333 {
2334 InitPartitionPruneContext(&pprune->initial_context,
2335 pprune->initial_pruning_steps,
2336 partdesc, partkey, NULL,
2337 econtext);
2338 /* Record whether initial pruning is needed at any level */
2339 prunestate->do_initial_prune = true;
2340 }
2341 pprune->exec_pruning_steps = pinfo->exec_pruning_steps;
2342 if (pinfo->exec_pruning_steps &&
2344 {
2345 /* Record whether exec pruning is needed at any level */
2346 prunestate->do_exec_prune = true;
2347 }
2348
2349 /*
2350 * Accumulate the IDs of all PARAM_EXEC Params affecting the
2351 * partitioning decisions at this plan node.
2352 */
2353 prunestate->execparamids = bms_add_members(prunestate->execparamids,
2354 pinfo->execparamids);
2355
2356 /*
2357 * Return all leaf partition indexes if we're skipping pruning in
2358 * the EXPLAIN (GENERIC_PLAN) case.
2359 */
2360 if (pinfo->initial_pruning_steps && !prunestate->do_initial_prune)
2361 {
2362 int part_index = -1;
2363
2364 while ((part_index = bms_next_member(pprune->present_parts,
2365 part_index)) >= 0)
2366 {
2367 Index rtindex = pprune->leafpart_rti_map[part_index];
2368
2369 if (rtindex)
2371 rtindex);
2372 }
2373 }
2374
2375 j++;
2376 }
2377 i++;
2378 }
2379
2380 return prunestate;
2381}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1305
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:814
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:916
Bitmapset * bms_copy(const Bitmapset *a)
Definition bitmapset.c:122
unsigned int Index
Definition c.h:628
#define OidIsValid(objectId)
Definition c.h:788
static void InitPartitionPruneContext(PartitionPruneContext *context, List *pruning_steps, PartitionDesc partdesc, PartitionKey partkey, PlanState *planstate, ExprContext *econtext)
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:67
#define palloc_array(type, count)
Definition fe_memutils.h:76
int j
Definition isn.c:78
int i
Definition isn.c:77
void * palloc(Size size)
Definition mcxt.c:1387
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
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:721
MemoryContext es_query_cxt
Definition execnodes.h:712
PartitionDirectory es_partition_directory
Definition execnodes.h:694
struct EState * ecxt_estate
Definition execnodes.h:317
Bitmapset * present_parts
Definition plannodes.h:1683

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, bms_add_member(), bms_add_members(), bms_copy(), bms_next_member(), CreateExprContext(), CreatePartitionDirectory(), CurrentMemoryContext, ExprContext::ecxt_estate, EState::es_partition_directory, EState::es_query_cxt, EState::es_top_eflags, EXEC_FLAG_EXPLAIN_GENERIC, PartitionedRelPruneInfo::exec_pruning_steps, ExecGetRangeTableRelation(), PartitionedRelPruneInfo::execparamids, fb(), i, PartitionedRelPruneInfo::initial_pruning_steps, InitPartitionPruneContext(), j, lfirst_node, list_length(), PartitionedRelPruneInfo::nparts, PartitionDescData::nparts, OidIsValid, PartitionDescData::oids, palloc(), palloc_array, PartitionDirectoryLookup(), PartitionedRelPruneInfo::present_parts, RelationGetPartitionKey(), and PartitionedRelPruneInfo::rtindex.

Referenced by ExecDoInitialPruning().

◆ ExecBuildSlotPartitionKeyDescription()

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

Definition at line 1767 of file execPartition.c.

1771{
1774 int partnatts = get_partition_natts(key);
1775 int i;
1776 Oid relid = RelationGetRelid(rel);
1778
1779 if (check_enable_rls(relid, InvalidOid, true) == RLS_ENABLED)
1780 return NULL;
1781
1782 /* If the user has table-level access, just go build the description. */
1784 if (aclresult != ACLCHECK_OK)
1785 {
1786 /*
1787 * Step through the columns of the partition key and make sure the
1788 * user has SELECT rights on all of them.
1789 */
1790 for (i = 0; i < partnatts; i++)
1791 {
1793
1794 /*
1795 * If this partition key column is an expression, we return no
1796 * detail rather than try to figure out what column(s) the
1797 * expression includes and if the user has SELECT rights on them.
1798 */
1799 if (attnum == InvalidAttrNumber ||
1802 return NULL;
1803 }
1804 }
1805
1807 appendStringInfo(&buf, "(%s) = (",
1808 pg_get_partkeydef_columns(relid, true));
1809
1810 for (i = 0; i < partnatts; i++)
1811 {
1812 char *val;
1813 int vallen;
1814
1815 if (isnull[i])
1816 val = "null";
1817 else
1818 {
1819 Oid foutoid;
1820 bool typisvarlena;
1821
1823 &foutoid, &typisvarlena);
1825 }
1826
1827 if (i > 0)
1829
1830 /* truncate if needed */
1831 vallen = strlen(val);
1832 if (vallen <= maxfieldlen)
1833 appendBinaryStringInfo(&buf, val, vallen);
1834 else
1835 {
1836 vallen = pg_mbcliplen(val, vallen, maxfieldlen);
1837 appendBinaryStringInfo(&buf, val, vallen);
1838 appendStringInfoString(&buf, "...");
1839 }
1840 }
1841
1843
1844 return buf.data;
1845}
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:3868
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4039
#define InvalidAttrNumber
Definition attnum.h:23
static Datum values[MAXATTR]
Definition bootstrap.c:155
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:3057
int pg_mbcliplen(const char *mbstr, int len, int limit)
Definition mbutils.c:1086
Oid GetUserId(void)
Definition miscinit.c:469
#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
static char buf[DEFAULT_XLOG_SEG_SIZE]
#define InvalidOid
unsigned int Oid
#define RelationGetRelid(relation)
Definition rel.h:514
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:1923
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(), fb(), get_partition_col_attnum(), get_partition_col_typid(), get_partition_natts(), getTypeOutputInfo(), GetUserId(), i, initStringInfo(), InvalidAttrNumber, InvalidOid, 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 1389 of file execPartition.c.

1391{
1392 int i;
1393
1394 /*
1395 * Remember, proute->partition_dispatch_info[0] corresponds to the root
1396 * partitioned table, which we must not try to close, because it is the
1397 * main target table of the query that will be closed by callers such as
1398 * ExecEndPlan() or DoCopy(). Also, tupslot is NULL for the root
1399 * partitioned table.
1400 */
1401 for (i = 1; i < proute->num_dispatch; i++)
1402 {
1404
1406
1407 if (pd->tupslot)
1409 }
1410
1411 for (i = 0; i < proute->num_partitions; i++)
1412 {
1413 ResultRelInfo *resultRelInfo = proute->partitions[i];
1414
1415 /* Allow any FDWs to shut down */
1416 if (resultRelInfo->ri_FdwRoutine != NULL &&
1417 resultRelInfo->ri_FdwRoutine->EndForeignInsert != NULL)
1418 resultRelInfo->ri_FdwRoutine->EndForeignInsert(mtstate->ps.state,
1419 resultRelInfo);
1420
1421 /*
1422 * Close it if it's not one of the result relations borrowed from the
1423 * owning ModifyTableState; those will be closed by ExecEndPlan().
1424 */
1425 if (proute->is_borrowed_rel[i])
1426 continue;
1427
1428 ExecCloseIndices(resultRelInfo);
1429 table_close(resultRelInfo->ri_RelationDesc, NoLock);
1430 }
1431}
void ExecCloseIndices(ResultRelInfo *resultRelInfo)
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
#define NoLock
Definition lockdefs.h:34
EndForeignInsert_function EndForeignInsert
Definition fdwapi.h:239
TupleTableSlot * tupslot
PartitionDispatch * partition_dispatch_info
ResultRelInfo ** partitions
EState * state
Definition execnodes.h:1169
Relation ri_RelationDesc
Definition execnodes.h:482
struct FdwRoutine * ri_FdwRoutine
Definition execnodes.h:535
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126

References FdwRoutine::EndForeignInsert, ExecCloseIndices(), ExecDropSingleTupleTableSlot(), fb(), 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 1972 of file execPartition.c.

1973{
1974 ListCell *lc;
1975
1976 foreach(lc, estate->es_part_prune_infos)
1977 {
1983
1984 /* Create and save the PartitionPruneState. */
1988 prunestate);
1989
1990 /*
1991 * Perform initial pruning steps, if any, and save the result
1992 * bitmapset or NULL as described in the header comment.
1993 */
1994 if (prunestate->do_initial_prune)
1997 else
1999
2004 }
2005}
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:672
Bitmapset * es_unpruned_relids
Definition execnodes.h:675
List * es_part_prune_states
Definition execnodes.h:673
List * es_part_prune_results
Definition execnodes.h:674

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

Referenced by InitPlan().

◆ ExecFindMatchingSubPlans()

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

Definition at line 2644 of file execPartition.c.

2647{
2648 Bitmapset *result = NULL;
2649 MemoryContext oldcontext;
2650 int i;
2651
2652 /*
2653 * Either we're here on the initial prune done during pruning
2654 * initialization, or we're at a point where PARAM_EXEC Params can be
2655 * evaluated *and* there are steps in which to do so.
2656 */
2657 Assert(initial_prune || prunestate->do_exec_prune);
2659
2660 /*
2661 * Switch to a temp context to avoid leaking memory in the executor's
2662 * query-lifespan memory context.
2663 */
2664 oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
2665
2666 /*
2667 * For each hierarchy, do the pruning tests, and add nondeletable
2668 * subplans' indexes to "result".
2669 */
2670 for (i = 0; i < prunestate->num_partprunedata; i++)
2671 {
2672 PartitionPruningData *prunedata = prunestate->partprunedata[i];
2674
2675 /*
2676 * We pass the zeroth item, belonging to the root table of the
2677 * hierarchy, and find_matching_subplans_recurse() takes care of
2678 * recursing to other (lower-level) parents as needed.
2679 */
2680 pprune = &prunedata->partrelprunedata[0];
2682 &result, validsubplan_rtis);
2683
2684 /*
2685 * Expression eval may have used space in ExprContext too. Avoid
2686 * accessing exec_context during initial pruning, as it is not valid
2687 * at that stage.
2688 */
2689 if (!initial_prune && pprune->exec_pruning_steps)
2690 ResetExprContext(pprune->exec_context.exprcontext);
2691 }
2692
2693 /* Add in any subplans that partition pruning didn't account for */
2694 result = bms_add_members(result, prunestate->other_subplans);
2695
2696 MemoryContextSwitchTo(oldcontext);
2697
2698 /* Copy result out of the temp context before we reset it */
2699 result = bms_copy(result);
2702
2703 MemoryContextReset(prunestate->prune_context);
2704
2705 return result;
2706}
static void find_matching_subplans_recurse(PartitionPruningData *prunedata, PartitionedRelPruningData *pprune, bool initial_prune, Bitmapset **validsubplans, Bitmapset **validsubplan_rtis)
#define ResetExprContext(econtext)
Definition executor.h:650
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:403
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124

References Assert, bms_add_members(), bms_copy(), fb(), find_matching_subplans_recurse(), i, MemoryContextReset(), MemoryContextSwitchTo(), 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 267 of file execPartition.c.

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

References Assert, TupleConversionMap::attrMap, PartitionDescData::boundinfo, CHECK_FOR_INTERRUPTS, CheckValidResultRel(), CMD_INSERT, PartitionBoundInfoData::default_index, ereport, errcode(), errdetail(), errmsg(), ERROR, errtable(), ExecBuildSlotPartitionKeyDescription(), ExecClearTuple(), ExecGetRootToChildMap(), ExecInitPartitionDispatchInfo(), ExecInitPartitionInfo(), ExecInitRoutingInfo(), ExecLookupResultRelByOid(), ExecPartitionCheck(), execute_attr_map_slot(), fb(), FormPartitionKeyDatum(), get_partition_for_tuple(), GetPerTupleExprContext, GetPerTupleMemoryContext, PartitionDescData::is_leaf, likely, MemoryContextSwitchTo(), NIL, PartitionTupleRouting::nonleaf_partitions, PartitionDescData::nparts, PartitionTupleRouting::num_dispatch, PartitionTupleRouting::num_partitions, OidIsValid, PartitionDescData::oids, ONCONFLICT_NONE, ModifyTable::onConflictAction, PartitionTupleRouting::partition_dispatch_info, PARTITION_MAX_KEYS, PartitionTupleRouting::partitions, PlanState::plan, ModifyTableState::ps, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, ResultRelInfo::ri_RelationDesc, ModifyTableState::rootResultRelInfo, 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 1252 of file execPartition.c.

1256{
1257 Relation rel;
1258 PartitionDesc partdesc;
1260 int dispatchidx;
1262
1263 /*
1264 * For data modification, it is better that executor does not include
1265 * partitions being detached, except when running in snapshot-isolation
1266 * mode. This means that a read-committed transaction immediately gets a
1267 * "no partition for tuple" error when a tuple is inserted into a
1268 * partition that's being detached concurrently, but a transaction in
1269 * repeatable-read mode can still use such a partition.
1270 */
1271 if (estate->es_partition_directory == NULL)
1272 estate->es_partition_directory =
1275
1277
1278 /*
1279 * Only sub-partitioned tables need to be locked here. The root
1280 * partitioned table will already have been locked as it's referenced in
1281 * the query's rtable.
1282 */
1283 if (partoid != RelationGetRelid(proute->partition_root))
1284 rel = table_open(partoid, RowExclusiveLock);
1285 else
1286 rel = proute->partition_root;
1287 partdesc = PartitionDirectoryLookup(estate->es_partition_directory, rel);
1288
1290 partdesc->nparts * sizeof(int));
1291 pd->reldesc = rel;
1292 pd->key = RelationGetPartitionKey(rel);
1293 pd->keystate = NIL;
1294 pd->partdesc = partdesc;
1295 if (parent_pd != NULL)
1296 {
1297 TupleDesc tupdesc = RelationGetDescr(rel);
1298
1299 /*
1300 * For sub-partitioned tables where the column order differs from its
1301 * direct parent partitioned table, we must store a tuple table slot
1302 * initialized with its tuple descriptor and a tuple conversion map to
1303 * convert a tuple from its parent's rowtype to its own. This is to
1304 * make sure that we are looking at the correct row using the correct
1305 * tuple descriptor when computing its partition key for tuple
1306 * routing.
1307 */
1309 tupdesc,
1310 false);
1311 pd->tupslot = pd->tupmap ?
1313 }
1314 else
1315 {
1316 /* Not required for the root partitioned table */
1317 pd->tupmap = NULL;
1318 pd->tupslot = NULL;
1319 }
1320
1321 /*
1322 * Initialize with -1 to signify that the corresponding partition's
1323 * ResultRelInfo or PartitionDispatch has not been created yet.
1324 */
1325 memset(pd->indexes, -1, sizeof(int) * partdesc->nparts);
1326
1327 /* Track in PartitionTupleRouting for later use */
1328 dispatchidx = proute->num_dispatch++;
1329
1330 /* Allocate or enlarge the array, as needed */
1331 if (proute->num_dispatch >= proute->max_dispatch)
1332 {
1333 if (proute->max_dispatch == 0)
1334 {
1335 proute->max_dispatch = 4;
1338 }
1339 else
1340 {
1341 proute->max_dispatch *= 2;
1344 sizeof(PartitionDispatch) * proute->max_dispatch);
1345 proute->nonleaf_partitions = (ResultRelInfo **)
1347 sizeof(ResultRelInfo *) * proute->max_dispatch);
1348 }
1349 }
1351
1352 /*
1353 * If setting up a PartitionDispatch for a sub-partitioned table, we may
1354 * also need a minimally valid ResultRelInfo for checking the partition
1355 * constraint later; set that up now.
1356 */
1357 if (parent_pd)
1358 {
1360
1361 InitResultRelInfo(rri, rel, 0, rootResultRelInfo, 0);
1363 }
1364 else
1366
1367 /*
1368 * Finally, if setting up a PartitionDispatch for a sub-partitioned table,
1369 * install a downlink in the parent to allow quick descent.
1370 */
1371 if (parent_pd)
1372 {
1373 Assert(parent_pd->indexes[partidx] == -1);
1374 parent_pd->indexes[partidx] = dispatchidx;
1375 }
1376
1378
1379 return pd;
1380}
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:1247
struct PartitionDispatchData * PartitionDispatch
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
const TupleTableSlotOps TTSOpsVirtual
Definition execTuples.c:84
#define RowExclusiveLock
Definition lockdefs.h:38
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1632
#define makeNode(_type_)
Definition nodes.h:161
#define RelationGetDescr(relation)
Definition rel.h:540
int indexes[FLEXIBLE_ARRAY_MEMBER]
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
#define IsolationUsesXactSnapshot()
Definition xact.h:52

References Assert, build_attrmap_by_name_if_req(), CreatePartitionDirectory(), EState::es_partition_directory, EState::es_query_cxt, fb(), 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(), palloc_array, 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 2028 of file execPartition.c.

2033{
2035 EState *estate = planstate->state;
2037
2038 /* Obtain the pruneinfo we need. */
2040 part_prune_index);
2041
2042 /* Its relids better match the plan node's or the planner messed up. */
2043 if (!bms_equal(relids, pruneinfo->relids))
2044 elog(ERROR, "wrong pruneinfo with relids=%s found at part_prune_index=%d contained in plan node with relids=%s",
2045 bmsToString(pruneinfo->relids), part_prune_index,
2046 bmsToString(relids));
2047
2048 /*
2049 * The PartitionPruneState would have been created by
2050 * ExecDoInitialPruning() and stored as the part_prune_index'th element of
2051 * EState.es_part_prune_states.
2052 */
2053 prunestate = list_nth(estate->es_part_prune_states, part_prune_index);
2055
2056 /* Use the result of initial pruning done by ExecDoInitialPruning(). */
2057 if (prunestate->do_initial_prune)
2059 estate->es_part_prune_results,
2060 part_prune_index);
2061 else
2062 {
2063 /* No pruning, so we'll need to initialize all subplans */
2066 n_total_subplans - 1);
2067 }
2068
2069 /*
2070 * The exec pruning state must also be initialized, if needed, before it
2071 * can be used for pruning during execution.
2072 *
2073 * This also re-sequences subplan indexes contained in prunestate to
2074 * account for any that were removed due to initial pruning; refer to the
2075 * condition in InitExecPartitionPruneContexts() that is used to determine
2076 * whether to do this. If no exec pruning needs to be done, we would thus
2077 * leave the maps to be in an invalid state, but that's ok since that data
2078 * won't be consulted again (cf initial Assert in
2079 * ExecFindMatchingSubPlans).
2080 */
2081 if (prunestate->do_exec_prune)
2085
2086 return prunestate;
2087}
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:1018
static void InitExecPartitionPruneContexts(PartitionPruneState *prunestate, PlanState *parent_plan, Bitmapset *initially_valid_subplans, int n_total_subplans)
char * bmsToString(const Bitmapset *bms)
Definition outfuncs.c:819
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

References Assert, bms_add_range(), bms_equal(), bmsToString(), elog, ERROR, EState::es_part_prune_infos, EState::es_part_prune_results, EState::es_part_prune_states, fb(), InitExecPartitionPruneContexts(), list_nth(), list_nth_node, 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 563 of file execPartition.c.

568{
569 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
570 Oid partOid = dispatch->partdesc->oids[partidx];
571 Relation partrel;
577 bool found_whole_row;
578
580
582
585 partrel,
586 0,
587 rootResultRelInfo,
588 estate->es_instrument);
589
590 /*
591 * Verify result relation is a valid target for an INSERT. An UPDATE of a
592 * partition-key becomes a DELETE+INSERT operation, so this check is still
593 * required when the operation is CMD_UPDATE.
594 */
596 node ? node->onConflictAction : ONCONFLICT_NONE, NIL);
597
598 /*
599 * Open partition indices. The user may have asked to check for conflicts
600 * within this leaf partition and do "nothing" instead of throwing an
601 * error. Be prepared in that case by initializing the index information
602 * needed by ExecInsert() to perform speculative insertions.
603 */
604 if (partrel->rd_rel->relhasindex &&
605 leaf_part_rri->ri_IndexRelationDescs == NULL)
607 (node != NULL &&
608 node->onConflictAction != ONCONFLICT_NONE));
609
610 /*
611 * Build WITH CHECK OPTION constraints for the partition. Note that we
612 * didn't build the withCheckOptionList for partitions within the planner,
613 * but simple translation of varattnos will suffice. This only occurs for
614 * the INSERT case or in the case of UPDATE/MERGE tuple routing where we
615 * didn't find a result rel to reuse.
616 */
617 if (node && node->withCheckOptionLists != NIL)
618 {
619 List *wcoList;
620 List *wcoExprs = NIL;
621 ListCell *ll;
622
623 /*
624 * In the case of INSERT on a partitioned table, there is only one
625 * plan. Likewise, there is only one WCO list, not one per partition.
626 * For UPDATE/MERGE, there are as many WCO lists as there are plans.
627 */
628 Assert((node->operation == CMD_INSERT &&
629 list_length(node->withCheckOptionLists) == 1 &&
630 list_length(node->resultRelations) == 1) ||
631 (node->operation == CMD_UPDATE &&
632 list_length(node->withCheckOptionLists) ==
633 list_length(node->resultRelations)) ||
634 (node->operation == CMD_MERGE &&
635 list_length(node->withCheckOptionLists) ==
636 list_length(node->resultRelations)));
637
638 /*
639 * Use the WCO list of the first plan as a reference to calculate
640 * attno's for the WCO list of this partition. In the INSERT case,
641 * that refers to the root partitioned table, whereas in the UPDATE
642 * tuple routing case, that refers to the first partition in the
643 * mtstate->resultRelInfo array. In any case, both that relation and
644 * this partition should have the same columns, so we should be able
645 * to map attributes successfully.
646 */
647 wcoList = linitial(node->withCheckOptionLists);
648
649 /*
650 * Convert Vars in it to contain this partition's attribute numbers.
651 */
655 false);
656 wcoList = (List *)
658 firstVarno, 0,
660 RelationGetForm(partrel)->reltype,
661 &found_whole_row);
662 /* We ignore the value of found_whole_row. */
663
664 foreach(ll, wcoList)
665 {
668 &mtstate->ps);
669
671 }
672
673 leaf_part_rri->ri_WithCheckOptions = wcoList;
674 leaf_part_rri->ri_WithCheckOptionExprs = wcoExprs;
675 }
676
677 /*
678 * Build the RETURNING projection for the partition. Note that we didn't
679 * build the returningList for partitions within the planner, but simple
680 * translation of varattnos will suffice. This only occurs for the INSERT
681 * case or in the case of UPDATE/MERGE tuple routing where we didn't find
682 * a result rel to reuse.
683 */
684 if (node && node->returningLists != NIL)
685 {
686 TupleTableSlot *slot;
687 ExprContext *econtext;
688 List *returningList;
689
690 /* See the comment above for WCO lists. */
691 Assert((node->operation == CMD_INSERT &&
692 list_length(node->returningLists) == 1 &&
693 list_length(node->resultRelations) == 1) ||
694 (node->operation == CMD_UPDATE &&
695 list_length(node->returningLists) ==
696 list_length(node->resultRelations)) ||
697 (node->operation == CMD_MERGE &&
698 list_length(node->returningLists) ==
699 list_length(node->resultRelations)));
700
701 /*
702 * Use the RETURNING list of the first plan as a reference to
703 * calculate attno's for the RETURNING list of this partition. See
704 * the comment above for WCO lists for more details on why this is
705 * okay.
706 */
707 returningList = linitial(node->returningLists);
708
709 /*
710 * Convert Vars in it to contain this partition's attribute numbers.
711 */
712 if (part_attmap == NULL)
716 false);
717 returningList = (List *)
718 map_variable_attnos((Node *) returningList,
719 firstVarno, 0,
721 RelationGetForm(partrel)->reltype,
722 &found_whole_row);
723 /* We ignore the value of found_whole_row. */
724
725 leaf_part_rri->ri_returningList = returningList;
726
727 /*
728 * Initialize the projection itself.
729 *
730 * Use the slot and the expression context that would have been set up
731 * in ExecInitModifyTable() for projection's output.
732 */
733 Assert(mtstate->ps.ps_ResultTupleSlot != NULL);
734 slot = mtstate->ps.ps_ResultTupleSlot;
735 Assert(mtstate->ps.ps_ExprContext != NULL);
736 econtext = mtstate->ps.ps_ExprContext;
737 leaf_part_rri->ri_projectReturning =
738 ExecBuildProjectionInfo(returningList, econtext, slot,
739 &mtstate->ps, RelationGetDescr(partrel));
740 }
741
742 /* Set up information needed for routing tuples to the partition. */
743 ExecInitRoutingInfo(mtstate, estate, proute, dispatch,
744 leaf_part_rri, partidx, false);
745
746 /*
747 * If there is an ON CONFLICT clause, initialize state for it.
748 */
749 if (node && node->onConflictAction != ONCONFLICT_NONE)
750 {
752 ExprContext *econtext = mtstate->ps.ps_ExprContext;
753 List *arbiterIndexes = NIL;
754 int additional_arbiters = 0;
755
756 /*
757 * If there is a list of arbiter indexes, map it to a list of indexes
758 * in the partition. We also add any "identical indexes" to any of
759 * those, to cover the case where one of them is concurrently being
760 * reindexed.
761 */
762 if (rootResultRelInfo->ri_onConflictArbiterIndexes != NIL)
763 {
767
768 for (int listidx = 0; listidx < leaf_part_rri->ri_NumIndices; listidx++)
769 {
770 Oid indexoid;
771 List *ancestors;
772
773 /*
774 * If one of this index's ancestors is in the root's arbiter
775 * list, then use this index as arbiter for this partition.
776 * Otherwise, if this index has no parent, track it for later,
777 * in case REINDEX CONCURRENTLY is working on one of the
778 * arbiters.
779 *
780 * However, if two indexes appear to have the same parent,
781 * treat the second of these as if it had no parent. This
782 * sounds counterintuitive, but it can happen if a transaction
783 * running REINDEX CONCURRENTLY commits right between those
784 * two indexes are checked by another process in this loop.
785 * This will have the effect of also treating that second
786 * index as arbiter.
787 *
788 * XXX get_partition_ancestors scans pg_inherits, which is not
789 * only slow, but also means the catalog snapshot can get
790 * invalidated each time through the loop (cf.
791 * GetNonHistoricCatalogSnapshot). Consider a syscache or
792 * some other way to cache?
793 */
794 indexoid = RelationGetRelid(leaf_part_rri->ri_IndexRelationDescs[listidx]);
795 ancestors = get_partition_ancestors(indexoid);
796 INJECTION_POINT("exec-init-partition-after-get-partition-ancestors", NULL);
797
798 if (ancestors != NIL &&
800 {
802 {
803 if (list_member_oid(ancestors, parent_idx))
804 {
806 arbiterIndexes = lappend_oid(arbiterIndexes, indexoid);
808 break;
809 }
810 }
811 }
812 else
814
815 list_free(ancestors);
816 }
817
818 /*
819 * If we found any indexes with no ancestors, it's possible that
820 * some arbiter index is undergoing concurrent reindex. Match all
821 * unparented indexes against arbiters; add unparented matching
822 * ones as "additional arbiters".
823 *
824 * This is critical so that all concurrent transactions use the
825 * same set as arbiters during REINDEX CONCURRENTLY, to avoid
826 * spurious "duplicate key" errors.
827 */
828 if (unparented_idxs && arbiterIndexes)
829 {
831 {
834
835 unparented_rel = leaf_part_rri->ri_IndexRelationDescs[unparented_i];
836 unparented_ii = leaf_part_rri->ri_IndexRelationInfo[unparented_i];
837
838 Assert(!list_member_oid(arbiterIndexes,
839 unparented_rel->rd_index->indexrelid));
840
841 /* Ignore indexes not ready */
842 if (!unparented_ii->ii_ReadyForInserts)
843 continue;
844
846 {
849
850 arbiter_rel = leaf_part_rri->ri_IndexRelationDescs[arbiter_i];
851 arbiter_ii = leaf_part_rri->ri_IndexRelationInfo[arbiter_i];
852
853 /*
854 * If the non-ancestor index is compatible with the
855 * arbiter, use the non-ancestor as arbiter too.
856 */
861 {
862 arbiterIndexes = lappend_oid(arbiterIndexes,
863 unparented_rel->rd_index->indexrelid);
865 break;
866 }
867 }
868 }
869 }
873 }
874
875 /*
876 * We expect to find as many arbiter indexes on this partition as the
877 * root has, plus however many "additional arbiters" (to wit: those
878 * being concurrently rebuilt) we found.
879 */
880 if (list_length(rootResultRelInfo->ri_onConflictArbiterIndexes) !=
881 list_length(arbiterIndexes) - additional_arbiters)
882 elog(ERROR, "invalid arbiter index list");
883 leaf_part_rri->ri_onConflictArbiterIndexes = arbiterIndexes;
884
885 /*
886 * In the DO UPDATE case, we have some more state to initialize.
887 */
888 if (node->onConflictAction == ONCONFLICT_UPDATE)
889 {
892
894
895 Assert(node->onConflictSet != NIL);
896 Assert(rootResultRelInfo->ri_onConflict != NULL);
897
898 leaf_part_rri->ri_onConflict = onconfl;
899
900 /*
901 * Need a separate existing slot for each partition, as the
902 * partition could be of a different AM, even if the tuple
903 * descriptors match.
904 */
905 onconfl->oc_Existing =
906 table_slot_create(leaf_part_rri->ri_RelationDesc,
907 &mtstate->ps.state->es_tupleTable);
908
909 /*
910 * If the partition's tuple descriptor matches exactly the root
911 * parent (the common case), we can re-use most of the parent's ON
912 * CONFLICT SET state, skipping a bunch of work. Otherwise, we
913 * need to create state specific to this partition.
914 */
915 if (map == NULL)
916 {
917 /*
918 * It's safe to reuse these from the partition root, as we
919 * only process one tuple at a time (therefore we won't
920 * overwrite needed data in slots), and the results of
921 * projections are independent of the underlying storage.
922 * Projections and where clauses themselves don't store state
923 * / are independent of the underlying storage.
924 */
925 onconfl->oc_ProjSlot =
926 rootResultRelInfo->ri_onConflict->oc_ProjSlot;
927 onconfl->oc_ProjInfo =
928 rootResultRelInfo->ri_onConflict->oc_ProjInfo;
929 onconfl->oc_WhereClause =
930 rootResultRelInfo->ri_onConflict->oc_WhereClause;
931 }
932 else
933 {
936
937 /*
938 * Translate expressions in onConflictSet to account for
939 * different attribute numbers. For that, map partition
940 * varattnos twice: first to catch the EXCLUDED
941 * pseudo-relation (INNER_VAR), and second to handle the main
942 * target relation (firstVarno).
943 */
944 onconflset = copyObject(node->onConflictSet);
945 if (part_attmap == NULL)
949 false);
950 onconflset = (List *)
952 INNER_VAR, 0,
954 RelationGetForm(partrel)->reltype,
955 &found_whole_row);
956 /* We ignore the value of found_whole_row. */
957 onconflset = (List *)
959 firstVarno, 0,
961 RelationGetForm(partrel)->reltype,
962 &found_whole_row);
963 /* We ignore the value of found_whole_row. */
964
965 /* Finally, adjust the target colnos to match the partition. */
966 onconflcols = adjust_partition_colnos(node->onConflictCols,
968
969 /* create the tuple slot for the UPDATE SET projection */
970 onconfl->oc_ProjSlot =
971 table_slot_create(partrel,
972 &mtstate->ps.state->es_tupleTable);
973
974 /* build UPDATE SET projection state */
975 onconfl->oc_ProjInfo =
977 true,
980 econtext,
981 onconfl->oc_ProjSlot,
982 &mtstate->ps);
983
984 /*
985 * If there is a WHERE clause, initialize state where it will
986 * be evaluated, mapping the attribute numbers appropriately.
987 * As with onConflictSet, we need to map partition varattnos
988 * to the partition's tupdesc.
989 */
990 if (node->onConflictWhere)
991 {
992 List *clause;
993
994 clause = copyObject((List *) node->onConflictWhere);
995 clause = (List *)
996 map_variable_attnos((Node *) clause,
997 INNER_VAR, 0,
999 RelationGetForm(partrel)->reltype,
1000 &found_whole_row);
1001 /* We ignore the value of found_whole_row. */
1002 clause = (List *)
1003 map_variable_attnos((Node *) clause,
1004 firstVarno, 0,
1006 RelationGetForm(partrel)->reltype,
1007 &found_whole_row);
1008 /* We ignore the value of found_whole_row. */
1009 onconfl->oc_WhereClause =
1010 ExecInitQual(clause, &mtstate->ps);
1011 }
1012 }
1013 }
1014 }
1015
1016 /*
1017 * Since we've just initialized this ResultRelInfo, it's not in any list
1018 * attached to the estate as yet. Add it, so that it can be found later.
1019 *
1020 * Note that the entries in this list appear in no predetermined order,
1021 * because partition result rels are initialized as and when they're
1022 * needed.
1023 */
1028
1029 /*
1030 * Initialize information about this partition that's needed to handle
1031 * MERGE. We take the "first" result relation's mergeActionList as
1032 * reference and make copy for this relation, converting stuff that
1033 * references attribute numbers to match this relation's.
1034 *
1035 * This duplicates much of the logic in ExecInitMerge(), so if something
1036 * changes there, look here too.
1037 */
1038 if (node && node->operation == CMD_MERGE)
1039 {
1040 List *firstMergeActionList = linitial(node->mergeActionLists);
1041 ListCell *lc;
1042 ExprContext *econtext = mtstate->ps.ps_ExprContext;
1043 Node *joinCondition;
1044
1045 if (part_attmap == NULL)
1046 part_attmap =
1049 false);
1050
1051 if (unlikely(!leaf_part_rri->ri_projectNewInfoValid))
1053
1054 /* Initialize state for join condition checking. */
1055 joinCondition =
1056 map_variable_attnos(linitial(node->mergeJoinConditions),
1057 firstVarno, 0,
1059 RelationGetForm(partrel)->reltype,
1060 &found_whole_row);
1061 /* We ignore the value of found_whole_row. */
1062 leaf_part_rri->ri_MergeJoinCondition =
1063 ExecInitQual((List *) joinCondition, &mtstate->ps);
1064
1065 foreach(lc, firstMergeActionList)
1066 {
1067 /* Make a copy for this relation to be safe. */
1069 MergeActionState *action_state;
1070
1071 /* Generate the action's state for this relation */
1072 action_state = makeNode(MergeActionState);
1073 action_state->mas_action = action;
1074
1075 /* And put the action in the appropriate list */
1076 leaf_part_rri->ri_MergeActions[action->matchKind] =
1077 lappend(leaf_part_rri->ri_MergeActions[action->matchKind],
1078 action_state);
1079
1080 switch (action->commandType)
1081 {
1082 case CMD_INSERT:
1083
1084 /*
1085 * ExecCheckPlanOutput() already done on the targetlist
1086 * when "first" result relation initialized and it is same
1087 * for all result relations.
1088 */
1089 action_state->mas_proj =
1090 ExecBuildProjectionInfo(action->targetList, econtext,
1091 leaf_part_rri->ri_newTupleSlot,
1092 &mtstate->ps,
1093 RelationGetDescr(partrel));
1094 break;
1095 case CMD_UPDATE:
1096
1097 /*
1098 * Convert updateColnos from "first" result relation
1099 * attribute numbers to this result rel's.
1100 */
1101 if (part_attmap)
1102 action->updateColnos =
1104 part_attmap);
1105 action_state->mas_proj =
1107 true,
1108 action->updateColnos,
1109 RelationGetDescr(leaf_part_rri->ri_RelationDesc),
1110 econtext,
1111 leaf_part_rri->ri_newTupleSlot,
1112 NULL);
1113 break;
1114 case CMD_DELETE:
1115 case CMD_NOTHING:
1116 /* Nothing to do */
1117 break;
1118
1119 default:
1120 elog(ERROR, "unknown action in MERGE WHEN clause");
1121 }
1122
1123 /* found_whole_row intentionally ignored. */
1124 action->qual =
1126 firstVarno, 0,
1128 RelationGetForm(partrel)->reltype,
1129 &found_whole_row);
1130 action_state->mas_whenqual =
1131 ExecInitQual((List *) action->qual, &mtstate->ps);
1132 }
1133 }
1135
1136 return leaf_part_rri;
1137}
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition attmap.c:175
#define unlikely(x)
Definition c.h:412
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)
static bool IsIndexCompatibleAsArbiter(Relation arbiterIndexRelation, IndexInfo *arbiterIndexInfo, Relation indexRelation, IndexInfo *indexInfo)
static List * adjust_partition_colnos(List *colnos, ResultRelInfo *leaf_part_rri)
#define INJECTION_POINT(name, arg)
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:232
@ ONCONFLICT_UPDATE
Definition nodes.h:430
@ CMD_MERGE
Definition nodes.h:279
@ CMD_DELETE
Definition nodes.h:278
@ CMD_UPDATE
Definition nodes.h:276
@ CMD_NOTHING
Definition nodes.h:282
#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 foreach_oid(var, lst)
Definition pg_list.h:471
#define linitial_oid(l)
Definition pg_list.h:180
#define foreach_int(var, lst)
Definition pg_list.h:470
#define INNER_VAR
Definition primnodes.h:242
#define RelationGetForm(relation)
Definition rel.h:508
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:700
int es_instrument
Definition execnodes.h:722
List * es_tupleTable
Definition execnodes.h:714
MergeAction * mas_action
Definition execnodes.h:451
ProjectionInfo * mas_proj
Definition execnodes.h:452
ExprState * mas_whenqual
Definition execnodes.h:454
ResultRelInfo * resultRelInfo
Definition execnodes.h:1410
Definition nodes.h:135
TupleTableSlot * oc_ProjSlot
Definition execnodes.h:436
ExprState * oc_WhereClause
Definition execnodes.h:438
ProjectionInfo * oc_ProjInfo
Definition execnodes.h:437
ExprContext * ps_ExprContext
Definition execnodes.h:1206
TupleTableSlot * ps_ResultTupleSlot
Definition execnodes.h:1205
OnConflictSetState * ri_onConflict
Definition execnodes.h:585
List * ri_onConflictArbiterIndexes
Definition execnodes.h:582
Index ri_RangeTableIndex
Definition execnodes.h:479
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition tableam.c:92

References 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(), fb(), foreach_int, foreach_oid, get_partition_ancestors(), InitResultRelInfo(), INJECTION_POINT, INNER_VAR, IsIndexCompatibleAsArbiter(), lappend(), lappend_int(), lappend_oid(), lfirst, lfirst_node, linitial, linitial_oid, 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_ProjInfo, OnConflictSetState::oc_ProjSlot, OnConflictSetState::oc_WhereClause, ONCONFLICT_NONE, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictCols, ModifyTable::onConflictSet, ModifyTable::onConflictWhere, ModifyTable::operation, PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, PlanState::ps_ResultTupleSlot, RelationData::rd_rel, RelationGetDescr, RelationGetForm, RelationGetRelid, 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 1146 of file execPartition.c.

1153{
1155 int rri_index;
1156
1158
1159 /*
1160 * Set up tuple conversion between root parent and the partition if the
1161 * two have different rowtypes. If conversion is indeed required, also
1162 * initialize a slot dedicated to storing this partition's converted
1163 * tuples. Various operations that are applied to tuples after routing,
1164 * such as checking constraints, will refer to this slot.
1165 */
1166 if (ExecGetRootToChildMap(partRelInfo, estate) != NULL)
1167 {
1168 Relation partrel = partRelInfo->ri_RelationDesc;
1169
1170 /*
1171 * This pins the partition's TupleDesc, which will be released at the
1172 * end of the command.
1173 */
1174 partRelInfo->ri_PartitionTupleSlot =
1175 table_slot_create(partrel, &estate->es_tupleTable);
1176 }
1177 else
1178 partRelInfo->ri_PartitionTupleSlot = NULL;
1179
1180 /*
1181 * If the partition is a foreign table, let the FDW init itself for
1182 * routing tuples to the partition.
1183 */
1184 if (partRelInfo->ri_FdwRoutine != NULL &&
1185 partRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL)
1186 partRelInfo->ri_FdwRoutine->BeginForeignInsert(mtstate, partRelInfo);
1187
1188 /*
1189 * Determine if the FDW supports batch insert and determine the batch size
1190 * (a FDW may support batching, but it may be disabled for the
1191 * server/table or for this particular query).
1192 *
1193 * If the FDW does not support batching, we set the batch size to 1.
1194 */
1195 if (partRelInfo->ri_FdwRoutine != NULL &&
1196 partRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
1197 partRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
1198 partRelInfo->ri_BatchSize =
1199 partRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(partRelInfo);
1200 else
1201 partRelInfo->ri_BatchSize = 1;
1202
1203 Assert(partRelInfo->ri_BatchSize >= 1);
1204
1205 partRelInfo->ri_CopyMultiInsertBuffer = NULL;
1206
1207 /*
1208 * Keep track of it in the PartitionTupleRouting->partitions array.
1209 */
1210 Assert(dispatch->indexes[partidx] == -1);
1211
1212 rri_index = proute->num_partitions++;
1213
1214 /* Allocate or enlarge the array, as needed */
1215 if (proute->num_partitions >= proute->max_partitions)
1216 {
1217 if (proute->max_partitions == 0)
1218 {
1219 proute->max_partitions = 8;
1221 proute->is_borrowed_rel = palloc_array(bool, proute->max_partitions);
1222 }
1223 else
1224 {
1225 proute->max_partitions *= 2;
1226 proute->partitions = (ResultRelInfo **)
1227 repalloc(proute->partitions, sizeof(ResultRelInfo *) *
1228 proute->max_partitions);
1229 proute->is_borrowed_rel = (bool *)
1230 repalloc(proute->is_borrowed_rel, sizeof(bool) *
1231 proute->max_partitions);
1232 }
1233 }
1234
1235 proute->partitions[rri_index] = partRelInfo;
1236 proute->is_borrowed_rel[rri_index] = is_borrowed_rel;
1237 dispatch->indexes[partidx] = rri_index;
1238
1240}

References Assert, EState::es_tupleTable, ExecGetRootToChildMap(), fb(), PartitionTupleRouting::is_borrowed_rel, PartitionTupleRouting::max_partitions, PartitionTupleRouting::memcxt, MemoryContextSwitchTo(), PartitionTupleRouting::num_partitions, palloc_array, PartitionTupleRouting::partitions, repalloc(), and table_slot_create().

Referenced by ExecFindPartition(), and ExecInitPartitionInfo().

◆ ExecSetupPartitionTupleRouting()

PartitionTupleRouting * ExecSetupPartitionTupleRouting ( EState estate,
Relation  rel 
)

Definition at line 220 of file execPartition.c.

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

References CurrentMemoryContext, ExecInitPartitionDispatchInfo(), fb(), PartitionTupleRouting::memcxt, palloc0_object, 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 2717 of file execPartition.c.

2722{
2724 int i;
2725
2726 /* Guard against stack overflow due to overly deep partition hierarchy. */
2728
2729 /*
2730 * Prune as appropriate, if we have pruning steps matching the current
2731 * execution context. Otherwise just include all partitions at this
2732 * level.
2733 */
2734 if (initial_prune && pprune->initial_pruning_steps)
2735 partset = get_matching_partitions(&pprune->initial_context,
2736 pprune->initial_pruning_steps);
2737 else if (!initial_prune && pprune->exec_pruning_steps)
2738 partset = get_matching_partitions(&pprune->exec_context,
2739 pprune->exec_pruning_steps);
2740 else
2741 partset = pprune->present_parts;
2742
2743 /* Translate partset into subplan indexes */
2744 i = -1;
2745 while ((i = bms_next_member(partset, i)) >= 0)
2746 {
2747 if (pprune->subplan_map[i] >= 0)
2748 {
2750 pprune->subplan_map[i]);
2751
2752 /*
2753 * Only report leaf partitions. Non-leaf partitions may appear
2754 * here when they use an unflattened Append or MergeAppend.
2755 */
2756 if (validsubplan_rtis && pprune->leafpart_rti_map[i])
2758 pprune->leafpart_rti_map[i]);
2759 }
2760 else
2761 {
2762 int partidx = pprune->subpart_map[i];
2763
2764 if (partidx >= 0)
2766 &prunedata->partrelprunedata[partidx],
2769 else
2770 {
2771 /*
2772 * We get here if the planner already pruned all the sub-
2773 * partitions for this partition. Silently ignore this
2774 * partition in this case. The end result is the same: we
2775 * would have pruned all partitions just the same, but we
2776 * don't have any pruning steps to execute to verify this.
2777 */
2778 }
2779 }
2780 }
2781}
Bitmapset * get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
Definition partprune.c:845
void check_stack_depth(void)
Definition stack_depth.c:95

References bms_add_member(), bms_next_member(), check_stack_depth(), fb(), find_matching_subplans_recurse(), get_matching_partitions(), and i.

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 1450 of file execPartition.c.

1455{
1457 int i;
1458
1459 if (pd->key->partexprs != NIL && pd->keystate == NIL)
1460 {
1461 /* Check caller has set up context correctly */
1462 Assert(estate != NULL &&
1463 GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
1464
1465 /* First time through, set up expression evaluation state */
1466 pd->keystate = ExecPrepareExprList(pd->key->partexprs, estate);
1467 }
1468
1470 for (i = 0; i < pd->key->partnatts; i++)
1471 {
1473 Datum datum;
1474 bool isNull;
1475
1476 if (keycol != 0)
1477 {
1478 /* Plain column; get the value directly from the heap tuple */
1479 datum = slot_getattr(slot, keycol, &isNull);
1480 }
1481 else
1482 {
1483 /* Expression; need to evaluate it */
1484 if (partexpr_item == NULL)
1485 elog(ERROR, "wrong number of partition key expressions");
1487 GetPerTupleExprContext(estate),
1488 &isNull);
1490 }
1491 values[i] = datum;
1492 isnull[i] = isNull;
1493 }
1494
1495 if (partexpr_item != NULL)
1496 elog(ERROR, "wrong number of partition key expressions");
1497}
List * ExecPrepareExprList(List *nodes, EState *estate)
Definition execExpr.c:839
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:436
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
AttrNumber * partattrs
Definition partcache.h:29
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition tuptable.h:398

References Assert, elog, ERROR, ExecEvalExprSwitchContext(), ExecPrepareExprList(), fb(), 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,
const Datum values,
const bool isnull 
)
static

Definition at line 1547 of file execPartition.c.

1548{
1549 int bound_offset = -1;
1550 int part_index = -1;
1551 PartitionKey key = pd->key;
1552 PartitionDesc partdesc = pd->partdesc;
1553 PartitionBoundInfo boundinfo = partdesc->boundinfo;
1554
1555 /*
1556 * In the switch statement below, when we perform a cached lookup for
1557 * RANGE and LIST partitioned tables, if we find that the last found
1558 * partition matches the 'values', we return the partition index right
1559 * away. We do this instead of breaking out of the switch as we don't
1560 * want to execute the code about the DEFAULT partition or do any updates
1561 * for any of the cache-related fields. That would be a waste of effort
1562 * as we already know it's not the DEFAULT partition and have no need to
1563 * increment the number of times we found the same partition any higher
1564 * than PARTITION_CACHED_FIND_THRESHOLD.
1565 */
1566
1567 /* Route as appropriate based on partitioning strategy. */
1568 switch (key->strategy)
1569 {
1571 {
1573
1574 /* hash partitioning is too cheap to bother caching */
1576 key->partsupfunc,
1577 key->partcollation,
1578 values, isnull);
1579
1580 /*
1581 * HASH partitions can't have a DEFAULT partition and we don't
1582 * do any caching work for them, so just return the part index
1583 */
1584 return boundinfo->indexes[rowHash % boundinfo->nindexes];
1585 }
1586
1588 if (isnull[0])
1589 {
1590 /* this is far too cheap to bother doing any caching */
1591 if (partition_bound_accepts_nulls(boundinfo))
1592 {
1593 /*
1594 * When there is a NULL partition we just return that
1595 * directly. We don't have a bound_offset so it's not
1596 * valid to drop into the code after the switch which
1597 * checks and updates the cache fields. We perhaps should
1598 * be invalidating the details of the last cached
1599 * partition but there's no real need to. Keeping those
1600 * fields set gives a chance at matching to the cached
1601 * partition on the next lookup.
1602 */
1603 return boundinfo->null_index;
1604 }
1605 }
1606 else
1607 {
1608 bool equal;
1609
1611 {
1613 Datum lastDatum = boundinfo->datums[last_datum_offset][0];
1614 int32 cmpval;
1615
1616 /* does the last found datum index match this datum? */
1617 cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
1618 key->partcollation[0],
1619 lastDatum,
1620 values[0]));
1621
1622 if (cmpval == 0)
1623 return boundinfo->indexes[last_datum_offset];
1624
1625 /* fall-through and do a manual lookup */
1626 }
1627
1629 key->partcollation,
1630 boundinfo,
1631 values[0], &equal);
1632 if (bound_offset >= 0 && equal)
1633 part_index = boundinfo->indexes[bound_offset];
1634 }
1635 break;
1636
1638 {
1639 bool equal = false,
1640 range_partkey_has_null = false;
1641 int i;
1642
1643 /*
1644 * No range includes NULL, so this will be accepted by the
1645 * default partition if there is one, and otherwise rejected.
1646 */
1647 for (i = 0; i < key->partnatts; i++)
1648 {
1649 if (isnull[i])
1650 {
1652 break;
1653 }
1654 }
1655
1656 /* NULLs belong in the DEFAULT partition */
1658 break;
1659
1661 {
1665 int32 cmpval;
1666
1667 /* check if the value is >= to the lower bound */
1668 cmpval = partition_rbound_datum_cmp(key->partsupfunc,
1669 key->partcollation,
1670 lastDatums,
1671 kind,
1672 values,
1673 key->partnatts);
1674
1675 /*
1676 * If it's equal to the lower bound then no need to check
1677 * the upper bound.
1678 */
1679 if (cmpval == 0)
1680 return boundinfo->indexes[last_datum_offset + 1];
1681
1682 if (cmpval < 0 && last_datum_offset + 1 < boundinfo->ndatums)
1683 {
1684 /* check if the value is below the upper bound */
1685 lastDatums = boundinfo->datums[last_datum_offset + 1];
1686 kind = boundinfo->kind[last_datum_offset + 1];
1687 cmpval = partition_rbound_datum_cmp(key->partsupfunc,
1688 key->partcollation,
1689 lastDatums,
1690 kind,
1691 values,
1692 key->partnatts);
1693
1694 if (cmpval > 0)
1695 return boundinfo->indexes[last_datum_offset + 1];
1696 }
1697 /* fall-through and do a manual lookup */
1698 }
1699
1701 key->partcollation,
1702 boundinfo,
1703 key->partnatts,
1704 values,
1705 &equal);
1706
1707 /*
1708 * The bound at bound_offset is less than or equal to the
1709 * tuple value, so the bound at offset+1 is the upper bound of
1710 * the partition we're looking for, if there actually exists
1711 * one.
1712 */
1713 part_index = boundinfo->indexes[bound_offset + 1];
1714 }
1715 break;
1716
1717 default:
1718 elog(ERROR, "unexpected partition strategy: %d",
1719 (int) key->strategy);
1720 }
1721
1722 /*
1723 * part_index < 0 means we failed to find a partition of this parent. Use
1724 * the default partition, if there is one.
1725 */
1726 if (part_index < 0)
1727 {
1728 /*
1729 * No need to reset the cache fields here. The next set of values
1730 * might end up belonging to the cached partition, so leaving the
1731 * cache alone improves the chances of a cache hit on the next lookup.
1732 */
1733 return boundinfo->default_index;
1734 }
1735
1736 /* we should only make it here when the code above set bound_offset */
1737 Assert(bound_offset >= 0);
1738
1739 /*
1740 * Attend to the cache fields. If the bound_offset matches the last
1741 * cached bound offset then we've found the same partition as last time,
1742 * so bump the count by one. If all goes well, we'll eventually reach
1743 * PARTITION_CACHED_FIND_THRESHOLD and try the cache path next time
1744 * around. Otherwise, we'll reset the cache count back to 1 to mark that
1745 * we've found this partition for the first time.
1746 */
1747 if (bound_offset == partdesc->last_found_datum_index)
1748 partdesc->last_found_count++;
1749 else
1750 {
1751 partdesc->last_found_count = 1;
1754 }
1755
1756 return part_index;
1757}
int32_t int32
Definition c.h:542
uint64_t uint64
Definition c.h:547
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:1150
@ PARTITION_STRATEGY_HASH
Definition parsenodes.h:903
@ PARTITION_STRATEGY_LIST
Definition parsenodes.h:901
@ PARTITION_STRATEGY_RANGE
Definition parsenodes.h:902
PartitionRangeDatumKind
Definition parsenodes.h:952
int32 partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, const Datum *rb_datums, PartitionRangeDatumKind *rb_kind, const Datum *tuple_datums, int n_tuple_datums)
uint64 compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc, const Oid *partcollation, const Datum *values, const bool *isnull)
int partition_range_datum_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, const Datum *values, bool *is_equal)
int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
#define partition_bound_accepts_nulls(bi)
Definition partbounds.h:98
static int32 DatumGetInt32(Datum X)
Definition postgres.h:212
PartitionRangeDatumKind ** kind
Definition partbounds.h:84
int last_found_datum_index
Definition partdesc.h:46
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, fb(), FunctionCall2Coll(), i, PartitionBoundInfoData::indexes, PartitionDispatchData::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 2487 of file execPartition.c.

2491{
2492 EState *estate;
2495 int i;
2496 int newidx;
2497 bool fix_subplan_map = false;
2498
2499 Assert(prunestate->do_exec_prune);
2501 estate = parent_plan->state;
2502
2503 /*
2504 * No need to fix subplans maps if initial pruning didn't eliminate any
2505 * subplans.
2506 */
2508 {
2509 fix_subplan_map = true;
2510
2511 /*
2512 * First we must build a temporary array which maps old subplan
2513 * indexes to new ones. For convenience of initialization, we use
2514 * 1-based indexes in this array and leave pruned items as 0.
2515 */
2517 newidx = 1;
2518 i = -1;
2519 while ((i = bms_next_member(initially_valid_subplans, i)) >= 0)
2520 {
2523 }
2524 }
2525
2526 /*
2527 * Now we can update each PartitionedRelPruneInfo's subplan_map with new
2528 * subplan indexes. We must also recompute its present_parts bitmap.
2529 */
2530 for (i = 0; i < prunestate->num_partprunedata; i++)
2531 {
2532 PartitionPruningData *prunedata = prunestate->partprunedata[i];
2533 int j;
2534
2535 /*
2536 * Within each hierarchy, we perform this loop in back-to-front order
2537 * so that we determine present_parts for the lowest-level partitioned
2538 * tables first. This way we can tell whether a sub-partitioned
2539 * table's partitions were entirely pruned so we can exclude it from
2540 * the current level's present_parts.
2541 */
2542 for (j = prunedata->num_partrelprunedata - 1; j >= 0; j--)
2543 {
2544 PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
2545 int nparts = pprune->nparts;
2546 int k;
2547
2548 /* Initialize PartitionPruneContext for exec pruning, if needed. */
2549 if (pprune->exec_pruning_steps != NIL)
2550 {
2552 PartitionDesc partdesc;
2553
2554 /*
2555 * See the comment in CreatePartitionPruneState() regarding
2556 * the usage of partdesc and partkey.
2557 */
2560 pprune->partrel);
2561
2562 InitPartitionPruneContext(&pprune->exec_context,
2563 pprune->exec_pruning_steps,
2564 partdesc, partkey, parent_plan,
2565 prunestate->econtext);
2566 }
2567
2568 if (!fix_subplan_map)
2569 continue;
2570
2571 /* We just rebuild present_parts from scratch */
2572 bms_free(pprune->present_parts);
2573 pprune->present_parts = NULL;
2574
2575 for (k = 0; k < nparts; k++)
2576 {
2577 int oldidx = pprune->subplan_map[k];
2578 int subidx;
2579
2580 /*
2581 * If this partition existed as a subplan then change the old
2582 * subplan index to the new subplan index. The new index may
2583 * become -1 if the partition was pruned above, or it may just
2584 * come earlier in the subplan list due to some subplans being
2585 * removed earlier in the list. If it's a subpartition, add
2586 * it to present_parts unless it's entirely pruned.
2587 */
2588 if (oldidx >= 0)
2589 {
2591 pprune->subplan_map[k] = new_subplan_indexes[oldidx] - 1;
2592
2593 if (new_subplan_indexes[oldidx] > 0)
2594 pprune->present_parts =
2595 bms_add_member(pprune->present_parts, k);
2596 }
2597 else if ((subidx = pprune->subpart_map[k]) >= 0)
2598 {
2600
2601 subprune = &prunedata->partrelprunedata[subidx];
2602
2603 if (!bms_is_empty(subprune->present_parts))
2605 bms_add_member(pprune->present_parts, k);
2606 }
2607 }
2608 }
2609 }
2610
2611 /*
2612 * If we fixed subplan maps, we must also recompute the other_subplans
2613 * set, since indexes in it may change.
2614 */
2615 if (fix_subplan_map)
2616 {
2618 i = -1;
2619 while ((i = bms_next_member(prunestate->other_subplans, i)) >= 0)
2621 new_subplan_indexes[i] - 1);
2622
2623 bms_free(prunestate->other_subplans);
2624 prunestate->other_subplans = new_other_subplans;
2625
2627 }
2628}
void bms_free(Bitmapset *a)
Definition bitmapset.c:239
int bms_num_members(const Bitmapset *a)
Definition bitmapset.c:750
#define bms_is_empty(a)
Definition bitmapset.h:118
#define palloc0_array(type, count)
Definition fe_memutils.h:77
void pfree(void *pointer)
Definition mcxt.c:1616

References Assert, bms_add_member(), bms_free(), bms_is_empty, bms_next_member(), bms_num_members(), EState::es_partition_directory, fb(), i, InitPartitionPruneContext(), j, NIL, PartitionedRelPruningData::nparts, palloc0_array, PartitionDirectoryLookup(), pfree(), PartitionedRelPruningData::present_parts, and RelationGetPartitionKey().

Referenced by ExecInitPartitionExecPruning().

◆ InitPartitionPruneContext()

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

Definition at line 2387 of file execPartition.c.

2393{
2394 int n_steps;
2395 int partnatts;
2396 ListCell *lc;
2397
2399
2400 context->strategy = partkey->strategy;
2401 context->partnatts = partnatts = partkey->partnatts;
2402 context->nparts = partdesc->nparts;
2403 context->boundinfo = partdesc->boundinfo;
2404 context->partcollation = partkey->partcollation;
2405 context->partsupfunc = partkey->partsupfunc;
2406
2407 /* We'll look up type-specific support functions as needed */
2408 context->stepcmpfuncs = palloc0_array(FmgrInfo, n_steps * partnatts);
2409
2411 context->planstate = planstate;
2412 context->exprcontext = econtext;
2413
2414 /* Initialize expression state for each expression we need */
2415 context->exprstates = palloc0_array(ExprState *, n_steps * partnatts);
2416 foreach(lc, pruning_steps)
2417 {
2419 ListCell *lc2 = list_head(step->exprs);
2420 int keyno;
2421
2422 /* not needed for other step kinds */
2423 if (!IsA(step, PartitionPruneStepOp))
2424 continue;
2425
2426 Assert(list_length(step->exprs) <= partnatts);
2427
2428 for (keyno = 0; keyno < partnatts; keyno++)
2429 {
2430 if (bms_is_member(keyno, step->nullkeys))
2431 continue;
2432
2433 if (lc2 != NULL)
2434 {
2435 Expr *expr = lfirst(lc2);
2436
2437 /* not needed for Consts */
2438 if (!IsA(expr, Const))
2439 {
2440 int stateidx = PruneCxtStateIdx(partnatts,
2441 step->step.step_id,
2442 keyno);
2443
2444 /*
2445 * When planstate is NULL, pruning_steps is known not to
2446 * contain any expressions that depend on the parent plan.
2447 * Information of any available EXTERN parameters must be
2448 * passed explicitly in that case, which the caller must
2449 * have made available via econtext.
2450 */
2451 if (planstate == NULL)
2452 context->exprstates[stateidx] =
2454 econtext->ecxt_param_list_info);
2455 else
2456 context->exprstates[stateidx] =
2457 ExecInitExpr(expr, context->planstate);
2458 }
2459 lc2 = lnext(step->exprs, lc2);
2460 }
2461 }
2462 }
2463}
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:287
FmgrInfo * partsupfunc
Definition partprune.h:56
ExprContext * exprcontext
Definition partprune.h:60
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:1758
Bitmapset * nullkeys
Definition plannodes.h:1763

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

Referenced by CreatePartitionPruneState(), and InitExecPartitionPruneContexts().

◆ IsIndexCompatibleAsArbiter()

static bool IsIndexCompatibleAsArbiter ( Relation  arbiterIndexRelation,
IndexInfo arbiterIndexInfo,
Relation  indexRelation,
IndexInfo indexInfo 
)
static

Definition at line 503 of file execPartition.c.

507{
508 Assert(arbiterIndexRelation->rd_index->indrelid == indexRelation->rd_index->indrelid);
509
510 /* must match whether they're unique */
511 if (arbiterIndexInfo->ii_Unique != indexInfo->ii_Unique)
512 return false;
513
514 /* No support currently for comparing exclusion indexes. */
515 if (arbiterIndexInfo->ii_ExclusionOps != NULL ||
516 indexInfo->ii_ExclusionOps != NULL)
517 return false;
518
519 /* the "nulls not distinct" criterion must match */
520 if (arbiterIndexInfo->ii_NullsNotDistinct !=
521 indexInfo->ii_NullsNotDistinct)
522 return false;
523
524 /* number of key attributes must match */
525 if (arbiterIndexInfo->ii_NumIndexKeyAttrs !=
526 indexInfo->ii_NumIndexKeyAttrs)
527 return false;
528
529 for (int i = 0; i < arbiterIndexInfo->ii_NumIndexKeyAttrs; i++)
530 {
531 if (arbiterIndexRelation->rd_indcollation[i] !=
532 indexRelation->rd_indcollation[i])
533 return false;
534
535 if (arbiterIndexRelation->rd_opfamily[i] !=
536 indexRelation->rd_opfamily[i])
537 return false;
538
539 if (arbiterIndexRelation->rd_index->indkey.values[i] !=
540 indexRelation->rd_index->indkey.values[i])
541 return false;
542 }
543
545 RelationGetIndexExpressions(indexRelation)) != NIL)
546 return false;
547
549 RelationGetIndexPredicate(indexRelation)) != NIL)
550 return false;
551 return true;
552}
List * list_difference(const List *list1, const List *list2)
Definition list.c:1237
List * RelationGetIndexPredicate(Relation relation)
Definition relcache.c:5205
List * RelationGetIndexExpressions(Relation relation)
Definition relcache.c:5092
bool ii_Unique
Definition execnodes.h:202
Oid * ii_ExclusionOps
Definition execnodes.h:190
bool ii_NullsNotDistinct
Definition execnodes.h:204
int ii_NumIndexKeyAttrs
Definition execnodes.h:171
Form_pg_index rd_index
Definition rel.h:192
Oid * rd_opfamily
Definition rel.h:207
Oid * rd_indcollation
Definition rel.h:217

References Assert, fb(), i, IndexInfo::ii_ExclusionOps, IndexInfo::ii_NullsNotDistinct, IndexInfo::ii_NumIndexKeyAttrs, IndexInfo::ii_Unique, list_difference(), NIL, RelationData::rd_indcollation, RelationData::rd_index, RelationData::rd_opfamily, RelationGetIndexExpressions(), and RelationGetIndexPredicate().

Referenced by ExecInitPartitionInfo().