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 "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
 

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_tlist (List *tlist, TupleConversionMap *map)
 
static void ExecInitPruningContext (PartitionPruneContext *context, List *pruning_steps, PartitionDesc partdesc, PartitionKey partkey, PlanState *planstate)
 
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)
 
PartitionPruneStateExecCreatePartitionPruneState (PlanState *planstate, PartitionPruneInfo *partitionpruneinfo)
 
BitmapsetExecFindInitialMatchingSubPlans (PartitionPruneState *prunestate, int nsubplans)
 
BitmapsetExecFindMatchingSubPlans (PartitionPruneState *prunestate)
 

Typedef Documentation

◆ PartitionDispatchData

Function Documentation

◆ adjust_partition_tlist()

static List * adjust_partition_tlist ( List tlist,
TupleConversionMap map 
)
static

Definition at line 1439 of file execPartition.c.

References Assert, AttrMap::attnums, TupleConversionMap::attrMap, InvalidAttrNumber, InvalidOid, lappend(), list_nth(), makeConst(), makeTargetEntry(), AttrMap::maplen, NameStr, TupleDescData::natts, NIL, TupleConversionMap::outdesc, pstrdup(), TargetEntry::resno, and TupleDescAttr.

Referenced by ExecInitPartitionInfo().

1440 {
1441  List *new_tlist = NIL;
1442  TupleDesc tupdesc = map->outdesc;
1443  AttrMap *attrMap = map->attrMap;
1444  AttrNumber attrno;
1445 
1446  Assert(tupdesc->natts == attrMap->maplen);
1447  for (attrno = 1; attrno <= tupdesc->natts; attrno++)
1448  {
1449  Form_pg_attribute att_tup = TupleDescAttr(tupdesc, attrno - 1);
1450  TargetEntry *tle;
1451 
1452  if (attrMap->attnums[attrno - 1] != InvalidAttrNumber)
1453  {
1454  Assert(!att_tup->attisdropped);
1455 
1456  /*
1457  * Use the corresponding entry from the parent's tlist, adjusting
1458  * the resno the match the partition's attno.
1459  */
1460  tle = (TargetEntry *) list_nth(tlist, attrMap->attnums[attrno - 1] - 1);
1461  tle->resno = attrno;
1462  }
1463  else
1464  {
1465  Const *expr;
1466 
1467  /*
1468  * For a dropped attribute in the partition, generate a dummy
1469  * entry with resno matching the partition's attno.
1470  */
1471  Assert(att_tup->attisdropped);
1472  expr = makeConst(INT4OID,
1473  -1,
1474  InvalidOid,
1475  sizeof(int32),
1476  (Datum) 0,
1477  true, /* isnull */
1478  true /* byval */ );
1479  tle = makeTargetEntry((Expr *) expr,
1480  attrno,
1481  pstrdup(NameStr(att_tup->attname)),
1482  false);
1483  }
1484 
1485  new_tlist = lappend(new_tlist, tle);
1486  }
1487 
1488  return new_tlist;
1489 }
#define NIL
Definition: pg_list.h:65
TupleDesc outdesc
Definition: tupconvert.h:27
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
char * pstrdup(const char *in)
Definition: mcxt.c:1299
int maplen
Definition: attmap.h:37
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:299
signed int int32
Definition: c.h:429
Definition: attmap.h:34
static void * list_nth(const List *list, int n)
Definition: pg_list.h:278
AttrNumber resno
Definition: primnodes.h:1445
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:238
AttrMap * attrMap
Definition: tupconvert.h:28
List * lappend(List *list, void *datum)
Definition: list.c:336
uintptr_t Datum
Definition: postgres.h:411
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:804
AttrNumber * attnums
Definition: attmap.h:36
#define InvalidAttrNumber
Definition: attnum.h:23
#define NameStr(name)
Definition: c.h:681
Definition: pg_list.h:50
int16 AttrNumber
Definition: attnum.h:21

◆ ExecBuildSlotPartitionKeyDescription()

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

Definition at line 1343 of file execPartition.c.

References ACL_SELECT, ACLCHECK_OK, appendBinaryStringInfo(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), attnum, buf, check_enable_rls(), StringInfoData::data, 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, and val.

Referenced by ExecFindPartition().

1347 {
1350  int partnatts = get_partition_natts(key);
1351  int i;
1352  Oid relid = RelationGetRelid(rel);
1353  AclResult aclresult;
1354 
1355  if (check_enable_rls(relid, InvalidOid, true) == RLS_ENABLED)
1356  return NULL;
1357 
1358  /* If the user has table-level access, just go build the description. */
1359  aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT);
1360  if (aclresult != ACLCHECK_OK)
1361  {
1362  /*
1363  * Step through the columns of the partition key and make sure the
1364  * user has SELECT rights on all of them.
1365  */
1366  for (i = 0; i < partnatts; i++)
1367  {
1369 
1370  /*
1371  * If this partition key column is an expression, we return no
1372  * detail rather than try to figure out what column(s) the
1373  * expression includes and if the user has SELECT rights on them.
1374  */
1375  if (attnum == InvalidAttrNumber ||
1376  pg_attribute_aclcheck(relid, attnum, GetUserId(),
1377  ACL_SELECT) != ACLCHECK_OK)
1378  return NULL;
1379  }
1380  }
1381 
1382  initStringInfo(&buf);
1383  appendStringInfo(&buf, "(%s) = (",
1384  pg_get_partkeydef_columns(relid, true));
1385 
1386  for (i = 0; i < partnatts; i++)
1387  {
1388  char *val;
1389  int vallen;
1390 
1391  if (isnull[i])
1392  val = "null";
1393  else
1394  {
1395  Oid foutoid;
1396  bool typisvarlena;
1397 
1399  &foutoid, &typisvarlena);
1400  val = OidOutputFunctionCall(foutoid, values[i]);
1401  }
1402 
1403  if (i > 0)
1404  appendStringInfoString(&buf, ", ");
1405 
1406  /* truncate if needed */
1407  vallen = strlen(val);
1408  if (vallen <= maxfieldlen)
1409  appendBinaryStringInfo(&buf, val, vallen);
1410  else
1411  {
1412  vallen = pg_mbcliplen(val, vallen, maxfieldlen);
1413  appendBinaryStringInfo(&buf, val, vallen);
1414  appendStringInfoString(&buf, "...");
1415  }
1416  }
1417 
1418  appendStringInfoChar(&buf, ')');
1419 
1420  return buf.data;
1421 }
char * pg_get_partkeydef_columns(Oid relid, bool pretty)
Definition: ruleutils.c:1834
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2854
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:4556
Oid GetUserId(void)
Definition: miscinit.c:478
static Oid get_partition_col_typid(PartitionKey key, int col)
Definition: partcache.h:85
static int get_partition_natts(PartitionKey key)
Definition: partcache.h:64
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
unsigned int Oid
Definition: postgres_ext.h:31
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
int pg_mbcliplen(const char *mbstr, int len, int limit)
Definition: mbutils.c:1026
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:176
static char * buf
Definition: pg_test_fsync.c:68
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:188
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
static int16 get_partition_col_attnum(PartitionKey key, int col)
Definition: partcache.h:79
AclResult
Definition: acl.h:177
#define ACL_SELECT
Definition: parsenodes.h:83
#define InvalidOid
Definition: postgres_ext.h:36
int16 attnum
Definition: pg_attribute.h:83
int check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
Definition: rls.c:52
#define InvalidAttrNumber
Definition: attnum.h:23
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4685
static Datum values[MAXATTR]
Definition: bootstrap.c:166
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1653
int i
int16 AttrNumber
Definition: attnum.h:21
#define RelationGetRelid(relation)
Definition: rel.h:472
void appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
Definition: stringinfo.c:227
long val
Definition: informix.c:664

◆ ExecCleanupTupleRouting()

void ExecCleanupTupleRouting ( ModifyTableState mtstate,
PartitionTupleRouting proute 
)

Definition at line 1119 of file execPartition.c.

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 apply_handle_tuple_routing(), CopyFrom(), and ExecEndModifyTable().

1121 {
1122  int i;
1123 
1124  /*
1125  * Remember, proute->partition_dispatch_info[0] corresponds to the root
1126  * partitioned table, which we must not try to close, because it is the
1127  * main target table of the query that will be closed by callers such as
1128  * ExecEndPlan() or DoCopy(). Also, tupslot is NULL for the root
1129  * partitioned table.
1130  */
1131  for (i = 1; i < proute->num_dispatch; i++)
1132  {
1134 
1135  table_close(pd->reldesc, NoLock);
1136 
1137  if (pd->tupslot)
1139  }
1140 
1141  for (i = 0; i < proute->num_partitions; i++)
1142  {
1143  ResultRelInfo *resultRelInfo = proute->partitions[i];
1144 
1145  /* Allow any FDWs to shut down */
1146  if (resultRelInfo->ri_FdwRoutine != NULL &&
1147  resultRelInfo->ri_FdwRoutine->EndForeignInsert != NULL)
1148  resultRelInfo->ri_FdwRoutine->EndForeignInsert(mtstate->ps.state,
1149  resultRelInfo);
1150 
1151  /*
1152  * Close it if it's not one of the result relations borrowed from the
1153  * owning ModifyTableState; those will be closed by ExecEndPlan().
1154  */
1155  if (proute->is_borrowed_rel[i])
1156  continue;
1157 
1158  ExecCloseIndices(resultRelInfo);
1159  table_close(resultRelInfo->ri_RelationDesc, NoLock);
1160  }
1161 }
Relation ri_RelationDesc
Definition: execnodes.h:411
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
EState * state
Definition: execnodes.h:966
ResultRelInfo ** partitions
PlanState ps
Definition: execnodes.h:1183
#define NoLock
Definition: lockdefs.h:34
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1254
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:455
PartitionDispatch * partition_dispatch_info
Definition: execPartition.c:96
TupleTableSlot * tupslot
int i
void ExecCloseIndices(ResultRelInfo *resultRelInfo)
Definition: execIndexing.c:231
EndForeignInsert_function EndForeignInsert
Definition: fdwapi.h:239

