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, int part_prune_index, Bitmapset *root_parent_relids, 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 1342 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 1693 of file execPartition.c.

1694 {
1695  TupleConversionMap *map = ExecGetChildToRootMap(leaf_part_rri);
1696 
1697  Assert(map != NULL);
1698 
1699  return adjust_partition_colnos_using_map(colnos, map->attrMap);
1700 }
static List * adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap)
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1236
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 1710 of file execPartition.c.

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

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

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

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), bms_add_members(), bms_copy(), CreatePartitionDirectory(), CurrentMemoryContext, PartitionPruneState::do_exec_prune, PartitionPruneState::do_initial_prune, elog(), ERROR, EState::es_partition_directory, EState::es_query_cxt, PartitionedRelPruningData::exec_context, 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 1605 of file execPartition.c.

1609 {
1612  int partnatts = get_partition_natts(key);
1613  int i;
1614  Oid relid = RelationGetRelid(rel);
1615  AclResult aclresult;
1616 
1617  if (check_enable_rls(relid, InvalidOid, true) == RLS_ENABLED)
1618  return NULL;
1619 
1620  /* If the user has table-level access, just go build the description. */
1621  aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT);
1622  if (aclresult != ACLCHECK_OK)
1623  {
1624  /*
1625  * Step through the columns of the partition key and make sure the
1626  * user has SELECT rights on all of them.
1627  */
1628  for (i = 0; i < partnatts; i++)
1629  {
1631 
1632  /*
1633  * If this partition key column is an expression, we return no
1634  * detail rather than try to figure out what column(s) the
1635  * expression includes and if the user has SELECT rights on them.
1636  */
1637  if (attnum == InvalidAttrNumber ||
1639  ACL_SELECT) != ACLCHECK_OK)
1640  return NULL;
1641  }
1642  }
1643 
1644  initStringInfo(&buf);
1645  appendStringInfo(&buf, "(%s) = (",
1646  pg_get_partkeydef_columns(relid, true));
1647 
1648  for (i = 0; i < partnatts; i++)
1649  {
1650  char *val;
1651  int vallen;
1652 
1653  if (isnull[i])
1654  val = "null";
1655  else
1656  {
1657  Oid foutoid;
1658  bool typisvarlena;
1659 
1661  &foutoid, &typisvarlena);
1662  val = OidOutputFunctionCall(foutoid, values[i]);
1663  }
1664 
1665  if (i > 0)
1666  appendStringInfoString(&buf, ", ");
1667 
1668  /* truncate if needed */
1669  vallen = strlen(val);
1670  if (vallen <= maxfieldlen)
1671  appendBinaryStringInfo(&buf, val, vallen);
1672  else
1673  {
1674  vallen = pg_mbcliplen(val, vallen, maxfieldlen);
1675  appendBinaryStringInfo(&buf, val, vallen);
1676  appendStringInfoString(&buf, "...");
1677  }
1678  }
1679 
1680  appendStringInfoChar(&buf, ')');
1681 
1682  return buf.data;
1683 }
AclResult
Definition: acl.h:183
@ ACLCHECK_OK
Definition: acl.h:184
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:4617
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4746
#define InvalidAttrNumber
Definition: attnum.h:23
static Datum values[MAXATTR]
Definition: bootstrap.c:156
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1639
long val
Definition: informix.c:664
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2865
int pg_mbcliplen(const char *mbstr, int len, int limit)
Definition: mbutils.c:1026
Oid GetUserId(void)
Definition: miscinit.c:497
#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:83
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:501
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:1874
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
void appendBinaryStringInfo(StringInfo str, const char *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 1227 of file execPartition.c.

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

2296 {
2297  Bitmapset *result = NULL;
2298  MemoryContext oldcontext;
2299  int i;
2300 
2301  /*
2302  * Either we're here on the initial prune done during pruning
2303  * initialization, or we're at a point where PARAM_EXEC Params can be
2304  * evaluated *and* there are steps in which to do so.
2305  */
2306  Assert(initial_prune || prunestate->do_exec_prune);
2307 
2308  /*
2309  * Switch to a temp context to avoid leaking memory in the executor's
2310  * query-lifespan memory context.
2311  */
2312  oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
2313 
2314  /*
2315  * For each hierarchy, do the pruning tests, and add nondeletable
2316  * subplans' indexes to "result".
2317  */
2318  for (i = 0; i < prunestate->num_partprunedata; i++)
2319  {
2320  PartitionPruningData *prunedata = prunestate->partprunedata[i];
2321  PartitionedRelPruningData *pprune;
2322 
2323  /*
2324  * We pass the zeroth item, belonging to the root table of the
2325  * hierarchy, and find_matching_subplans_recurse() takes care of
2326  * recursing to other (lower-level) parents as needed.
2327  */
2328  pprune = &prunedata->partrelprunedata[0];
2329  find_matching_subplans_recurse(prunedata, pprune, initial_prune,
2330  &result);
2331 
2332  /* Expression eval may have used space in ExprContext too */
2333  if (pprune->exec_pruning_steps)
2335  }
2336 
2337  /* Add in any subplans that partition pruning didn't account for */
2338  result = bms_add_members(result, prunestate->other_subplans);
2339 
2340  MemoryContextSwitchTo(oldcontext);
2341 
2342  /* Copy result out of the temp context before we reset it */
2343  result = bms_copy(result);
2344 
2345  MemoryContextReset(prunestate->prune_context);
2346 
2347  return result;
2348 }
static void find_matching_subplans_recurse(PartitionPruningData *prunedata, PartitionedRelPruningData *pprune, bool initial_prune, Bitmapset **validsubplans)
#define ResetExprContext(econtext)
Definition: executor.h:529
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:303
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:135
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:294
int errdetail(const char *fmt,...)
Definition: elog.c:1039
int errcode(int sqlerrcode)
Definition: elog.c:695
int errmsg(const char *fmt,...)
Definition: elog.c:906
#define ereport(elevel,...)
Definition: elog.h:145
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1786
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
Definition: execMain.c:995
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:1262
#define GetPerTupleExprContext(estate)
Definition: executor.h:535
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:540
#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:267
#define PARTITION_MAX_KEYS
uintptr_t Datum
Definition: postgres.h:412
#define RelationGetRelationName(relation)
Definition: rel.h:535
int errtable(Relation rel)
Definition: relcache.c:5876
Definition: attmap.h:35
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:247
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1269
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:110
TupleTableSlot * ri_PartitionTupleSlot
Definition: execnodes.h:568
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:433

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

1092 {
1093  Relation rel;
1094  PartitionDesc partdesc;
1095  PartitionDispatch pd;
1096  int dispatchidx;
1097  MemoryContext oldcxt;
1098 
1099  /*
1100  * For data modification, it is better that executor does not include
1101  * partitions being detached, except when running in snapshot-isolation
1102  * mode. This means that a read-committed transaction immediately gets a
1103  * "no partition for tuple" error when a tuple is inserted into a
1104  * partition that's being detached concurrently, but a transaction in
1105  * repeatable-read mode can still use such a partition.
1106  */
1107  if (estate->es_partition_directory == NULL)
1108  estate->es_partition_directory =
1111 
1112  oldcxt = MemoryContextSwitchTo(proute->memcxt);
1113 
1114  /*
1115  * Only sub-partitioned tables need to be locked here. The root
1116  * partitioned table will already have been locked as it's referenced in
1117  * the query's rtable.
1118  */
1119  if (partoid != RelationGetRelid(proute->partition_root))
1120  rel = table_open(partoid, RowExclusiveLock);
1121  else
1122  rel = proute->partition_root;
1123  partdesc = PartitionDirectoryLookup(estate->es_partition_directory, rel);
1124 
1125  pd = (PartitionDispatch) palloc(offsetof(PartitionDispatchData, indexes) +
1126  partdesc->nparts * sizeof(int));
1127  pd->reldesc = rel;
1128  pd->key = RelationGetPartitionKey(rel);
1129  pd->keystate = NIL;
1130  pd->partdesc = partdesc;
1131  if (parent_pd != NULL)
1132  {
1133  TupleDesc tupdesc = RelationGetDescr(rel);
1134 
1135  /*
1136  * For sub-partitioned tables where the column order differs from its
1137  * direct parent partitioned table, we must store a tuple table slot
1138  * initialized with its tuple descriptor and a tuple conversion map to
1139  * convert a tuple from its parent's rowtype to its own. This is to
1140  * make sure that we are looking at the correct row using the correct
1141  * tuple descriptor when computing its partition key for tuple
1142  * routing.
1143  */
1145  tupdesc,
1146  false);
1147  pd->tupslot = pd->tupmap ?
1148  MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL;
1149  }
1150  else
1151  {
1152  /* Not required for the root partitioned table */
1153  pd->tupmap = NULL;
1154  pd->tupslot = NULL;
1155  }
1156 
1157  /*
1158  * Initialize with -1 to signify that the corresponding partition's
1159  * ResultRelInfo or PartitionDispatch has not been created yet.
1160  */
1161  memset(pd->indexes, -1, sizeof(int) * partdesc->nparts);
1162 
1163  /* Track in PartitionTupleRouting for later use */
1164  dispatchidx = proute->num_dispatch++;
1165 
1166  /* Allocate or enlarge the array, as needed */
1167  if (proute->num_dispatch >= proute->max_dispatch)
1168  {
1169  if (proute->max_dispatch == 0)
1170  {
1171  proute->max_dispatch = 4;
1173  palloc(sizeof(PartitionDispatch) * proute->max_dispatch);
1174  proute->nonleaf_partitions = (ResultRelInfo **)
1175  palloc(sizeof(ResultRelInfo *) * proute->max_dispatch);
1176  }
1177  else
1178  {
1179  proute->max_dispatch *= 2;
1182  sizeof(PartitionDispatch) * proute->max_dispatch);
1183  proute->nonleaf_partitions = (ResultRelInfo **)
1184  repalloc(proute->nonleaf_partitions,
1185  sizeof(ResultRelInfo *) * proute->max_dispatch);
1186  }
1187  }
1188  proute->partition_dispatch_info[dispatchidx] = pd;
1189 
1190  /*
1191  * If setting up a PartitionDispatch for a sub-partitioned table, we may
1192  * also need a minimally valid ResultRelInfo for checking the partition
1193  * constraint later; set that up now.
1194  */
1195  if (parent_pd)
1196  {
1198 
1199  InitResultRelInfo(rri, rel, 0, rootResultRelInfo, 0);
1200  proute->nonleaf_partitions[dispatchidx] = rri;
1201  }
1202  else
1203  proute->nonleaf_partitions[dispatchidx] = NULL;
1204 
1205  /*
1206  * Finally, if setting up a PartitionDispatch for a sub-partitioned table,
1207  * install a downlink in the parent to allow quick descent.
1208  */
1209  if (parent_pd)
1210  {
1211  Assert(parent_pd->indexes[partidx] == -1);
1212  parent_pd->indexes[partidx] = dispatchidx;
1213  }
1214 
1215  MemoryContextSwitchTo(oldcxt);
1216 
1217  return pd;
1218 }
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:1196
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:1238
#define RowExclusiveLock
Definition: lockdefs.h:38
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1321
#define makeNode(_type_)
Definition: nodes.h:165
#define RelationGetDescr(relation)
Definition: rel.h:527
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 tuple routing where we didn't
549  * 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, 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 
569  /*
570  * Use the WCO list of the first plan as a reference to calculate
571  * attno's for the WCO list of this partition. In the INSERT case,
572  * that refers to the root partitioned table, whereas in the UPDATE
573  * tuple routing case, that refers to the first partition in the
574  * mtstate->resultRelInfo array. In any case, both that relation and
575  * this partition should have the same columns, so we should be able
576  * to map attributes successfully.
577  */
578  wcoList = linitial(node->withCheckOptionLists);
579 
580  /*
581  * Convert Vars in it to contain this partition's attribute numbers.
582  */
583  part_attmap =
585  RelationGetDescr(firstResultRel),
586  false);
587  wcoList = (List *)
588  map_variable_attnos((Node *) wcoList,
589  firstVarno, 0,
590  part_attmap,
591  RelationGetForm(partrel)->reltype,
592  &found_whole_row);
593  /* We ignore the value of found_whole_row. */
594 
595  foreach(ll, wcoList)
596  {
598  ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual),
599  &mtstate->ps);
600 
601  wcoExprs = lappend(wcoExprs, wcoExpr);
602  }
603 
604  leaf_part_rri->ri_WithCheckOptions = wcoList;
605  leaf_part_rri->ri_WithCheckOptionExprs = wcoExprs;
606  }
607 
608  /*
609  * Build the RETURNING projection for the partition. Note that we didn't
610  * build the returningList for partitions within the planner, but simple
611  * translation of varattnos will suffice. This only occurs for the INSERT
612  * case or in the case of UPDATE tuple routing where we didn't find a
613  * result rel to reuse.
614  */
615  if (node && node->returningLists != NIL)
616  {
617  TupleTableSlot *slot;
618  ExprContext *econtext;
619  List *returningList;
620 
621  /* See the comment above for WCO lists. */
622  Assert((node->operation == CMD_INSERT &&
623  list_length(node->returningLists) == 1 &&
624  list_length(node->resultRelations) == 1) ||
625  (node->operation == CMD_UPDATE &&
626  list_length(node->returningLists) ==
627  list_length(node->resultRelations)));
628 
629  /*
630  * Use the RETURNING list of the first plan as a reference to
631  * calculate attno's for the RETURNING list of this partition. See
632  * the comment above for WCO lists for more details on why this is
633  * okay.
634  */
635  returningList = linitial(node->returningLists);
636 
637  /*
638  * Convert Vars in it to contain this partition's attribute numbers.
639  */
640  if (part_attmap == NULL)
641  part_attmap =
643  RelationGetDescr(firstResultRel),
644  false);
645  returningList = (List *)
646  map_variable_attnos((Node *) returningList,
647  firstVarno, 0,
648  part_attmap,
649  RelationGetForm(partrel)->reltype,
650  &found_whole_row);
651  /* We ignore the value of found_whole_row. */
652 
653  leaf_part_rri->ri_returningList = returningList;
654 
655  /*
656  * Initialize the projection itself.
657  *
658  * Use the slot and the expression context that would have been set up
659  * in ExecInitModifyTable() for projection's output.
660  */
661  Assert(mtstate->ps.ps_ResultTupleSlot != NULL);
662  slot = mtstate->ps.ps_ResultTupleSlot;
663  Assert(mtstate->ps.ps_ExprContext != NULL);
664  econtext = mtstate->ps.ps_ExprContext;
665  leaf_part_rri->ri_projectReturning =
666  ExecBuildProjectionInfo(returningList, econtext, slot,
667  &mtstate->ps, RelationGetDescr(partrel));
668  }
669 
670  /* Set up information needed for routing tuples to the partition. */
671  ExecInitRoutingInfo(mtstate, estate, proute, dispatch,
672  leaf_part_rri, partidx, false);
673 
674  /*
675  * If there is an ON CONFLICT clause, initialize state for it.
676  */
677  if (node && node->onConflictAction != ONCONFLICT_NONE)
678  {
679  TupleDesc partrelDesc = RelationGetDescr(partrel);
680  ExprContext *econtext = mtstate->ps.ps_ExprContext;
681  ListCell *lc;
682  List *arbiterIndexes = NIL;
683 
684  /*
685  * If there is a list of arbiter indexes, map it to a list of indexes
686  * in the partition. We do that by scanning the partition's index
687  * list and searching for ancestry relationships to each index in the
688  * ancestor table.
689  */
690  if (rootResultRelInfo->ri_onConflictArbiterIndexes != NIL)
691  {
692  List *childIdxs;
693 
694  childIdxs = RelationGetIndexList(leaf_part_rri->ri_RelationDesc);
695 
696  foreach(lc, childIdxs)
697  {
698  Oid childIdx = lfirst_oid(lc);
699  List *ancestors;
700  ListCell *lc2;
701 
702  ancestors = get_partition_ancestors(childIdx);
703  foreach(lc2, rootResultRelInfo->ri_onConflictArbiterIndexes)
704  {
705  if (list_member_oid(ancestors, lfirst_oid(lc2)))
706  arbiterIndexes = lappend_oid(arbiterIndexes, childIdx);
707  }
708  list_free(ancestors);
709  }
710  }
711 
712  /*
713  * If the resulting lists are of inequal length, something is wrong.
714  * (This shouldn't happen, since arbiter index selection should not
715  * pick up an invalid index.)
716  */
717  if (list_length(rootResultRelInfo->ri_onConflictArbiterIndexes) !=
718  list_length(arbiterIndexes))
719  elog(ERROR, "invalid arbiter index list");
720  leaf_part_rri->ri_onConflictArbiterIndexes = arbiterIndexes;
721 
722  /*
723  * In the DO UPDATE case, we have some more state to initialize.
724  */
725  if (node->onConflictAction == ONCONFLICT_UPDATE)
726  {
728  TupleConversionMap *map;
729 
730  map = ExecGetRootToChildMap(leaf_part_rri, estate);
731 
732  Assert(node->onConflictSet != NIL);
733  Assert(rootResultRelInfo->ri_onConflict != NULL);
734 
735  leaf_part_rri->ri_onConflict = onconfl;
736 
737  /*
738  * Need a separate existing slot for each partition, as the
739  * partition could be of a different AM, even if the tuple
740  * descriptors match.
741  */
742  onconfl->oc_Existing =
743  table_slot_create(leaf_part_rri->ri_RelationDesc,
744  &mtstate->ps.state->es_tupleTable);
745 
746  /*
747  * If the partition's tuple descriptor matches exactly the root
748  * parent (the common case), we can re-use most of the parent's ON
749  * CONFLICT SET state, skipping a bunch of work. Otherwise, we
750  * need to create state specific to this partition.
751  */
752  if (map == NULL)
753  {
754  /*
755  * It's safe to reuse these from the partition root, as we
756  * only process one tuple at a time (therefore we won't
757  * overwrite needed data in slots), and the results of
758  * projections are independent of the underlying storage.
759  * Projections and where clauses themselves don't store state
760  * / are independent of the underlying storage.
761  */
762  onconfl->oc_ProjSlot =
763  rootResultRelInfo->ri_onConflict->oc_ProjSlot;
764  onconfl->oc_ProjInfo =
765  rootResultRelInfo->ri_onConflict->oc_ProjInfo;
766  onconfl->oc_WhereClause =
767  rootResultRelInfo->ri_onConflict->oc_WhereClause;
768  }
769  else
770  {
771  List *onconflset;
772  List *onconflcols;
773 
774  /*
775  * Translate expressions in onConflictSet to account for
776  * different attribute numbers. For that, map partition
777  * varattnos twice: first to catch the EXCLUDED
778  * pseudo-relation (INNER_VAR), and second to handle the main
779  * target relation (firstVarno).
780  */
781  onconflset = copyObject(node->onConflictSet);
782  if (part_attmap == NULL)
783  part_attmap =
785  RelationGetDescr(firstResultRel),
786  false);
787  onconflset = (List *)
788  map_variable_attnos((Node *) onconflset,
789  INNER_VAR, 0,
790  part_attmap,
791  RelationGetForm(partrel)->reltype,
792  &found_whole_row);
793  /* We ignore the value of found_whole_row. */
794  onconflset = (List *)
795  map_variable_attnos((Node *) onconflset,
796  firstVarno, 0,
797  part_attmap,
798  RelationGetForm(partrel)->reltype,
799  &found_whole_row);
800  /* We ignore the value of found_whole_row. */
801 
802  /* Finally, adjust the target colnos to match the partition. */
803  onconflcols = adjust_partition_colnos(node->onConflictCols,
804  leaf_part_rri);
805 
806  /* create the tuple slot for the UPDATE SET projection */
807  onconfl->oc_ProjSlot =
808  table_slot_create(partrel,
809  &mtstate->ps.state->es_tupleTable);
810 
811  /* build UPDATE SET projection state */
812  onconfl->oc_ProjInfo =
813  ExecBuildUpdateProjection(onconflset,
814  true,
815  onconflcols,
816  partrelDesc,
817  econtext,
818  onconfl->oc_ProjSlot,
819  &mtstate->ps);
820 
821  /*
822  * If there is a WHERE clause, initialize state where it will
823  * be evaluated, mapping the attribute numbers appropriately.
824  * As with onConflictSet, we need to map partition varattnos
825  * to the partition's tupdesc.
826  */
827  if (node->onConflictWhere)
828  {
829  List *clause;
830 
831  clause = copyObject((List *) node->onConflictWhere);
832  clause = (List *)
833  map_variable_attnos((Node *) clause,
834  INNER_VAR, 0,
835  part_attmap,
836  RelationGetForm(partrel)->reltype,
837  &found_whole_row);
838  /* We ignore the value of found_whole_row. */
839  clause = (List *)
840  map_variable_attnos((Node *) clause,
841  firstVarno, 0,
842  part_attmap,
843  RelationGetForm(partrel)->reltype,
844  &found_whole_row);
845  /* We ignore the value of found_whole_row. */
846  onconfl->oc_WhereClause =
847  ExecInitQual((List *) clause, &mtstate->ps);
848  }
849  }
850  }
851  }
852 
853  /*
854  * Since we've just initialized this ResultRelInfo, it's not in any list
855  * attached to the estate as yet. Add it, so that it can be found later.
856  *
857  * Note that the entries in this list appear in no predetermined order,
858  * because partition result rels are initialized as and when they're
859  * needed.
860  */
864  leaf_part_rri);
865 
866  /*
867  * Initialize information about this partition that's needed to handle
868  * MERGE. We take the "first" result relation's mergeActionList as
869  * reference and make copy for this relation, converting stuff that
870  * references attribute numbers to match this relation's.
871  *
872  * This duplicates much of the logic in ExecInitMerge(), so something
873  * changes there, look here too.
874  */
875  if (node && node->operation == CMD_MERGE)
876  {
877  List *firstMergeActionList = linitial(node->mergeActionLists);
878  ListCell *lc;
879  ExprContext *econtext = mtstate->ps.ps_ExprContext;
880 
881  if (part_attmap == NULL)
882  part_attmap =
884  RelationGetDescr(firstResultRel),
885  false);
886 
887  if (unlikely(!leaf_part_rri->ri_projectNewInfoValid))
888  ExecInitMergeTupleSlots(mtstate, leaf_part_rri);
889 
890  foreach(lc, firstMergeActionList)
891  {
892  /* Make a copy for this relation to be safe. */
894  MergeActionState *action_state;
895  List **list;
896 
897  /* Generate the action's state for this relation */
898  action_state = makeNode(MergeActionState);
899  action_state->mas_action = action;
900 
901  /* And put the action in the appropriate list */
902  if (action->matched)
903  list = &leaf_part_rri->ri_matchedMergeAction;
904  else
905  list = &leaf_part_rri->ri_notMatchedMergeAction;
906  *list = lappend(*list, action_state);
907 
908  switch (action->commandType)
909  {
910  case CMD_INSERT:
911 
912  /*
913  * ExecCheckPlanOutput() already done on the targetlist
914  * when "first" result relation initialized and it is same
915  * for all result relations.
916  */
917  action_state->mas_proj =
918  ExecBuildProjectionInfo(action->targetList, econtext,
919  leaf_part_rri->ri_newTupleSlot,
920  &mtstate->ps,
921  RelationGetDescr(partrel));
922  break;
923  case CMD_UPDATE:
924 
925  /*
926  * Convert updateColnos from "first" result relation
927  * attribute numbers to this result rel's.
928  */
929  if (part_attmap)
930  action->updateColnos =
932  part_attmap);
933  action_state->mas_proj =
934  ExecBuildUpdateProjection(action->targetList,
935  true,
936  action->updateColnos,
937  RelationGetDescr(leaf_part_rri->ri_RelationDesc),
938  econtext,
939  leaf_part_rri->ri_newTupleSlot,
940  NULL);
941  break;
942  case CMD_DELETE:
943  break;
944 
945  default:
946  elog(ERROR, "unknown action in MERGE WHEN clause");
947  }
948 
949  /* found_whole_row intentionally ignored. */
950  action->qual =
952  firstVarno, 0,
953  part_attmap,
954  RelationGetForm(partrel)->reltype,
955  &found_whole_row);
956  action_state->mas_whenqual =
957  ExecInitQual((List *) action->qual, &mtstate->ps);
958  }
959  }
960  MemoryContextSwitchTo(oldcxt);
961 
962  return leaf_part_rri;
963 }
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:178
#define unlikely(x)
Definition: c.h:295
ProjectionInfo * ExecBuildUpdateProjection(List *targetList, bool evalTargetList, List *targetColnos, TupleDesc relDesc, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent)
Definition: execExpr.c:514
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:210
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:354
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:233
@ ONCONFLICT_NONE
Definition: nodes.h:415
@ ONCONFLICT_UPDATE
Definition: nodes.h:417
@ CMD_MERGE
Definition: nodes.h:269
@ CMD_DELETE
Definition: nodes.h:268
@ CMD_UPDATE
Definition: nodes.h:266
#define castNode(_type_, nodeptr)
Definition: nodes.h:186
List * get_partition_ancestors(Oid relid)
Definition: partition.c:133
#define lfirst(lc)
Definition: pg_list.h:170
#define linitial(l)
Definition: pg_list.h:176
#define lfirst_oid(lc)
Definition: pg_list.h:172
#define INNER_VAR
Definition: primnodes.h:193
#define RelationGetForm(relation)
Definition: rel.h:495
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4738
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:643
int es_instrument
Definition: execnodes.h:662
List * es_tupleTable
Definition: execnodes.h:657
MergeAction * mas_action
Definition: execnodes.h:417
ProjectionInfo * mas_proj
Definition: execnodes.h:418
ExprState * mas_whenqual
Definition: execnodes.h:420
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1261
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:118
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:402
TupleTableSlot * oc_Existing
Definition: execnodes.h:401
ExprState * oc_WhereClause
Definition: execnodes.h:404
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:403
Plan * plan
Definition: execnodes.h:1029
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1067
OnConflictSetState * ri_onConflict
Definition: execnodes.h:532
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:529
Index ri_RangeTableIndex
Definition: execnodes.h:445
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,
int  part_prune_index,
Bitmapset root_parent_relids,
Bitmapset **  initially_valid_subplans 
)

