PostgreSQL Source Code  git master
execPartition.c File Reference
#include "postgres.h"
#include "access/table.h"
#include "access/tableam.h"
#include "catalog/partition.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_type.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 "nodes/makefuncs.h"
#include "partitioning/partbounds.h"
#include "partitioning/partdesc.h"
#include "partitioning/partprune.h"
#include "rewrite/rewriteManip.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
#include "utils/partcache.h"
#include "utils/rls.h"
#include "utils/ruleutils.h"
Include dependency graph for execPartition.c:

Go to the source code of this file.

Data Structures

struct  PartitionTupleRouting
 
struct  PartitionDispatchData
 

Macros

#define PARTITION_CACHED_FIND_THRESHOLD   16
 

Typedefs

typedef struct PartitionDispatchData PartitionDispatchData
 

Functions

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

Macro Definition Documentation

◆ PARTITION_CACHED_FIND_THRESHOLD

#define PARTITION_CACHED_FIND_THRESHOLD   16

Definition at line 1338 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 1689 of file execPartition.c.

1690 {
1691  TupleConversionMap *map = ExecGetChildToRootMap(leaf_part_rri);
1692 
1693  Assert(map != NULL);
1694 
1695  return adjust_partition_colnos_using_map(colnos, map->attrMap);
1696 }
static List * adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap)
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1211
Assert(fmt[strlen(fmt) - 1] !='\n')
AttrMap * attrMap
Definition: tupconvert.h:28

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

Referenced by ExecInitPartitionInfo().

◆ adjust_partition_colnos_using_map()

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

Definition at line 1706 of file execPartition.c.

1707 {
1708  List *new_colnos = NIL;
1709  ListCell *lc;
1710 
1711  Assert(attrMap != NULL); /* else we shouldn't be here */
1712 
1713  foreach(lc, colnos)
1714  {
1715  AttrNumber parentattrno = lfirst_int(lc);
1716 
1717  if (parentattrno <= 0 ||
1718  parentattrno > attrMap->maplen ||
1719  attrMap->attnums[parentattrno - 1] == 0)
1720  elog(ERROR, "unexpected attno %d in target column list",
1721  parentattrno);
1722  new_colnos = lappend_int(new_colnos,
1723  attrMap->attnums[parentattrno - 1]);
1724  }
1725 
1726  return new_colnos;
1727 }
int16 AttrNumber
Definition: attnum.h:21
#define ERROR
Definition: elog.h:39
List * lappend_int(List *list, int datum)
Definition: list.c:356
#define NIL
Definition: pg_list.h:68
#define lfirst_int(lc)
Definition: pg_list.h:173
int maplen
Definition: attmap.h:37
AttrNumber * attnums
Definition: attmap.h:36
Definition: pg_list.h:54

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

Referenced by adjust_partition_colnos(), and ExecInitPartitionInfo().

◆ CreatePartitionPruneState()

static PartitionPruneState * CreatePartitionPruneState ( PlanState planstate,
PartitionPruneInfo pruneinfo 
)
static

Definition at line 1857 of file execPartition.c.

