Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the implementation logic of ExecModifyTable function in PostgreSQL?

2025-01-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly explains "what is the implementation logic of ExecModifyTable function in PostgreSQL". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "what is the implementation logic of ExecModifyTable function in PostgreSQL".

I. basic information

The data structures, macro definitions, and dependent functions used by the ExecModifyTable function.

Data structure / Macro definition

1 、 PartitionTupleRouting

/ / data structure related to the partition table, and then update / *-- * PartitionTupleRouting-Encapsulates all information required to execute * tuple-routing between partitions when processing the partition table. * * partition_dispatch_info Array of PartitionDispatch objects with one * entry for every partitioned table in the * partition tree. * num_dispatch number of partitioned tables in the partition * tree (= length of partition_dispatch_info []) * partition_oids Array of leaf partitions OIDs with one entry * for every leaf partition in the partition tree * initialized in full by * ExecSetupPartitionTupleRouting. * partitions Array of ResultRelInfo* objects with one entry * for every leaf partition in the partition tree, * initialized lazily by ExecInitPartitionInfo. * num_partitions Number of leaf partitions in the partition tree * (= 'partitions_oid'/'partitions' array length) * parent_child_tupconv_maps Array of TupleConversionMap objects with one * entry for every leaf partition (required to * convert tuple from the root table's rowtype to * A leaf partition's rowtype after tuple routing * is done) * child_parent_tupconv_maps Array of TupleConversionMap objects with one * entry for every leaf partition (required to * convert an updated tuple from the leaf * Partition's rowtype to the root table's rowtype * so that tuple routing can be done) * child_parent_map_not_required Array of bool. True value means that a map is * determined to be not required for the given * partition. False means either we haven't yet * checked if a map is required, or it was * determined to be required. * subplan_partition_offsets Integer array ordered by UPDATE subplans. Each * element of this array has the index into the * corresponding partition in partitions array. * num_subplan_partition_offsets Length of 'subplan_partition_offsets' array * partition_tuple_slot TupleTableSlot to be used to manipulate any * given leaf partition's rowtype after that * partition is chosen for insertion by * tuple-routing. * root_tuple_slot TupleTableSlot to be used to transiently hold * copy of a tuple that's being moved across * partitions in the root partitioned table's * rowtype *-- * / typedef struct PartitionTupleRouting {PartitionDispatch * partition_dispatch_info Int num_dispatch; Oid * partition_oids; ResultRelInfo * * partitions; int num_partitions; TupleConversionMap * * parent_child_tupconv_maps; TupleConversionMap * * child_parent_tupconv_maps; bool * child_parent_map_not_required; int * subplan_partition_offsets; int num_subplan_partition_offsets; TupleTableSlot * partition_tuple_slot TupleTableSlot * root_tuple_slot;} PartitionTupleRouting

2 、 CmdType

/ / Command type, DDL commands are all included in CMD_UTILITY / * * CmdType-* enums for type of operation represented by a Query or PlannedStmt * * This is needed in both parsenodes.h and plannodes.h, so put it here... * / typedef enum CmdType {CMD_UNKNOWN, CMD_SELECT, / * select stmt * / CMD_UPDATE, / * update stmt * / CMD_INSERT, / * insert stmt * / CMD_DELETE, CMD_UTILITY, / * cmds like create, destroy, copy, vacuum * etc. * / CMD_NOTHING / * dummy command for instead nothing rules * with qual * /} CmdType

3 、 JunkFilter

/ / the Attribute required at runtime becomes junk attribute, and the actual Tuple needs to use JunkFilter to filter these attributes / *-* JunkFilter * * This class is used to store information regarding junk attributes. * A junk attribute is an attribute in a tuple that is needed only for * storing intermediate information in the executor, and does not belong * in emitted tuples. For example, when we do an UPDATE query, * the planner adds a "junk" entry to the targetlist so that the tuples * returned to ExecutePlan () contain an extra attribute: the ctid of * the tuple to be updated. This is needed to do the update, but we * don't want the ctid to be part of the stored new tuple! So, we * apply a "junk filter" to remove the junk attributes and form the * real output tuple. The junkfilter code also provides routines to * extract the values of the junk attribute (s) from the input tuple. * * targetList: the original target list (including junk attributes). * cleanTupType: the tuple descriptor for the "clean" tuple (with * junk attributes removed) * cleanMap: A map with the correspondence between the non-junk * attribute numbers of the "original" tuple and the * attribute numbers of the "clean" tuple. * resultSlot: tuple slot used to hold cleaned tuple. * junkAttNo: not used by junkfilter code. Can be used by caller * to remember the attno of a specific junk attribute * (nodeModifyTable.c keeps the "ctid" or "wholerow" * attno here). *-* / typedef struct JunkFilter {NodeTag type; List * jf_targetList; TupleDesc jf_cleanTupType; AttrNumber * jf_cleanMap; TupleTableSlot * jf_resultSlot; AttrNumber jf_junkAttNo;} JunkFilter