Definition at line 1798 of file execPartition.c.

1803 {
1804  PartitionPruneState *prunestate;
1805  EState *estate = planstate->state;
1806  PartitionPruneInfo *pruneinfo;
1807 
1808  /* Obtain the pruneinfo we need, and make sure it's the right one */
1809  pruneinfo = list_nth(estate->es_part_prune_infos, part_prune_index);
1810  if (!bms_equal(root_parent_relids, pruneinfo->root_parent_relids))
1811  ereport(ERROR,
1812  errcode(ERRCODE_INTERNAL_ERROR),
1813  errmsg_internal("mismatching PartitionPruneInfo found at part_prune_index %d",
1814  part_prune_index),
1815  errdetail_internal("plan node relids %s, pruneinfo relids %s",
1816  bmsToString(root_parent_relids),
1817  bmsToString(pruneinfo->root_parent_relids)));
1818 
1819  /* We may need an expression context to evaluate partition exprs */
1820  ExecAssignExprContext(estate, planstate);
1821 
1822  /* Create the working data structure for pruning */
1823  prunestate = CreatePartitionPruneState(planstate, pruneinfo);
1824 
1825  /*
1826  * Perform an initial partition prune pass, if required.
1827  */
1828  if (prunestate->do_initial_prune)
1829  *initially_valid_subplans = ExecFindMatchingSubPlans(prunestate, true);
1830  else
1831  {
1832  /* No pruning, so we'll need to initialize all subplans */
1833  Assert(n_total_subplans > 0);
1834  *initially_valid_subplans = bms_add_range(NULL, 0,
1835  n_total_subplans - 1);
1836  }
1837 
1838  /*
1839  * Re-sequence subplan indexes contained in prunestate to account for any
1840  * that were removed above due to initial pruning. No need to do this if
1841  * no steps were removed.
1842  */
1843  if (bms_num_members(*initially_valid_subplans) < n_total_subplans)
1844  {
1845  /*
1846  * We can safely skip this when !do_exec_prune, even though that
1847  * leaves invalid data in prunestate, because that data won't be
1848  * consulted again (cf initial Assert in ExecFindMatchingSubPlans).
1849  */
1850  if (prunestate->do_exec_prune)
1851  PartitionPruneFixSubPlanMap(prunestate,
1852  *initially_valid_subplans,
1853  n_total_subplans);
1854  }
1855 
1856  return prunestate;
1857 }
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:94
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:649
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:837
int errmsg_internal(const char *fmt,...)
Definition: elog.c:993
int errdetail_internal(const char *fmt,...)
Definition: elog.c:1066
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:482
char * bmsToString(const Bitmapset *bms)
Definition: outfuncs.c:893
static void * list_nth(const List *list, int n)
Definition: pg_list.h:297
List * es_part_prune_infos
Definition: execnodes.h:621
Bitmapset * root_parent_relids
Definition: plannodes.h:1426