◆ ExecCreatePartitionPruneState()

PartitionPruneState* ExecCreatePartitionPruneState ( PlanState planstate,
PartitionPruneInfo partitionpruneinfo 
)

Definition at line 1562 of file execPartition.c.

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(), ExecInitPruningContext(), PartitionPruneState::execparamids, PartitionedRelPruneInfo::execparamids, i, PartitionedRelPruningData::initial_context, PartitionedRelPruningData::initial_pruning_steps, PartitionedRelPruneInfo::initial_pruning_steps, lfirst_node, list_length(), PartitionDescData::nparts, PartitionedRelPruningData::nparts, PartitionedRelPruneInfo::nparts, PartitionPruneState::num_partprunedata, PartitionPruningData::num_partrelprunedata, offsetof, 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, RelationGetPartitionKey(), PartitionedRelPruneInfo::relid_map, PartitionedRelPruneInfo::rtindex, PlanState::state, PartitionedRelPruningData::subpart_map, PartitionedRelPruneInfo::subpart_map, PartitionedRelPruningData::subplan_map, and PartitionedRelPruneInfo::subplan_map.

Referenced by ExecInitAppend(), and ExecInitMergeAppend().

1564 {
1565  EState *estate = planstate->state;
1566  PartitionPruneState *prunestate;
1567  int n_part_hierarchies;
1568  ListCell *lc;
1569  int i;
1570 
1571  /* For data reading, executor always omits detached partitions */
1572  if (estate->es_partition_directory == NULL)
1573  estate->es_partition_directory =
1574  CreatePartitionDirectory(estate->es_query_cxt, false);
1575 
1576  n_part_hierarchies = list_length(partitionpruneinfo->prune_infos);
1577  Assert(n_part_hierarchies > 0);
1578 
1579  /*
1580  * Allocate the data structure
1581  */
1582  prunestate = (PartitionPruneState *)
1583  palloc(offsetof(PartitionPruneState, partprunedata) +
1584  sizeof(PartitionPruningData *) * n_part_hierarchies);
1585 
1586  prunestate->execparamids = NULL;
1587  /* other_subplans can change at runtime, so we need our own copy */
1588  prunestate->other_subplans = bms_copy(partitionpruneinfo->other_subplans);
1589  prunestate->do_initial_prune = false; /* may be set below */
1590  prunestate->do_exec_prune = false; /* may be set below */
1591  prunestate->num_partprunedata = n_part_hierarchies;
1592 
1593  /*
1594  * Create a short-term memory context which we'll use when making calls to
1595  * the partition pruning functions. This avoids possible memory leaks,
1596  * since the pruning functions call comparison functions that aren't under
1597  * our control.
1598  */
1599  prunestate->prune_context =
1601  "Partition Prune",
1603 
1604  i = 0;
1605  foreach(lc, partitionpruneinfo->prune_infos)
1606  {
1607  List *partrelpruneinfos = lfirst_node(List, lc);
1608  int npartrelpruneinfos = list_length(partrelpruneinfos);
1609  PartitionPruningData *prunedata;
1610  ListCell *lc2;
1611  int j;
1612 
1613  prunedata = (PartitionPruningData *)
1614  palloc(offsetof(PartitionPruningData, partrelprunedata) +
1615  npartrelpruneinfos * sizeof(PartitionedRelPruningData));
1616  prunestate->partprunedata[i] = prunedata;
1617  prunedata->num_partrelprunedata = npartrelpruneinfos;
1618 
1619  j = 0;
1620  foreach(lc2, partrelpruneinfos)
1621  {
1623  PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
1624  Relation partrel;
1625  PartitionDesc partdesc;
1626  PartitionKey partkey;
1627 
1628  /*
1629  * We can rely on the copies of the partitioned table's partition
1630  * key and partition descriptor appearing in its relcache entry,
1631  * because that entry will be held open and locked for the
1632  * duration of this executor run.
1633  */
1634  partrel = ExecGetRangeTableRelation(estate, pinfo->rtindex);
1635  partkey = RelationGetPartitionKey(partrel);
1637  partrel);
1638 
1639  /*
1640  * Initialize the subplan_map and subpart_map.
1641  *
1642  * Because we request detached partitions to be included, and
1643  * detaching waits for old transactions, it is safe to assume that
1644  * no partitions have disappeared since this query was planned.
1645  *
1646  * However, new partitions may have been added.
1647  */
1648  Assert(partdesc->nparts >= pinfo->nparts);
1649  pprune->nparts = partdesc->nparts;
1650  pprune->subplan_map = palloc(sizeof(int) * partdesc->nparts);
1651  if (partdesc->nparts == pinfo->nparts)
1652  {
1653  /*
1654  * There are no new partitions, so this is simple. We can
1655  * simply point to the subpart_map from the plan, but we must
1656  * copy the subplan_map since we may change it later.
1657  */
1658  pprune->subpart_map = pinfo->subpart_map;
1659  memcpy(pprune->subplan_map, pinfo->subplan_map,
1660  sizeof(int) * pinfo->nparts);
1661 
1662  /*
1663  * Double-check that the list of unpruned relations has not
1664  * changed. (Pruned partitions are not in relid_map[].)
1665  */
1666 #ifdef USE_ASSERT_CHECKING
1667  for (int k = 0; k < pinfo->nparts; k++)
1668  {
1669  Assert(partdesc->oids[k] == pinfo->relid_map[k] ||
1670  pinfo->subplan_map[k] == -1);
1671  }
1672 #endif
1673  }
1674  else
1675  {
1676  int pd_idx = 0;
1677  int pp_idx;
1678 
1679  /*
1680  * Some new partitions have appeared since plan time, and
1681  * those are reflected in our PartitionDesc but were not
1682  * present in the one used to construct subplan_map and
1683  * subpart_map. So we must construct new and longer arrays
1684  * where the partitions that were originally present map to
1685  * the same sub-structures, and any added partitions map to
1686  * -1, as if the new partitions had been pruned.
1687  *
1688  * Note: pinfo->relid_map[] may contain InvalidOid entries for
1689  * partitions pruned by the planner. We cannot tell exactly
1690  * which of the partdesc entries these correspond to, but we
1691  * don't have to; just skip over them. The non-pruned
1692  * relid_map entries, however, had better be a subset of the
1693  * partdesc entries and in the same order.
1694  */
1695  pprune->subpart_map = palloc(sizeof(int) * partdesc->nparts);
1696  for (pp_idx = 0; pp_idx < partdesc->nparts; pp_idx++)
1697  {
1698  /* Skip any InvalidOid relid_map entries */
1699  while (pd_idx < pinfo->nparts &&
1700  !OidIsValid(pinfo->relid_map[pd_idx]))
1701  pd_idx++;
1702 
1703  if (pd_idx < pinfo->nparts &&
1704  pinfo->relid_map[pd_idx] == partdesc->oids[pp_idx])
1705  {
1706  /* match... */
1707  pprune->subplan_map[pp_idx] =
1708  pinfo->subplan_map[pd_idx];
1709  pprune->subpart_map[pp_idx] =
1710  pinfo->subpart_map[pd_idx];
1711  pd_idx++;
1712  }
1713  else
1714  {
1715  /* this partdesc entry is not in the plan */
1716  pprune->subplan_map[pp_idx] = -1;
1717  pprune->subpart_map[pp_idx] = -1;
1718  }
1719  }
1720 
1721  /*
1722  * It might seem that we need to skip any trailing InvalidOid
1723  * entries in pinfo->relid_map before checking that we scanned
1724  * all of the relid_map. But we will have skipped them above,
1725  * because they must correspond to some partdesc->oids
1726  * entries; we just couldn't tell which.
1727  */
1728  if (pd_idx != pinfo->nparts)
1729  elog(ERROR, "could not match partition child tables to plan elements");
1730  }
1731 
1732  /* present_parts is also subject to later modification */
1733  pprune->present_parts = bms_copy(pinfo->present_parts);
1734 
1735  /*
1736  * Initialize pruning contexts as needed.
1737  */
1739  if (pinfo->initial_pruning_steps)
1740  {
1742  pinfo->initial_pruning_steps,
1743  partdesc, partkey, planstate);
1744  /* Record whether initial pruning is needed at any level */
1745  prunestate->do_initial_prune = true;
1746  }
1747  pprune->exec_pruning_steps = pinfo->exec_pruning_steps;
1748  if (pinfo->exec_pruning_steps)
1749  {
1751  pinfo->exec_pruning_steps,
1752  partdesc, partkey, planstate);
1753  /* Record whether exec pruning is needed at any level */
1754  prunestate->do_exec_prune = true;
1755  }
1756 
1757  /*
1758  * Accumulate the IDs of all PARAM_EXEC Params affecting the
1759  * partitioning decisions at this plan node.
1760  */
1761  prunestate->execparamids = bms_add_members(prunestate->execparamids,
1762  pinfo->execparamids);
1763 
1764  j++;
1765  }
1766  i++;
1767  }
1768 
1769  return prunestate;
1770 }
Bitmapset * execparamids
Definition: plannodes.h:1201
MemoryContext prune_context
#define AllocSetContextCreate
Definition: memutils.h:173
static void ExecInitPruningContext(PartitionPruneContext *context, List *pruning_steps, PartitionDesc partdesc, PartitionKey partkey, PlanState *planstate)
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:74
PartitionDirectory CreatePartitionDirectory(MemoryContext mcxt, bool omit_detached)
Definition: partdesc.c:367
PartitionPruningData * partprunedata[FLEXIBLE_ARRAY_MEMBER]
EState * state
Definition: execnodes.h:966
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
#define OidIsValid(objectId)
Definition: c.h:710
MemoryContext es_query_cxt
Definition: execnodes.h:598
#define ERROR
Definition: elog.h:46
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:195
#define lfirst_node(type, lc)
Definition: pg_list.h:172
Bitmapset * present_parts
Definition: plannodes.h:1186
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
Bitmapset * execparamids
PartitionPruneContext exec_context
Definition: execPartition.h:57
Relation ExecGetRangeTableRelation(EState *estate, Index rti)
Definition: execUtils.c:782
Bitmapset * other_subplans
Definition: plannodes.h:1162
#define Assert(condition)
Definition: c.h:804
static int list_length(const List *l)
Definition: pg_list.h:149
PartitionDirectory es_partition_directory
Definition: execnodes.h:580
PartitionedRelPruningData partrelprunedata[FLEXIBLE_ARRAY_MEMBER]
Definition: execPartition.h:70
void * palloc(Size size)
Definition: mcxt.c:1062
#define elog(elevel,...)
Definition: elog.h:232
int i
PartitionDesc PartitionDirectoryLookup(PartitionDirectory pdir, Relation rel)
Definition: partdesc.c:400
Definition: pg_list.h:50
PartitionPruneContext initial_context
Definition: execPartition.h:56
#define offsetof(type, field)
Definition: c.h:727
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:793
Bitmapset * other_subplans
struct PartitionedRelPruningData PartitionedRelPruningData

