温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

PostgreSQL中ExecModifyTable函数的实现逻辑是什么

发布时间:2021-11-11 11:53:07 来源:亿速云 阅读:239 作者:iii 栏目:关系型数据库

这篇文章主要讲解了“PostgreSQL中ExecModifyTable函数的实现逻辑是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PostgreSQL中ExecModifyTable函数的实现逻辑是什么”吧!

一、基础信息

ExecModifyTable函数使用的数据结构、宏定义以及依赖的函数等。
数据结构/宏定义
1、PartitionTupleRouting

//分区表相关的数据结构,再续解读分区表处理时再行更新  /*-----------------------   * PartitionTupleRouting - Encapsulates all information required to execute   * tuple-routing between partitions.   *   * 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

//命令类型,DDL命令都归到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

 //运行期需要的Attribute成为junk attribute,实际的Tuple需要使用JunkFilter过滤这些属性  /* ----------------   *    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

//实际类型为unsigned long int    ,无符号长整型整数  /*   * 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 <stdint.h>  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)

依赖的函数
1、fireBSTriggers

//触发语句级的触发器  /*   * 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

//执行子计划时使用,这个函数同时也是高N级的函数,后续再行解读

4、TupIsNull

//判断TupleTableSlot 是否为Null(包括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

//分区表使用,后续再行解析 //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->mt_nplans);          return mtstate->mt_per_subplan_tupconv_maps[whichplan];      }  }

7、ExecProcessReturning