4 、 Datum

/ / the actual type is unsigned long int, unsigned long integer / * * A Datum contains either a value of a pass-by-value type or a pointer to a * value of a pass-by-reference type. Therefore, we require: * * sizeof (Datum) = = sizeof (void *) = = 4 or 8 * * The macros below and the analogous macros for other types should be used to * convert between a Datum and the appropriate C type. * / typedef uintptr_t Datum;uintptr_t unsigned integer type capable of holding a pointer,defined in header typedef unsigned long int uintptr_t

5 、 CHECK_FOR_INTERRUPTS

/ * The CHECK_FOR_INTERRUPTS () macro is called at strategically located spots * where it is normally safe to accept a cancel or die interrupt. In some * cases, we invoke CHECK_FOR_INTERRUPTS () inside low-level subroutines that * might sometimes be called in contexts that do * not* want to allow a cancel * or die interrupt. The HOLD_INTERRUPTS () and RESUME_INTERRUPTS () macros * allow code to ensure that no cancel or die interrupt will be accepted, * even if CHECK_FOR_INTERRUPTS () gets called in a subroutine. The interrupt * will be held off until CHECK_FOR_INTERRUPTS () is done outside any * HOLD_INTERRUPTS ()... RESUME_INTERRUPTS () section. * / # define CHECK_FOR_INTERRUPTS ()\ do {\ if (InterruptPending)\ ProcessInterrupts ();\} while (0)

Dependent function

1 、 fireBSTriggers

/ / trigger statement-level trigger / * * Process BEFORE EACH STATEMENT triggers * / static void fireBSTriggers (ModifyTableState * node) {ModifyTable * plan = (ModifyTable *) node- > ps.plan; ResultRelInfo * resultRelInfo = node- > resultRelInfo; / * * If the node modifies a partitioned table, we must fire its triggers. * Note that in that case, node- > resultRelInfo points to the first leaf * partition, not the root table. * / if (node- > rootResultRelInfo! = NULL) resultRelInfo = node- > rootResultRelInfo; switch (node- > operation) {case CMD_INSERT: ExecBSInsertTriggers (node- > ps.state, resultRelInfo); if (plan- > onConflictAction = = ONCONFLICT_UPDATE) ExecBSUpdateTriggers (node- > ps.state, resultRelInfo); break Case CMD_UPDATE: ExecBSUpdateTriggers (node- > ps.state, resultRelInfo); break; case CMD_DELETE: ExecBSDeleteTriggers (node- > ps.state, resultRelInfo); break; default: elog (ERROR, "unknown operation"); break;}}

2 、 ResetPerTupleExprContext

/ * Reset an EState's per-output-tuple exprcontext, if one's been created * / # define ResetPerTupleExprContext (estate)\ do {\ if ((estate)-> es_per_tuple_exprcontext)\ ResetExprContext ((estate)-> es_per_tuple_exprcontext);\} while (0)

3 、 ExecProcNode

/ / used when executing the subplan. This function is also a high N-level function, which will be interpreted later.

4 、 TupIsNull

/ / determine whether TupleTableSlot is Null (including Empty) / * * TupIsNull-- is a TupleTableSlot empty? * / # define TupIsNull (slot)\ ((slot) = = NULL | | (slot)-> tts_isempty)

5 、 EvalPlanQualSetPlan