◆ ExecFindInitialMatchingSubPlans()

Bitmapset* ExecFindInitialMatchingSubPlans ( PartitionPruneState prunestate,
int  nsubplans 
)

Definition at line 1854 of file execPartition.c.

References Assert, bms_add_member(), bms_add_members(), bms_copy(), bms_free(), bms_is_empty(), bms_next_member(), bms_num_members(), PartitionPruneState::do_exec_prune, PartitionPruneState::do_initial_prune, find_matching_subplans_recurse(), i, PartitionedRelPruningData::initial_context, PartitionedRelPruningData::initial_pruning_steps, MemoryContextReset(), MemoryContextSwitchTo(), PartitionedRelPruningData::nparts, PartitionPruneState::num_partprunedata, PartitionPruningData::num_partrelprunedata, PartitionPruneState::other_subplans, palloc0(), PartitionPruneState::partprunedata, PartitionPruningData::partrelprunedata, pfree(), PartitionPruneContext::planstate, PartitionedRelPruningData::present_parts, PartitionPruneState::prune_context, PlanState::ps_ExprContext, ResetExprContext, PartitionedRelPruningData::subpart_map, and PartitionedRelPruningData::subplan_map.

Referenced by ExecInitAppend(), and ExecInitMergeAppend().

1855 {
1856  Bitmapset *result = NULL;
1857  MemoryContext oldcontext;
1858  int i;
1859 
1860  /* Caller error if we get here without do_initial_prune */
1861  Assert(prunestate->do_initial_prune);
1862 
1863  /*
1864  * Switch to a temp context to avoid leaking memory in the executor's
1865  * query-lifespan memory context.
1866  */
1867  oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
1868 
1869  /*
1870  * For each hierarchy, do the pruning tests, and add nondeletable
1871  * subplans' indexes to "result".
1872  */
1873  for (i = 0; i < prunestate->num_partprunedata; i++)
1874  {
1875  PartitionPruningData *prunedata;
1876  PartitionedRelPruningData *pprune;
1877 
1878  prunedata = prunestate->partprunedata[i];
1879  pprune = &prunedata->partrelprunedata[0];
1880 
1881  /* Perform pruning without using PARAM_EXEC Params */
1882  find_matching_subplans_recurse(prunedata, pprune, true, &result);
1883 
1884  /* Expression eval may have used space in node's ps_ExprContext too */
1885  if (pprune->initial_pruning_steps)
1887  }
1888 
1889  /* Add in any subplans that partition pruning didn't account for */
1890  result = bms_add_members(result, prunestate->other_subplans);
1891 
1892  MemoryContextSwitchTo(oldcontext);
1893 
1894  /* Copy result out of the temp context before we reset it */
1895  result = bms_copy(result);
1896 
1897  MemoryContextReset(prunestate->prune_context);
1898 
1899  /*
1900  * If exec-time pruning is required and we pruned subplans above, then we
1901  * must re-sequence the subplan indexes so that ExecFindMatchingSubPlans
1902  * properly returns the indexes from the subplans which will remain after
1903  * execution of this function.
1904  *
1905  * We can safely skip this when !do_exec_prune, even though that leaves
1906  * invalid data in prunestate, because that data won't be consulted again
1907  * (cf initial Assert in ExecFindMatchingSubPlans).
1908  */
1909  if (prunestate->do_exec_prune && bms_num_members(result) < nsubplans)
1910  {
1911  int *new_subplan_indexes;
1912  Bitmapset *new_other_subplans;
1913  int i;
1914  int newidx;
1915 
1916  /*
1917  * First we must build a temporary array which maps old subplan
1918  * indexes to new ones. For convenience of initialization, we use
1919  * 1-based indexes in this array and leave pruned items as 0.
1920  */
1921  new_subplan_indexes = (int *) palloc0(sizeof(int) * nsubplans);
1922  newidx = 1;
1923  i = -1;
1924  while ((i = bms_next_member(result, i)) >= 0)
1925  {
1926  Assert(i < nsubplans);
1927  new_subplan_indexes[i] = newidx++;
1928  }
1929 
1930  /*
1931  * Now we can update each PartitionedRelPruneInfo's subplan_map with
1932  * new subplan indexes. We must also recompute its present_parts
1933  * bitmap.
1934  */
1935  for (i = 0; i < prunestate->num_partprunedata; i++)
1936  {
1937  PartitionPruningData *prunedata = prunestate->partprunedata[i];
1938  int j;
1939 
1940  /*
1941  * Within each hierarchy, we perform this loop in back-to-front
1942  * order so that we determine present_parts for the lowest-level
1943  * partitioned tables first. This way we can tell whether a
1944  * sub-partitioned table's partitions were entirely pruned so we
1945  * can exclude it from the current level's present_parts.
1946  */
1947  for (j = prunedata->num_partrelprunedata - 1; j >= 0; j--)
1948  {
1949  PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
1950  int nparts = pprune->nparts;
1951  int k;
1952 
1953  /* We just rebuild present_parts from scratch */
1954  bms_free(pprune->present_parts);
1955  pprune->present_parts = NULL;
1956 
1957  for (k = 0; k < nparts; k++)
1958  {
1959  int oldidx = pprune->subplan_map[k];
1960  int subidx;
1961 
1962  /*
1963  * If this partition existed as a subplan then change the
1964  * old subplan index to the new subplan index. The new
1965  * index may become -1 if the partition was pruned above,
1966  * or it may just come earlier in the subplan list due to
1967  * some subplans being removed earlier in the list. If
1968  * it's a subpartition, add it to present_parts unless
1969  * it's entirely pruned.
1970  */
1971  if (oldidx >= 0)
1972  {
1973  Assert(oldidx < nsubplans);
1974  pprune->subplan_map[k] = new_subplan_indexes[oldidx] - 1;
1975 
1976  if (new_subplan_indexes[oldidx] > 0)
1977  pprune->present_parts =
1978  bms_add_member(pprune->present_parts, k);
1979  }
1980  else if ((subidx = pprune->subpart_map[k]) >= 0)
1981  {
1982  PartitionedRelPruningData *subprune;
1983 
1984  subprune = &prunedata->partrelprunedata[subidx];
1985 
1986  if (!bms_is_empty(subprune->present_parts))
1987  pprune->present_parts =
1988  bms_add_member(pprune->present_parts, k);
1989  }
1990  }
1991  }
1992  }
1993 
1994  /*
1995  * We must also recompute the other_subplans set, since indexes in it
1996  * may change.
1997  */
1998  new_other_subplans = NULL;
1999  i = -1;
2000  while ((i = bms_next_member(prunestate->other_subplans, i)) >= 0)
2001  new_other_subplans = bms_add_member(new_other_subplans,
2002  new_subplan_indexes[i] - 1);
2003 
2004  bms_free(prunestate->other_subplans);
2005  prunestate->other_subplans = new_other_subplans;
2006 
2007  pfree(new_subplan_indexes);
2008  }
2009 
2010  return result;
2011 }
MemoryContext prune_context
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:74
ExprContext * ps_ExprContext
Definition: execnodes.h:1003
PartitionPruningData * partprunedata[FLEXIBLE_ARRAY_MEMBER]
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1043
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:143
void pfree(void *pointer)
Definition: mcxt.c:1169
static void find_matching_subplans_recurse(PartitionPruningData *prunedata, PartitionedRelPruningData *pprune, bool initial_prune, Bitmapset **validsubplans)
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:646
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:701
void * palloc0(Size size)
Definition: mcxt.c:1093
void bms_free(Bitmapset *a)
Definition: bitmapset.c:208
#define Assert(condition)
Definition: c.h:804
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:736
PartitionedRelPruningData partrelprunedata[FLEXIBLE_ARRAY_MEMBER]
Definition: execPartition.h:70
int i
PartitionPruneContext initial_context
Definition: execPartition.h:56
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:793
#define ResetExprContext(econtext)
Definition: executor.h:526
Bitmapset * other_subplans
PlanState * planstate
Definition: partprune.h:58