1858 {
1859  EState *estate = planstate->state;
1860  PartitionPruneState *prunestate;
1861  int n_part_hierarchies;
1862  ListCell *lc;
1863  int i;
1864  ExprContext *econtext = planstate->ps_ExprContext;
1865 
1866  /* For data reading, executor always omits detached partitions */
1867  if (estate->es_partition_directory == NULL)
1868  estate->es_partition_directory =
1869  CreatePartitionDirectory(estate->es_query_cxt, false);
1870 
1871  n_part_hierarchies = list_length(pruneinfo->prune_infos);
1872  Assert(n_part_hierarchies > 0);
1873 
1874  /*
1875  * Allocate the data structure
1876  */
1877  prunestate = (PartitionPruneState *)
1878  palloc(offsetof(PartitionPruneState, partprunedata) +
1879  sizeof(PartitionPruningData *) * n_part_hierarchies);
1880 
1881  prunestate->execparamids = NULL;
1882  /* other_subplans can change at runtime, so we need our own copy */
1883  prunestate->other_subplans = bms_copy(pruneinfo->other_subplans);
1884  prunestate->do_initial_prune = false; /* may be set below */
1885  prunestate->do_exec_prune = false; /* may be set below */
1886  prunestate->num_partprunedata = n_part_hierarchies;
1887 
1888  /*
1889  * Create a short-term memory context which we'll use when making calls to
1890  * the partition pruning functions. This avoids possible memory leaks,
1891  * since the pruning functions call comparison functions that aren't under
1892  * our control.
1893  */
1894  prunestate->prune_context =
1896  "Partition Prune",
1898 
1899  i = 0;
1900  foreach(lc, pruneinfo->prune_infos)
1901  {
1902  List *partrelpruneinfos = lfirst_node(List, lc);
1903  int npartrelpruneinfos = list_length(partrelpruneinfos);
1904  PartitionPruningData *prunedata;
1905  ListCell *lc2;
1906  int j;
1907 
1908  prunedata = (PartitionPruningData *)
1909  palloc(offsetof(PartitionPruningData, partrelprunedata) +
1910  npartrelpruneinfos * sizeof(PartitionedRelPruningData));
1911  prunestate->partprunedata[i] = prunedata;
1912  prunedata->num_partrelprunedata = npartrelpruneinfos;
1913 
1914  j = 0;
1915  foreach(lc2, partrelpruneinfos)
1916  {
1918  PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
1919  Relation partrel;
1920  PartitionDesc partdesc;
1921  PartitionKey partkey;
1922 
1923  /*
1924  * We can rely on the copies of the partitioned table's partition
1925  * key and partition descriptor appearing in its relcache entry,
1926  * because that entry will be held open and locked for the
1927  * duration of this executor run.
1928  */
1929  partrel = ExecGetRangeTableRelation(estate, pinfo->rtindex);
1930  partkey = RelationGetPartitionKey(partrel);
1932  partrel);
1933 
1934  /*
1935  * Initialize the subplan_map and subpart_map.
1936  *
1937  * Because we request detached partitions to be included, and
1938  * detaching waits for old transactions, it is safe to assume that
1939  * no partitions have disappeared since this query was planned.
1940  *
1941  * However, new partitions may have been added.
1942  */
1943  Assert(partdesc->nparts >= pinfo->nparts);
1944  pprune->nparts = partdesc->nparts;
1945  pprune->subplan_map = palloc(sizeof(int) * partdesc->nparts);
1946  if (partdesc->nparts == pinfo->nparts)
1947  {
1948  /*
1949  * There are no new partitions, so this is simple. We can
1950  * simply point to the subpart_map from the plan, but we must
1951  * copy the subplan_map since we may change it later.
1952  */
1953  pprune->subpart_map = pinfo->subpart_map;
1954  memcpy(pprune->subplan_map, pinfo->subplan_map,
1955  sizeof(int) * pinfo->nparts);
1956 
1957  /*
1958  * Double-check that the list of unpruned relations has not
1959  * changed. (Pruned partitions are not in relid_map[].)
1960  */
1961 #ifdef USE_ASSERT_CHECKING
1962  for (int k = 0; k < pinfo->nparts; k++)
1963  {
1964  Assert(partdesc->oids[k] == pinfo->relid_map[k] ||
1965  pinfo->subplan_map[k] == -1);
1966  }
1967 #endif
1968  }
1969  else
1970  {
1971  int pd_idx = 0;
1972  int pp_idx;
1973 
1974  /*
1975  * Some new partitions have appeared since plan time, and
1976  * those are reflected in our PartitionDesc but were not
1977  * present in the one used to construct subplan_map and
1978  * subpart_map. So we must construct new and longer arrays
1979  * where the partitions that were originally present map to
1980  * the same sub-structures, and any added partitions map to
1981  * -1, as if the new partitions had been pruned.
1982  *
1983  * Note: pinfo->relid_map[] may contain InvalidOid entries for
1984  * partitions pruned by the planner. We cannot tell exactly
1985  * which of the partdesc entries these correspond to, but we
1986  * don't have to; just skip over them. The non-pruned
1987  * relid_map entries, however, had better be a subset of the
1988  * partdesc entries and in the same order.
1989  */
1990  pprune->subpart_map = palloc(sizeof(int) * partdesc->nparts);
1991  for (pp_idx = 0; pp_idx < partdesc->nparts; pp_idx++)
1992  {
1993  /* Skip any InvalidOid relid_map entries */
1994  while (pd_idx < pinfo->nparts &&
1995  !OidIsValid(pinfo->relid_map[pd_idx]))
1996  pd_idx++;
1997 
1998  if (pd_idx < pinfo->nparts &&
1999  pinfo->relid_map[pd_idx] == partdesc->oids[pp_idx])
2000  {
2001  /* match... */
2002  pprune->subplan_map[pp_idx] =
2003  pinfo->subplan_map[pd_idx];
2004  pprune->subpart_map[pp_idx] =
2005  pinfo->subpart_map[pd_idx];
2006  pd_idx++;
2007  }
2008  else
2009  {
2010  /* this partdesc entry is not in the plan */
2011  pprune->subplan_map[pp_idx] = -1;
2012  pprune->subpart_map[pp_idx] = -1;
2013  }
2014  }
2015 
2016  /*
2017  * It might seem that we need to skip any trailing InvalidOid
2018  * entries in pinfo->relid_map before checking that we scanned
2019  * all of the relid_map. But we will have skipped them above,
2020  * because they must correspond to some partdesc->oids
2021  * entries; we just couldn't tell which.
2022  */
2023  if (pd_idx != pinfo->nparts)
2024  elog(ERROR, "could not match partition child tables to plan elements");
2025  }
2026 
2027  /* present_parts is also subject to later modification */
2028  pprune->present_parts = bms_copy(pinfo->present_parts);
2029 
2030  /*
2031  * Initialize pruning contexts as needed. Note that we must skip
2032  * execution-time partition pruning in EXPLAIN (GENERIC_PLAN),
2033  * since parameter values may be missing.
2034  */
2036  if (pinfo->initial_pruning_steps &&
2038  {
2040  pinfo->initial_pruning_steps,
2041  partdesc, partkey, planstate,
2042  econtext);
2043  /* Record whether initial pruning is needed at any level */
2044  prunestate->do_initial_prune = true;
2045  }
2046  pprune->exec_pruning_steps = pinfo->exec_pruning_steps;
2047  if (pinfo->exec_pruning_steps &&
2049  {
2051  pinfo->exec_pruning_steps,
2052  partdesc, partkey, planstate,
2053  econtext);
2054  /* Record whether exec pruning is needed at any level */
2055  prunestate->do_exec_prune = true;
2056  }
2057 
2058  /*
2059  * Accumulate the IDs of all PARAM_EXEC Params affecting the
2060  * partitioning decisions at this plan node.
2061  */
2062  prunestate->execparamids = bms_add_members(prunestate->execparamids,
2063  pinfo->execparamids);
2064 
2065  j++;
2066  }
2067  i++;
2068  }
2069 
2070  return prunestate;
2071 }
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:835
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:80
#define OidIsValid(objectId)
Definition: c.h:764
static void InitPartitionPruneContext(PartitionPruneContext *context, List *pruning_steps, PartitionDesc partdesc, PartitionKey partkey, PlanState *planstate, ExprContext *econtext)
struct PartitionedRelPruningData PartitionedRelPruningData
Relation ExecGetRangeTableRelation(EState *estate, Index rti)
Definition: execUtils.c:767
#define EXEC_FLAG_EXPLAIN_GENERIC
Definition: executor.h:66
int j
Definition: isn.c:74
int i
Definition: isn.c:73
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
void * palloc(Size size)
Definition: mcxt.c:1226
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
PartitionDirectory CreatePartitionDirectory(MemoryContext mcxt, bool omit_detached)
Definition: partdesc.c:383
PartitionDesc PartitionDirectoryLookup(PartitionDirectory pdir, Relation rel)
Definition: partdesc.c:416
#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:668
MemoryContext es_query_cxt
Definition: execnodes.h:659
PartitionDirectory es_partition_directory
Definition: execnodes.h:641
struct EState * ecxt_estate
Definition: execnodes.h:285
Bitmapset * other_subplans
Definition: plannodes.h:1427
PartitionPruningData * partprunedata[FLEXIBLE_ARRAY_MEMBER]
Bitmapset * execparamids
Bitmapset * other_subplans
MemoryContext prune_context
PartitionedRelPruningData partrelprunedata[FLEXIBLE_ARRAY_MEMBER]
Definition: execPartition.h:81
Bitmapset * present_parts
Definition: plannodes.h:1457
Bitmapset * execparamids
Definition: plannodes.h:1481
PartitionPruneContext exec_context
Definition: execPartition.h:68
PartitionPruneContext initial_context
Definition: execPartition.h:67
EState * state
Definition: execnodes.h:1039
ExprContext * ps_ExprContext
Definition: execnodes.h:1076

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