//返回结果Tuple //更详细的解读有待执行查询语句时的分析解读  /*   * ExecProcessReturning --- evaluate a RETURNING list   *   * resultRelInfo: current result rel   * tupleSlot: slot holding tuple actually inserted/updated/deleted   * planSlot: slot holding tuple returned by top subplan node   *   * Note: If tupleSlot is NULL, the FDW should have already provided econtext's   * scan tuple.   *   * Returns a slot holding the result tuple   */  static TupleTableSlot *  ExecProcessReturning(ResultRelInfo *resultRelInfo,                       TupleTableSlot *tupleSlot,                       TupleTableSlot *planSlot)  {      ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;      ExprContext *econtext = projectReturning->pi_exprContext;        /*       * Reset per-tuple memory context to free any expression evaluation       * storage allocated in the previous cycle.       */      ResetExprContext(econtext);        /* Make tuple and any needed join variables available to ExecProject */      if (tupleSlot)          econtext->ecxt_scantuple = tupleSlot;      else      {          HeapTuple   tuple;            /*           * RETURNING expressions might reference the tableoid column, so           * initialize t_tableOid before evaluating them.           */          Assert(!TupIsNull(econtext->ecxt_scantuple));          tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);          tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);      }      econtext->ecxt_outertuple = planSlot;        /* Compute the RETURNING expressions */      return ExecProject(projectReturning);  }  /* ----------------   *      ProjectionInfo node information   *   *      This is all the information needed to perform projections ---   *      that is, form new tuples by evaluation of targetlist expressions.   *      Nodes which need to do projections create one of these.   *   *      The target tuple slot is kept in ProjectionInfo->pi_state.resultslot.   *      ExecProject() evaluates the tlist, forms a tuple, and stores it   *      in the given slot.  Note that the result will be a "virtual" tuple   *      unless ExecMaterializeSlot() is then called to force it to be   *      converted to a physical tuple.  The slot must have a tupledesc   *      that matches the output of the tlist!   * ----------------   */  typedef struct ProjectionInfo  {      NodeTag     type;      /* instructions to evaluate projection */      ExprState   pi_state;      /* expression context in which to evaluate expression */      ExprContext *pi_exprContext;  } ProjectionInfo;    /* ----------------   *    ExprContext   *   *      This class holds the "current context" information   *      needed to evaluate expressions for doing tuple qualifications   *      and tuple projections.  For example, if an expression refers   *      to an attribute in the current inner tuple then we need to know   *      what the current inner tuple is and so we look at the expression   *      context.   *   *  There are two memory contexts associated with an ExprContext:   *  * ecxt_per_query_memory is a query-lifespan context, typically the same   *    context the ExprContext node itself is allocated in.  This context   *    can be used for purposes such as storing function call cache info.   *  * ecxt_per_tuple_memory is a short-term context for expression results.   *    As the name suggests, it will typically be reset once per tuple,   *    before we begin to evaluate expressions for that tuple.  Each   *    ExprContext normally has its very own per-tuple memory context.   *   *  CurrentMemoryContext should be set to ecxt_per_tuple_memory before   *  calling ExecEvalExpr() --- see ExecEvalExprSwitchContext().   * ----------------   */  typedef struct ExprContext  {      NodeTag     type;        /* Tuples that Var nodes in expression may refer to */  #define FIELDNO_EXPRCONTEXT_SCANTUPLE 1      TupleTableSlot *ecxt_scantuple;  #define FIELDNO_EXPRCONTEXT_INNERTUPLE 2      TupleTableSlot *ecxt_innertuple;  #define FIELDNO_EXPRCONTEXT_OUTERTUPLE 3      TupleTableSlot *ecxt_outertuple;        /* Memory contexts for expression evaluation --- see notes above */      MemoryContext ecxt_per_query_memory;      MemoryContext ecxt_per_tuple_memory;        /* Values to substitute for Param nodes in expression */      ParamExecData *ecxt_param_exec_vals;    /* for PARAM_EXEC params */      ParamListInfo ecxt_param_list_info; /* for other param types */        /*       * Values to substitute for Aggref nodes in the expressions of an Agg       * node, or for WindowFunc nodes within a WindowAgg node.       */  #define FIELDNO_EXPRCONTEXT_AGGVALUES 8      Datum      *ecxt_aggvalues; /* precomputed values for aggs/windowfuncs */  #define FIELDNO_EXPRCONTEXT_AGGNULLS 9      bool       *ecxt_aggnulls;  /* null flags for aggs/windowfuncs */        /* Value to substitute for CaseTestExpr nodes in expression */  #define FIELDNO_EXPRCONTEXT_CASEDATUM 10      Datum       caseValue_datum;  #define FIELDNO_EXPRCONTEXT_CASENULL 11      bool        caseValue_isNull;        /* Value to substitute for CoerceToDomainValue nodes in expression */  #define FIELDNO_EXPRCONTEXT_DOMAINDATUM 12      Datum       domainValue_datum;  #define FIELDNO_EXPRCONTEXT_DOMAINNULL 13      bool        domainValue_isNull;        /* Link to containing EState (NULL if a standalone ExprContext) */      struct EState *ecxt_estate;        /* Functions to call back when ExprContext is shut down or rescanned */      ExprContext_CB *ecxt_callbacks;  } ExprContext;  /*   * ExecProject   *   * Projects a tuple based on projection info and stores it in the slot passed   * to ExecBuildProjectInfo().   *   * Note: the result is always a virtual tuple; therefore it may reference   * the contents of the exprContext's scan tuples and/or temporary results   * constructed in the exprContext.  If the caller wishes the result to be   * valid longer than that data will be valid, he must call ExecMaterializeSlot   * on the result slot.   */  #ifndef FRONTEND  static inline TupleTableSlot *  ExecProject(ProjectionInfo *projInfo)  {      ExprContext *econtext = projInfo->pi_exprContext;      ExprState  *state = &projInfo->pi_state;      TupleTableSlot *slot = state->resultslot;      bool        isnull;        /*       * Clear any former contents of the result slot.  This makes it safe for       * us to use the slot's Datum/isnull arrays as workspace.       */      ExecClearTuple(slot);        /* Run the expression, discarding scalar result from the last column. */      (void) ExecEvalExprSwitchContext(state, econtext, &isnull);        /*       * Successfully formed a result row.  Mark the result slot as containing a       * valid virtual tuple (inlined version of ExecStoreVirtualTuple()).       */      slot->tts_isempty = false;      slot->tts_nvalid = slot->tts_tupleDescriptor->natts;        return slot;  }  #endif