◆ ExecFindMatchingSubPlans()

Bitmapset* ExecFindMatchingSubPlans ( PartitionPruneState prunestate)

Definition at line 2021 of file execPartition.c.

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

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

2022 {
2023  Bitmapset *result = NULL;
2024  MemoryContext oldcontext;
2025  int i;
2026 
2027  /*
2028  * If !do_exec_prune, we've got problems because
2029  * ExecFindInitialMatchingSubPlans will not have bothered to update
2030  * prunestate for whatever pruning it did.
2031  */
2032  Assert(prunestate->do_exec_prune);
2033 
2034  /*
2035  * Switch to a temp context to avoid leaking memory in the executor's
2036  * query-lifespan memory context.
2037  */
2038  oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
2039 
2040  /*
2041  * For each hierarchy, do the pruning tests, and add nondeletable
2042  * subplans' indexes to "result".
2043  */
2044  for (i = 0; i < prunestate->num_partprunedata; i++)
2045  {
2046  PartitionPruningData *prunedata;
2047  PartitionedRelPruningData *pprune;
2048 
2049  prunedata = prunestate->partprunedata[i];
2050  pprune = &prunedata->partrelprunedata[0];
2051 
2052  find_matching_subplans_recurse(prunedata, pprune, false, &result);
2053 
2054  /* Expression eval may have used space in node's ps_ExprContext too */
2055  if (pprune->exec_pruning_steps)
2057  }
2058 
2059  /* Add in any subplans that partition pruning didn't account for */
2060  result = bms_add_members(result, prunestate->other_subplans);
2061 
2062  MemoryContextSwitchTo(oldcontext);
2063 
2064  /* Copy result out of the temp context before we reset it */
2065  result = bms_copy(result);
2066 
2067  MemoryContextReset(prunestate->prune_context);
2068 
2069  return result;
2070 }
MemoryContext prune_context
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:74
ExprContext * ps_ExprContext
Definition: execnodes.h:1003
PartitionPruningData * partprunedata[FLEXIBLE_ARRAY_MEMBER]
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:143
static void find_matching_subplans_recurse(PartitionPruningData *prunedata, PartitionedRelPruningData *pprune, bool initial_prune, Bitmapset **validsubplans)
PartitionPruneContext exec_context
Definition: execPartition.h:57
#define Assert(condition)
Definition: c.h:804
PartitionedRelPruningData partrelprunedata[FLEXIBLE_ARRAY_MEMBER]
Definition: execPartition.h:70
int i
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:793
#define ResetExprContext(econtext)
Definition: executor.h:526
Bitmapset * other_subplans
PlanState * planstate
Definition: partprune.h:58

◆ ExecFindPartition()

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

Definition at line 257 of file execPartition.c.

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(), 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, ResultRelInfo::ri_RootToPartitionMap, ModifyTableState::rootResultRelInfo, PartitionDispatchData::tupmap, PartitionDispatchData::tupslot, and values.

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

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

◆ ExecInitPartitionDispatchInfo()

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

Definition at line 981 of file execPartition.c.

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

985 {
986  Relation rel;
987  PartitionDesc partdesc;
989  int dispatchidx;
990  MemoryContext oldcxt;
991 
992  /*
993  * For data modification, it is better that executor does not include
994  * partitions being detached, except when running in snapshot-isolation
995  * mode. This means that a read-committed transaction immediately gets a
996  * "no partition for tuple" error when a tuple is inserted into a
997  * partition that's being detached concurrently, but a transaction in
998  * repeatable-read mode can still use such a partition.
999  */
1000  if (estate->es_partition_directory == NULL)
1001  estate->es_partition_directory =
1004 
1005  oldcxt = MemoryContextSwitchTo(proute->memcxt);
1006 
1007  /*
1008  * Only sub-partitioned tables need to be locked here. The root
1009  * partitioned table will already have been locked as it's referenced in
1010  * the query's rtable.
1011  */
1012  if (partoid != RelationGetRelid(proute->partition_root))
1013  rel = table_open(partoid, RowExclusiveLock);
1014  else
1015  rel = proute->partition_root;
1016  partdesc = PartitionDirectoryLookup(estate->es_partition_directory, rel);
1017 
1019  partdesc->nparts * sizeof(int));
1020  pd->reldesc = rel;
1021  pd->key = RelationGetPartitionKey(rel);
1022  pd->keystate = NIL;
1023  pd->partdesc = partdesc;
1024  if (parent_pd != NULL)
1025  {
1026  TupleDesc tupdesc = RelationGetDescr(rel);
1027 
1028  /*
1029  * For sub-partitioned tables where the column order differs from its
1030  * direct parent partitioned table, we must store a tuple table slot
1031  * initialized with its tuple descriptor and a tuple conversion map to
1032  * convert a tuple from its parent's rowtype to its own. This is to
1033  * make sure that we are looking at the correct row using the correct
1034  * tuple descriptor when computing its partition key for tuple
1035  * routing.
1036  */
1038  tupdesc);
1039  pd->tupslot = pd->tupmap ?
1040  MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL;
1041  }
1042  else
1043  {
1044  /* Not required for the root partitioned table */
1045  pd->tupmap = NULL;
1046  pd->tupslot = NULL;
1047  }
1048 
1049  /*
1050  * Initialize with -1 to signify that the corresponding partition's
1051  * ResultRelInfo or PartitionDispatch has not been created yet.
1052  */
1053  memset(pd->indexes, -1, sizeof(int) * partdesc->nparts);
1054 
1055  /* Track in PartitionTupleRouting for later use */
1056  dispatchidx = proute->num_dispatch++;
1057 
1058  /* Allocate or enlarge the array, as needed */
1059  if (proute->num_dispatch >= proute->max_dispatch)
1060  {
1061  if (proute->max_dispatch == 0)
1062  {
1063  proute->max_dispatch = 4;
1065  palloc(sizeof(PartitionDispatch) * proute->max_dispatch);
1066  proute->nonleaf_partitions = (ResultRelInfo **)
1067  palloc(sizeof(ResultRelInfo *) * proute->max_dispatch);
1068  }
1069  else
1070  {
1071  proute->max_dispatch *= 2;
1074  sizeof(PartitionDispatch) * proute->max_dispatch);
1075  proute->nonleaf_partitions = (ResultRelInfo **)
1076  repalloc(proute->nonleaf_partitions,
1077  sizeof(ResultRelInfo *) * proute->max_dispatch);
1078  }
1079  }
1080  proute->partition_dispatch_info[dispatchidx] = pd;
1081 
1082  /*
1083  * If setting up a PartitionDispatch for a sub-partitioned table, we may
1084  * also need a minimally valid ResultRelInfo for checking the partition
1085  * constraint later; set that up now.
1086  */
1087  if (parent_pd)
1088  {
1090 
1091  InitResultRelInfo(rri, rel, 0, rootResultRelInfo, 0);
1092  proute->nonleaf_partitions[dispatchidx] = rri;
1093  }
1094  else
1095  proute->nonleaf_partitions[dispatchidx] = NULL;
1096 
1097  /*
1098  * Finally, if setting up a PartitionDispatch for a sub-partitioned table,
1099  * install a downlink in the parent to allow quick descent.
1100  */
1101  if (parent_pd)
1102  {
1103  Assert(parent_pd->indexes[partidx] == -1);
1104  parent_pd->indexes[partidx] = dispatchidx;
1105  }
1106 
1107  MemoryContextSwitchTo(oldcxt);
1108 
1109  return pd;
1110 }
#define NIL
Definition: pg_list.h:65
PartitionDesc partdesc
PartitionDirectory CreatePartitionDirectory(MemoryContext mcxt, bool omit_detached)
Definition: partdesc.c:367
#define RelationGetDescr(relation)
Definition: rel.h:498
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1238
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
MemoryContext es_query_cxt
Definition: execnodes.h:598
AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, TupleDesc outdesc)
Definition: attmap.c:259
#define RowExclusiveLock
Definition: lockdefs.h:38
PartitionDispatch * partition_dispatch_info
Definition: execPartition.c:96
ResultRelInfo ** nonleaf_partitions
Definition: execPartition.c:97
void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, ResultRelInfo *partition_root_rri, int instrument_options)
Definition: execMain.c:1193
TupleTableSlot * tupslot
#define makeNode(_type_)
Definition: nodes.h:587
#define Assert(condition)
Definition: c.h:804
PartitionDirectory es_partition_directory
Definition: execnodes.h:580
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1182
void * palloc(Size size)
Definition: mcxt.c:1062
PartitionDesc PartitionDirectoryLookup(PartitionDirectory pdir, Relation rel)
Definition: partdesc.c:400
MemoryContext memcxt
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
struct PartitionDispatchData * PartitionDispatch
Definition: execPartition.h:22
#define RelationGetRelid(relation)
Definition: rel.h:472
#define offsetof(type, field)
Definition: c.h:727
int indexes[FLEXIBLE_ARRAY_MEMBER]