Referenced by ExecInitPartitionPruning().

◆ ExecBuildSlotPartitionKeyDescription()

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

Definition at line 1601 of file execPartition.c.

1605 {
1608  int partnatts = get_partition_natts(key);
1609  int i;
1610  Oid relid = RelationGetRelid(rel);
1611  AclResult aclresult;
1612 
1613  if (check_enable_rls(relid, InvalidOid, true) == RLS_ENABLED)
1614  return NULL;
1615 
1616  /* If the user has table-level access, just go build the description. */
1617  aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT);
1618  if (aclresult != ACLCHECK_OK)
1619  {
1620  /*
1621  * Step through the columns of the partition key and make sure the
1622  * user has SELECT rights on all of them.
1623  */
1624  for (i = 0; i < partnatts; i++)
1625  {
1627 
1628  /*
1629  * If this partition key column is an expression, we return no
1630  * detail rather than try to figure out what column(s) the
1631  * expression includes and if the user has SELECT rights on them.
1632  */
1633  if (attnum == InvalidAttrNumber ||
1635  ACL_SELECT) != ACLCHECK_OK)
1636  return NULL;
1637  }
1638  }
1639 
1640  initStringInfo(&buf);
1641  appendStringInfo(&buf, "(%s) = (",
1642  pg_get_partkeydef_columns(relid, true));
1643 
1644  for (i = 0; i < partnatts; i++)
1645  {
1646  char *val;
1647  int vallen;
1648 
1649  if (isnull[i])
1650  val = "null";
1651  else
1652  {
1653  Oid foutoid;
1654  bool typisvarlena;
1655 
1657  &foutoid, &typisvarlena);
1658  val = OidOutputFunctionCall(foutoid, values[i]);
1659  }
1660 
1661  if (i > 0)
1662  appendStringInfoString(&buf, ", ");
1663 
1664  /* truncate if needed */
1665  vallen = strlen(val);
1666  if (vallen <= maxfieldlen)
1667  appendBinaryStringInfo(&buf, val, vallen);
1668  else
1669  {
1670  vallen = pg_mbcliplen(val, vallen, maxfieldlen);
1671  appendBinaryStringInfo(&buf, val, vallen);
1672  appendStringInfoString(&buf, "...");
1673  }
1674  }
1675 
1676  appendStringInfoChar(&buf, ')');
1677 
1678  return buf.data;
1679 }
AclResult
Definition: acl.h:181
@ ACLCHECK_OK
Definition: acl.h:182
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3779
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:3908
#define InvalidAttrNumber
Definition: attnum.h:23
static Datum values[MAXATTR]
Definition: bootstrap.c:156
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1746
long val
Definition: informix.c:664
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2889
int pg_mbcliplen(const char *mbstr, int len, int limit)
Definition: mbutils.c:1084
Oid GetUserId(void)
Definition: miscinit.c:509
#define ACL_SELECT
Definition: parsenodes.h:84
static int16 get_partition_col_attnum(PartitionKey key, int col)
Definition: partcache.h:80
static int get_partition_natts(PartitionKey key)
Definition: partcache.h:65
static Oid get_partition_col_typid(PartitionKey key, int col)
Definition: partcache.h:86
int16 attnum
Definition: pg_attribute.h:74
static char * buf
Definition: pg_test_fsync.c:67
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetRelid(relation)
Definition: rel.h:504
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:1895
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
void appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
Definition: stringinfo.c:227
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:176
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:188
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59

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

