PostgreSQL Source Code  git master
execPartition.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * execPartition.c
4  * Support routines for partitioning.
5  *
6  * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  * src/backend/executor/execPartition.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
16 #include "access/table.h"
17 #include "access/tableam.h"
18 #include "catalog/partition.h"
19 #include "catalog/pg_inherits.h"
20 #include "catalog/pg_type.h"
21 #include "executor/execPartition.h"
22 #include "executor/executor.h"
24 #include "foreign/fdwapi.h"
25 #include "mb/pg_wchar.h"
26 #include "miscadmin.h"
27 #include "nodes/makefuncs.h"
29 #include "partitioning/partdesc.h"
30 #include "partitioning/partprune.h"
31 #include "rewrite/rewriteManip.h"
32 #include "utils/acl.h"
33 #include "utils/lsyscache.h"
34 #include "utils/partcache.h"
35 #include "utils/rls.h"
36 #include "utils/ruleutils.h"
37 
38 
39 /*-----------------------
40  * PartitionTupleRouting - Encapsulates all information required to
41  * route a tuple inserted into a partitioned table to one of its leaf
42  * partitions.
43  *
44  * partition_root
45  * The partitioned table that's the target of the command.
46  *
47  * partition_dispatch_info
48  * Array of 'max_dispatch' elements containing a pointer to a
49  * PartitionDispatch object for every partitioned table touched by tuple
50  * routing. The entry for the target partitioned table is *always*
51  * present in the 0th element of this array. See comment for
52  * PartitionDispatchData->indexes for details on how this array is
53  * indexed.
54  *
55  * nonleaf_partitions
56  * Array of 'max_dispatch' elements containing pointers to fake
57  * ResultRelInfo objects for nonleaf partitions, useful for checking
58  * the partition constraint.
59  *
60  * num_dispatch
61  * The current number of items stored in the 'partition_dispatch_info'
62  * array. Also serves as the index of the next free array element for
63  * new PartitionDispatch objects that need to be stored.
64  *
65  * max_dispatch
66  * The current allocated size of the 'partition_dispatch_info' array.
67  *
68  * partitions
69  * Array of 'max_partitions' elements containing a pointer to a
70  * ResultRelInfo for every leaf partition touched by tuple routing.
71  * Some of these are pointers to ResultRelInfos which are borrowed out of
72  * the owning ModifyTableState node. The remainder have been built
73  * especially for tuple routing. See comment for
74  * PartitionDispatchData->indexes for details on how this array is
75  * indexed.
76  *
77  * is_borrowed_rel
78  * Array of 'max_partitions' booleans recording whether a given entry
79  * in 'partitions' is a ResultRelInfo pointer borrowed from the owning
80  * ModifyTableState node, rather than being built here.
81  *
82  * num_partitions
83  * The current number of items stored in the 'partitions' array. Also
84  * serves as the index of the next free array element for new
85  * ResultRelInfo objects that need to be stored.
86  *
87  * max_partitions
88  * The current allocated size of the 'partitions' array.
89  *
90  * memcxt
91  * Memory context used to allocate subsidiary structs.
92  *-----------------------
93  */
95 {
106 };
107 
108 /*-----------------------
109  * PartitionDispatch - information about one partitioned table in a partition
110  * hierarchy required to route a tuple to any of its partitions. A
111  * PartitionDispatch is always encapsulated inside a PartitionTupleRouting
112  * struct and stored inside its 'partition_dispatch_info' array.
113  *
114  * reldesc
115  * Relation descriptor of the table
116  *
117  * key
118  * Partition key information of the table
119  *
120  * keystate
121  * Execution state required for expressions in the partition key
122  *
123  * partdesc
124  * Partition descriptor of the table
125  *
126  * tupslot
127  * A standalone TupleTableSlot initialized with this table's tuple
128  * descriptor, or NULL if no tuple conversion between the parent is
129  * required.
130  *
131  * tupmap
132  * TupleConversionMap to convert from the parent's rowtype to this table's
133  * rowtype (when extracting the partition key of a tuple just before
134  * routing it through this table). A NULL value is stored if no tuple
135  * conversion is required.
136  *
137  * indexes
138  * Array of partdesc->nparts elements. For leaf partitions the index
139  * corresponds to the partition's ResultRelInfo in the encapsulating
140  * PartitionTupleRouting's partitions array. For partitioned partitions,
141  * the index corresponds to the PartitionDispatch for it in its
142  * partition_dispatch_info array. -1 indicates we've not yet allocated
143  * anything in PartitionTupleRouting for the partition.
144  *-----------------------
145  */
146 typedef struct PartitionDispatchData
147 {
150  List *keystate; /* list of ExprState */
156 
157 
159  EState *estate, PartitionTupleRouting *proute,
160  PartitionDispatch dispatch,
161  ResultRelInfo *rootResultRelInfo,
162  int partidx);
163 static void ExecInitRoutingInfo(ModifyTableState *mtstate,
164  EState *estate,
165  PartitionTupleRouting *proute,
166  PartitionDispatch dispatch,
167  ResultRelInfo *partRelInfo,
168  int partidx,
169  bool is_borrowed_rel);
171  PartitionTupleRouting *proute,
172  Oid partoid, PartitionDispatch parent_pd,
173  int partidx, ResultRelInfo *rootResultRelInfo);
175  TupleTableSlot *slot,
176  EState *estate,
177  Datum *values,
178  bool *isnull);
180  bool *isnull);
182  Datum *values,
183  bool *isnull,
184  int maxfieldlen);
185 static List *adjust_partition_colnos(List *colnos, ResultRelInfo *leaf_part_rri);
186 static List *adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap);
188  PartitionPruneInfo *pruneinfo);
190  List *pruning_steps,
191  PartitionDesc partdesc,
192  PartitionKey partkey,
193  PlanState *planstate,
194  ExprContext *econtext);
195 static void PartitionPruneFixSubPlanMap(PartitionPruneState *prunestate,
196  Bitmapset *initially_valid_subplans,
197  int n_total_subplans);
200  bool initial_prune,
201  Bitmapset **validsubplans);
202 
203 
204 /*
205  * ExecSetupPartitionTupleRouting - sets up information needed during
206  * tuple routing for partitioned tables, encapsulates it in
207  * PartitionTupleRouting, and returns it.
208  *
209  * Callers must use the returned PartitionTupleRouting during calls to
210  * ExecFindPartition(). The actual ResultRelInfo for a partition is only
211  * allocated when the partition is found for the first time.
212  *
213  * The current memory context is used to allocate this struct and all
214  * subsidiary structs that will be allocated from it later on. Typically
215  * it should be estate->es_query_cxt.
216  */
219 {
220  PartitionTupleRouting *proute;
221 
222  /*
223  * Here we attempt to expend as little effort as possible in setting up
224  * the PartitionTupleRouting. Each partition's ResultRelInfo is built on
225  * demand, only when we actually need to route a tuple to that partition.
226  * The reason for this is that a common case is for INSERT to insert a
227  * single tuple into a partitioned table and this must be fast.
228  */
230  proute->partition_root = rel;
231  proute->memcxt = CurrentMemoryContext;
232  /* Rest of members initialized by zeroing */
233 
234  /*
235  * Initialize this table's PartitionDispatch object. Here we pass in the
236  * parent as NULL as we don't need to care about any parent of the target
237  * partitioned table.
238  */
239  ExecInitPartitionDispatchInfo(estate, proute, RelationGetRelid(rel),
240  NULL, 0, NULL);
241 
242  return proute;
243 }
244 
245 /*
246  * ExecFindPartition -- Return the ResultRelInfo for the leaf partition that
247  * the tuple contained in *slot should belong to.
248  *
249  * If the partition's ResultRelInfo does not yet exist in 'proute' then we set
250  * one up or reuse one from mtstate's resultRelInfo array. When reusing a
251  * ResultRelInfo from the mtstate we verify that the relation is a valid
252  * target for INSERTs and initialize tuple routing information.
253  *
254  * rootResultRelInfo is the relation named in the query.
255  *
256  * estate must be non-NULL; we'll need it to compute any expressions in the
257  * partition keys. Also, its per-tuple contexts are used as evaluation
258  * scratch space.
259  *
260  * If no leaf partition is found, this routine errors out with the appropriate
261  * error message. An error may also be raised if the found target partition
262  * is not a valid target for an INSERT.
263  */
266  ResultRelInfo *rootResultRelInfo,
267  PartitionTupleRouting *proute,
268  TupleTableSlot *slot, EState *estate)
269 {
272  bool isnull[PARTITION_MAX_KEYS];
273  Relation rel;
274  PartitionDispatch dispatch;
275  PartitionDesc partdesc;
276  ExprContext *ecxt = GetPerTupleExprContext(estate);
277  TupleTableSlot *ecxt_scantuple_saved = ecxt->ecxt_scantuple;
278  TupleTableSlot *rootslot = slot;
279  TupleTableSlot *myslot = NULL;
280  MemoryContext oldcxt;
281  ResultRelInfo *rri = NULL;
282 
283  /* use per-tuple context here to avoid leaking memory */
285 
286  /*
287  * First check the root table's partition constraint, if any. No point in
288  * routing the tuple if it doesn't belong in the root table itself.
289  */
290  if (rootResultRelInfo->ri_RelationDesc->rd_rel->relispartition)
291  ExecPartitionCheck(rootResultRelInfo, slot, estate, true);
292 
293  /* start with the root partitioned table */
294  dispatch = pd[0];
295  while (dispatch != NULL)
296  {
297  int partidx = -1;
298  bool is_leaf;
299 
301 
302  rel = dispatch->reldesc;
303  partdesc = dispatch->partdesc;
304 
305  /*
306  * Extract partition key from tuple. Expression evaluation machinery
307  * that FormPartitionKeyDatum() invokes expects ecxt_scantuple to
308  * point to the correct tuple slot. The slot might have changed from
309  * what was used for the parent table if the table of the current
310  * partitioning level has different tuple descriptor from the parent.
311  * So update ecxt_scantuple accordingly.
312  */
313  ecxt->ecxt_scantuple = slot;
314  FormPartitionKeyDatum(dispatch, slot, estate, values, isnull);
315 
316  /*
317  * If this partitioned table has no partitions or no partition for
318  * these values, error out.
319  */
320  if (partdesc->nparts == 0 ||
321  (partidx = get_partition_for_tuple(dispatch, values, isnull)) < 0)
322  {
323  char *val_desc;
324 
326  values, isnull, 64);
328  ereport(ERROR,
329  (errcode(ERRCODE_CHECK_VIOLATION),
330  errmsg("no partition of relation \"%s\" found for row",
332  val_desc ?
333  errdetail("Partition key of the failing row contains %s.",
334  val_desc) : 0,
335  errtable(rel)));
336  }
337 
338  is_leaf = partdesc->is_leaf[partidx];
339  if (is_leaf)
340  {
341  /*
342  * We've reached the leaf -- hurray, we're done. Look to see if
343  * we've already got a ResultRelInfo for this partition.
344  */
345  if (likely(dispatch->indexes[partidx] >= 0))
346  {
347  /* ResultRelInfo already built */
348  Assert(dispatch->indexes[partidx] < proute->num_partitions);
349  rri = proute->partitions[dispatch->indexes[partidx]];
350  }
351  else
352  {
353  /*
354  * If the partition is known in the owning ModifyTableState
355  * node, we can re-use that ResultRelInfo instead of creating
356  * a new one with ExecInitPartitionInfo().
357  */
358  rri = ExecLookupResultRelByOid(mtstate,
359  partdesc->oids[partidx],
360  true, false);
361  if (rri)
362  {
363  /* Verify this ResultRelInfo allows INSERTs */
365 
366  /*
367  * Initialize information needed to insert this and
368  * subsequent tuples routed to this partition.
369  */
370  ExecInitRoutingInfo(mtstate, estate, proute, dispatch,
371  rri, partidx, true);
372  }
373  else
374  {
375  /* We need to create a new one. */
376  rri = ExecInitPartitionInfo(mtstate, estate, proute,
377  dispatch,
378  rootResultRelInfo, partidx);
379  }
380  }
381  Assert(rri != NULL);
382 
383  /* Signal to terminate the loop */
384  dispatch = NULL;
385  }
386  else
387  {
388  /*
389  * Partition is a sub-partitioned table; get the PartitionDispatch
390  */
391  if (likely(dispatch->indexes[partidx] >= 0))
392  {
393  /* Already built. */
394  Assert(dispatch->indexes[partidx] < proute->num_dispatch);
395 
396  rri = proute->nonleaf_partitions[dispatch->indexes[partidx]];
397 
398  /*
399  * Move down to the next partition level and search again
400  * until we find a leaf partition that matches this tuple
401  */
402  dispatch = pd[dispatch->indexes[partidx]];
403  }
404  else
405  {
406  /* Not yet built. Do that now. */
407  PartitionDispatch subdispatch;
408 
409  /*
410  * Create the new PartitionDispatch. We pass the current one
411  * in as the parent PartitionDispatch
412  */
413  subdispatch = ExecInitPartitionDispatchInfo(estate,
414  proute,
415  partdesc->oids[partidx],
416  dispatch, partidx,
417  mtstate->rootResultRelInfo);
418  Assert(dispatch->indexes[partidx] >= 0 &&
419  dispatch->indexes[partidx] < proute->num_dispatch);
420 
421  rri = proute->nonleaf_partitions[dispatch->indexes[partidx]];
422  dispatch = subdispatch;
423  }
424 
425  /*
426  * Convert the tuple to the new parent's layout, if different from
427  * the previous parent.
428  */
429  if (dispatch->tupslot)
430  {
431  AttrMap *map = dispatch->tupmap;
432  TupleTableSlot *tempslot = myslot;
433 
434  myslot = dispatch->tupslot;
435  slot = execute_attr_map_slot(map, slot, myslot);
436 
437  if (tempslot != NULL)
438  ExecClearTuple(tempslot);
439  }
440  }
441 
442  /*
443  * If this partition is the default one, we must check its partition
444  * constraint now, which may have changed concurrently due to
445  * partitions being added to the parent.
446  *
447  * (We do this here, and do not rely on ExecInsert doing it, because
448  * we don't want to miss doing it for non-leaf partitions.)
449  */
450  if (partidx == partdesc->boundinfo->default_index)
451  {
452  /*
453  * The tuple must match the partition's layout for the constraint
454  * expression to be evaluated successfully. If the partition is
455  * sub-partitioned, that would already be the case due to the code
456  * above, but for a leaf partition the tuple still matches the
457  * parent's layout.
458  *
459  * Note that we have a map to convert from root to current
460  * partition, but not from immediate parent to current partition.
461  * So if we have to convert, do it from the root slot; if not, use
462  * the root slot as-is.
463  */
464  if (is_leaf)
465  {
466  TupleConversionMap *map = ExecGetRootToChildMap(rri, estate);
467 
468  if (map)
469  slot = execute_attr_map_slot(map->attrMap, rootslot,
470  rri->ri_PartitionTupleSlot);
471  else
472  slot = rootslot;
473  }
474 
475  ExecPartitionCheck(rri, slot, estate, true);
476  }
477  }
478 
479  /* Release the tuple in the lowest parent's dedicated slot. */
480  if (myslot != NULL)
481  ExecClearTuple(myslot);
482  /* and restore ecxt's scantuple */
483  ecxt->ecxt_scantuple = ecxt_scantuple_saved;
484  MemoryContextSwitchTo(oldcxt);
485 
486  return rri;
487 }
488 
489 /*
490  * ExecInitPartitionInfo
491  * Lock the partition and initialize ResultRelInfo. Also setup other
492  * information for the partition and store it in the next empty slot in
493  * the proute->partitions array.
494  *
495  * Returns the ResultRelInfo
496  */
497 static ResultRelInfo *
499  PartitionTupleRouting *proute,
500  PartitionDispatch dispatch,
501  ResultRelInfo *rootResultRelInfo,
502  int partidx)
503 {
504  ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
505  Oid partOid = dispatch->partdesc->oids[partidx];
506  Relation partrel;
507  int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
508  Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
509  ResultRelInfo *leaf_part_rri;
510  MemoryContext oldcxt;
511  AttrMap *part_attmap = NULL;
512  bool found_whole_row;
513 
514  oldcxt = MemoryContextSwitchTo(proute->memcxt);
515 
516  partrel = table_open(partOid, RowExclusiveLock);
517 
518  leaf_part_rri = makeNode(ResultRelInfo);
519  InitResultRelInfo(leaf_part_rri,
520  partrel,
521  0,
522  rootResultRelInfo,
523  estate->es_instrument);
524 
525  /*
526  * Verify result relation is a valid target for an INSERT. An UPDATE of a
527  * partition-key becomes a DELETE+INSERT operation, so this check is still
528  * required when the operation is CMD_UPDATE.
529  */
530  CheckValidResultRel(leaf_part_rri, CMD_INSERT);
531 
532  /*
533  * Open partition indices. The user may have asked to check for conflicts
534  * within this leaf partition and do "nothing" instead of throwing an
535  * error. Be prepared in that case by initializing the index information
536  * needed by ExecInsert() to perform speculative insertions.
537  */
538  if (partrel->rd_rel->relhasindex &&
539  leaf_part_rri->ri_IndexRelationDescs == NULL)
540  ExecOpenIndices(leaf_part_rri,
541  (node != NULL &&
543 
544  /*
545  * Build WITH CHECK OPTION constraints for the partition. Note that we
546  * didn't build the withCheckOptionList for partitions within the planner,
547  * but simple translation of varattnos will suffice. This only occurs for
548  * the INSERT case or in the case of UPDATE tuple routing where we didn't
549  * find a result rel to reuse.
550  */
551  if (node && node->withCheckOptionLists != NIL)
552  {
553  List *wcoList;
554  List *wcoExprs = NIL;
555  ListCell *ll;
556 
557  /*
558  * In the case of INSERT on a partitioned table, there is only one
559  * plan. Likewise, there is only one WCO list, not one per partition.
560  * For UPDATE, there are as many WCO lists as there are plans.
561  */
562  Assert((node->operation == CMD_INSERT &&
563  list_length(node->withCheckOptionLists) == 1 &&
564  list_length(node->resultRelations) == 1) ||
565  (node->operation == CMD_UPDATE &&
567  list_length(node->resultRelations)));
568 
569  /*
570  * Use the WCO list of the first plan as a reference to calculate
571  * attno's for the WCO list of this partition. In the INSERT case,
572  * that refers to the root partitioned table, whereas in the UPDATE
573  * tuple routing case, that refers to the first partition in the
574  * mtstate->resultRelInfo array. In any case, both that relation and
575  * this partition should have the same columns, so we should be able
576  * to map attributes successfully.
577  */
578  wcoList = linitial(node->withCheckOptionLists);
579 
580  /*
581  * Convert Vars in it to contain this partition's attribute numbers.
582  */
583  part_attmap =
585  RelationGetDescr(firstResultRel),
586  false);
587  wcoList = (List *)
588  map_variable_attnos((Node *) wcoList,
589  firstVarno, 0,
590  part_attmap,
591  RelationGetForm(partrel)->reltype,
592  &found_whole_row);
593  /* We ignore the value of found_whole_row. */
594 
595  foreach(ll, wcoList)
596  {
598  ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual),
599  &mtstate->ps);
600 
601  wcoExprs = lappend(wcoExprs, wcoExpr);
602  }
603 
604  leaf_part_rri->ri_WithCheckOptions = wcoList;
605  leaf_part_rri->ri_WithCheckOptionExprs = wcoExprs;
606  }
607 
608  /*
609  * Build the RETURNING projection for the partition. Note that we didn't
610  * build the returningList for partitions within the planner, but simple
611  * translation of varattnos will suffice. This only occurs for the INSERT
612  * case or in the case of UPDATE tuple routing where we didn't find a
613  * result rel to reuse.
614  */
615  if (node && node->returningLists != NIL)
616  {
617  TupleTableSlot *slot;
618  ExprContext *econtext;
619  List *returningList;
620 
621  /* See the comment above for WCO lists. */
622  Assert((node->operation == CMD_INSERT &&
623  list_length(node->returningLists) == 1 &&
624  list_length(node->resultRelations) == 1) ||
625  (node->operation == CMD_UPDATE &&
626  list_length(node->returningLists) ==
627  list_length(node->resultRelations)));
628 
629  /*
630  * Use the RETURNING list of the first plan as a reference to
631  * calculate attno's for the RETURNING list of this partition. See
632  * the comment above for WCO lists for more details on why this is
633  * okay.
634  */
635  returningList = linitial(node->returningLists);
636 
637  /*
638  * Convert Vars in it to contain this partition's attribute numbers.
639  */
640  if (part_attmap == NULL)
641  part_attmap =
643  RelationGetDescr(firstResultRel),
644  false);
645  returningList = (List *)
646  map_variable_attnos((Node *) returningList,
647  firstVarno, 0,
648  part_attmap,
649  RelationGetForm(partrel)->reltype,
650  &found_whole_row);
651  /* We ignore the value of found_whole_row. */
652 
653  leaf_part_rri->ri_returningList = returningList;
654 
655  /*
656  * Initialize the projection itself.
657  *
658  * Use the slot and the expression context that would have been set up
659  * in ExecInitModifyTable() for projection's output.
660  */
661  Assert(mtstate->ps.ps_ResultTupleSlot != NULL);
662  slot = mtstate->ps.ps_ResultTupleSlot;
663  Assert(mtstate->ps.ps_ExprContext != NULL);
664  econtext = mtstate->ps.ps_ExprContext;
665  leaf_part_rri->ri_projectReturning =
666  ExecBuildProjectionInfo(returningList, econtext, slot,
667  &mtstate->ps, RelationGetDescr(partrel));
668  }
669 
670  /* Set up information needed for routing tuples to the partition. */
671  ExecInitRoutingInfo(mtstate, estate, proute, dispatch,
672  leaf_part_rri, partidx, false);
673 
674  /*
675  * If there is an ON CONFLICT clause, initialize state for it.
676  */
677  if (node && node->onConflictAction != ONCONFLICT_NONE)
678  {
679  TupleDesc partrelDesc = RelationGetDescr(partrel);
680  ExprContext *econtext = mtstate->ps.ps_ExprContext;
681  ListCell *lc;
682  List *arbiterIndexes = NIL;
683 
684  /*
685  * If there is a list of arbiter indexes, map it to a list of indexes
686  * in the partition. We do that by scanning the partition's index
687  * list and searching for ancestry relationships to each index in the
688  * ancestor table.
689  */
690  if (rootResultRelInfo->ri_onConflictArbiterIndexes != NIL)
691  {
692  List *childIdxs;
693 
694  childIdxs = RelationGetIndexList(leaf_part_rri->ri_RelationDesc);
695 
696  foreach(lc, childIdxs)
697  {
698  Oid childIdx = lfirst_oid(lc);
699  List *ancestors;
700  ListCell *lc2;
701 
702  ancestors = get_partition_ancestors(childIdx);
703  foreach(lc2, rootResultRelInfo->ri_onConflictArbiterIndexes)
704  {
705  if (list_member_oid(ancestors, lfirst_oid(lc2)))
706  arbiterIndexes = lappend_oid(arbiterIndexes, childIdx);
707  }
708  list_free(ancestors);
709  }
710  }
711 
712  /*
713  * If the resulting lists are of inequal length, something is wrong.
714  * (This shouldn't happen, since arbiter index selection should not
715  * pick up an invalid index.)
716  */
717  if (list_length(rootResultRelInfo->ri_onConflictArbiterIndexes) !=
718  list_length(arbiterIndexes))
719  elog(ERROR, "invalid arbiter index list");
720  leaf_part_rri->ri_onConflictArbiterIndexes = arbiterIndexes;
721 
722  /*
723  * In the DO UPDATE case, we have some more state to initialize.
724  */
725  if (node->onConflictAction == ONCONFLICT_UPDATE)
726  {
728  TupleConversionMap *map;
729 
730  map = ExecGetRootToChildMap(leaf_part_rri, estate);
731 
732  Assert(node->onConflictSet != NIL);
733  Assert(rootResultRelInfo->ri_onConflict != NULL);
734 
735  leaf_part_rri->ri_onConflict = onconfl;
736 
737  /*
738  * Need a separate existing slot for each partition, as the
739  * partition could be of a different AM, even if the tuple
740  * descriptors match.
741  */
742  onconfl->oc_Existing =
743  table_slot_create(leaf_part_rri->ri_RelationDesc,
744  &mtstate->ps.state->es_tupleTable);
745 
746  /*
747  * If the partition's tuple descriptor matches exactly the root
748  * parent (the common case), we can re-use most of the parent's ON
749  * CONFLICT SET state, skipping a bunch of work. Otherwise, we
750  * need to create state specific to this partition.
751  */
752  if (map == NULL)
753  {
754  /*
755  * It's safe to reuse these from the partition root, as we
756  * only process one tuple at a time (therefore we won't
757  * overwrite needed data in slots), and the results of
758  * projections are independent of the underlying storage.
759  * Projections and where clauses themselves don't store state
760  * / are independent of the underlying storage.
761  */
762  onconfl->oc_ProjSlot =
763  rootResultRelInfo->ri_onConflict->oc_ProjSlot;
764  onconfl->oc_ProjInfo =
765  rootResultRelInfo->ri_onConflict->oc_ProjInfo;
766  onconfl->oc_WhereClause =
767  rootResultRelInfo->ri_onConflict->oc_WhereClause;
768  }
769  else
770  {
771  List *onconflset;
772  List *onconflcols;
773 
774  /*
775  * Translate expressions in onConflictSet to account for
776  * different attribute numbers. For that, map partition
777  * varattnos twice: first to catch the EXCLUDED
778  * pseudo-relation (INNER_VAR), and second to handle the main
779  * target relation (firstVarno).
780  */
781  onconflset = copyObject(node->onConflictSet);
782  if (part_attmap == NULL)
783  part_attmap =
785  RelationGetDescr(firstResultRel),
786  false);
787  onconflset = (List *)
788  map_variable_attnos((Node *) onconflset,
789  INNER_VAR, 0,
790  part_attmap,
791  RelationGetForm(partrel)->reltype,
792  &found_whole_row);
793  /* We ignore the value of found_whole_row. */
794  onconflset = (List *)
795  map_variable_attnos((Node *) onconflset,
796  firstVarno, 0,
797  part_attmap,
798  RelationGetForm(partrel)->reltype,
799  &found_whole_row);
800  /* We ignore the value of found_whole_row. */
801 
802  /* Finally, adjust the target colnos to match the partition. */
803  onconflcols = adjust_partition_colnos(node->onConflictCols,
804  leaf_part_rri);
805 
806  /* create the tuple slot for the UPDATE SET projection */
807  onconfl->oc_ProjSlot =
808  table_slot_create(partrel,
809  &mtstate->ps.state->es_tupleTable);
810 
811  /* build UPDATE SET projection state */
812  onconfl->oc_ProjInfo =
813  ExecBuildUpdateProjection(onconflset,
814  true,
815  onconflcols,
816  partrelDesc,
817  econtext,
818  onconfl->oc_ProjSlot,
819  &mtstate->ps);
820 
821  /*
822  * If there is a WHERE clause, initialize state where it will
823  * be evaluated, mapping the attribute numbers appropriately.
824  * As with onConflictSet, we need to map partition varattnos
825  * to the partition's tupdesc.
826  */
827  if (node->onConflictWhere)
828  {
829  List *clause;
830 
831  clause = copyObject((List *) node->onConflictWhere);
832  clause = (List *)
833  map_variable_attnos((Node *) clause,
834  INNER_VAR, 0,
835  part_attmap,
836  RelationGetForm(partrel)->reltype,
837  &found_whole_row);
838  /* We ignore the value of found_whole_row. */
839  clause = (List *)
840  map_variable_attnos((Node *) clause,
841  firstVarno, 0,
842  part_attmap,
843  RelationGetForm(partrel)->reltype,
844  &found_whole_row);
845  /* We ignore the value of found_whole_row. */
846  onconfl->oc_WhereClause =
847  ExecInitQual((List *) clause, &mtstate->ps);
848  }
849  }
850  }
851  }
852 
853  /*
854  * Since we've just initialized this ResultRelInfo, it's not in any list
855  * attached to the estate as yet. Add it, so that it can be found later.
856  *
857  * Note that the entries in this list appear in no predetermined order,
858  * because partition result rels are initialized as and when they're
859  * needed.
860  */
864  leaf_part_rri);
865 
866  /*
867  * Initialize information about this partition that's needed to handle
868  * MERGE. We take the "first" result relation's mergeActionList as
869  * reference and make copy for this relation, converting stuff that
870  * references attribute numbers to match this relation's.
871  *
872  * This duplicates much of the logic in ExecInitMerge(), so something
873  * changes there, look here too.
874  */
875  if (node && node->operation == CMD_MERGE)
876  {
877  List *firstMergeActionList = linitial(node->mergeActionLists);
878  ListCell *lc;
879  ExprContext *econtext = mtstate->ps.ps_ExprContext;
880 
881  if (part_attmap == NULL)
882  part_attmap =
884  RelationGetDescr(firstResultRel),
885  false);
886 
887  if (unlikely(!leaf_part_rri->ri_projectNewInfoValid))
888  ExecInitMergeTupleSlots(mtstate, leaf_part_rri);
889 
890  foreach(lc, firstMergeActionList)
891  {
892  /* Make a copy for this relation to be safe. */
894  MergeActionState *action_state;
895  List **list;
896 
897  /* Generate the action's state for this relation */
898  action_state = makeNode(MergeActionState);
899  action_state->mas_action = action;
900 
901  /* And put the action in the appropriate list */
902  if (action->matched)
903  list = &leaf_part_rri->ri_matchedMergeAction;
904  else
905  list = &leaf_part_rri->ri_notMatchedMergeAction;
906  *list = lappend(*list, action_state);
907 
908  switch (action->commandType)
909  {
910  case CMD_INSERT:
911 
912  /*
913  * ExecCheckPlanOutput() already done on the targetlist
914  * when "first" result relation initialized and it is same
915  * for all result relations.
916  */
917  action_state->mas_proj =
918  ExecBuildProjectionInfo(action->targetList, econtext,
919  leaf_part_rri->ri_newTupleSlot,
920  &mtstate->ps,
921  RelationGetDescr(partrel));
922  break;
923  case CMD_UPDATE:
924 
925  /*
926  * Convert updateColnos from "first" result relation
927  * attribute numbers to this result rel's.
928  */
929  if (part_attmap)
930  action->updateColnos =
932  part_attmap);
933  action_state->mas_proj =
934  ExecBuildUpdateProjection(action->targetList,
935  true,
936  action->updateColnos,
937  RelationGetDescr(leaf_part_rri->ri_RelationDesc),
938  econtext,
939  leaf_part_rri->ri_newTupleSlot,
940  NULL);
941  break;
942  case CMD_DELETE:
943  break;
944 
945  default:
946  elog(ERROR, "unknown action in MERGE WHEN clause");
947  }
948 
949  /* found_whole_row intentionally ignored. */
950  action->qual =
952  firstVarno, 0,
953  part_attmap,
954  RelationGetForm(partrel)->reltype,
955  &found_whole_row);
956  action_state->mas_whenqual =
957  ExecInitQual((List *) action->qual, &mtstate->ps);
958  }
959  }
960  MemoryContextSwitchTo(oldcxt);
961 
962  return leaf_part_rri;
963 }
964 
965 /*
966  * ExecInitRoutingInfo
967  * Set up information needed for translating tuples between root
968  * partitioned table format and partition format, and keep track of it
969  * in PartitionTupleRouting.
970  */
971 static void
973  EState *estate,
974  PartitionTupleRouting *proute,
975  PartitionDispatch dispatch,
976  ResultRelInfo *partRelInfo,
977  int partidx,
978  bool is_borrowed_rel)
979 {
980  MemoryContext oldcxt;
981  int rri_index;
982 
983  oldcxt = MemoryContextSwitchTo(proute->memcxt);
984 
985  /*
986  * Set up tuple conversion between root parent and the partition if the
987  * two have different rowtypes. If conversion is indeed required, also
988  * initialize a slot dedicated to storing this partition's converted
989  * tuples. Various operations that are applied to tuples after routing,
990  * such as checking constraints, will refer to this slot.
991  */
992  if (ExecGetRootToChildMap(partRelInfo, estate) != NULL)
993  {
994  Relation partrel = partRelInfo->ri_RelationDesc;
995 
996  /*
997  * This pins the partition's TupleDesc, which will be released at the
998  * end of the command.
999  */
1000  partRelInfo->ri_PartitionTupleSlot =
1001  table_slot_create(partrel, &estate->es_tupleTable);
1002  }
1003  else
1004  partRelInfo->ri_PartitionTupleSlot = NULL;
1005 
1006  /*
1007  * If the partition is a foreign table, let the FDW init itself for
1008  * routing tuples to the partition.
1009  */
1010  if (partRelInfo->ri_FdwRoutine != NULL &&
1011  partRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL)
1012  partRelInfo->ri_FdwRoutine->BeginForeignInsert(mtstate, partRelInfo);
1013 
1014  /*
1015  * Determine if the FDW supports batch insert and determine the batch size
1016  * (a FDW may support batching, but it may be disabled for the
1017  * server/table or for this particular query).
1018  *
1019  * If the FDW does not support batching, we set the batch size to 1.
1020  */
1021  if (mtstate->operation == CMD_INSERT &&
1022  partRelInfo->ri_FdwRoutine != NULL &&
1023  partRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
1024  partRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
1025  partRelInfo->ri_BatchSize =
1026  partRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(partRelInfo);
1027  else
1028  partRelInfo->ri_BatchSize = 1;
1029 
1030  Assert(partRelInfo->ri_BatchSize >= 1);
1031 
1032  partRelInfo->ri_CopyMultiInsertBuffer = NULL;
1033 
1034  /*
1035  * Keep track of it in the PartitionTupleRouting->partitions array.
1036  */
1037  Assert(dispatch->indexes[partidx] == -1);
1038 
1039  rri_index = proute->num_partitions++;
1040 
1041  /* Allocate or enlarge the array, as needed */
1042  if (proute->num_partitions >= proute->max_partitions)
1043  {
1044  if (proute->max_partitions == 0)
1045  {
1046  proute->max_partitions = 8;
1047  proute->partitions = (ResultRelInfo **)
1048  palloc(sizeof(ResultRelInfo *) * proute->max_partitions);
1049  proute->is_borrowed_rel = (bool *)
1050  palloc(sizeof(bool) * proute->max_partitions);
1051  }
1052  else
1053  {
1054  proute->max_partitions *= 2;
1055  proute->partitions = (ResultRelInfo **)
1056  repalloc(proute->partitions, sizeof(ResultRelInfo *) *
1057  proute->max_partitions);
1058  proute->is_borrowed_rel = (bool *)
1059  repalloc(proute->is_borrowed_rel, sizeof(bool) *
1060  proute->max_partitions);
1061  }
1062  }
1063 
1064  proute->partitions[rri_index] = partRelInfo;
1065  proute->is_borrowed_rel[rri_index] = is_borrowed_rel;
1066  dispatch->indexes[partidx] = rri_index;
1067 
1068  MemoryContextSwitchTo(oldcxt);
1069 }
1070 
1071 /*
1072  * ExecInitPartitionDispatchInfo
1073  * Lock the partitioned table (if not locked already) and initialize
1074  * PartitionDispatch for a partitioned table and store it in the next
1075  * available slot in the proute->partition_dispatch_info array. Also,
1076  * record the index into this array in the parent_pd->indexes[] array in
1077  * the partidx element so that we can properly retrieve the newly created
1078  * PartitionDispatch later.
1079  */
1080 static PartitionDispatch
1082  PartitionTupleRouting *proute, Oid partoid,
1083  PartitionDispatch parent_pd, int partidx,
1084  ResultRelInfo *rootResultRelInfo)
1085 {
1086  Relation rel;
1087  PartitionDesc partdesc;
1088  PartitionDispatch pd;
1089  int dispatchidx;
1090  MemoryContext oldcxt;
1091 
1092  /*
1093  * For data modification, it is better that executor does not include
1094  * partitions being detached, except when running in snapshot-isolation
1095  * mode. This means that a read-committed transaction immediately gets a
1096  * "no partition for tuple" error when a tuple is inserted into a
1097  * partition that's being detached concurrently, but a transaction in
1098  * repeatable-read mode can still use such a partition.
1099  */
1100  if (estate->es_partition_directory == NULL)
1101  estate->es_partition_directory =
1104 
1105  oldcxt = MemoryContextSwitchTo(proute->memcxt);
1106 
1107  /*
1108  * Only sub-partitioned tables need to be locked here. The root
1109  * partitioned table will already have been locked as it's referenced in
1110  * the query's rtable.
1111  */
1112  if (partoid != RelationGetRelid(proute->partition_root))
1113  rel = table_open(partoid, RowExclusiveLock);
1114  else
1115  rel = proute->partition_root;
1116  partdesc = PartitionDirectoryLookup(estate->es_partition_directory, rel);
1117 
1118  pd = (PartitionDispatch) palloc(offsetof(PartitionDispatchData, indexes) +
1119  partdesc->nparts * sizeof(int));
1120  pd->reldesc = rel;
1121  pd->key = RelationGetPartitionKey(rel);
1122  pd->keystate = NIL;
1123  pd->partdesc = partdesc;
1124  if (parent_pd != NULL)
1125  {
1126  TupleDesc tupdesc = RelationGetDescr(rel);
1127 
1128  /*
1129  * For sub-partitioned tables where the column order differs from its
1130  * direct parent partitioned table, we must store a tuple table slot
1131  * initialized with its tuple descriptor and a tuple conversion map to
1132  * convert a tuple from its parent's rowtype to its own. This is to
1133  * make sure that we are looking at the correct row using the correct
1134  * tuple descriptor when computing its partition key for tuple
1135  * routing.
1136  */
1138  tupdesc,
1139  false);
1140  pd->tupslot = pd->tupmap ?
1141  MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL;
1142  }
1143  else
1144  {
1145  /* Not required for the root partitioned table */
1146  pd->tupmap = NULL;
1147  pd->tupslot = NULL;
1148  }
1149 
1150  /*
1151  * Initialize with -1 to signify that the corresponding partition's
1152  * ResultRelInfo or PartitionDispatch has not been created yet.
1153  */
1154  memset(pd->indexes, -1, sizeof(int) * partdesc->nparts);
1155 
1156  /* Track in PartitionTupleRouting for later use */
1157  dispatchidx = proute->num_dispatch++;
1158 
1159  /* Allocate or enlarge the array, as needed */
1160  if (proute->num_dispatch >= proute->max_dispatch)
1161  {
1162  if (proute->max_dispatch == 0)
1163  {
1164  proute->max_dispatch = 4;
1166  palloc(sizeof(PartitionDispatch) * proute->max_dispatch);
1167  proute->nonleaf_partitions = (ResultRelInfo **)
1168  palloc(sizeof(ResultRelInfo *) * proute->max_dispatch);
1169  }
1170  else
1171  {
1172  proute->max_dispatch *= 2;
1175  sizeof(PartitionDispatch) * proute->max_dispatch);
1176  proute->nonleaf_partitions = (ResultRelInfo **)
1177  repalloc(proute->nonleaf_partitions,
1178  sizeof(ResultRelInfo *) * proute->max_dispatch);
1179  }
1180  }
1181  proute->partition_dispatch_info[dispatchidx] = pd;
1182 
1183  /*
1184  * If setting up a PartitionDispatch for a sub-partitioned table, we may
1185  * also need a minimally valid ResultRelInfo for checking the partition
1186  * constraint later; set that up now.
1187  */
1188  if (parent_pd)
1189  {
1191 
1192  InitResultRelInfo(rri, rel, 0, rootResultRelInfo, 0);
1193  proute->nonleaf_partitions[dispatchidx] = rri;
1194  }
1195  else
1196  proute->nonleaf_partitions[dispatchidx] = NULL;
1197 
1198  /*
1199  * Finally, if setting up a PartitionDispatch for a sub-partitioned table,
1200  * install a downlink in the parent to allow quick descent.
1201  */
1202  if (parent_pd)
1203  {
1204  Assert(parent_pd->indexes[partidx] == -1);
1205  parent_pd->indexes[partidx] = dispatchidx;
1206  }
1207 
1208  MemoryContextSwitchTo(oldcxt);
1209 
1210  return pd;
1211 }
1212 
1213 /*
1214  * ExecCleanupTupleRouting -- Clean up objects allocated for partition tuple
1215  * routing.
1216  *
1217  * Close all the partitioned tables, leaf partitions, and their indices.
1218  */
1219 void
1221  PartitionTupleRouting *proute)
1222 {
1223  int i;
1224 
1225  /*
1226  * Remember, proute->partition_dispatch_info[0] corresponds to the root
1227  * partitioned table, which we must not try to close, because it is the
1228  * main target table of the query that will be closed by callers such as
1229  * ExecEndPlan() or DoCopy(). Also, tupslot is NULL for the root
1230  * partitioned table.
1231  */
1232  for (i = 1; i < proute->num_dispatch; i++)
1233  {
1235 
1236  table_close(pd->reldesc, NoLock);
1237 
1238  if (pd->tupslot)
1240  }
1241 
1242  for (i = 0; i < proute->num_partitions; i++)
1243  {
1244  ResultRelInfo *resultRelInfo = proute->partitions[i];
1245 
1246  /* Allow any FDWs to shut down */
1247  if (resultRelInfo->ri_FdwRoutine != NULL &&
1248  resultRelInfo->ri_FdwRoutine->EndForeignInsert != NULL)
1249  resultRelInfo->ri_FdwRoutine->EndForeignInsert(mtstate->ps.state,
1250  resultRelInfo);
1251 
1252  /*
1253  * Close it if it's not one of the result relations borrowed from the
1254  * owning ModifyTableState; those will be closed by ExecEndPlan().
1255  */
1256  if (proute->is_borrowed_rel[i])
1257  continue;
1258 
1259  ExecCloseIndices(resultRelInfo);
1260  table_close(resultRelInfo->ri_RelationDesc, NoLock);
1261  }
1262 }
1263 
1264 /* ----------------
1265  * FormPartitionKeyDatum
1266  * Construct values[] and isnull[] arrays for the partition key
1267  * of a tuple.
1268  *
1269  * pd Partition dispatch object of the partitioned table
1270  * slot Heap tuple from which to extract partition key
1271  * estate executor state for evaluating any partition key
1272  * expressions (must be non-NULL)
1273  * values Array of partition key Datums (output area)
1274  * isnull Array of is-null indicators (output area)
1275  *
1276  * the ecxt_scantuple slot of estate's per-tuple expr context must point to
1277  * the heap tuple passed in.
1278  * ----------------
1279  */
1280 static void
1282  TupleTableSlot *slot,
1283  EState *estate,
1284  Datum *values,
1285  bool *isnull)
1286 {
1287  ListCell *partexpr_item;
1288  int i;
1289 
1290  if (pd->key->partexprs != NIL && pd->keystate == NIL)
1291  {
1292  /* Check caller has set up context correctly */
1293  Assert(estate != NULL &&
1294  GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
1295 
1296  /* First time through, set up expression evaluation state */
1297  pd->keystate = ExecPrepareExprList(pd->key->partexprs, estate);
1298  }
1299 
1300  partexpr_item = list_head(pd->keystate);
1301  for (i = 0; i < pd->key->partnatts; i++)
1302  {
1303  AttrNumber keycol = pd->key->partattrs[i];
1304  Datum datum;
1305  bool isNull;
1306 
1307  if (keycol != 0)
1308  {
1309  /* Plain column; get the value directly from the heap tuple */
1310  datum = slot_getattr(slot, keycol, &isNull);
1311  }
1312  else
1313  {
1314  /* Expression; need to evaluate it */
1315  if (partexpr_item == NULL)
1316  elog(ERROR, "wrong number of partition key expressions");
1317  datum = ExecEvalExprSwitchContext((ExprState *) lfirst(partexpr_item),
1318  GetPerTupleExprContext(estate),
1319  &isNull);
1320  partexpr_item = lnext(pd->keystate, partexpr_item);
1321  }
1322  values[i] = datum;
1323  isnull[i] = isNull;
1324  }
1325 
1326  if (partexpr_item != NULL)
1327  elog(ERROR, "wrong number of partition key expressions");
1328 }
1329 
1330 /*
1331  * The number of times the same partition must be found in a row before we
1332  * switch from a binary search for the given values to just checking if the
1333  * values belong to the last found partition. This must be above 0.
1334  */
1335 #define PARTITION_CACHED_FIND_THRESHOLD 16
1336 
1337 /*
1338  * get_partition_for_tuple
1339  * Finds partition of relation which accepts the partition key specified
1340  * in values and isnull.
1341  *
1342  * Calling this function can be quite expensive when LIST and RANGE
1343  * partitioned tables have many partitions. This is due to the binary search
1344  * that's done to find the correct partition. Many of the use cases for LIST
1345  * and RANGE partitioned tables make it likely that the same partition is
1346  * found in subsequent ExecFindPartition() calls. This is especially true for
1347  * cases such as RANGE partitioned tables on a TIMESTAMP column where the
1348  * partition key is the current time. When asked to find a partition for a
1349  * RANGE or LIST partitioned table, we record the partition index and datum
1350  * offset we've found for the given 'values' in the PartitionDesc (which is
1351  * stored in relcache), and if we keep finding the same partition
1352  * PARTITION_CACHED_FIND_THRESHOLD times in a row, then we'll enable caching
1353  * logic and instead of performing a binary search to find the correct
1354  * partition, we'll just double-check that 'values' still belong to the last
1355  * found partition, and if so, we'll return that partition index, thus
1356  * skipping the need for the binary search. If we fail to match the last
1357  * partition when double checking, then we fall back on doing a binary search.
1358  * In this case, unless we find 'values' belong to the DEFAULT partition,
1359  * we'll reset the number of times we've hit the same partition so that we
1360  * don't attempt to use the cache again until we've found that partition at
1361  * least PARTITION_CACHED_FIND_THRESHOLD times in a row.
1362  *
1363  * For cases where the partition changes on each lookup, the amount of
1364  * additional work required just amounts to recording the last found partition
1365  * and bound offset then resetting the found counter. This is cheap and does
1366  * not appear to cause any meaningful slowdowns for such cases.
1367  *
1368  * No caching of partitions is done when the last found partition is the
1369  * DEFAULT or NULL partition. For the case of the DEFAULT partition, there
1370  * is no bound offset storing the matching datum, so we cannot confirm the
1371  * indexes match. For the NULL partition, this is just so cheap, there's no
1372  * sense in caching.
1373  *
1374  * Return value is index of the partition (>= 0 and < partdesc->nparts) if one
1375  * found or -1 if none found.
1376  */
1377 static int
1379 {
1380  int bound_offset = -1;
1381  int part_index = -1;
1382  PartitionKey key = pd->key;
1383  PartitionDesc partdesc = pd->partdesc;
1384  PartitionBoundInfo boundinfo = partdesc->boundinfo;
1385 
1386  /*
1387  * In the switch statement below, when we perform a cached lookup for
1388  * RANGE and LIST partitioned tables, if we find that the last found
1389  * partition matches the 'values', we return the partition index right
1390  * away. We do this instead of breaking out of the switch as we don't
1391  * want to execute the code about the DEFAULT partition or do any updates
1392  * for any of the cache-related fields. That would be a waste of effort
1393  * as we already know it's not the DEFAULT partition and have no need to
1394  * increment the number of times we found the same partition any higher
1395  * than PARTITION_CACHED_FIND_THRESHOLD.
1396  */
1397 
1398  /* Route as appropriate based on partitioning strategy. */
1399  switch (key->strategy)
1400  {
1402  {
1403  uint64 rowHash;
1404 
1405  /* hash partitioning is too cheap to bother caching */
1406  rowHash = compute_partition_hash_value(key->partnatts,
1407  key->partsupfunc,
1408  key->partcollation,
1409  values, isnull);
1410 
1411  /*
1412  * HASH partitions can't have a DEFAULT partition and we don't
1413  * do any caching work for them, so just return the part index
1414  */
1415  return boundinfo->indexes[rowHash % boundinfo->nindexes];
1416  }
1417 
1419  if (isnull[0])
1420  {
1421  /* this is far too cheap to bother doing any caching */
1422  if (partition_bound_accepts_nulls(boundinfo))
1423  {
1424  /*
1425  * When there is a NULL partition we just return that
1426  * directly. We don't have a bound_offset so it's not
1427  * valid to drop into the code after the switch which
1428  * checks and updates the cache fields. We perhaps should
1429  * be invalidating the details of the last cached
1430  * partition but there's no real need to. Keeping those
1431  * fields set gives a chance at matching to the cached
1432  * partition on the next lookup.
1433  */
1434  return boundinfo->null_index;
1435  }
1436  }
1437  else
1438  {
1439  bool equal;
1440 
1442  {
1443  int last_datum_offset = partdesc->last_found_datum_index;
1444  Datum lastDatum = boundinfo->datums[last_datum_offset][0];
1445  int32 cmpval;
1446 
1447  /* does the last found datum index match this datum? */
1448  cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
1449  key->partcollation[0],
1450  lastDatum,
1451  values[0]));
1452 
1453  if (cmpval == 0)
1454  return boundinfo->indexes[last_datum_offset];
1455 
1456  /* fall-through and do a manual lookup */
1457  }
1458 
1459  bound_offset = partition_list_bsearch(key->partsupfunc,
1460  key->partcollation,
1461  boundinfo,
1462  values[0], &equal);
1463  if (bound_offset >= 0 && equal)
1464  part_index = boundinfo->indexes[bound_offset];
1465  }
1466  break;
1467 
1469  {
1470  bool equal = false,
1471  range_partkey_has_null = false;
1472  int i;
1473 
1474  /*
1475  * No range includes NULL, so this will be accepted by the
1476  * default partition if there is one, and otherwise rejected.
1477  */
1478  for (i = 0; i < key->partnatts; i++)
1479  {
1480  if (isnull[i])
1481  {
1482  range_partkey_has_null = true;
1483  break;
1484  }
1485  }
1486 
1487  /* NULLs belong in the DEFAULT partition */
1488  if (range_partkey_has_null)
1489  break;
1490 
1492  {
1493  int last_datum_offset = partdesc->last_found_datum_index;
1494  Datum *lastDatums = boundinfo->datums[last_datum_offset];
1495  PartitionRangeDatumKind *kind = boundinfo->kind[last_datum_offset];
1496  int32 cmpval;
1497 
1498  /* check if the value is >= to the lower bound */
1499  cmpval = partition_rbound_datum_cmp(key->partsupfunc,
1500  key->partcollation,
1501  lastDatums,
1502  kind,
1503  values,
1504  key->partnatts);
1505 
1506  /*
1507  * If it's equal to the lower bound then no need to check
1508  * the upper bound.
1509  */
1510  if (cmpval == 0)
1511  return boundinfo->indexes[last_datum_offset + 1];
1512 
1513  if (cmpval < 0 && last_datum_offset + 1 < boundinfo->ndatums)
1514  {
1515  /* check if the value is below the upper bound */
1516  lastDatums = boundinfo->datums[last_datum_offset + 1];
1517  kind = boundinfo->kind[last_datum_offset + 1];
1518  cmpval = partition_rbound_datum_cmp(key->partsupfunc,
1519  key->partcollation,
1520  lastDatums,
1521  kind,
1522  values,
1523  key->partnatts);
1524 
1525  if (cmpval > 0)
1526  return boundinfo->indexes[last_datum_offset + 1];
1527  }
1528  /* fall-through and do a manual lookup */
1529  }
1530 
1531  bound_offset = partition_range_datum_bsearch(key->partsupfunc,
1532  key->partcollation,
1533  boundinfo,
1534  key->partnatts,
1535  values,
1536  &equal);
1537 
1538  /*
1539  * The bound at bound_offset is less than or equal to the
1540  * tuple value, so the bound at offset+1 is the upper bound of
1541  * the partition we're looking for, if there actually exists
1542  * one.
1543  */
1544  part_index = boundinfo->indexes[bound_offset + 1];
1545  }
1546  break;
1547 
1548  default:
1549  elog(ERROR, "unexpected partition strategy: %d",
1550  (int) key->strategy);
1551  }
1552 
1553  /*
1554  * part_index < 0 means we failed to find a partition of this parent. Use
1555  * the default partition, if there is one.
1556  */
1557  if (part_index < 0)
1558  {
1559  /*
1560  * No need to reset the cache fields here. The next set of values
1561  * might end up belonging to the cached partition, so leaving the
1562  * cache alone improves the chances of a cache hit on the next lookup.
1563  */
1564  return boundinfo->default_index;
1565  }
1566 
1567  /* we should only make it here when the code above set bound_offset */
1568  Assert(bound_offset >= 0);
1569 
1570  /*
1571  * Attend to the cache fields. If the bound_offset matches the last
1572  * cached bound offset then we've found the same partition as last time,
1573  * so bump the count by one. If all goes well, we'll eventually reach
1574  * PARTITION_CACHED_FIND_THRESHOLD and try the cache path next time
1575  * around. Otherwise, we'll reset the cache count back to 1 to mark that
1576  * we've found this partition for the first time.
1577  */
1578  if (bound_offset == partdesc->last_found_datum_index)
1579  partdesc->last_found_count++;
1580  else
1581  {
1582  partdesc->last_found_count = 1;
1583  partdesc->last_found_part_index = part_index;
1584  partdesc->last_found_datum_index = bound_offset;
1585  }
1586 
1587  return part_index;
1588 }
1589 
1590 /*
1591  * ExecBuildSlotPartitionKeyDescription
1592  *
1593  * This works very much like BuildIndexValueDescription() and is currently
1594  * used for building error messages when ExecFindPartition() fails to find
1595  * partition for a row.
1596  */
1597 static char *
1599  Datum *values,
1600  bool *isnull,
1601  int maxfieldlen)
1602 {
1605  int partnatts = get_partition_natts(key);
1606  int i;
1607  Oid relid = RelationGetRelid(rel);
1608  AclResult aclresult;
1609 
1610  if (check_enable_rls(relid, InvalidOid, true) == RLS_ENABLED)
1611  return NULL;
1612 
1613  /* If the user has table-level access, just go build the description. */
1614  aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT);
1615  if (aclresult != ACLCHECK_OK)
1616  {
1617  /*
1618  * Step through the columns of the partition key and make sure the
1619  * user has SELECT rights on all of them.
1620  */
1621  for (i = 0; i < partnatts; i++)
1622  {
1624 
1625  /*
1626  * If this partition key column is an expression, we return no
1627  * detail rather than try to figure out what column(s) the
1628  * expression includes and if the user has SELECT rights on them.
1629  */
1630  if (attnum == InvalidAttrNumber ||
1632  ACL_SELECT) != ACLCHECK_OK)
1633  return NULL;
1634  }
1635  }
1636 
1637  initStringInfo(&buf);
1638  appendStringInfo(&buf, "(%s) = (",
1639  pg_get_partkeydef_columns(relid, true));
1640 
1641  for (i = 0; i < partnatts; i++)
1642  {
1643  char *val;
1644  int vallen;
1645 
1646  if (isnull[i])
1647  val = "null";
1648  else
1649  {
1650  Oid foutoid;
1651  bool typisvarlena;
1652 
1654  &foutoid, &typisvarlena);
1655  val = OidOutputFunctionCall(foutoid, values[i]);
1656  }
1657 
1658  if (i > 0)
1659  appendStringInfoString(&buf, ", ");
1660 
1661  /* truncate if needed */
1662  vallen = strlen(val);
1663  if (vallen <= maxfieldlen)
1664  appendBinaryStringInfo(&buf, val, vallen);
1665  else
1666  {
1667  vallen = pg_mbcliplen(val, vallen, maxfieldlen);
1668  appendBinaryStringInfo(&buf, val, vallen);
1669  appendStringInfoString(&buf, "...");
1670  }
1671  }
1672 
1673  appendStringInfoChar(&buf, ')');
1674 
1675  return buf.data;
1676 }
1677 
1678 /*
1679  * adjust_partition_colnos
1680  * Adjust the list of UPDATE target column numbers to account for
1681  * attribute differences between the parent and the partition.
1682  *
1683  * Note: mustn't be called if no adjustment is required.
1684  */
1685 static List *
1687 {
1688  TupleConversionMap *map = ExecGetChildToRootMap(leaf_part_rri);
1689 
1690  Assert(map != NULL);
1691 
1692  return adjust_partition_colnos_using_map(colnos, map->attrMap);
1693 }
1694 
1695 /*
1696  * adjust_partition_colnos_using_map
1697  * Like adjust_partition_colnos, but uses a caller-supplied map instead
1698  * of assuming to map from the "root" result relation.
1699  *
1700  * Note: mustn't be called if no adjustment is required.
1701  */
1702 static List *
1704 {
1705  List *new_colnos = NIL;
1706  ListCell *lc;
1707 
1708  Assert(attrMap != NULL); /* else we shouldn't be here */
1709 
1710  foreach(lc, colnos)
1711  {
1712  AttrNumber parentattrno = lfirst_int(lc);
1713 
1714  if (parentattrno <= 0 ||
1715  parentattrno > attrMap->maplen ||
1716  attrMap->attnums[parentattrno - 1] == 0)
1717  elog(ERROR, "unexpected attno %d in target column list",
1718  parentattrno);
1719  new_colnos = lappend_int(new_colnos,
1720  attrMap->attnums[parentattrno - 1]);
1721  }
1722 
1723  return new_colnos;
1724 }
1725 
1726 /*-------------------------------------------------------------------------
1727  * Run-Time Partition Pruning Support.
1728  *
1729  * The following series of functions exist to support the removal of unneeded
1730  * subplans for queries against partitioned tables. The supporting functions
1731  * here are designed to work with any plan type which supports an arbitrary
1732  * number of subplans, e.g. Append, MergeAppend.
1733  *
1734  * When pruning involves comparison of a partition key to a constant, it's
1735  * done by the planner. However, if we have a comparison to a non-constant
1736  * but not volatile expression, that presents an opportunity for run-time
1737  * pruning by the executor, allowing irrelevant partitions to be skipped
1738  * dynamically.
1739  *
1740  * We must distinguish expressions containing PARAM_EXEC Params from
1741  * expressions that don't contain those. Even though a PARAM_EXEC Param is
1742  * considered to be a stable expression, it can change value from one plan
1743  * node scan to the next during query execution. Stable comparison
1744  * expressions that don't involve such Params allow partition pruning to be
1745  * done once during executor startup. Expressions that do involve such Params
1746  * require us to prune separately for each scan of the parent plan node.
1747  *
1748  * Note that pruning away unneeded subplans during executor startup has the
1749  * added benefit of not having to initialize the unneeded subplans at all.
1750  *
1751  *
1752  * Functions:
1753  *
1754  * ExecInitPartitionPruning:
1755  * Creates the PartitionPruneState required by ExecFindMatchingSubPlans.
1756  * Details stored include how to map the partition index returned by the
1757  * partition pruning code into subplan indexes. Also determines the set
1758  * of subplans to initialize considering the result of performing initial
1759  * pruning steps if any. Maps in PartitionPruneState are updated to
1760  * account for initial pruning possibly having eliminated some of the
1761  * subplans.
1762  *
1763  * ExecFindMatchingSubPlans:
1764  * Returns indexes of matching subplans after evaluating the expressions
1765  * that are safe to evaluate at a given point. This function is first
1766  * called during ExecInitPartitionPruning() to find the initially
1767  * matching subplans based on performing the initial pruning steps and
1768  * then must be called again each time the value of a Param listed in
1769  * PartitionPruneState's 'execparamids' changes.
1770  *-------------------------------------------------------------------------
1771  */
1772 
1773 /*
1774  * ExecInitPartitionPruning
1775  * Initialize data structure needed for run-time partition pruning and
1776  * do initial pruning if needed
1777  *
1778  * 'root_parent_relids' identifies the relation to which both the parent plan
1779  * and the PartitionPruneInfo given by 'part_prune_index' belong.
1780  *
1781  * On return, *initially_valid_subplans is assigned the set of indexes of
1782  * child subplans that must be initialized along with the parent plan node.
1783  * Initial pruning is performed here if needed and in that case only the
1784  * surviving subplans' indexes are added.
1785  *
1786  * If subplans are indeed pruned, subplan_map arrays contained in the returned
1787  * PartitionPruneState are re-sequenced to not count those, though only if the
1788  * maps will be needed for subsequent execution pruning passes.
1789  */
1792  int n_total_subplans,
1793  int part_prune_index,
1794  Bitmapset *root_parent_relids,
1795  Bitmapset **initially_valid_subplans)
1796 {
1797  PartitionPruneState *prunestate;
1798  EState *estate = planstate->state;
1799  PartitionPruneInfo *pruneinfo;
1800 
1801  /* Obtain the pruneinfo we need, and make sure it's the right one */
1802  pruneinfo = list_nth(estate->es_part_prune_infos, part_prune_index);
1803  if (!bms_equal(root_parent_relids, pruneinfo->root_parent_relids))
1804  ereport(ERROR,
1805  errcode(ERRCODE_INTERNAL_ERROR),
1806  errmsg_internal("mismatching PartitionPruneInfo found at part_prune_index %d",
1807  part_prune_index),
1808  errdetail_internal("plan node relids %s, pruneinfo relids %s",
1809  bmsToString(root_parent_relids),
1810  bmsToString(pruneinfo->root_parent_relids)));
1811 
1812  /* We may need an expression context to evaluate partition exprs */
1813  ExecAssignExprContext(estate, planstate);
1814 
1815  /* Create the working data structure for pruning */
1816  prunestate = CreatePartitionPruneState(planstate, pruneinfo);
1817 
1818  /*
1819  * Perform an initial partition prune pass, if required.
1820  */
1821  if (prunestate->do_initial_prune)
1822  *initially_valid_subplans = ExecFindMatchingSubPlans(prunestate, true);
1823  else
1824  {
1825  /* No pruning, so we'll need to initialize all subplans */
1826  Assert(n_total_subplans > 0);
1827  *initially_valid_subplans = bms_add_range(NULL, 0,
1828  n_total_subplans - 1);
1829  }
1830 
1831  /*
1832  * Re-sequence subplan indexes contained in prunestate to account for any
1833  * that were removed above due to initial pruning. No need to do this if
1834  * no steps were removed.
1835  */
1836  if (bms_num_members(*initially_valid_subplans) < n_total_subplans)
1837  {
1838  /*
1839  * We can safely skip this when !do_exec_prune, even though that
1840  * leaves invalid data in prunestate, because that data won't be
1841  * consulted again (cf initial Assert in ExecFindMatchingSubPlans).
1842  */
1843  if (prunestate->do_exec_prune)
1844  PartitionPruneFixSubPlanMap(prunestate,
1845  *initially_valid_subplans,
1846  n_total_subplans);
1847  }
1848 
1849  return prunestate;
1850 }
1851 
1852 /*
1853  * CreatePartitionPruneState
1854  * Build the data structure required for calling ExecFindMatchingSubPlans
1855  *
1856  * 'planstate' is the parent plan node's execution state.
1857  *
1858  * 'pruneinfo' is a PartitionPruneInfo as generated by
1859  * make_partition_pruneinfo. Here we build a PartitionPruneState containing a
1860  * PartitionPruningData for each partitioning hierarchy (i.e., each sublist of
1861  * pruneinfo->prune_infos), each of which contains a PartitionedRelPruningData
1862  * for each PartitionedRelPruneInfo appearing in that sublist. This two-level
1863  * system is needed to keep from confusing the different hierarchies when a
1864  * UNION ALL contains multiple partitioned tables as children. The data
1865  * stored in each PartitionedRelPruningData can be re-used each time we
1866  * re-evaluate which partitions match the pruning steps provided in each
1867  * PartitionedRelPruneInfo.
1868  */
1869 static PartitionPruneState *
1871 {
1872  EState *estate = planstate->state;
1873  PartitionPruneState *prunestate;
1874  int n_part_hierarchies;
1875  ListCell *lc;
1876  int i;
1877  ExprContext *econtext = planstate->ps_ExprContext;
1878 
1879  /* For data reading, executor always omits detached partitions */
1880  if (estate->es_partition_directory == NULL)
1881  estate->es_partition_directory =
1882  CreatePartitionDirectory(estate->es_query_cxt, false);
1883 
1884  n_part_hierarchies = list_length(pruneinfo->prune_infos);
1885  Assert(n_part_hierarchies > 0);
1886 
1887  /*
1888  * Allocate the data structure
1889  */
1890  prunestate = (PartitionPruneState *)
1891  palloc(offsetof(PartitionPruneState, partprunedata) +
1892  sizeof(PartitionPruningData *) * n_part_hierarchies);
1893 
1894  prunestate->execparamids = NULL;
1895  /* other_subplans can change at runtime, so we need our own copy */
1896  prunestate->other_subplans = bms_copy(pruneinfo->other_subplans);
1897  prunestate->do_initial_prune = false; /* may be set below */
1898  prunestate->do_exec_prune = false; /* may be set below */
1899  prunestate->num_partprunedata = n_part_hierarchies;
1900 
1901  /*
1902  * Create a short-term memory context which we'll use when making calls to
1903  * the partition pruning functions. This avoids possible memory leaks,
1904  * since the pruning functions call comparison functions that aren't under
1905  * our control.
1906  */
1907  prunestate->prune_context =
1909  "Partition Prune",
1911 
1912  i = 0;
1913  foreach(lc, pruneinfo->prune_infos)
1914  {
1915  List *partrelpruneinfos = lfirst_node(List, lc);
1916  int npartrelpruneinfos = list_length(partrelpruneinfos);
1917  PartitionPruningData *prunedata;
1918  ListCell *lc2;
1919  int j;
1920 
1921  prunedata = (PartitionPruningData *)
1922  palloc(offsetof(PartitionPruningData, partrelprunedata) +
1923  npartrelpruneinfos * sizeof(PartitionedRelPruningData));
1924  prunestate->partprunedata[i] = prunedata;
1925  prunedata->num_partrelprunedata = npartrelpruneinfos;
1926 
1927  j = 0;
1928  foreach(lc2, partrelpruneinfos)
1929  {
1931  PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
1932  Relation partrel;
1933  PartitionDesc partdesc;
1934  PartitionKey partkey;
1935 
1936  /*
1937  * We can rely on the copies of the partitioned table's partition
1938  * key and partition descriptor appearing in its relcache entry,
1939  * because that entry will be held open and locked for the
1940  * duration of this executor run.
1941  */
1942  partrel = ExecGetRangeTableRelation(estate, pinfo->rtindex);
1943  partkey = RelationGetPartitionKey(partrel);
1945  partrel);
1946 
1947  /*
1948  * Initialize the subplan_map and subpart_map.
1949  *
1950  * Because we request detached partitions to be included, and
1951  * detaching waits for old transactions, it is safe to assume that
1952  * no partitions have disappeared since this query was planned.
1953  *
1954  * However, new partitions may have been added.
1955  */
1956  Assert(partdesc->nparts >= pinfo->nparts);
1957  pprune->nparts = partdesc->nparts;
1958  pprune->subplan_map = palloc(sizeof(int) * partdesc->nparts);
1959  if (partdesc->nparts == pinfo->nparts)
1960  {
1961  /*
1962  * There are no new partitions, so this is simple. We can
1963  * simply point to the subpart_map from the plan, but we must
1964  * copy the subplan_map since we may change it later.
1965  */
1966  pprune->subpart_map = pinfo->subpart_map;
1967  memcpy(pprune->subplan_map, pinfo->subplan_map,
1968  sizeof(int) * pinfo->nparts);
1969 
1970  /*
1971  * Double-check that the list of unpruned relations has not
1972  * changed. (Pruned partitions are not in relid_map[].)
1973  */
1974 #ifdef USE_ASSERT_CHECKING
1975  for (int k = 0; k < pinfo->nparts; k++)
1976  {
1977  Assert(partdesc->oids[k] == pinfo->relid_map[k] ||
1978  pinfo->subplan_map[k] == -1);
1979  }
1980 #endif
1981  }
1982  else
1983  {
1984  int pd_idx = 0;
1985  int pp_idx;
1986 
1987  /*
1988  * Some new partitions have appeared since plan time, and
1989  * those are reflected in our PartitionDesc but were not
1990  * present in the one used to construct subplan_map and
1991  * subpart_map. So we must construct new and longer arrays
1992  * where the partitions that were originally present map to
1993  * the same sub-structures, and any added partitions map to
1994  * -1, as if the new partitions had been pruned.
1995  *
1996  * Note: pinfo->relid_map[] may contain InvalidOid entries for
1997  * partitions pruned by the planner. We cannot tell exactly
1998  * which of the partdesc entries these correspond to, but we
1999  * don't have to; just skip over them. The non-pruned
2000  * relid_map entries, however, had better be a subset of the
2001  * partdesc entries and in the same order.
2002  */
2003  pprune->subpart_map = palloc(sizeof(int) * partdesc->nparts);
2004  for (pp_idx = 0; pp_idx < partdesc->nparts; pp_idx++)
2005  {
2006  /* Skip any InvalidOid relid_map entries */
2007  while (pd_idx < pinfo->nparts &&
2008  !OidIsValid(pinfo->relid_map[pd_idx]))
2009  pd_idx++;
2010 
2011  if (pd_idx < pinfo->nparts &&
2012  pinfo->relid_map[pd_idx] == partdesc->oids[pp_idx])
2013  {
2014  /* match... */
2015  pprune->subplan_map[pp_idx] =
2016  pinfo->subplan_map[pd_idx];
2017  pprune->subpart_map[pp_idx] =
2018  pinfo->subpart_map[pd_idx];
2019  pd_idx++;
2020  }
2021  else
2022  {
2023  /* this partdesc entry is not in the plan */
2024  pprune->subplan_map[pp_idx] = -1;
2025  pprune->subpart_map[pp_idx] = -1;
2026  }
2027  }
2028 
2029  /*
2030  * It might seem that we need to skip any trailing InvalidOid
2031  * entries in pinfo->relid_map before checking that we scanned
2032  * all of the relid_map. But we will have skipped them above,
2033  * because they must correspond to some partdesc->oids
2034  * entries; we just couldn't tell which.
2035  */
2036  if (pd_idx != pinfo->nparts)
2037  elog(ERROR, "could not match partition child tables to plan elements");
2038  }
2039 
2040  /* present_parts is also subject to later modification */
2041  pprune->present_parts = bms_copy(pinfo->present_parts);
2042 
2043  /*
2044  * Initialize pruning contexts as needed.
2045  */
2047  if (pinfo->initial_pruning_steps)
2048  {
2050  pinfo->initial_pruning_steps,
2051  partdesc, partkey, planstate,
2052  econtext);
2053  /* Record whether initial pruning is needed at any level */
2054  prunestate->do_initial_prune = true;
2055  }
2056  pprune->exec_pruning_steps = pinfo->exec_pruning_steps;
2057  if (pinfo->exec_pruning_steps)
2058  {
2060  pinfo->exec_pruning_steps,
2061  partdesc, partkey, planstate,
2062  econtext);
2063  /* Record whether exec pruning is needed at any level */
2064  prunestate->do_exec_prune = true;
2065  }
2066 
2067  /*
2068  * Accumulate the IDs of all PARAM_EXEC Params affecting the
2069  * partitioning decisions at this plan node.
2070  */
2071  prunestate->execparamids = bms_add_members(prunestate->execparamids,
2072  pinfo->execparamids);
2073 
2074  j++;
2075  }
2076  i++;
2077  }
2078 
2079  return prunestate;
2080 }
2081 
2082 /*
2083  * Initialize a PartitionPruneContext for the given list of pruning steps.
2084  */
2085 static void
2087  List *pruning_steps,
2088  PartitionDesc partdesc,
2089  PartitionKey partkey,
2090  PlanState *planstate,
2091  ExprContext *econtext)
2092 {
2093  int n_steps;
2094  int partnatts;
2095  ListCell *lc;
2096 
2097  n_steps = list_length(pruning_steps);
2098 
2099  context->strategy = partkey->strategy;
2100  context->partnatts = partnatts = partkey->partnatts;
2101  context->nparts = partdesc->nparts;
2102  context->boundinfo = partdesc->boundinfo;
2103  context->partcollation = partkey->partcollation;
2104  context->partsupfunc = partkey->partsupfunc;
2105 
2106  /* We'll look up type-specific support functions as needed */
2107  context->stepcmpfuncs = (FmgrInfo *)
2108  palloc0(sizeof(FmgrInfo) * n_steps * partnatts);
2109 
2110  context->ppccontext = CurrentMemoryContext;
2111  context->planstate = planstate;
2112  context->exprcontext = econtext;
2113 
2114  /* Initialize expression state for each expression we need */
2115  context->exprstates = (ExprState **)
2116  palloc0(sizeof(ExprState *) * n_steps * partnatts);
2117  foreach(lc, pruning_steps)
2118  {
2120  ListCell *lc2;
2121  int keyno;
2122 
2123  /* not needed for other step kinds */
2124  if (!IsA(step, PartitionPruneStepOp))
2125  continue;
2126 
2127  Assert(list_length(step->exprs) <= partnatts);
2128 
2129  keyno = 0;
2130  foreach(lc2, step->exprs)
2131  {
2132  Expr *expr = (Expr *) lfirst(lc2);
2133 
2134  /* not needed for Consts */
2135  if (!IsA(expr, Const))
2136  {
2137  int stateidx = PruneCxtStateIdx(partnatts,
2138  step->step.step_id,
2139  keyno);
2140 
2141  /*
2142  * When planstate is NULL, pruning_steps is known not to
2143  * contain any expressions that depend on the parent plan.
2144  * Information of any available EXTERN parameters must be
2145  * passed explicitly in that case, which the caller must have
2146  * made available via econtext.
2147  */
2148  if (planstate == NULL)
2149  context->exprstates[stateidx] =
2151  econtext->ecxt_param_list_info);
2152  else
2153  context->exprstates[stateidx] =
2154  ExecInitExpr(expr, context->planstate);
2155  }
2156  keyno++;
2157  }
2158  }
2159 }
2160 
2161 /*
2162  * PartitionPruneFixSubPlanMap
2163  * Fix mapping of partition indexes to subplan indexes contained in
2164  * prunestate by considering the new list of subplans that survived
2165  * initial pruning
2166  *
2167  * Current values of the indexes present in PartitionPruneState count all the
2168  * subplans that would be present before initial pruning was done. If initial
2169  * pruning got rid of some of the subplans, any subsequent pruning passes will
2170  * be looking at a different set of target subplans to choose from than those
2171  * in the pre-initial-pruning set, so the maps in PartitionPruneState
2172  * containing those indexes must be updated to reflect the new indexes of
2173  * subplans in the post-initial-pruning set.
2174  */
2175 static void
2177  Bitmapset *initially_valid_subplans,
2178  int n_total_subplans)
2179 {
2180  int *new_subplan_indexes;
2181  Bitmapset *new_other_subplans;
2182  int i;
2183  int newidx;
2184 
2185  /*
2186  * First we must build a temporary array which maps old subplan indexes to
2187  * new ones. For convenience of initialization, we use 1-based indexes in
2188  * this array and leave pruned items as 0.
2189  */
2190  new_subplan_indexes = (int *) palloc0(sizeof(int) * n_total_subplans);
2191  newidx = 1;
2192  i = -1;
2193  while ((i = bms_next_member(initially_valid_subplans, i)) >= 0)
2194  {
2195  Assert(i < n_total_subplans);
2196  new_subplan_indexes[i] = newidx++;
2197  }
2198 
2199  /*
2200  * Now we can update each PartitionedRelPruneInfo's subplan_map with new
2201  * subplan indexes. We must also recompute its present_parts bitmap.
2202  */
2203  for (i = 0; i < prunestate->num_partprunedata; i++)
2204  {
2205  PartitionPruningData *prunedata = prunestate->partprunedata[i];
2206  int j;
2207 
2208  /*
2209  * Within each hierarchy, we perform this loop in back-to-front order
2210  * so that we determine present_parts for the lowest-level partitioned
2211  * tables first. This way we can tell whether a sub-partitioned
2212  * table's partitions were entirely pruned so we can exclude it from
2213  * the current level's present_parts.
2214  */
2215  for (j = prunedata->num_partrelprunedata - 1; j >= 0; j--)
2216  {
2217  PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
2218  int nparts = pprune->nparts;
2219  int k;
2220 
2221  /* We just rebuild present_parts from scratch */
2222  bms_free(pprune->present_parts);
2223  pprune->present_parts = NULL;
2224 
2225  for (k = 0; k < nparts; k++)
2226  {
2227  int oldidx = pprune->subplan_map[k];
2228  int subidx;
2229 
2230  /*
2231  * If this partition existed as a subplan then change the old
2232  * subplan index to the new subplan index. The new index may
2233  * become -1 if the partition was pruned above, or it may just
2234  * come earlier in the subplan list due to some subplans being
2235  * removed earlier in the list. If it's a subpartition, add
2236  * it to present_parts unless it's entirely pruned.
2237  */
2238  if (oldidx >= 0)
2239  {
2240  Assert(oldidx < n_total_subplans);
2241  pprune->subplan_map[k] = new_subplan_indexes[oldidx] - 1;
2242 
2243  if (new_subplan_indexes[oldidx] > 0)
2244  pprune->present_parts =
2245  bms_add_member(pprune->present_parts, k);
2246  }
2247  else if ((subidx = pprune->subpart_map[k]) >= 0)
2248  {
2249  PartitionedRelPruningData *subprune;
2250 
2251  subprune = &prunedata->partrelprunedata[subidx];
2252 
2253  if (!bms_is_empty(subprune->present_parts))
2254  pprune->present_parts =
2255  bms_add_member(pprune->present_parts, k);
2256  }
2257  }
2258  }
2259  }
2260 
2261  /*
2262  * We must also recompute the other_subplans set, since indexes in it may
2263  * change.
2264  */
2265  new_other_subplans = NULL;
2266  i = -1;
2267  while ((i = bms_next_member(prunestate->other_subplans, i)) >= 0)
2268  new_other_subplans = bms_add_member(new_other_subplans,
2269  new_subplan_indexes[i] - 1);
2270 
2271  bms_free(prunestate->other_subplans);
2272  prunestate->other_subplans = new_other_subplans;
2273 
2274  pfree(new_subplan_indexes);
2275 }
2276 
2277 /*
2278  * ExecFindMatchingSubPlans
2279  * Determine which subplans match the pruning steps detailed in
2280  * 'prunestate' for the current comparison expression values.
2281  *
2282  * Pass initial_prune if PARAM_EXEC Params cannot yet be evaluated. This
2283  * differentiates the initial executor-time pruning step from later
2284  * runtime pruning.
2285  */
2286 Bitmapset *
2288  bool initial_prune)
2289 {
2290  Bitmapset *result = NULL;
2291  MemoryContext oldcontext;
2292  int i;
2293 
2294  /*
2295  * Either we're here on the initial prune done during pruning
2296  * initialization, or we're at a point where PARAM_EXEC Params can be
2297  * evaluated *and* there are steps in which to do so.
2298  */
2299  Assert(initial_prune || prunestate->do_exec_prune);
2300 
2301  /*
2302  * Switch to a temp context to avoid leaking memory in the executor's
2303  * query-lifespan memory context.
2304  */
2305  oldcontext = MemoryContextSwitchTo(prunestate->prune_context);
2306 
2307  /*
2308  * For each hierarchy, do the pruning tests, and add nondeletable
2309  * subplans' indexes to "result".
2310  */
2311  for (i = 0; i < prunestate->num_partprunedata; i++)
2312  {
2313  PartitionPruningData *prunedata = prunestate->partprunedata[i];
2314  PartitionedRelPruningData *pprune;
2315 
2316  /*
2317  * We pass the zeroth item, belonging to the root table of the
2318  * hierarchy, and find_matching_subplans_recurse() takes care of
2319  * recursing to other (lower-level) parents as needed.
2320  */
2321  pprune = &prunedata->partrelprunedata[0];
2322  find_matching_subplans_recurse(prunedata, pprune, initial_prune,
2323  &result);
2324 
2325  /* Expression eval may have used space in ExprContext too */
2326  if (pprune->exec_pruning_steps)
2328  }
2329 
2330  /* Add in any subplans that partition pruning didn't account for */
2331  result = bms_add_members(result, prunestate->other_subplans);
2332 
2333  MemoryContextSwitchTo(oldcontext);
2334 
2335  /* Copy result out of the temp context before we reset it */
2336  result = bms_copy(result);
2337 
2338  MemoryContextReset(prunestate->prune_context);
2339 
2340  return result;
2341 }
2342 
2343 /*
2344  * find_matching_subplans_recurse
2345  * Recursive worker function for ExecFindMatchingSubPlans
2346  *
2347  * Adds valid (non-prunable) subplan IDs to *validsubplans
2348  */
2349 static void
2351  PartitionedRelPruningData *pprune,
2352  bool initial_prune,
2353  Bitmapset **validsubplans)
2354 {
2355  Bitmapset *partset;
2356  int i;
2357 
2358  /* Guard against stack overflow due to overly deep partition hierarchy. */
2360 
2361  /*
2362  * Prune as appropriate, if we have pruning steps matching the current
2363  * execution context. Otherwise just include all partitions at this
2364  * level.
2365  */
2366  if (initial_prune && pprune->initial_pruning_steps)
2367  partset = get_matching_partitions(&pprune->initial_context,
2368  pprune->initial_pruning_steps);
2369  else if (!initial_prune && pprune->exec_pruning_steps)
2370  partset = get_matching_partitions(&pprune->exec_context,
2371  pprune->exec_pruning_steps);
2372  else
2373  partset = pprune->present_parts;
2374 
2375  /* Translate partset into subplan indexes */
2376  i = -1;
2377  while ((i = bms_next_member(partset, i)) >= 0)
2378  {
2379  if (pprune->subplan_map[i] >= 0)
2380  *validsubplans = bms_add_member(*validsubplans,
2381  pprune->subplan_map[i]);
2382  else
2383  {
2384  int partidx = pprune->subpart_map[i];
2385 
2386  if (partidx >= 0)
2388  &prunedata->partrelprunedata[partidx],
2389  initial_prune, validsubplans);
2390  else
2391  {
2392  /*
2393  * We get here if the planner already pruned all the sub-
2394  * partitions for this partition. Silently ignore this
2395  * partition in this case. The end result is the same: we
2396  * would have pruned all partitions just the same, but we
2397  * don't have any pruning steps to execute to verify this.
2398  */
2399  }
2400  }
2401  }
2402 }
AclResult
Definition: acl.h:183
@ ACLCHECK_OK
Definition: acl.h:184
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:4617
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4746
AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:264
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:178
int16 AttrNumber
Definition: attnum.h:21
#define InvalidAttrNumber
Definition: attnum.h:23
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:94
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1047
void bms_free(Bitmapset *a)
Definition: bitmapset.c:209
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:649
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:739
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:796
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:704
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:74
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:837
static Datum values[MAXATTR]
Definition: bootstrap.c:156
#define likely(x)
Definition: c.h:294
signed int int32
Definition: c.h:430
#define FLEXIBLE_ARRAY_MEMBER
Definition: c.h:362
#define unlikely(x)
Definition: c.h:295
#define OidIsValid(objectId)
Definition: c.h:711
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1156
int errdetail_internal(const char *fmt,...)
Definition: elog.c:1229
int errdetail(const char *fmt,...)
Definition: elog.c:1202
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:225
ExprState * ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
Definition: execExpr.c:161
ProjectionInfo * ExecBuildUpdateProjection(List *targetList, bool evalTargetList, List *targetColnos, TupleDesc relDesc, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent)
Definition: execExpr.c:514
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:210
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:124
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:354
List * ExecPrepareExprList(List *nodes, EState *estate)
Definition: execExpr.c:821
void ExecCloseIndices(ResultRelInfo *resultRelInfo)
Definition: execIndexing.c:231
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
Definition: execIndexing.c:156
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1778
void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, ResultRelInfo *partition_root_rri, int instrument_options)
Definition: execMain.c:1189
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
Definition: execMain.c:988
static PartitionDispatch ExecInitPartitionDispatchInfo(EState *estate, PartitionTupleRouting *proute, Oid partoid, PartitionDispatch parent_pd, int partidx, ResultRelInfo *rootResultRelInfo)
static PartitionPruneState * CreatePartitionPruneState(PlanState *planstate, PartitionPruneInfo *pruneinfo)
static ResultRelInfo * ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, PartitionDispatch dispatch, ResultRelInfo *rootResultRelInfo, int partidx)
static void ExecInitRoutingInfo(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, PartitionDispatch dispatch, ResultRelInfo *partRelInfo, int partidx, bool is_borrowed_rel)
static char * ExecBuildSlotPartitionKeyDescription(Relation rel, Datum *values, bool *isnull, int maxfieldlen)
Bitmapset * ExecFindMatchingSubPlans(PartitionPruneState *prunestate, bool initial_prune)
PartitionPruneState * ExecInitPartitionPruning(PlanState *planstate, int n_total_subplans, int part_prune_index, Bitmapset *root_parent_relids, Bitmapset **initially_valid_subplans)
static void FormPartitionKeyDatum(PartitionDispatch pd, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
static void PartitionPruneFixSubPlanMap(PartitionPruneState *prunestate, Bitmapset *initially_valid_subplans, int n_total_subplans)
#define PARTITION_CACHED_FIND_THRESHOLD
static List * adjust_partition_colnos(List *colnos, ResultRelInfo *leaf_part_rri)
static List * adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap)
ResultRelInfo * ExecFindPartition(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, TupleTableSlot *slot, EState *estate)
static void InitPartitionPruneContext(PartitionPruneContext *context, List *pruning_steps, PartitionDesc partdesc, PartitionKey partkey, PlanState *planstate, ExprContext *econtext)
struct PartitionDispatchData PartitionDispatchData
static void find_matching_subplans_recurse(PartitionPruningData *prunedata, PartitionedRelPruningData *pprune, bool initial_prune, Bitmapset **validsubplans)
static int get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
PartitionTupleRouting * ExecSetupPartitionTupleRouting(EState *estate, Relation rel)
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
struct PartitionDispatchData * PartitionDispatch
Definition: execPartition.h:22
struct PartitionedRelPruningData PartitionedRelPruningData
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1254
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1238
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:486
Relation ExecGetRangeTableRelation(EState *estate, Index rti)
Definition: execUtils.c:788
TupleConversionMap * ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, EState *estate)
Definition: execUtils.c:1266
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1240
#define GetPerTupleExprContext(estate)
Definition: executor.h:538
#define ResetExprContext(econtext)
Definition: executor.h:532
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:543
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:336
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1136
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1705
long val
Definition: informix.c:664
int j
Definition: isn.c:74
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
List * lappend(List *list, void *datum)
Definition: list.c:338
List * lappend_int(List *list, int datum)
Definition: list.c:356
List * lappend_oid(List *list, Oid datum)
Definition: list.c:374
void list_free(List *list)
Definition: list.c:1545
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:721
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2865
int pg_mbcliplen(const char *mbstr, int len, int limit)
Definition: mbutils.c:1026
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:303
void pfree(void *pointer)
Definition: mcxt.c:1306
void * palloc0(Size size)
Definition: mcxt.c:1230
MemoryContext CurrentMemoryContext
Definition: mcxt.c:124
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1321
void * palloc(Size size)
Definition: mcxt.c:1199
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
Oid GetUserId(void)
Definition: miscinit.c:497
void ExecInitMergeTupleSlots(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
ResultRelInfo * ExecLookupResultRelByOid(ModifyTableState *node, Oid resultoid, bool missing_ok, bool update_cache)
#define IsA(nodeptr, _type_)
Definition: nodes.h:168
#define copyObject(obj)
Definition: nodes.h:233
@ ONCONFLICT_NONE
Definition: nodes.h:415
@ ONCONFLICT_UPDATE
Definition: nodes.h:417
@ CMD_MERGE
Definition: nodes.h:269
@ CMD_INSERT
Definition: nodes.h:267
@ CMD_DELETE
Definition: nodes.h:268
@ CMD_UPDATE
Definition: nodes.h:266
#define makeNode(_type_)
Definition: nodes.h:165
#define castNode(_type_, nodeptr)
Definition: nodes.h:186
char * bmsToString(const Bitmapset *bms)
Definition: outfuncs.c:889
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:135
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:837
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:835
@ PARTITION_STRATEGY_RANGE
Definition: parsenodes.h:836
PartitionRangeDatumKind
Definition: parsenodes.h:886
#define ACL_SELECT
Definition: parsenodes.h:84
int32 partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums, int n_tuple_datums)
Definition: partbounds.c:3557
uint64 compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation, Datum *values, bool *isnull)
Definition: partbounds.c:4728
int partition_range_datum_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, int nvalues, Datum *values, bool *is_equal)
Definition: partbounds.c:3696
int partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation, PartitionBoundInfo boundinfo, Datum value, bool *is_equal)
Definition: partbounds.c:3608
#define partition_bound_accepts_nulls(bi)
Definition: partbounds.h:98
PartitionKey RelationGetPartitionKey(Relation rel)
Definition: partcache.c:54
static int16 get_partition_col_attnum(PartitionKey key, int col)
Definition: partcache.h:80
static int get_partition_natts(PartitionKey key)
Definition: partcache.h:65
static Oid get_partition_col_typid(PartitionKey key, int col)
Definition: partcache.h:86
PartitionDirectory CreatePartitionDirectory(MemoryContext mcxt, bool omit_detached)
Definition: partdesc.c:383
PartitionDesc PartitionDirectoryLookup(PartitionDirectory pdir, Relation rel)
Definition: partdesc.c:416
List * get_partition_ancestors(Oid relid)
Definition: partition.c:133
Bitmapset * get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
Definition: partprune.c:826
#define PruneCxtStateIdx(partnatts, step_id, keyno)
Definition: partprune.h:70
int16 attnum
Definition: pg_attribute.h:83
#define PARTITION_MAX_KEYS
#define lfirst(lc)
Definition: pg_list.h:170
#define lfirst_node(type, lc)
Definition: pg_list.h:174
static int list_length(const List *l)
Definition: pg_list.h:150
#define NIL
Definition: pg_list.h:66
#define lfirst_int(lc)
Definition: pg_list.h:171
static ListCell * list_head(const List *l)
Definition: pg_list.h:126
#define linitial(l)
Definition: pg_list.h:176
static void * list_nth(const List *list, int n)
Definition: pg_list.h:297
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:341
#define lfirst_oid(lc)
Definition: pg_list.h:172
static char * buf
Definition: pg_test_fsync.c:67
void check_stack_depth(void)
Definition: postgres.c:3440
uintptr_t Datum
Definition: postgres.h:412
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:550
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define INNER_VAR
Definition: primnodes.h:193
#define RelationGetForm(relation)
Definition: rel.h:495
#define RelationGetRelid(relation)
Definition: rel.h:501
#define RelationGetDescr(relation)
Definition: rel.h:527
#define RelationGetRelationName(relation)
Definition: rel.h:535
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4738
int errtable(Relation rel)
Definition: relcache.c:5876
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
int check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
Definition: rls.c:52
@ RLS_ENABLED
Definition: rls.h:45
char * pg_get_partkeydef_columns(Oid relid, bool pretty)
Definition: ruleutils.c:1874
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
void appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
Definition: stringinfo.c:227
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:176
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:188
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
Definition: attmap.h:35
int maplen
Definition: attmap.h:37
AttrNumber * attnums
Definition: attmap.h:36
List * es_part_prune_infos
Definition: execnodes.h:619
List * es_tuple_routing_result_relations
Definition: execnodes.h:641
int es_instrument
Definition: execnodes.h:660
MemoryContext es_query_cxt
Definition: execnodes.h:653
List * es_tupleTable
Definition: execnodes.h:655
PartitionDirectory es_partition_directory
Definition: execnodes.h:635
ParamListInfo ecxt_param_list_info
Definition: execnodes.h:259
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:247
EndForeignInsert_function EndForeignInsert
Definition: fdwapi.h:239
BeginForeignInsert_function BeginForeignInsert
Definition: fdwapi.h:238
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:233
GetForeignModifyBatchSize_function GetForeignModifyBatchSize
Definition: fdwapi.h:234
Definition: fmgr.h:57
Definition: pg_list.h:52
MergeAction * mas_action
Definition: execnodes.h:417
ProjectionInfo * mas_proj
Definition: execnodes.h:418
ExprState * mas_whenqual
Definition: execnodes.h:420
CmdType operation
Definition: execnodes.h:1256
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1260
PlanState ps
Definition: execnodes.h:1255
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1268
List * onConflictCols
Definition: plannodes.h:251
CmdType operation
Definition: plannodes.h:235
List * resultRelations
Definition: plannodes.h:240
List * onConflictSet
Definition: plannodes.h:250
List * mergeActionLists
Definition: plannodes.h:255
List * returningLists
Definition: plannodes.h:243
List * withCheckOptionLists
Definition: plannodes.h:242
Node * onConflictWhere
Definition: plannodes.h:252
OnConflictAction onConflictAction
Definition: plannodes.h:248
Definition: nodes.h:118
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:402
TupleTableSlot * oc_Existing
Definition: execnodes.h:401
ExprState * oc_WhereClause
Definition: execnodes.h:404
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:403
PartitionRangeDatumKind ** kind
Definition: partbounds.h:84
int last_found_datum_index
Definition: partdesc.h:46
PartitionBoundInfo boundinfo
Definition: partdesc.h:38
int last_found_count
Definition: partdesc.h:63
bool * is_leaf
Definition: partdesc.h:35
int last_found_part_index
Definition: partdesc.h:52
TupleTableSlot * tupslot
PartitionDesc partdesc
int indexes[FLEXIBLE_ARRAY_MEMBER]
Oid * partcollation
Definition: partcache.h:39
PartitionStrategy strategy
Definition: partcache.h:27
List * partexprs
Definition: partcache.h:31
FmgrInfo * partsupfunc
Definition: partcache.h:36
AttrNumber * partattrs
Definition: partcache.h:29
FmgrInfo * partsupfunc
Definition: partprune.h:56
ExprContext * exprcontext
Definition: partprune.h:60
MemoryContext ppccontext
Definition: partprune.h:58
PartitionBoundInfo boundinfo
Definition: partprune.h:54
PlanState * planstate
Definition: partprune.h:59
FmgrInfo * stepcmpfuncs
Definition: partprune.h:57
ExprState ** exprstates
Definition: partprune.h:61
Bitmapset * other_subplans
Definition: plannodes.h:1431
Bitmapset * root_parent_relids
Definition: plannodes.h:1429
PartitionPruningData * partprunedata[FLEXIBLE_ARRAY_MEMBER]
Bitmapset * execparamids
Bitmapset * other_subplans
MemoryContext prune_context
PartitionPruneStep step
Definition: plannodes.h:1531
PartitionedRelPruningData partrelprunedata[FLEXIBLE_ARRAY_MEMBER]
Definition: execPartition.h:81
PartitionDispatch * partition_dispatch_info
Definition: execPartition.c:97
ResultRelInfo ** partitions
MemoryContext memcxt
ResultRelInfo ** nonleaf_partitions
Definition: execPartition.c:98
Bitmapset * present_parts
Definition: plannodes.h:1461
Bitmapset * execparamids
Definition: plannodes.h:1485
PartitionPruneContext exec_context
Definition: execPartition.h:68
PartitionPruneContext initial_context
Definition: execPartition.h:67
Plan * plan
Definition: execnodes.h:1028
EState * state
Definition: execnodes.h:1030
ExprContext * ps_ExprContext
Definition: execnodes.h:1067
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1066
Form_pg_class rd_rel
Definition: rel.h:110
TupleTableSlot * ri_PartitionTupleSlot
Definition: execnodes.h:568
OnConflictSetState * ri_onConflict
Definition: execnodes.h:532
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:529
Relation ri_RelationDesc
Definition: execnodes.h:448
struct CopyMultiInsertBuffer * ri_CopyMultiInsertBuffer
Definition: execnodes.h:571
Index ri_RangeTableIndex
Definition: execnodes.h:445
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:492
int ri_BatchSize
Definition: execnodes.h:503
AttrMap * attrMap
Definition: tupconvert.h:28
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:192
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:433
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:389
#define IsolationUsesXactSnapshot()
Definition: xact.h:51