◆ ExecInitPartitionInfo()

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

Definition at line 490 of file execPartition.c.

References adjust_partition_tlist(), Assert, build_attrmap_by_name(), castNode, CheckValidResultRel(), CMD_INSERT, CMD_UPDATE, copyObject, elog, ERROR, EState::es_instrument, EState::es_query_cxt, EState::es_tuple_routing_result_relations, EState::es_tupleTable, ExecBuildProjectionInfo(), ExecInitExtraTupleSlot(), ExecInitQual(), ExecInitRoutingInfo(), ExecOpenIndices(), ExecTypeFromTL(), get_partition_ancestors(), InitResultRelInfo(), INNER_VAR, lappend(), lappend_oid(), lfirst, lfirst_oid, linitial, list_free(), list_length(), list_member_oid(), makeNode, map_variable_attnos(), PartitionTupleRouting::memcxt, MemoryContextSwitchTo(), NIL, OnConflictSetState::oc_ProjInfo, OnConflictSetState::oc_ProjSlot, OnConflictSetState::oc_WhereClause, PartitionDescData::oids, ONCONFLICT_NONE, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, 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(), TTSOpsVirtual, and ModifyTable::withCheckOptionLists.

Referenced by ExecFindPartition().

495 {
496  ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
497  Oid partOid = dispatch->partdesc->oids[partidx];
498  Relation partrel;
499  int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
500  Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
501  ResultRelInfo *leaf_part_rri;
502  MemoryContext oldcxt;
503  AttrMap *part_attmap = NULL;
504  bool found_whole_row;
505 
506  oldcxt = MemoryContextSwitchTo(proute->memcxt);
507 
508  partrel = table_open(partOid, RowExclusiveLock);
509 
510  leaf_part_rri = makeNode(ResultRelInfo);
511  InitResultRelInfo(leaf_part_rri,
512  partrel,
513  0,
514  rootResultRelInfo,
515  estate->es_instrument);
516 
517  /*
518  * Verify result relation is a valid target for an INSERT. An UPDATE of a
519  * partition-key becomes a DELETE+INSERT operation, so this check is still
520  * required when the operation is CMD_UPDATE.
521  */
522  CheckValidResultRel(leaf_part_rri, CMD_INSERT);
523 
524  /*
525  * Open partition indices. The user may have asked to check for conflicts
526  * within this leaf partition and do "nothing" instead of throwing an
527  * error. Be prepared in that case by initializing the index information
528  * needed by ExecInsert() to perform speculative insertions.
529  */
530  if (partrel->rd_rel->relhasindex &&
531  leaf_part_rri->ri_IndexRelationDescs == NULL)
532  ExecOpenIndices(leaf_part_rri,
533  (node != NULL &&
535 
536  /*
537  * Build WITH CHECK OPTION constraints for the partition. Note that we
538  * didn't build the withCheckOptionList for partitions within the planner,
539  * but simple translation of varattnos will suffice. This only occurs for
540  * the INSERT case or in the case of UPDATE tuple routing where we didn't
541  * find a result rel to reuse.
542  */
543  if (node && node->withCheckOptionLists != NIL)
544  {
545  List *wcoList;
546  List *wcoExprs = NIL;
547  ListCell *ll;
548 
549  /*
550  * In the case of INSERT on a partitioned table, there is only one
551  * plan. Likewise, there is only one WCO list, not one per partition.
552  * For UPDATE, there are as many WCO lists as there are plans.
553  */
554  Assert((node->operation == CMD_INSERT &&
555  list_length(node->withCheckOptionLists) == 1 &&
556  list_length(node->resultRelations) == 1) ||
557  (node->operation == CMD_UPDATE &&
559  list_length(node->resultRelations)));
560 
561  /*
562  * Use the WCO list of the first plan as a reference to calculate
563  * attno's for the WCO list of this partition. In the INSERT case,
564  * that refers to the root partitioned table, whereas in the UPDATE
565  * tuple routing case, that refers to the first partition in the
566  * mtstate->resultRelInfo array. In any case, both that relation and
567  * this partition should have the same columns, so we should be able
568  * to map attributes successfully.
569  */
570  wcoList = linitial(node->withCheckOptionLists);
571 
572  /*
573  * Convert Vars in it to contain this partition's attribute numbers.
574  */
575  part_attmap =
577  RelationGetDescr(firstResultRel));
578  wcoList = (List *)
579  map_variable_attnos((Node *) wcoList,
580  firstVarno, 0,
581  part_attmap,
582  RelationGetForm(partrel)->reltype,
583  &found_whole_row);
584  /* We ignore the value of found_whole_row. */
585 
586  foreach(ll, wcoList)
587  {
589  ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual),
590  &mtstate->ps);
591 
592  wcoExprs = lappend(wcoExprs, wcoExpr);
593  }
594 
595  leaf_part_rri->ri_WithCheckOptions = wcoList;
596  leaf_part_rri->ri_WithCheckOptionExprs = wcoExprs;
597  }
598 
599  /*
600  * Build the RETURNING projection for the partition. Note that we didn't
601  * build the returningList for partitions within the planner, but simple
602  * translation of varattnos will suffice. This only occurs for the INSERT
603  * case or in the case of UPDATE tuple routing where we didn't find a
604  * result rel to reuse.
605  */
606  if (node && node->returningLists != NIL)
607  {
608  TupleTableSlot *slot;
609  ExprContext *econtext;
610  List *returningList;
611 
612  /* See the comment above for WCO lists. */
613  Assert((node->operation == CMD_INSERT &&
614  list_length(node->returningLists) == 1 &&
615  list_length(node->resultRelations) == 1) ||
616  (node->operation == CMD_UPDATE &&
617  list_length(node->returningLists) ==
618  list_length(node->resultRelations)));
619 
620  /*
621  * Use the RETURNING list of the first plan as a reference to
622  * calculate attno's for the RETURNING list of this partition. See
623  * the comment above for WCO lists for more details on why this is
624  * okay.
625  */
626  returningList = linitial(node->returningLists);
627 
628  /*
629  * Convert Vars in it to contain this partition's attribute numbers.
630  */
631  if (part_attmap == NULL)
632  part_attmap =
634  RelationGetDescr(firstResultRel));
635  returningList = (List *)
636  map_variable_attnos((Node *) returningList,
637  firstVarno, 0,
638  part_attmap,
639  RelationGetForm(partrel)->reltype,
640  &found_whole_row);
641  /* We ignore the value of found_whole_row. */
642 
643  leaf_part_rri->ri_returningList = returningList;
644 
645  /*
646  * Initialize the projection itself.
647  *
648  * Use the slot and the expression context that would have been set up
649  * in ExecInitModifyTable() for projection's output.
650  */
651  Assert(mtstate->ps.ps_ResultTupleSlot != NULL);
652  slot = mtstate->ps.ps_ResultTupleSlot;
653  Assert(mtstate->ps.ps_ExprContext != NULL);
654  econtext = mtstate->ps.ps_ExprContext;
655  leaf_part_rri->ri_projectReturning =
656  ExecBuildProjectionInfo(returningList, econtext, slot,
657  &mtstate->ps, RelationGetDescr(partrel));
658  }
659 
660  /* Set up information needed for routing tuples to the partition. */
661  ExecInitRoutingInfo(mtstate, estate, proute, dispatch,
662  leaf_part_rri, partidx, false);
663 
664  /*
665  * If there is an ON CONFLICT clause, initialize state for it.
666  */
667  if (node && node->onConflictAction != ONCONFLICT_NONE)
668  {
669  TupleDesc partrelDesc = RelationGetDescr(partrel);
670  ExprContext *econtext = mtstate->ps.ps_ExprContext;
671  ListCell *lc;
672  List *arbiterIndexes = NIL;
673 
674  /*
675  * If there is a list of arbiter indexes, map it to a list of indexes
676  * in the partition. We do that by scanning the partition's index
677  * list and searching for ancestry relationships to each index in the
678  * ancestor table.
679  */
680  if (list_length(rootResultRelInfo->ri_onConflictArbiterIndexes) > 0)
681  {
682  List *childIdxs;
683 
684  childIdxs = RelationGetIndexList(leaf_part_rri->ri_RelationDesc);
685 
686  foreach(lc, childIdxs)
687  {
688  Oid childIdx = lfirst_oid(lc);
689  List *ancestors;
690  ListCell *lc2;
691 
692  ancestors = get_partition_ancestors(childIdx);
693  foreach(lc2, rootResultRelInfo->ri_onConflictArbiterIndexes)
694  {
695  if (list_member_oid(ancestors, lfirst_oid(lc2)))
696  arbiterIndexes = lappend_oid(arbiterIndexes, childIdx);
697  }
698  list_free(ancestors);
699  }
700  }
701 
702  /*
703  * If the resulting lists are of inequal length, something is wrong.
704  * (This shouldn't happen, since arbiter index selection should not
705  * pick up an invalid index.)
706  */
707  if (list_length(rootResultRelInfo->ri_onConflictArbiterIndexes) !=
708  list_length(arbiterIndexes))
709  elog(ERROR, "invalid arbiter index list");
710  leaf_part_rri->ri_onConflictArbiterIndexes = arbiterIndexes;
711 
712  /*
713  * In the DO UPDATE case, we have some more state to initialize.
714  */
715  if (node->onConflictAction == ONCONFLICT_UPDATE)
716  {
717  TupleConversionMap *map;
718 
719  map = leaf_part_rri->ri_RootToPartitionMap;
720 
721  Assert(node->onConflictSet != NIL);
722  Assert(rootResultRelInfo->ri_onConflict != NULL);
723 
724  leaf_part_rri->ri_onConflict = makeNode(OnConflictSetState);
725 
726  /*
727  * Need a separate existing slot for each partition, as the
728  * partition could be of a different AM, even if the tuple
729  * descriptors match.
730  */
731  leaf_part_rri->ri_onConflict->oc_Existing =
732  table_slot_create(leaf_part_rri->ri_RelationDesc,
733  &mtstate->ps.state->es_tupleTable);
734 
735  /*
736  * If the partition's tuple descriptor matches exactly the root
737  * parent (the common case), we can re-use most of the parent's ON
738  * CONFLICT SET state, skipping a bunch of work. Otherwise, we
739  * need to create state specific to this partition.
740  */
741  if (map == NULL)
742  {
743  /*
744  * It's safe to reuse these from the partition root, as we
745  * only process one tuple at a time (therefore we won't
746  * overwrite needed data in slots), and the results of
747  * projections are independent of the underlying storage.
748  * Projections and where clauses themselves don't store state
749  * / are independent of the underlying storage.
750  */
751  leaf_part_rri->ri_onConflict->oc_ProjSlot =
752  rootResultRelInfo->ri_onConflict->oc_ProjSlot;
753  leaf_part_rri->ri_onConflict->oc_ProjInfo =
754  rootResultRelInfo->ri_onConflict->oc_ProjInfo;
755  leaf_part_rri->ri_onConflict->oc_WhereClause =
756  rootResultRelInfo->ri_onConflict->oc_WhereClause;
757  }
758  else
759  {
760  List *onconflset;
761  TupleDesc tupDesc;
762  bool found_whole_row;
763 
764  /*
765  * Translate expressions in onConflictSet to account for
766  * different attribute numbers. For that, map partition
767  * varattnos twice: first to catch the EXCLUDED
768  * pseudo-relation (INNER_VAR), and second to handle the main
769  * target relation (firstVarno).
770  */
771  onconflset = (List *) copyObject((Node *) node->onConflictSet);
772  if (part_attmap == NULL)
773  part_attmap =
775  RelationGetDescr(firstResultRel));
776  onconflset = (List *)
777  map_variable_attnos((Node *) onconflset,
778  INNER_VAR, 0,
779  part_attmap,
780  RelationGetForm(partrel)->reltype,
781  &found_whole_row);
782  /* We ignore the value of found_whole_row. */
783  onconflset = (List *)
784  map_variable_attnos((Node *) onconflset,
785  firstVarno, 0,
786  part_attmap,
787  RelationGetForm(partrel)->reltype,
788  &found_whole_row);
789  /* We ignore the value of found_whole_row. */
790 
791  /* Finally, adjust this tlist to match the partition. */
792  onconflset = adjust_partition_tlist(onconflset, map);
793 
794  /* create the tuple slot for the UPDATE SET projection */
795  tupDesc = ExecTypeFromTL(onconflset);
796  leaf_part_rri->ri_onConflict->oc_ProjSlot =
797  ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc,
798  &TTSOpsVirtual);
799 
800  /* build UPDATE SET projection state */
801  leaf_part_rri->ri_onConflict->oc_ProjInfo =
802  ExecBuildProjectionInfo(onconflset, econtext,
803  leaf_part_rri->ri_onConflict->oc_ProjSlot,
804  &mtstate->ps, partrelDesc);
805 
806  /*
807  * If there is a WHERE clause, initialize state where it will
808  * be evaluated, mapping the attribute numbers appropriately.
809  * As with onConflictSet, we need to map partition varattnos
810  * to the partition's tupdesc.
811  */
812  if (node->onConflictWhere)
813  {
814  List *clause;
815 
816  clause = copyObject((List *) node->onConflictWhere);
817  clause = (List *)
818  map_variable_attnos((Node *) clause,
819  INNER_VAR, 0,
820  part_attmap,
821  RelationGetForm(partrel)->reltype,
822  &found_whole_row);
823  /* We ignore the value of found_whole_row. */
824  clause = (List *)
825  map_variable_attnos((Node *) clause,
826  firstVarno, 0,
827  part_attmap,
828  RelationGetForm(partrel)->reltype,
829  &found_whole_row);
830  /* We ignore the value of found_whole_row. */
831  leaf_part_rri->ri_onConflict->oc_WhereClause =
832  ExecInitQual((List *) clause, &mtstate->ps);
833  }
834  }
835  }
836  }
837 
838  /*
839  * Since we've just initialized this ResultRelInfo, it's not in any list
840  * attached to the estate as yet. Add it, so that it can be found later.
841  *
842  * Note that the entries in this list appear in no predetermined order,
843  * because partition result rels are initialized as and when they're
844  * needed.
845  */
849  leaf_part_rri);
850 
851  MemoryContextSwitchTo(oldcxt);
852 
853  return leaf_part_rri;
854 }
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
#define NIL
Definition: pg_list.h:65
Relation ri_RelationDesc
Definition: execnodes.h:411
PartitionDesc partdesc
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1831
#define RelationGetDescr(relation)
Definition: rel.h:498
List * withCheckOptionLists
Definition: plannodes.h:227
#define castNode(_type_, nodeptr)
Definition: nodes.h:608
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1188
#define RelationGetForm(relation)
Definition: rel.h:466
ExprContext * ps_ExprContext
Definition: execnodes.h:1003
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
Definition: nodes.h:539
EState * state
Definition: execnodes.h:966
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
unsigned int Oid
Definition: postgres_ext.h:31
List * lappend_oid(List *list, Oid datum)
Definition: list.c:372
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:209
Index ri_RangeTableIndex
Definition: execnodes.h:408
List * onConflictSet
Definition: plannodes.h:235
Definition: attmap.h:34
List * resultRelations
Definition: plannodes.h:225
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
Definition: execIndexing.c:156
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1002
MemoryContext es_query_cxt
Definition: execnodes.h:598
#define linitial(l)
Definition: pg_list.h:174
#define ERROR
Definition: elog.h:46
PlanState ps
Definition: execnodes.h:1183
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:382
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
Definition: execMain.c:992
#define RowExclusiveLock
Definition: lockdefs.h:38
int es_instrument
Definition: execnodes.h:605
ExprState * oc_WhereClause
Definition: execnodes.h:383
List * lappend(List *list, void *datum)
Definition: list.c:336
OnConflictSetState * ri_onConflict
Definition: execnodes.h:494
List * es_tupleTable
Definition: execnodes.h:600
void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, ResultRelInfo *partition_root_rri, int instrument_options)
Definition: execMain.c:1193
Plan * plan
Definition: execnodes.h:964
List * es_tuple_routing_result_relations
Definition: execnodes.h:586
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:381
#define INNER_VAR
Definition: primnodes.h:175
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc)
Definition: attmap.c:174
#define makeNode(_type_)
Definition: nodes.h:587
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:689
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
OnConflictAction onConflictAction
Definition: plannodes.h:233
static List * adjust_partition_tlist(List *tlist, TupleConversionMap *map)
static int list_length(const List *l)
Definition: pg_list.h:149
TupleDesc ExecTypeFromTL(List *targetList)
Definition: execTuples.c:1938
static void ExecInitRoutingInfo(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, PartitionDispatch dispatch, ResultRelInfo *partRelInfo, int partidx, bool is_borrowed_rel)
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4571
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:353
CmdType operation
Definition: plannodes.h:220
void list_free(List *list)
Definition: list.c:1391
#define elog(elevel,...)
Definition: elog.h:232
List * returningLists
Definition: plannodes.h:228
MemoryContext memcxt
#define copyObject(obj)
Definition: nodes.h:655
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
Definition: pg_list.h:50
List * get_partition_ancestors(Oid relid)
Definition: partition.c:133
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:491
#define lfirst_oid(lc)
Definition: pg_list.h:171
Node * onConflictWhere
Definition: plannodes.h:236