Referenced by ExecFindPartition().

◆ ExecCleanupTupleRouting()

void ExecCleanupTupleRouting ( ModifyTableState mtstate,
PartitionTupleRouting proute 
)

Definition at line 1223 of file execPartition.c.

1225 {
1226  int i;
1227 
1228  /*
1229  * Remember, proute->partition_dispatch_info[0] corresponds to the root
1230  * partitioned table, which we must not try to close, because it is the
1231  * main target table of the query that will be closed by callers such as
1232  * ExecEndPlan() or DoCopy(). Also, tupslot is NULL for the root
1233  * partitioned table.
1234  */
1235  for (i = 1; i < proute->num_dispatch; i++)
1236  {
1238 
1239  table_close(pd->reldesc, NoLock);
1240 
1241  if (pd->tupslot)
1243  }
1244 
1245  for (i = 0; i < proute->num_partitions; i++)
1246  {
1247  ResultRelInfo *resultRelInfo = proute->partitions[i];
1248 
1249  /* Allow any FDWs to shut down */
1250  if (resultRelInfo->ri_FdwRoutine != NULL &&
1251  resultRelInfo->ri_FdwRoutine->EndForeignInsert != NULL)
1252  resultRelInfo->ri_FdwRoutine->EndForeignInsert(mtstate->ps.state,
1253  resultRelInfo);
1254 
1255  /*
1256  * Close it if it's not one of the result relations borrowed from the
1257  * owning ModifyTableState; those will be closed by ExecEndPlan().
1258  */
1259  if (proute->is_borrowed_rel[i])
1260  continue;
1261 
1262  ExecCloseIndices(resultRelInfo);
1263  table_close(resultRelInfo->ri_RelationDesc, NoLock);
1264  }
1265 }
void ExecCloseIndices(ResultRelInfo *resultRelInfo)
Definition: execIndexing.c:231
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1255
#define NoLock
Definition: lockdefs.h:34
EndForeignInsert_function EndForeignInsert
Definition: fdwapi.h:239
PlanState ps
Definition: execnodes.h:1275
TupleTableSlot * tupslot
PartitionDispatch * partition_dispatch_info
Definition: execPartition.c:97
ResultRelInfo ** partitions
Relation ri_RelationDesc
Definition: execnodes.h:450
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:497
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126

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

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