References Assert(), bms_add_range(), bms_equal(), bms_num_members(), bmsToString(), CreatePartitionPruneState(), PartitionPruneState::do_exec_prune, PartitionPruneState::do_initial_prune, ereport, errcode(), errdetail_internal(), errmsg_internal(), ERROR, EState::es_part_prune_infos, ExecAssignExprContext(), ExecFindMatchingSubPlans(), list_nth(), PartitionPruneFixSubPlanMap(), PartitionPruneInfo::root_parent_relids, 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 972 of file execPartition.c.

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

References Assert(), FdwRoutine::BeginForeignInsert, CMD_INSERT, EState::es_tupleTable, FdwRoutine::ExecForeignBatchInsert, ExecGetRootToChildMap(), FdwRoutine::GetForeignModifyBatchSize, PartitionDispatchData::indexes, PartitionTupleRouting::is_borrowed_rel, PartitionTupleRouting::max_partitions, PartitionTupleRouting::memcxt, MemoryContextSwitchTo(), PartitionTupleRouting::num_partitions, ModifyTableState::operation, palloc(), PartitionTupleRouting::partitions, repalloc(), ResultRelInfo::ri_BatchSize, ResultRelInfo::ri_CopyMultiInsertBuffer, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_ModifyTableState, 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:1230

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