◆ ExecInitPruningContext()

static void ExecInitPruningContext ( PartitionPruneContext context,
List pruning_steps,
PartitionDesc  partdesc,
PartitionKey  partkey,
PlanState planstate 
)
static

Definition at line 1776 of file execPartition.c.

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

Referenced by ExecCreatePartitionPruneState().

1781 {
1782  int n_steps;
1783  int partnatts;
1784  ListCell *lc;
1785 
1786  n_steps = list_length(pruning_steps);
1787 
1788  context->strategy = partkey->strategy;
1789  context->partnatts = partnatts = partkey->partnatts;
1790  context->nparts = partdesc->nparts;
1791  context->boundinfo = partdesc->boundinfo;
1792  context->partcollation = partkey->partcollation;
1793  context->partsupfunc = partkey->partsupfunc;
1794 
1795  /* We'll look up type-specific support functions as needed */
1796  context->stepcmpfuncs = (FmgrInfo *)
1797  palloc0(sizeof(FmgrInfo) * n_steps * partnatts);
1798 
1799  context->ppccontext = CurrentMemoryContext;
1800  context->planstate = planstate;
1801 
1802  /* Initialize expression state for each expression we need */
1803  context->exprstates = (ExprState **)
1804  palloc0(sizeof(ExprState *) * n_steps * partnatts);
1805  foreach(lc, pruning_steps)
1806  {
1808  ListCell *lc2;
1809  int keyno;
1810 
1811  /* not needed for other step kinds */
1812  if (!IsA(step, PartitionPruneStepOp))
1813  continue;
1814 
1815  Assert(list_length(step->exprs) <= partnatts);
1816 
1817  keyno = 0;
1818  foreach(lc2, step->exprs)
1819  {
1820  Expr *expr = (Expr *) lfirst(lc2);
1821 
1822  /* not needed for Consts */
1823  if (!IsA(expr, Const))
1824  {
1825  int stateidx = PruneCxtStateIdx(partnatts,
1826  step->step.step_id,
1827  keyno);
1828 
1829  context->exprstates[stateidx] =
1830  ExecInitExpr(expr, context->planstate);
1831  }
1832  keyno++;
1833  }
1834  }
1835 }
Definition: fmgr.h:56
FmgrInfo * partsupfunc
Definition: partprune.h:55
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
FmgrInfo * stepcmpfuncs
Definition: partprune.h:56
FmgrInfo * partsupfunc
Definition: partcache.h:35
MemoryContext ppccontext
Definition: partprune.h:57
PartitionBoundInfo boundinfo
Definition: partdesc.h:38
ExprState ** exprstates
Definition: partprune.h:59
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
PartitionPruneStep step
Definition: plannodes.h:1246
Oid * partcollation
Definition: partcache.h:38
void * palloc0(Size size)
Definition: mcxt.c:1093
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
static int list_length(const List *l)
Definition: pg_list.h:149
PartitionBoundInfo boundinfo
Definition: partprune.h:53
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:123
#define PruneCxtStateIdx(partnatts, step_id, keyno)
Definition: partprune.h:68
PlanState * planstate
Definition: partprune.h:58