◆ ExecFindMatchingSubPlans()

Bitmapset* ExecFindMatchingSubPlans ( PartitionPruneState prunestate,
bool  initial_prune 
)

Definition at line 2278 of file execPartition.c.

2280 {
2281  Bitmapset *result = NULL;
2282  MemoryContext oldcontext;
2283  int i;
2284 
2285  /*
2286  * Either we're here on the initial prune done during pruning
2287  * initialization, or we're at a point where PARAM_EXEC Params can be
2288  * evaluated *and* there are steps in which to do so.
2289  */
2290  Assert(initial_prune || prunestate->do_exec_prune);
2291 
2292  /*
2293  * Switch to a temp context to avoid leaking memory in the executor's
2294  * query-lifespan memory context.
2295  */
2296  oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
2297 
2298  /*
2299  * For each hierarchy, do the pruning tests, and add nondeletable
2300  * subplans' indexes to "result".
2301  */
2302  for (i = 0; i < prunestate->num_partprunedata; i++)
2303  {
2304  PartitionPruningData *prunedata = prunestate->partprunedata[i];
2305  PartitionedRelPruningData *pprune;
2306 
2307  /*
2308  * We pass the zeroth item, belonging to the root table of the
2309  * hierarchy, and find_matching_subplans_recurse() takes care of
2310  * recursing to other (lower-level) parents as needed.
2311  */
2312  pprune = &prunedata->partrelprunedata[0];
2313  find_matching_subplans_recurse(prunedata, pprune, initial_prune,
2314  &result);
2315 
2316  /* Expression eval may have used space in ExprContext too */
2317  if (pprune->exec_pruning_steps)
2319  }
2320 
2321  /* Add in any subplans that partition pruning didn't account for */
2322  result = bms_add_members(result, prunestate->other_subplans);
2323 
2324  MemoryContextSwitchTo(oldcontext);
2325 
2326  /* Copy result out of the temp context before we reset it */
2327  result = bms_copy(result);
2328 
2329  MemoryContextReset(prunestate->prune_context);
2330 
2331  return result;
2332 }
static void find_matching_subplans_recurse(PartitionPruningData *prunedata, PartitionedRelPruningData *pprune, bool initial_prune, Bitmapset **validsubplans)
#define ResetExprContext(econtext)
Definition: executor.h:543
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:330
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
ExprContext * exprcontext
Definition: partprune.h:60

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

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

◆ ExecFindPartition()

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

Definition at line 265 of file execPartition.c.

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

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

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

◆ ExecInitPartitionDispatchInfo()

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

Definition at line 1084 of file execPartition.c.

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

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

Referenced by ExecFindPartition(), and ExecSetupPartitionTupleRouting().

◆ ExecInitPartitionInfo()

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

Definition at line 498 of file execPartition.c.

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

References generate_unaccent_rules::action, adjust_partition_colnos(), adjust_partition_colnos_using_map(), Assert(), build_attrmap_by_name(), castNode, CheckValidResultRel(), CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_UPDATE, copyObject, elog(), ERROR, EState::es_instrument, EState::es_query_cxt, EState::es_tuple_routing_result_relations, EState::es_tupleTable, ExecBuildProjectionInfo(), ExecBuildUpdateProjection(), ExecGetRootToChildMap(), ExecInitMergeTupleSlots(), ExecInitQual(), ExecInitRoutingInfo(), ExecOpenIndices(), get_partition_ancestors(), InitResultRelInfo(), INNER_VAR, lappend(), lappend_oid(), lfirst, lfirst_node, lfirst_oid, linitial, sort-test::list, 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, NIL, OnConflictSetState::oc_Existing, OnConflictSetState::oc_ProjInfo, OnConflictSetState::oc_ProjSlot, OnConflictSetState::oc_WhereClause, PartitionDescData::oids, ONCONFLICT_NONE, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictCols, ModifyTable::onConflictSet, ModifyTable::onConflictWhere, ModifyTable::operation, PartitionDispatchData::partdesc, PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, PlanState::ps_ResultTupleSlot, WithCheckOption::qual, RelationGetDescr, RelationGetForm, RelationGetIndexList(), ModifyTable::resultRelations, ModifyTableState::resultRelInfo, ModifyTable::returningLists, ResultRelInfo::ri_onConflict, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, RowExclusiveLock, PlanState::state, table_open(), table_slot_create(), unlikely, and ModifyTable::withCheckOptionLists.