2361 {
2362  Bitmapset *partset;
2363  int i;
2364 
2365  /* Guard against stack overflow due to overly deep partition hierarchy. */
2367 
2368  /*
2369  * Prune as appropriate, if we have pruning steps matching the current
2370  * execution context. Otherwise just include all partitions at this
2371  * level.
2372  */
2373  if (initial_prune && pprune->initial_pruning_steps)
2374  partset = get_matching_partitions(&pprune->initial_context,
2375  pprune->initial_pruning_steps);
2376  else if (!initial_prune && pprune->exec_pruning_steps)
2377  partset = get_matching_partitions(&pprune->exec_context,
2378  pprune->exec_pruning_steps);
2379  else
2380  partset = pprune->present_parts;
2381 
2382  /* Translate partset into subplan indexes */
2383  i = -1;
2384  while ((i = bms_next_member(partset, i)) >= 0)
2385  {
2386  if (pprune->subplan_map[i] >= 0)
2387  *validsubplans = bms_add_member(*validsubplans,
2388  pprune->subplan_map[i]);
2389  else
2390  {
2391  int partidx = pprune->subpart_map[i];
2392 
2393  if (partidx >= 0)
2395  &prunedata->partrelprunedata[partidx],
2396  initial_prune, validsubplans);
2397  else
2398  {
2399  /*
2400  * We get here if the planner already pruned all the sub-
2401  * partitions for this partition. Silently ignore this
2402  * partition in this case. The end result is the same: we
2403  * would have pruned all partitions just the same, but we
2404  * don't have any pruning steps to execute to verify this.
2405  */
2406  }
2407  }
2408  }
2409 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1047
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:739
Bitmapset * get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
Definition: partprune.c:826
void check_stack_depth(void)
Definition: postgres.c:3440

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

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

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

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