◆ ExecInitRoutingInfo()

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

Definition at line 863 of file execPartition.c.

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

Referenced by ExecFindPartition(), and ExecInitPartitionInfo().

870 {
871  ResultRelInfo *rootRelInfo = partRelInfo->ri_RootResultRelInfo;
872  MemoryContext oldcxt;
873  int rri_index;
874 
875  oldcxt = MemoryContextSwitchTo(proute->memcxt);
876 
877  /*
878  * Set up a tuple conversion map to convert a tuple routed to the
879  * partition from the parent's type to the partition's.
880  */
881  partRelInfo->ri_RootToPartitionMap =
883  RelationGetDescr(partRelInfo->ri_RelationDesc));
884 
885  /*
886  * If a partition has a different rowtype than the root parent, initialize
887  * a slot dedicated to storing this partition's tuples. The slot is used
888  * for various operations that are applied to tuples after routing, such
889  * as checking constraints.
890  */
891  if (partRelInfo->ri_RootToPartitionMap != NULL)
892  {
893  Relation partrel = partRelInfo->ri_RelationDesc;
894 
895  /*
896  * Initialize the slot itself setting its descriptor to this
897  * partition's TupleDesc; TupleDesc reference will be released at the
898  * end of the command.
899  */
900  partRelInfo->ri_PartitionTupleSlot =
901  table_slot_create(partrel, &estate->es_tupleTable);
902  }
903  else
904  partRelInfo->ri_PartitionTupleSlot = NULL;
905 
906  /*
907  * If the partition is a foreign table, let the FDW init itself for
908  * routing tuples to the partition.
909  */
910  if (partRelInfo->ri_FdwRoutine != NULL &&
911  partRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL)
912  partRelInfo->ri_FdwRoutine->BeginForeignInsert(mtstate, partRelInfo);
913 
914  /*
915  * Determine if the FDW supports batch insert and determine the batch
916  * size (a FDW may support batching, but it may be disabled for the
917  * server/table or for this particular query).
918  *
919  * If the FDW does not support batching, we set the batch size to 1.
920  */
921  if (mtstate->operation == CMD_INSERT &&
922  partRelInfo->ri_FdwRoutine != NULL &&
925  partRelInfo->ri_BatchSize =
926  partRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(partRelInfo);
927  else
928  partRelInfo->ri_BatchSize = 1;
929 
930  Assert(partRelInfo->ri_BatchSize >= 1);
931 
932  partRelInfo->ri_CopyMultiInsertBuffer = NULL;
933 
934  /*
935  * Keep track of it in the PartitionTupleRouting->partitions array.
936  */
937  Assert(dispatch->indexes[partidx] == -1);
938 
939  rri_index = proute->num_partitions++;
940 
941  /* Allocate or enlarge the array, as needed */
942  if (proute->num_partitions >= proute->max_partitions)
943  {
944  if (proute->max_partitions == 0)
945  {
946  proute->max_partitions = 8;
947  proute->partitions = (ResultRelInfo **)
948  palloc(sizeof(ResultRelInfo *) * proute->max_partitions);
949  proute->is_borrowed_rel = (bool *)
950  palloc(sizeof(bool) * proute->max_partitions);
951  }
952  else
953  {
954  proute->max_partitions *= 2;
955  proute->partitions = (ResultRelInfo **)
956  repalloc(proute->partitions, sizeof(ResultRelInfo *) *
957  proute->max_partitions);
958  proute->is_borrowed_rel = (bool *)
959  repalloc(proute->is_borrowed_rel, sizeof(bool) *
960  proute->max_partitions);
961  }
962  }
963 
964  proute->partitions[rri_index] = partRelInfo;
965  proute->is_borrowed_rel[rri_index] = is_borrowed_rel;
966  dispatch->indexes[partidx] = rri_index;
967 
968  MemoryContextSwitchTo(oldcxt);
969 }
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
Relation ri_RelationDesc
Definition: execnodes.h:411
struct CopyMultiInsertBuffer * ri_CopyMultiInsertBuffer
Definition: execnodes.h:525
#define RelationGetDescr(relation)
Definition: rel.h:498
BeginForeignInsert_function BeginForeignInsert
Definition: fdwapi.h:238
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
CmdType operation
Definition: execnodes.h:1184
ResultRelInfo ** partitions
TupleTableSlot * ri_PartitionTupleSlot
Definition: execnodes.h:513
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:511
TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc)
Definition: tupconvert.c:102
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:455
GetForeignModifyBatchSize_function GetForeignModifyBatchSize
Definition: fdwapi.h:234
List * es_tupleTable
Definition: execnodes.h:600
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:233
int ri_BatchSize
Definition: execnodes.h:465
#define Assert(condition)
Definition: c.h:804
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1182
void * palloc(Size size)
Definition: mcxt.c:1062
TupleConversionMap * ri_RootToPartitionMap
Definition: execnodes.h:512
MemoryContext memcxt
int indexes[FLEXIBLE_ARRAY_MEMBER]

◆ ExecSetupPartitionTupleRouting()

PartitionTupleRouting* ExecSetupPartitionTupleRouting ( EState estate,
Relation  rel 
)

Definition at line 210 of file execPartition.c.

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

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

211 {
212  PartitionTupleRouting *proute;
213 
214  /*
215  * Here we attempt to expend as little effort as possible in setting up
216  * the PartitionTupleRouting. Each partition's ResultRelInfo is built on
217  * demand, only when we actually need to route a tuple to that partition.
218  * The reason for this is that a common case is for INSERT to insert a
219  * single tuple into a partitioned table and this must be fast.
220  */
222  proute->partition_root = rel;
223  proute->memcxt = CurrentMemoryContext;
224  /* Rest of members initialized by zeroing */
225 
226  /*
227  * Initialize this table's PartitionDispatch object. Here we pass in the
228  * parent as NULL as we don't need to care about any parent of the target
229  * partitioned table.
230  */
231  ExecInitPartitionDispatchInfo(estate, proute, RelationGetRelid(rel),
232  NULL, 0, NULL);
233 
234  return proute;
235 }
static PartitionDispatch ExecInitPartitionDispatchInfo(EState *estate, PartitionTupleRouting *proute, Oid partoid, PartitionDispatch parent_pd, int partidx, ResultRelInfo *rootResultRelInfo)
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
void * palloc0(Size size)
Definition: mcxt.c:1093
MemoryContext memcxt
#define RelationGetRelid(relation)
Definition: rel.h:472