Referenced by ExecFindPartition().

◆ ExecInitPartitionPruning()

PartitionPruneState* ExecInitPartitionPruning ( PlanState planstate,
int  n_total_subplans,
PartitionPruneInfo pruneinfo,
Bitmapset **  initially_valid_subplans 
)

Definition at line 1791 of file execPartition.c.

1795 {
1796  PartitionPruneState *prunestate;
1797  EState *estate = planstate->state;
1798 
1799  /* We may need an expression context to evaluate partition exprs */
1800  ExecAssignExprContext(estate, planstate);
1801 
1802  /* Create the working data structure for pruning */
1803  prunestate = CreatePartitionPruneState(planstate, pruneinfo);
1804 
1805  /*
1806  * Perform an initial partition prune pass, if required.
1807  */
1808  if (prunestate->do_initial_prune)
1809  *initially_valid_subplans = ExecFindMatchingSubPlans(prunestate, true);
1810  else
1811  {
1812  /* No pruning, so we'll need to initialize all subplans */
1813  Assert(n_total_subplans > 0);
1814  *initially_valid_subplans = bms_add_range(NULL, 0,
1815  n_total_subplans - 1);
1816  }
1817 
1818  /*
1819  * Re-sequence subplan indexes contained in prunestate to account for any
1820  * that were removed above due to initial pruning. No need to do this if
1821  * no steps were removed.
1822  */
1823  if (bms_num_members(*initially_valid_subplans) < n_total_subplans)
1824  {
1825  /*
1826  * We can safely skip this when !do_exec_prune, even though that
1827  * leaves invalid data in prunestate, because that data won't be
1828  * consulted again (cf initial Assert in ExecFindMatchingSubPlans).
1829  */
1830  if (prunestate->do_exec_prune)
1831  PartitionPruneFixSubPlanMap(prunestate,
1832  *initially_valid_subplans,
1833  n_total_subplans);
1834  }
1835 
1836  return prunestate;
1837 }
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:685
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:879
static PartitionPruneState * CreatePartitionPruneState(PlanState *planstate, PartitionPruneInfo *pruneinfo)
Bitmapset * ExecFindMatchingSubPlans(PartitionPruneState *prunestate, bool initial_prune)
static void PartitionPruneFixSubPlanMap(PartitionPruneState *prunestate, Bitmapset *initially_valid_subplans, int n_total_subplans)
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:488

References Assert(), bms_add_range(), bms_num_members(), CreatePartitionPruneState(), PartitionPruneState::do_exec_prune, PartitionPruneState::do_initial_prune, ExecAssignExprContext(), ExecFindMatchingSubPlans(), PartitionPruneFixSubPlanMap(), and PlanState::state.

Referenced by ExecInitAppend(), and ExecInitMergeAppend().

◆ ExecInitRoutingInfo()

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

Definition at line 976 of file execPartition.c.

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

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

Referenced by ExecFindPartition(), and ExecInitPartitionInfo().

◆ ExecSetupPartitionTupleRouting()

PartitionTupleRouting* ExecSetupPartitionTupleRouting ( EState estate,
Relation  rel 
)

Definition at line 218 of file execPartition.c.

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

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

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

◆ find_matching_subplans_recurse()

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

Definition at line 2341 of file execPartition.c.

2345 {
2346  Bitmapset *partset;
2347  int i;
2348 
2349  /* Guard against stack overflow due to overly deep partition hierarchy. */
2351 
2352  /*
2353  * Prune as appropriate, if we have pruning steps matching the current
2354  * execution context. Otherwise just include all partitions at this
2355  * level.
2356  */
2357  if (initial_prune && pprune->initial_pruning_steps)
2358  partset = get_matching_partitions(&pprune->initial_context,
2359  pprune->initial_pruning_steps);
2360  else if (!initial_prune && pprune->exec_pruning_steps)
2361  partset = get_matching_partitions(&pprune->exec_context,
2362  pprune->exec_pruning_steps);
2363  else
2364  partset = pprune->present_parts;
2365 
2366  /* Translate partset into subplan indexes */
2367  i = -1;
2368  while ((i = bms_next_member(partset, i)) >= 0)
2369  {
2370  if (pprune->subplan_map[i] >= 0)
2371  *validsubplans = bms_add_member(*validsubplans,
2372  pprune->subplan_map[i]);
2373  else
2374  {
2375  int partidx = pprune->subpart_map[i];
2376 
2377  if (partidx >= 0)
2379  &prunedata->partrelprunedata[partidx],
2380  initial_prune, validsubplans);
2381  else
2382  {
2383  /*
2384  * We get here if the planner already pruned all the sub-
2385  * partitions for this partition. Silently ignore this
2386  * partition in this case. The end result is the same: we
2387  * would have pruned all partitions just the same, but we
2388  * don't have any pruning steps to execute to verify this.
2389  */
2390  }
2391  }
2392  }
2393 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1106
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:753
Bitmapset * get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
Definition: partprune.c:820
void check_stack_depth(void)
Definition: postgres.c:3523

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