8、EvalPlanQualSetSlot

 #define EvalPlanQualSetSlot(epqstate, slot)  ((epqstate)->origslot = (slot))

9、ExecGetJunkAttribute

//获取Junk中的某个属性值 /*   * ExecGetJunkAttribute   *   * Given a junk filter's input tuple (slot) and a junk attribute's number   * previously found by ExecFindJunkAttribute, extract & return the value and   * isNull flag of the attribute.   */  Datum  ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno,                       bool *isNull)  {      Assert(attno > 0);        return slot_getattr(slot, attno, isNull);  }

10、DatumGetPointer

// 将无符号长整型转换为指针然后进行传递  /*   * DatumGetPointer   *      Returns pointer value of a datum.   */    #define DatumGetPointer(X) ((Pointer) (X))

11、AttributeNumberIsValid

//判断属性(号)是否合法  /* ----------------   *      support macros   * ----------------   */  /*   * AttributeNumberIsValid   *      True iff the attribute number is valid.   */  #define AttributeNumberIsValid(attributeNumber) \      ((bool) ((attributeNumber) != InvalidAttrNumber))

12、DatumGetHeapTupleHeader

 #define DatumGetHeapTupleHeader(X)  ((HeapTupleHeader) PG_DETOAST_DATUM(X))

13、HeapTupleHeaderGetDatumLength

 #define HeapTupleHeaderGetDatumLength(tup) \      VARSIZE(tup)

14、ItemPointerSetInvalid

//块号&块内偏移设置为Invalid  /*   * ItemPointerSetInvalid   *      Sets a disk item pointer to be invalid.   */  #define ItemPointerSetInvalid(pointer) \  ( \      AssertMacro(PointerIsValid(pointer)), \      BlockIdSet(&((pointer)->ip_blkid), InvalidBlockNumber), \      (pointer)->ip_posid = InvalidOffsetNumber \  )

15、ExecFilterJunk

//去掉所有的Junk属性 /*   * ExecFilterJunk   *   * Construct and return a slot with all the junk attributes removed.   */  TupleTableSlot *  ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)  {      TupleTableSlot *resultSlot;      AttrNumber *cleanMap;      TupleDesc   cleanTupType;      int         cleanLength;      int         i;      Datum      *values;      bool       *isnull;      Datum      *old_values;      bool       *old_isnull;        /*       * Extract all the values of the old tuple.       */      slot_getallattrs(slot);      old_values = slot->tts_values;      old_isnull = slot->tts_isnull;        /*       * get info from the junk filter       */      cleanTupType = junkfilter->jf_cleanTupType;      cleanLength = cleanTupType->natts;      cleanMap = junkfilter->jf_cleanMap;      resultSlot = junkfilter->jf_resultSlot;        /*       * Prepare to build a virtual result tuple.       */      ExecClearTuple(resultSlot);      values = resultSlot->tts_values;      isnull = resultSlot->tts_isnull;        /*       * Transpose data into proper fields of the new tuple.       */      for (i = 0; i < 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

//上一节已解读

18、ExecUpdate/ExecDelete

//执行更新/删除,在后续实验Update/Delete时再行解析

19、fireASTriggers

//触发执行语句后的触发器(语句级)  /*   * Process AFTER EACH STATEMENT triggers   */  static void  fireASTriggers(ModifyTableState *node)  {      ModifyTable *plan = (ModifyTable *) node->ps.plan;      ResultRelInfo *resultRelInfo = getTargetResultRelInfo(node);        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;      }  }

二、源码解读