◆ find_matching_subplans_recurse()

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

Definition at line 2080 of file execPartition.c.

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 ExecFindInitialMatchingSubPlans(), and ExecFindMatchingSubPlans().

2084 {
2085  Bitmapset *partset;
2086  int i;
2087 
2088  /* Guard against stack overflow due to overly deep partition hierarchy. */
2090 
2091  /* Only prune if pruning would be useful at this level. */
2092  if (initial_prune && pprune->initial_pruning_steps)
2093  {
2094  partset = get_matching_partitions(&pprune->initial_context,
2095  pprune->initial_pruning_steps);
2096  }
2097  else if (!initial_prune && pprune->exec_pruning_steps)
2098  {
2099  partset = get_matching_partitions(&pprune->exec_context,
2100  pprune->exec_pruning_steps);
2101  }
2102  else
2103  {
2104  /*
2105  * If no pruning is to be done, just include all partitions at this
2106  * level.
2107  */
2108  partset = pprune->present_parts;
2109  }
2110 
2111  /* Translate partset into subplan indexes */
2112  i = -1;
2113  while ((i = bms_next_member(partset, i)) >= 0)
2114  {
2115  if (pprune->subplan_map[i] >= 0)
2116  *validsubplans = bms_add_member(*validsubplans,
2117  pprune->subplan_map[i]);
2118  else
2119  {
2120  int partidx = pprune->subpart_map[i];
2121 
2122  if (partidx >= 0)
2124  &prunedata->partrelprunedata[partidx],
2125  initial_prune, validsubplans);
2126  else
2127  {
2128  /*
2129  * We get here if the planner already pruned all the sub-
2130  * partitions for this partition. Silently ignore this
2131  * partition in this case. The end result is the same: we
2132  * would have pruned all partitions just the same, but we
2133  * don't have any pruning steps to execute to verify this.
2134  */
2135  }
2136  }
2137  }
2138 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1043
static void find_matching_subplans_recurse(PartitionPruningData *prunedata, PartitionedRelPruningData *pprune, bool initial_prune, Bitmapset **validsubplans)
void check_stack_depth(void)
Definition: postgres.c:3459
PartitionPruneContext exec_context
Definition: execPartition.h:57
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:736
PartitionedRelPruningData partrelprunedata[FLEXIBLE_ARRAY_MEMBER]
Definition: execPartition.h:70
Bitmapset * get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
Definition: partprune.c:819
int i
PartitionPruneContext initial_context
Definition: execPartition.h:56

◆ FormPartitionKeyDatum()

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

Definition at line 1180 of file execPartition.c.

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

Referenced by ExecFindPartition().

1185 {
1186  ListCell *partexpr_item;
1187  int i;
1188 
1189  if (pd->key->partexprs != NIL && pd->keystate == NIL)
1190  {
1191  /* Check caller has set up context correctly */
1192  Assert(estate != NULL &&
1193  GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
1194 
1195  /* First time through, set up expression evaluation state */
1196  pd->keystate = ExecPrepareExprList(pd->key->partexprs, estate);
1197  }
1198 
1199  partexpr_item = list_head(pd->keystate);
1200  for (i = 0; i < pd->key->partnatts; i++)
1201  {
1202  AttrNumber keycol = pd->key->partattrs[i];
1203  Datum datum;
1204  bool isNull;
1205 
1206  if (keycol != 0)
1207  {
1208  /* Plain column; get the value directly from the heap tuple */
1209  datum = slot_getattr(slot, keycol, &isNull);
1210  }
1211  else
1212  {
1213  /* Expression; need to evaluate it */
1214  if (partexpr_item == NULL)
1215  elog(ERROR, "wrong number of partition key expressions");
1216  datum = ExecEvalExprSwitchContext((ExprState *) lfirst(partexpr_item),
1217  GetPerTupleExprContext(estate),
1218  &isNull);
1219  partexpr_item = lnext(pd->keystate, partexpr_item);
1220  }
1221  values[i] = datum;
1222  isnull[i] = isNull;
1223  }
1224 
1225  if (partexpr_item != NULL)
1226  elog(ERROR, "wrong number of partition key expressions");
1227 }
#define NIL
Definition: pg_list.h:65
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:330
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:322
List * partexprs
Definition: partcache.h:30
#define GetPerTupleExprContext(estate)
Definition: executor.h:532
#define ERROR
Definition: elog.h:46
static ListCell * list_head(const List *l)
Definition: pg_list.h:125
List * ExecPrepareExprList(List *nodes, EState *estate)
Definition: execExpr.c:766
AttrNumber * partattrs
Definition: partcache.h:28
uintptr_t Datum
Definition: postgres.h:411
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:381
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
static Datum values[MAXATTR]
Definition: bootstrap.c:166
#define elog(elevel,...)
Definition: elog.h:232
int i
int16 AttrNumber
Definition: attnum.h:21

◆ get_partition_for_tuple()

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

Definition at line 1238 of file execPartition.c.

References PartitionDescData::boundinfo, compute_partition_hash_value(), PartitionBoundInfoData::default_index, elog, equal(), ERROR, i, PartitionBoundInfoData::indexes, sort-test::key, PartitionDispatchData::key, PartitionBoundInfoData::nindexes, PartitionBoundInfoData::null_index, PartitionKeyData::partcollation, PartitionDispatchData::partdesc, partition_bound_accepts_nulls, partition_list_bsearch(), partition_range_datum_bsearch(), PARTITION_STRATEGY_HASH, PARTITION_STRATEGY_LIST, PARTITION_STRATEGY_RANGE, PartitionKeyData::partnatts, PartitionKeyData::partsupfunc, and PartitionKeyData::strategy.

Referenced by ExecFindPartition().

1239 {
1240  int bound_offset;
1241  int part_index = -1;
1242  PartitionKey key = pd->key;
1243  PartitionDesc partdesc = pd->partdesc;
1244  PartitionBoundInfo boundinfo = partdesc->boundinfo;
1245 
1246  /* Route as appropriate based on partitioning strategy. */
1247  switch (key->strategy)
1248  {
1250  {
1251  uint64 rowHash;
1252 
1253  rowHash = compute_partition_hash_value(key->partnatts,
1254  key->partsupfunc,
1255  key->partcollation,
1256  values, isnull);
1257 
1258  part_index = boundinfo->indexes[rowHash % boundinfo->nindexes];
1259  }
1260  break;
1261 
1263  if (isnull[0])
1264  {
1265  if (partition_bound_accepts_nulls(boundinfo))
1266  part_index = boundinfo->null_index;
1267  }
1268  else
1269  {
1270  bool equal = false;
1271 
1272  bound_offset = partition_list_bsearch(key->partsupfunc,
1273  key->partcollation,
1274  boundinfo,
1275  values[0], &equal);
1276  if (bound_offset >= 0 && equal)
1277  part_index = boundinfo->indexes[bound_offset];
1278  }
1279  break;
1280 
1282  {
1283  bool equal = false,
1284  range_partkey_has_null = false;
1285  int i;
1286 
1287  /*
1288  * No range includes NULL, so this will be accepted by the
1289  * default partition if there is one, and otherwise rejected.
1290  */
1291  for (i = 0; i < key->partnatts; i++)
1292  {
1293  if (isnull[i])
1294  {
1295  range_partkey_has_null = true;
1296  break;
1297  }
1298  }
1299 
1300  if (!range_partkey_has_null)
1301  {
1302  bound_offset = partition_range_datum_bsearch(key->partsupfunc,
1303  key->partcollation,
1304  boundinfo,
1305  key->partnatts,
1306  values,
1307  &equal);
1308 
1309  /*
1310  * The bound at bound_offset is less than or equal to the
1311  * tuple value, so the bound at offset+1 is the upper
1312  * bound of the partition we're looking for, if there
1313  * actually exists one.
1314  */
1315  part_index = boundinfo->indexes[bound_offset + 1];
1316  }
1317  }
1318  break;
1319 
1320  default:
1321  elog(ERROR, "unexpected partition strategy: %d",
1322  (int) key->strategy);
1323  }
1324 
1325  /*
1326  * part_index < 0 means we failed to find a partition of this parent. Use
1327  * the default partition, if there is one.
1328  */
1329  if (part_index < 0)
1330  part_index = boundinfo->default_index;
1331 
1332  return part_index;
1333 }
PartitionDesc partdesc
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3105
FmgrInfo * partsupfunc
Definition: partcache.h:35
PartitionBoundInfo boundinfo
Definition: partdesc.h:38
int partition_range_datum_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, Datum *values, bool *is_equal)
Definition: partbounds.c:3587
#define ERROR
Definition: elog.h:46
Oid * partcollation
Definition: partcache.h:38
#define PARTITION_STRATEGY_HASH
Definition: parsenodes.h:814
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:81
int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
Definition: partbounds.c:3499
uint64 compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *values, bool *isnull)
Definition: partbounds.c:4618
#define PARTITION_STRATEGY_LIST
Definition: parsenodes.h:815
static Datum values[MAXATTR]
Definition: bootstrap.c:166
#define PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:816
#define elog(elevel,...)
Definition: elog.h:232
int i