/ / TODO / * * EvalPlanQualSetPlan-set or change subplan of an EPQState. * * We need this so that ModifyTable can deal with multiple subplans. * / void EvalPlanQualSetPlan (EPQState * epqstate, Plan * subplan, List * auxrowmarks) {/ * If we have a live EPQ query, shut it down * / EvalPlanQualEnd (epqstate); / * And set/change the plan pointer * / epqstate- > plan = subplan; / * The rowmarks depend on the plan, too * / epqstate- > arowMarks = auxrowmarks;}

6 、 tupconv_map_for_subplan

/ / Partition table is used, then parse / / TODO / * * For a given subplan index, get the tuple conversion map. * / static TupleConversionMap * tupconv_map_for_subplan (ModifyTableState * mtstate, int whichplan) {/ * If a partition-index tuple conversion map array is allocated, we need * to first get the index into the partition array. Exactly * one* of the * two arrays is allocated. This is because if there is a partition array * required, we don't require subplan-indexed array since we can translate * subplan index into partition index. And, we create a subplan-indexed * array * only* if partition-indexed array is not required. * / if (mtstate- > mt_per_subplan_tupconv_maps = = NULL) {int leaf_index; PartitionTupleRouting * proute = mtstate- > mt_partition_tuple_routing; / * * If subplan-indexed array is NULL, things should have been arranged * to convert the subplan index to partition index. * / Assert (proute & & proute- > subplan_partition_offsets! = NULL & & whichplan

< proute->

Num_subplan_partition_offsets); leaf_index = proute- > subplan_partition_ offsets [whichplan]; return TupConvMapForLeaf (proute, getTargetResultRelInfo (mtstate), leaf_index);} else {Assert (whichplan > = 0 & & whichplan)

< mtstate->

< cleanLength; i++) { int j = cleanMap[i]; if (j == 0) { values[i] = (Datum) 0; isnull[i] = true; } else { values[i] = old_values[j - 1]; isnull[i] = old_isnull[j - 1]; } } /* * And return the virtual tuple. */ return ExecStoreVirtualTuple(resultSlot); } 16、ExecPrepareTupleRouting //分区表使用,后续再行解析 /* * ExecPrepareTupleRouting --- prepare for routing one tuple * * Determine the partition in which the tuple in slot is to be inserted, * and modify mtstate and estate to prepare for it. * * Caller must revert the estate changes after executing the insertion! * In mtstate, transition capture changes may also need to be reverted. * * Returns a slot holding the tuple of the partition rowtype. */ static TupleTableSlot * ExecPrepareTupleRouting(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, ResultRelInfo *targetRelInfo, TupleTableSlot *slot) { ModifyTable *node; int partidx; ResultRelInfo *partrel; HeapTuple tuple; /* * Determine the target partition. If ExecFindPartition does not find a * partition after all, it doesn't return here; otherwise, the returned * value is to be used as an index into the arrays for the ResultRelInfo * and TupleConversionMap for the partition. */ partidx = ExecFindPartition(targetRelInfo, proute->