Referenced by ExecFindMatchingSubPlans().

◆ FormPartitionKeyDatum()

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

Definition at line 1284 of file execPartition.c.

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

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

Referenced by ExecFindPartition().

◆ get_partition_for_tuple()

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

Definition at line 1381 of file execPartition.c.

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

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

Referenced by ExecFindPartition().

◆ InitPartitionPruneContext()

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

Definition at line 2077 of file execPartition.c.

2083 {
2084  int n_steps;
2085  int partnatts;
2086  ListCell *lc;
2087 
2088  n_steps = list_length(pruning_steps);
2089 
2090  context->strategy = partkey->strategy;
2091  context->partnatts = partnatts = partkey->partnatts;
2092  context->nparts = partdesc->nparts;
2093  context->boundinfo = partdesc->boundinfo;
2094  context->partcollation = partkey->partcollation;
2095  context->partsupfunc = partkey->partsupfunc;
2096 
2097  /* We'll look up type-specific support functions as needed */
2098  context->stepcmpfuncs = (FmgrInfo *)
2099  palloc0(sizeof(FmgrInfo) * n_steps * partnatts);
2100 
2101  context->ppccontext = CurrentMemoryContext;
2102  context->planstate = planstate;
2103  context->exprcontext = econtext;
2104 
2105  /* Initialize expression state for each expression we need */
2106  context->exprstates = (ExprState **)
2107  palloc0(sizeof(ExprState *) * n_steps * partnatts);
2108  foreach(lc, pruning_steps)
2109  {
2111  ListCell *lc2;
2112  int keyno;
2113 
2114  /* not needed for other step kinds */
2115  if (!IsA(step, PartitionPruneStepOp))
2116  continue;
2117 
2118  Assert(list_length(step->exprs) <= partnatts);
2119 
2120  keyno = 0;
2121  foreach(lc2, step->exprs)
2122  {
2123  Expr *expr = (Expr *) lfirst(lc2);
2124 
2125  /* not needed for Consts */
2126  if (!IsA(expr, Const))
2127  {
2128  int stateidx = PruneCxtStateIdx(partnatts,
2129  step->step.step_id,
2130  keyno);
2131 
2132  /*
2133  * When planstate is NULL, pruning_steps is known not to
2134  * contain any expressions that depend on the parent plan.
2135  * Information of any available EXTERN parameters must be
2136  * passed explicitly in that case, which the caller must have
2137  * made available via econtext.
2138  */
2139  if (planstate == NULL)
2140  context->exprstates[stateidx] =
2142  econtext->ecxt_param_list_info);
2143  else
2144  context->exprstates[stateidx] =
2145  ExecInitExpr(expr, context->planstate);
2146  }
2147  keyno++;
2148  }
2149  }
2150 }
ExprState * ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
Definition: execExpr.c:165
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:128
#define IsA(nodeptr, _type_)
Definition: nodes.h:179
#define PruneCxtStateIdx(partnatts, step_id, keyno)
Definition: partprune.h:70
ParamListInfo ecxt_param_list_info
Definition: execnodes.h:261
Definition: fmgr.h:57
Oid * partcollation
Definition: partcache.h:39
PartitionStrategy strategy
Definition: partcache.h:27
FmgrInfo * partsupfunc
Definition: partcache.h:36
FmgrInfo * partsupfunc
Definition: partprune.h:56
MemoryContext ppccontext
Definition: partprune.h:58
PartitionBoundInfo boundinfo
Definition: partprune.h:54
PlanState * planstate
Definition: partprune.h:59
FmgrInfo * stepcmpfuncs
Definition: partprune.h:57
ExprState ** exprstates
Definition: partprune.h:61
PartitionPruneStep step
Definition: plannodes.h:1527

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

Referenced by CreatePartitionPruneState().

◆ PartitionPruneFixSubPlanMap()