2099 {
2100  int n_steps;
2101  int partnatts;
2102  ListCell *lc;
2103 
2104  n_steps = list_length(pruning_steps);
2105 
2106  context->strategy = partkey->strategy;
2107  context->partnatts = partnatts = partkey->partnatts;
2108  context->nparts = partdesc->nparts;
2109  context->boundinfo = partdesc->boundinfo;
2110  context->partcollation = partkey->partcollation;
2111  context->partsupfunc = partkey->partsupfunc;
2112 
2113  /* We'll look up type-specific support functions as needed */
2114  context->stepcmpfuncs = (FmgrInfo *)
2115  palloc0(sizeof(FmgrInfo) * n_steps * partnatts);
2116 
2117  context->ppccontext = CurrentMemoryContext;
2118  context->planstate = planstate;
2119  context->exprcontext = econtext;
2120 
2121  /* Initialize expression state for each expression we need */
2122  context->exprstates = (ExprState **)
2123  palloc0(sizeof(ExprState *) * n_steps * partnatts);
2124  foreach(lc, pruning_steps)
2125  {
2127  ListCell *lc2;
2128  int keyno;
2129 
2130  /* not needed for other step kinds */
2131  if (!IsA(step, PartitionPruneStepOp))
2132  continue;
2133 
2134  Assert(list_length(step->exprs) <= partnatts);
2135 
2136  keyno = 0;
2137  foreach(lc2, step->exprs)
2138  {
2139  Expr *expr = (Expr *) lfirst(lc2);
2140 
2141  /* not needed for Consts */
2142  if (!IsA(expr, Const))
2143  {
2144  int stateidx = PruneCxtStateIdx(partnatts,
2145  step->step.step_id,
2146  keyno);
2147 
2148  /*
2149  * When planstate is NULL, pruning_steps is known not to
2150  * contain any expressions that depend on the parent plan.
2151  * Information of any available EXTERN parameters must be
2152  * passed explicitly in that case, which the caller must have
2153  * made available via econtext.
2154  */
2155  if (planstate == NULL)
2156  context->exprstates[stateidx] =
2158  econtext->ecxt_param_list_info);
2159  else
2160  context->exprstates[stateidx] =
2161  ExecInitExpr(expr, context->planstate);
2162  }
2163  keyno++;
2164  }
2165  }
2166 }
ExprState * ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
Definition: execExpr.c:161
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:124
#define IsA(nodeptr, _type_)
Definition: nodes.h:168
#define PruneCxtStateIdx(partnatts, step_id, keyno)
Definition: partprune.h:70
ParamListInfo ecxt_param_list_info
Definition: execnodes.h:259
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:1528

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

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

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().