Partition_dispatch_info, slot, estate); Assert (partidx > = 0 & & partidx

< proute->

Num_partitions); / * * Get the ResultRelInfo corresponding to the selected partition; if not * yet there, initialize it. * / partrel = proute- > partitions [partidx]; if (partrel = = NULL) partrel = ExecInitPartitionInfo (mtstate, targetRelInfo, proute, estate, partidx) / * Check whether the partition is routable if we didn't yet * * Note: an UPDATE of a partition key invokes an INSERT that moves the * tuple to a new partition. This check would be applied to a subplan * partition of such an UPDATE that is chosen as the partition to route * the tuple to. The reason we do this check here rather than in * ExecSetupPartitionTupleRouting is to avoid aborting such an UPDATE * unnecessarily due to non-routable subplan partitions that may not be * chosen for update tuple movement after all. * / if (! partrel- > ri_PartitionReadyForRouting) {/ * Verify the partition is a valid target for INSERT. * / CheckValidResultRel (partrel, CMD_INSERT); / * Set up information needed for routing tuples to the partition. * / ExecInitRoutingInfo (mtstate, estate, proute, partrel, partidx);} / * Make it look like we are inserting into the partition. * / estate- > es_result_relation_info = partrel; / * Get the heap tuple out of the given slot. * / tuple = ExecMaterializeSlot (slot); / * If we're capturing transition tuples, we might need to convert from the * partition rowtype to parent rowtype. * / if (mtstate- > mt_transition_capture! = NULL) {if (partrel- > ri_TrigDesc & & partrel- > ri_TrigDesc- > trig_insert_before_row) {/ * * If there are any BEFORE triggers on the partition, we'll have * to be ready to convert their result back to tuplestore format. * / mtstate- > mt_transition_capture- > tcs_original_insert_tuple = NULL; mtstate- > mt_transition_capture- > tcs_map = TupConvMapForLeaf (proute, targetRelInfo, partidx);} else {/ * * Otherwise, just remember the original unconverted tuple, to * avoid a needless round trip conversion. * / mtstate- > mt_transition_capture- > tcs_original_insert_tuple = tuple; mtstate- > mt_transition_capture- > tcs_map = NULL;}} if (mtstate- > mt_oc_transition_capture! = NULL) {mtstate- > mt_oc_transition_capture- > tcs_map = TupConvMapForLeaf (proute, targetRelInfo, partidx);} / * Convert the tuple, if necessary. * / ConvertPartitionTupleSlot (proute- > parent_child_tupconv_maps [partidx], tuple, proute- > partition_tuple_slot, & slot, true); / * Initialize information needed to handle ON CONFLICT DO UPDATE. * / Assert (mtstate! = NULL); node = (ModifyTable *) mtstate- > ps.plan; if (node- > onConflictAction = = ONCONFLICT_UPDATE) {Assert (mtstate- > mt_existing! = NULL); ExecSetSlotDescriptor (mtstate- > mt_existing, RelationGetDescr (partrel- > ri_RelationDesc)); Assert (mtstate- > mt_conflproj! = NULL) ExecSetSlotDescriptor (mtstate- > mt_conflproj, partrel- > ri_onConflict- > oc_ProjTupdesc);} return slot;}

17 、 ExecInsert

/ / the previous section has been interpreted

18 、 ExecUpdate/ExecDelete

/ / perform updates / deletions, and then parse in subsequent experiments on Update/Delete

19 、 fireASTriggers

/ / trigger (statement level) / * * Process AFTER EACH STATEMENT triggers * / static void fireASTriggers (ModifyTableState * node) {ModifyTable * plan = (ModifyTable *) node- > ps.plan; ResultRelInfo * resultRelInfo = getTargetResultRelInfo (node) after the statement is executed Switch (node- > operation) {case CMD_INSERT: if (plan- > onConflictAction = = ONCONFLICT_UPDATE) ExecASUpdateTriggers (node- > ps.state, resultRelInfo, node- > mt_oc_transition_capture) ExecASInsertTriggers (node- > ps.state, resultRelInfo, node- > mt_transition_capture); break; case CMD_UPDATE: ExecASUpdateTriggers (node- > ps.state, resultRelInfo, node- > mt_transition_capture); break Case CMD_DELETE: ExecASDeleteTriggers (node- > ps.state, resultRelInfo, node- > mt_transition_capture); break; default: elog (ERROR, "unknown operation"); break Second, source code interpretation / *-* ExecModifyTable * * Perform table modifications as required, and return RETURNING results * if needed. *-* / / * input: State (various status information) output of pstate-SQL statement execution plan: TupleTableSlot pointer Store the Slot pointer of Tuple after execution * / static TupleTableSlot * ExecModifyTable (PlanState * pstate) {ModifyTableState * node = castNode (ModifyTableState, pstate) / / cast type to ModifyTableState type PartitionTupleRouting * proute = node- > mt_partition_tuple_routing;// partition table execute EState * estate = node- > ps.state;// executor Executor status CmdType operation = node- > operation;// command type ResultRelInfo * saved_resultRelInfo;// result RelationInfo ResultRelInfo * resultRelInfo;// same as PlanState * subplanstate;// sub-execution plan status JunkFilter * junkfilter / / useless attribute filter TupleTableSlot * Slot pointer TupleTableSlot stored by slot;//Tuple * Slot pointer stored by planSlot;//Plan ItemPointer tupleid;//Tuple row pointer data (Block number, offset, etc.) HeapTupleData oldtupdata;// raw Tuple data HeapTuple oldtuple;// original Tuple data pointer CHECK_FOR_INTERRUPTS (); / / check interrupt signal / * * This should NOT get called during EvalPlanQual We should have passed a * subplan tree to EvalPlanQual, instead. Use a runtime test not just * Assert because this condition is easy to miss in testing. (Note: * although ModifyTable should not get executed within an EvalPlanQual * operation, we do have to allow it to be initialized and shut down in * case it is within a CTE subplan. Hence this test must be here, not in * ExecInitModifyTable.) * / if (estate- > es_epqTuple! = NULL) elog (ERROR, "ModifyTable should not be called during EvalPlanQual"); / * If we've already completed processing, don't try to do more. We need * this test because ExecPostprocessPlan might call us an extra time, and * our subplan's nodes aren't necessarily robust against being called * extra times. * / / has been completed. Return NULL if (node- > mt_done) return NULL; / * * On first call, fire BEFORE STATEMENT triggers before proceeding. * / / statement-level trigger triggers if (node- > fireBSTriggers) {fireBSTriggers (node); node- > fireBSTriggers = false;} / * Preload local variables * / / pre-construct the local variable resultRelInfo = node- > resultRelInfo + node- > mt_whichplan;// sets the Relation,mt_whichplan in operation as offset subplanstate = node- > mt_ plans [node-> mt_whichplan] / / execution plan State junkfilter = resultRelInfo- > ri_junkFilter;// extra (worthless) information filter / * * es_result_relation_info must point to the currently active result * relation while we are within this ModifyTable node. Even though * ModifyTable nodes can't be nested statically, they can be nested * dynamically (since our subplan could include a reference to a modifying * CTE). So we have to save and restore the caller's value. * / / switch the active Relation saved_resultRelInfo = estate- > es_result_relation_info; estate- > es_result_relation_info = resultRelInfo; / * * Fetch rows from subplan (s), and execute the required table modification * for each row. * / for (;;) {/ * * Reset the per-output-tuple exprcontext. This is needed because * triggers expect to use that context as workspace. It's a bit ugly * to do this below the top level of the plan, however. We might need * to rethink this later. * / ResetPerTupleExprContext (estate); / / set context planSlot = ExecProcNode (subplanstate); / / get the Slot of the child Plan (insert data operation return value should be NULL) if (TupIsNull (planSlot)) / / No execution plan (execution plan has been completed) {/ * advance to next subplan if any * / node- > mt_whichplan++ / / move to the next execution Plan if (node- > mt_whichplan)

< node->

< node->

Mt_nplans) (gdb) p * node$23 = {ps = {type = T_ModifyTableState, plan = 0x2ce66c8, state = 0x2cde2f0, ExecProcNode = 0x6c2485, ExecProcNodeReal = 0x6c2485, instrument = 0x0, worker_instrument = 0x0, qual = 0x0, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x2cdf3f0, ps_ExprContext = 0x0, ps_ProjInfo = 0x0, 0x0 = scandesc}, 0x0 = 0x0, scandesc = scandesc}, scandesc = 0x0, operation = operation, =, = 1, = 1 ResultRelInfo = 0x2cde530, rootResultRelInfo = 0x0, mt_arowmarks = 0x2cde868, mt_epqstate = {estate = 0x0, planstate = 0x0, origslot = 0x2cdedc0, plan = 0x2cd21f8, arowMarks = 0x0, epqParam = 0}, fireBSTriggers = false, mt_existing = 0x0, mt_excludedtlist = 0x0, mt_conflproj = 0x0, mt_partition_tuple_routing = 0x0, mt_transition_capture = 0x0, mt_oc_transition_capture = 0x0, mt_per_subplan_tupconv_maps = 0x0} / / execution completed Jump out of the cycle (gdb) next2020 break (gdb) p * slotCannot access memory at address 0x0 (gdb) next2159 estate- > es_result_relation_info = saved_resultRelInfo; (gdb) p saved_resultRelInfo$24 = (ResultRelInfo *) 0x0 (gdb) next2164 fireASTriggers (node); (gdb) next2166 node- > mt_done = true; (gdb) # successfully, NULL (Insert statement) 2168 return NULL is returned Thank you for your reading, the above is the content of "what is the implementation logic of ExecModifyTable function in PostgreSQL". After the study of this article, I believe you have a deeper understanding of what the implementation logic of ExecModifyTable function in PostgreSQL is, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Database

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report