static void PartitionPruneFixSubPlanMap ( PartitionPruneState prunestate,
Bitmapset initially_valid_subplans,
int  n_total_subplans 
)
static

Definition at line 2167 of file execPartition.c.

2170 {
2171  int *new_subplan_indexes;
2172  Bitmapset *new_other_subplans;
2173  int i;
2174  int newidx;
2175 
2176  /*
2177  * First we must build a temporary array which maps old subplan indexes to
2178  * new ones. For convenience of initialization, we use 1-based indexes in
2179  * this array and leave pruned items as 0.
2180  */
2181  new_subplan_indexes = (int *) palloc0(sizeof(int) * n_total_subplans);
2182  newidx = 1;
2183  i = -1;
2184  while ((i = bms_next_member(initially_valid_subplans, i)) >= 0)
2185  {
2186  Assert(i < n_total_subplans);
2187  new_subplan_indexes[i] = newidx++;
2188  }
2189 
2190  /*
2191  * Now we can update each PartitionedRelPruneInfo's subplan_map with new
2192  * subplan indexes. We must also recompute its present_parts bitmap.
2193  */
2194  for (i = 0; i < prunestate->num_partprunedata; i++)
2195  {
2196  PartitionPruningData *prunedata = prunestate->partprunedata[i];
2197  int j;
2198 
2199  /*
2200  * Within each hierarchy, we perform this loop in back-to-front order
2201  * so that we determine present_parts for the lowest-level partitioned
2202  * tables first. This way we can tell whether a sub-partitioned
2203  * table's partitions were entirely pruned so we can exclude it from
2204  * the current level's present_parts.
2205  */
2206  for (j = prunedata->num_partrelprunedata - 1; j >= 0; j--)
2207  {
2208  PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
2209  int nparts = pprune->nparts;
2210  int k;
2211 
2212  /* We just rebuild present_parts from scratch */
2213  bms_free(pprune->present_parts);
2214  pprune->present_parts = NULL;
2215 
2216  for (k = 0; k < nparts; k++)
2217  {
2218  int oldidx = pprune->subplan_map[k];
2219  int subidx;
2220 
2221  /*
2222  * If this partition existed as a subplan then change the old
2223  * subplan index to the new subplan index. The new index may
2224  * become -1 if the partition was pruned above, or it may just
2225  * come earlier in the subplan list due to some subplans being
2226  * removed earlier in the list. If it's a subpartition, add
2227  * it to present_parts unless it's entirely pruned.
2228  */
2229  if (oldidx >= 0)
2230  {
2231  Assert(oldidx < n_total_subplans);
2232  pprune->subplan_map[k] = new_subplan_indexes[oldidx] - 1;
2233 
2234  if (new_subplan_indexes[oldidx] > 0)
2235  pprune->present_parts =
2236  bms_add_member(pprune->present_parts, k);
2237  }
2238  else if ((subidx = pprune->subpart_map[k]) >= 0)
2239  {
2240  PartitionedRelPruningData *subprune;
2241 
2242  subprune = &prunedata->partrelprunedata[subidx];
2243 
2244  if (!bms_is_empty(subprune->present_parts))
2245  pprune->present_parts =
2246  bms_add_member(pprune->present_parts, k);
2247  }
2248  }
2249  }
2250  }
2251 
2252  /*
2253  * We must also recompute the other_subplans set, since indexes in it may
2254  * change.
2255  */
2256  new_other_subplans = NULL;
2257  i = -1;
2258  while ((i = bms_next_member(prunestate->other_subplans, i)) >= 0)
2259  new_other_subplans = bms_add_member(new_other_subplans,
2260  new_subplan_indexes[i] - 1);
2261 
2262  bms_free(prunestate->other_subplans);
2263  prunestate->other_subplans = new_other_subplans;
2264 
2265  pfree(new_subplan_indexes);
2266 }
void bms_free(Bitmapset *a)
Definition: bitmapset.c:194
#define bms_is_empty(a)
Definition: bitmapset.h:105
void pfree(void *pointer)
Definition: mcxt.c:1456

References Assert(), bms_add_member(), bms_free(), bms_is_empty, bms_next_member(), i, j, PartitionedRelPruningData::nparts, PartitionPruneState::num_partprunedata, PartitionPruningData::num_partrelprunedata, PartitionPruneState::other_subplans, palloc0(), PartitionPruneState::partprunedata, PartitionPruningData::partrelprunedata, pfree(), PartitionedRelPruningData::present_parts, PartitionedRelPruningData::subpart_map, and PartitionedRelPruningData::subplan_map.

Referenced by ExecInitPartitionPruning().