/* ----------------------------------------------------------------  *     ExecModifyTable  *  *      Perform table modifications as required, and return RETURNING results  *      if needed.  * ----------------------------------------------------------------  */ /* 输入:     pstate-SQL语句执行计划的State(各种状态信息) 输出:     TupleTableSlot指针,存储执行后Tuple的Slot指针 */ static TupleTableSlot * ExecModifyTable(PlanState *pstate) {     ModifyTableState *node = castNode(ModifyTableState, pstate);//强制类型转换为ModifyTableState类型     PartitionTupleRouting *proute = node->mt_partition_tuple_routing;//分区表执行     EState     *estate = node->ps.state;//执行器Executor状态     CmdType     operation = node->operation;//命令类型     ResultRelInfo *saved_resultRelInfo;//结果RelationInfo     ResultRelInfo *resultRelInfo;//同上     PlanState  *subplanstate;//子执行计划状态     JunkFilter *junkfilter;//无用属性过滤器     TupleTableSlot *slot;//Tuple存储的Slot指针     TupleTableSlot *planSlot;//Plan存储的Slot指针     ItemPointer tupleid;//Tuple行指针     ItemPointerData tuple_ctid;//Tuple行指针数据(Block号、偏移等)     HeapTupleData oldtupdata;//原Tuple数据     HeapTuple   oldtuple;//原Tuple数据指针     CHECK_FOR_INTERRUPTS();//检查中断信号     /*      * 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.      */     //已经完成,返回NULL     if (node->mt_done)         return NULL;     /*      * On first call, fire BEFORE STATEMENT triggers before proceeding.      */     //语句级触发器触发     if (node->fireBSTriggers)     {         fireBSTriggers(node);         node->fireBSTriggers = false;     }     /* Preload local variables */     //预先构造本地变量     resultRelInfo = node->resultRelInfo + node->mt_whichplan;//设置正在操作的Relation,mt_whichplan是偏移     subplanstate = node->mt_plans[node->mt_whichplan];//执行计划的State     junkfilter = resultRelInfo->ri_junkFilter;//额外(无价值)的信息过滤器     /*      * 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.      */     //切换当前活跃的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);//设置上下文         planSlot = ExecProcNode(subplanstate);//获取子Plan的Slot(插入数据操作返回值应为NULL)         if (TupIsNull(planSlot))//没有执行计划(执行计划已全部完成)         {             /* advance to next subplan if any */             node->mt_whichplan++;//移至下一个执行Plan             if (node->mt_whichplan < node->mt_nplans)             {                 resultRelInfo++;//切换至相应的Relation,如果是插入操作,只有一个                 subplanstate = node->mt_plans[node->mt_whichplan];//切换至相应的子Plan状态                 junkfilter = resultRelInfo->ri_junkFilter;//切换至相应的junkfilter                 estate->es_result_relation_info = resultRelInfo;//切换                 EvalPlanQualSetPlan(&node->mt_epqstate, subplanstate->plan,                                     node->mt_arowmarks[node->mt_whichplan]);//设置子Plan                 /* Prepare to convert transition tuples from this child. */                 if (node->mt_transition_capture != NULL)                 {                     node->mt_transition_capture->tcs_map =                         tupconv_map_for_subplan(node, node->mt_whichplan);//插入数据暂不使用                 }                 if (node->mt_oc_transition_capture != NULL)                 {                     node->mt_oc_transition_capture->tcs_map =                         tupconv_map_for_subplan(node, node->mt_whichplan);//插入数据暂不使用                 }                 continue;             }             else                 break;//所有的Plan均已执行,跳出循环         }         //存在子Plan         /*          * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do          * here is compute the RETURNING expressions.          */         if (resultRelInfo->ri_usesFdwDirectModify)         {             Assert(resultRelInfo->ri_projectReturning);             /*              * A scan slot containing the data that was actually inserted,              * updated or deleted has already been made available to              * ExecProcessReturning by IterateDirectModify, so no need to              * provide it here.              */             slot = ExecProcessReturning(resultRelInfo, NULL, planSlot);//FDW,直接返回             estate->es_result_relation_info = saved_resultRelInfo;             return slot;         }         EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);//设置相应的Slot(子Plan Slot)         slot = planSlot;         tupleid = NULL;//Tuple ID         oldtuple = NULL;//原Tuple         if (junkfilter != NULL)//去掉废弃的属性         {             /*              * extract the 'ctid' or 'wholerow' junk attribute.              */             if (operation == CMD_UPDATE || operation == CMD_DELETE)//更新或者删除操作             {                 char        relkind;                 Datum       datum;                 bool        isNull;                 relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;                 if (relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW)                 {                     datum = ExecGetJunkAttribute(slot,                                                  junkfilter->jf_junkAttNo,                                                  &isNull);                     /* shouldn't ever get a null result... */                     if (isNull)                         elog(ERROR, "ctid is NULL");                     tupleid = (ItemPointer) DatumGetPointer(datum);                     tuple_ctid = *tupleid;  /* be sure we don't free ctid!! */                     tupleid = &tuple_ctid;                 }                 /*                  * Use the wholerow attribute, when available, to reconstruct                  * the old relation tuple.                  *                  * Foreign table updates have a wholerow attribute when the                  * relation has a row-level trigger.  Note that the wholerow                  * attribute does not carry system columns.  Foreign table                  * triggers miss seeing those, except that we know enough here                  * to set t_tableOid.  Quite separately from this, the FDW may                  * fetch its own junk attrs to identify the row.                  *                  * Other relevant relkinds, currently limited to views, always                  * have a wholerow attribute.                  */                 else if (AttributeNumberIsValid(junkfilter->jf_junkAttNo))                 {                     datum = ExecGetJunkAttribute(slot,                                                  junkfilter->jf_junkAttNo,                                                  &isNull);                     /* shouldn't ever get a null result... */                     if (isNull)                         elog(ERROR, "wholerow is NULL");                     oldtupdata.t_data = DatumGetHeapTupleHeader(datum);                     oldtupdata.t_len =                         HeapTupleHeaderGetDatumLength(oldtupdata.t_data);                     ItemPointerSetInvalid(&(oldtupdata.t_self));                     /* Historically, view triggers see invalid t_tableOid. */                     oldtupdata.t_tableOid =                         (relkind == RELKIND_VIEW) ? InvalidOid :                         RelationGetRelid(resultRelInfo->ri_RelationDesc);                     oldtuple = &oldtupdata;                 }                 else                     Assert(relkind == RELKIND_FOREIGN_TABLE);             }             /*              * apply the junkfilter if needed.              */             if (operation != CMD_DELETE)                 slot = ExecFilterJunk(junkfilter, slot);//去掉废弃的属性         }         switch (operation)//根据操作类型,执行相应的操作         {             case CMD_INSERT://插入                 /* Prepare for tuple routing if needed. */                 if (proute)                     slot = ExecPrepareTupleRouting(node, estate, proute,                                                    resultRelInfo, slot);//准备相应的Tuple Slot                 slot = ExecInsert(node, slot, planSlot,                                   estate, node->canSetTag);//执行插入                 /* Revert ExecPrepareTupleRouting's state change. */                 if (proute)                     estate->es_result_relation_info = resultRelInfo;                 break;             case CMD_UPDATE:                 slot = ExecUpdate(node, tupleid, oldtuple, slot, planSlot,                                   &node->mt_epqstate, estate, node->canSetTag);                 break;             case CMD_DELETE:                 slot = ExecDelete(node, tupleid, oldtuple, planSlot,                                   &node->mt_epqstate, estate,                                   NULL, true, node->canSetTag,                                   false /* changingPart */ );                 break;             default:                 elog(ERROR, "unknown operation");                 break;         }         /*          * If we got a RETURNING result, return it to caller.  We'll continue          * the work on next call.          */         if (slot)         {             estate->es_result_relation_info = saved_resultRelInfo;             return slot;         }     }     /* Restore es_result_relation_info before exiting */     estate->es_result_relation_info = saved_resultRelInfo;     /*      * We're done, but fire AFTER STATEMENT triggers before exiting.      */     fireASTriggers(node);     node->mt_done = true;     //Insert语句,返回NULL     return NULL; }

三、跟踪分析

执行测试脚本:

alter table t_insert alter column c1 type varchar(40); alter table t_insert alter column c2 type varchar(40); alter table t_insert alter column c3 type varchar(40); testdb=# select pg_backend_pid();  pg_backend_pid  ----------------            1570 (1 row) testdb=# insert into t_insert values(13,'ExecModifyTable','ExecModifyTable','ExecModifyTable'); (挂起)

启动gdb跟踪:

[root@localhost ~]# gdb -p 1570 GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7 Copyright (C) 2013 Free Software Foundation, Inc. ... (gdb) b ExecModifyTable Breakpoint 1 at 0x6c2498: file nodeModifyTable.c, line 1915. (gdb) c Continuing. Breakpoint 1, ExecModifyTable (pstate=0x2cde640) at nodeModifyTable.c:1915 1915        ModifyTableState *node = castNode(ModifyTableState, pstate); #查看参数 (gdb) p *pstate #PlanState $1 = {type = T_ModifyTableState, plan = 0x2ce66c8, state = 0x2cde2f0, ExecProcNode = 0x6c2485 <ExecModifyTable>, ExecProcNodeReal = 0x6c2485 <ExecModifyTable>, 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,    scandesc = 0x0} #执行计划Plan (gdb) p *(pstate->plan) $2 = {type = T_ModifyTable, startup_cost = 0, total_cost = 0.01, plan_rows = 1, plan_width = 298, parallel_aware = false, parallel_safe = false, plan_node_id = 0, targetlist = 0x0, qual = 0x0,    lefttree = 0x0, righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0} #执行计划对应的执行环境(State) (gdb) p *(pstate->state) $3 = {type = T_EState, es_direction = ForwardScanDirection, es_snapshot = 0x2ccac80, es_crosscheck_snapshot = 0x0, es_range_table = 0x2ce6970, es_plannedstmt = 0x2ce6a40,    es_sourceText = 0x2c1bef0 "insert into t_insert values(13,'ExecModifyTable','ExecModifyTable','ExecModifyTable');", es_junkFilter = 0x0, es_output_cid = 0, es_result_relations = 0x2cde530,    es_num_result_relations = 1, es_result_relation_info = 0x0, es_root_result_relations = 0x0, es_num_root_result_relations = 0, es_tuple_routing_result_relations = 0x0, es_trig_target_relations = 0x0,    es_trig_tuple_slot = 0x2cdf4a0, es_trig_oldtup_slot = 0x0, es_trig_newtup_slot = 0x0, es_param_list_info = 0x0, es_param_exec_vals = 0x2cde500, es_queryEnv = 0x0, es_query_cxt = 0x2cde1e0,    es_tupleTable = 0x2cdeef0, es_rowMarks = 0x0, es_processed = 0, es_lastoid = 0, es_top_eflags = 0, es_instrument = 0, es_finished = false, es_exprcontexts = 0x2cde8a0, es_subplanstates = 0x0,    es_auxmodifytables = 0x0, es_per_tuple_exprcontext = 0x0, es_epqTuple = 0x0, es_epqTupleSet = 0x0, es_epqScanDone = 0x0, es_use_parallel_mode = false, es_query_dsa = 0x0, es_jit_flags = 0,    es_jit = 0x0} #结果Tuple Slot (gdb) p *(pstate->ps_ResultTupleSlot) $6 = {type = T_TupleTableSlot, tts_isempty = true, tts_shouldFree = false, tts_shouldFreeMin = false, tts_slow = false, tts_tuple = 0x0, tts_tupleDescriptor = 0x2cdf3c0, tts_mcxt = 0x2cde1e0,    tts_buffer = 0, tts_nvalid = 0, tts_values = 0x2cdf450, tts_isnull = 0x2cdf450, tts_mintuple = 0x0, tts_minhdr = {t_len = 0, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 0},      t_tableOid = 0, t_data = 0x0}, tts_off = 0, tts_fixedTupleDescriptor = true} #继续执行 #proute应为NULL (gdb) p proute $7 = (PartitionTupleRouting *) 0x0 #插入操作 (gdb) p operation $9 = CMD_INSERT #查看本地变量 (gdb) p resultRelInfo $11 = (ResultRelInfo *) 0x2cde530 (gdb) p *resultRelInfo $12 = {type = T_ResultRelInfo, ri_RangeTableIndex = 1, ri_RelationDesc = 0x7f3c13f56150, ri_NumIndices = 1, ri_IndexRelationDescs = 0x2cde8d0, ri_IndexRelationInfo = 0x2cde8e8, ri_TrigDesc = 0x0,    ri_TrigFunctions = 0x0, ri_TrigWhenExprs = 0x0, ri_TrigInstrument = 0x0, ri_FdwRoutine = 0x0, ri_FdwState = 0x0, ri_usesFdwDirectModify = false, ri_WithCheckOptions = 0x0,    ri_WithCheckOptionExprs = 0x0, ri_ConstraintExprs = 0x0, ri_junkFilter = 0x0, ri_returningList = 0x0, ri_projectReturning = 0x0, ri_onConflictArbiterIndexes = 0x0, ri_onConflict = 0x0,    ri_PartitionCheck = 0x0, ri_PartitionCheckExpr = 0x0, ri_PartitionRoot = 0x0, ri_PartitionReadyForRouting = false} (gdb) p subplanstate $13 = (PlanState *) 0x2cdea10 (gdb) p *subplanstate $14 = {type = T_ResultState, plan = 0x2cd21f8, state = 0x2cde2f0, ExecProcNode = 0x69a78b <ExecProcNodeFirst>, ExecProcNodeReal = 0x6c5094 <ExecResult>, instrument = 0x0, worker_instrument = 0x0,    qual = 0x0, lefttree = 0x0, righttree = 0x0, initPlan = 0x0, subPlan = 0x0, chgParam = 0x0, ps_ResultTupleSlot = 0x2cdedc0, ps_ExprContext = 0x2cdeb20, ps_ProjInfo = 0x2cdef20, scandesc = 0x0} (gdb) p *(subplanstate->plan) $15 = {type = T_Result, startup_cost = 0, total_cost = 0.01, plan_rows = 1, plan_width = 298, parallel_aware = false, parallel_safe = false, plan_node_id = 1, targetlist = 0x2cd22f8, qual = 0x0,    lefttree = 0x0, righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0} (gdb) p junkfilter $18 = (JunkFilter *) 0x830 (gdb) p *junkfilter Cannot access memory at address 0x830 gdb) next 1974        saved_resultRelInfo = estate->es_result_relation_info; (gdb)  1976        estate->es_result_relation_info = resultRelInfo; (gdb)  1990            ResetPerTupleExprContext(estate); (gdb)  1992            planSlot = ExecProcNode(subplanstate); (gdb) p *planSlot $19 = {type = T_Invalid, tts_isempty = false, tts_shouldFree = false, tts_shouldFreeMin = false, tts_slow = false, tts_tuple = 0x3000000000064, tts_tupleDescriptor = 0x2c1dcb8, tts_mcxt = 0x2c,    tts_buffer = 0, tts_nvalid = 0, tts_values = 0x0, tts_isnull = 0x0, tts_mintuple = 0x0, tts_minhdr = {t_len = 512, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 57824}, t_tableOid = 0,      t_data = 0x3c}, tts_off = 47081160, tts_fixedTupleDescriptor = false} (gdb) next 1994            if (TupIsNull(planSlot)) (gdb) p *planSlot $20 = {type = T_TupleTableSlot, tts_isempty = false, tts_shouldFree = false, tts_shouldFreeMin = false, tts_slow = false, tts_tuple = 0x0, tts_tupleDescriptor = 0x2cdebb0, tts_mcxt = 0x2cde1e0,    tts_buffer = 0, tts_nvalid = 4, tts_values = 0x2cdee20, tts_isnull = 0x2cdee40, tts_mintuple = 0x0, tts_minhdr = {t_len = 0, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 0},      t_tableOid = 0, t_data = 0x0}, tts_off = 0, tts_fixedTupleDescriptor = true} (gdb) next 2027            if (resultRelInfo->ri_usesFdwDirectModify) (gdb)  2043            EvalPlanQualSetSlot(&node->mt_epqstate, planSlot); (gdb)  2044            slot = planSlot; (gdb)  2046            tupleid = NULL; (gdb)  2047            oldtuple = NULL; (gdb)  2048            if (junkfilter != NULL) (gdb)  2119            switch (operation) (gdb) p junkfilter $21 = (JunkFilter *) 0x0 (gdb) next 2123                    if (proute) (gdb)  2127                                      estate, node->canSetTag); (gdb)  2126                    slot = ExecInsert(node, slot, planSlot, (gdb)  2129                    if (proute) (gdb) p *slot Cannot access memory at address 0x0 (gdb) next 2151            if (slot) (gdb)  2156        } #进入第2轮循环 (gdb)  1990            ResetPerTupleExprContext(estate); (gdb)  1992            planSlot = ExecProcNode(subplanstate); (gdb) p *planSlot $22 = {type = T_TupleTableSlot, tts_isempty = false, tts_shouldFree = true, tts_shouldFreeMin = false, tts_slow = false, tts_tuple = 0x2cdf550, tts_tupleDescriptor = 0x2cdebb0, tts_mcxt = 0x2cde1e0,    tts_buffer = 0, tts_nvalid = 1, tts_values = 0x2cdee20, tts_isnull = 0x2cdee40, tts_mintuple = 0x0, tts_minhdr = {t_len = 0, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 0},      t_tableOid = 0, t_data = 0x0}, tts_off = 4, tts_fixedTupleDescriptor = true} (gdb) next 1994            if (TupIsNull(planSlot)) (gdb)  1997                node->mt_whichplan++; (gdb) next 1998                if (node->mt_whichplan < node->mt_nplans) (gdb) p *node $23 = {ps = {type = T_ModifyTableState, plan = 0x2ce66c8, state = 0x2cde2f0, ExecProcNode = 0x6c2485 <ExecModifyTable>, ExecProcNodeReal = 0x6c2485 <ExecModifyTable>, 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,      scandesc = 0x0}, operation = CMD_INSERT, canSetTag = true, mt_done = false, mt_plans = 0x2cde850, mt_nplans = 1, mt_whichplan = 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} //执行完毕,跳出循环 (gdb) next 2020                    break; (gdb) p *slot Cannot access memory at address 0x0 (gdb) next 2159        estate->es_result_relation_info = saved_resultRelInfo; (gdb) p saved_resultRelInfo $24 = (ResultRelInfo *) 0x0 (gdb) next 2164        fireASTriggers(node); (gdb) next 2166        node->mt_done = true; (gdb)  #执行成功,返回NULL(Insert语句) 2168        return NULL;

感谢各位的阅读,以上就是“PostgreSQL中ExecModifyTable函数的实现逻辑是什么”的内容了,经过本文的学习后,相信大家对PostgreSQL中ExecModifyTable函数的实现逻辑是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI