文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-04-02 19:55

关注

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

一、基础信息

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

//分区表相关的数据结构,再续解读分区表处理时再行更新
 
 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里面了
 
 typedef enum CmdType
 {
     CMD_UNKNOWN,
     CMD_SELECT,                 
     CMD_UPDATE,                 
     CMD_INSERT,                 
     CMD_DELETE,
     CMD_UTILITY,                
     CMD_NOTHING                 
 } CmdType;

3、JunkFilter

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

//触发语句级的触发器
 
 static void
 fireBSTriggers(ModifyTableState *node)
 {
     ModifyTable *plan = (ModifyTable *) node->ps.plan;
     ResultRelInfo *resultRelInfo = node->resultRelInfo;
 
     
     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

 
 #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)
 
 #define TupIsNull(slot) \
     ((slot) == NULL || (slot)->tts_isempty)

5、EvalPlanQualSetPlan

//TODO
 
 void
 EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
 {
     
     EvalPlanQualEnd(epqstate);
     
     epqstate->plan = subplan;
     
     epqstate->arowMarks = auxrowmarks;
 }

6、tupconv_map_for_subplan

//分区表使用,后续再行解析
//TODO
 
 static TupleConversionMap *
 tupconv_map_for_subplan(ModifyTableState *mtstate, int whichplan)
 {
     
     if (mtstate->mt_per_subplan_tupconv_maps == NULL)
     {
         int         leaf_index;
         PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
 
         
         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
//更详细的解读有待执行查询语句时的分析解读
 
 static TupleTableSlot *
 ExecProcessReturning(ResultRelInfo *resultRelInfo,
                      TupleTableSlot *tupleSlot,
                      TupleTableSlot *planSlot)
 {
     ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
     ExprContext *econtext = projectReturning->pi_exprContext;
 
     
     ResetExprContext(econtext);
 
     
     if (tupleSlot)
         econtext->ecxt_scantuple = tupleSlot;
     else
     {
         HeapTuple   tuple;
 
         
         Assert(!TupIsNull(econtext->ecxt_scantuple));
         tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
         tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
     }
     econtext->ecxt_outertuple = planSlot;
 
     
     return ExecProject(projectReturning);
 }

 
 typedef struct ProjectionInfo
 {
     NodeTag     type;
     
     ExprState   pi_state;
     
     ExprContext *pi_exprContext;
 } ProjectionInfo;
 
 
 typedef struct ExprContext
 {
     NodeTag     type;
 
     
 #define FIELDNO_EXPRCONTEXT_SCANTUPLE 1
     TupleTableSlot *ecxt_scantuple;
 #define FIELDNO_EXPRCONTEXT_INNERTUPLE 2
     TupleTableSlot *ecxt_innertuple;
 #define FIELDNO_EXPRCONTEXT_OUTERTUPLE 3
     TupleTableSlot *ecxt_outertuple;
 
     
     MemoryContext ecxt_per_query_memory;
     MemoryContext ecxt_per_tuple_memory;
 
     
     ParamExecData *ecxt_param_exec_vals;    
     ParamListInfo ecxt_param_list_info; 
 
     
 #define FIELDNO_EXPRCONTEXT_AGGVALUES 8
     Datum      *ecxt_aggvalues; 
 #define FIELDNO_EXPRCONTEXT_AGGNULLS 9
     bool       *ecxt_aggnulls;  
 
     
 #define FIELDNO_EXPRCONTEXT_CASEDATUM 10
     Datum       caseValue_datum;
 #define FIELDNO_EXPRCONTEXT_CASENULL 11
     bool        caseValue_isNull;
 
     
 #define FIELDNO_EXPRCONTEXT_DOMAINDATUM 12
     Datum       domainValue_datum;
 #define FIELDNO_EXPRCONTEXT_DOMAINNULL 13
     bool        domainValue_isNull;
 
     
     struct EState *ecxt_estate;
 
     
     ExprContext_CB *ecxt_callbacks;
 } ExprContext;

 
 #ifndef FRONTEND
 static inline TupleTableSlot *
 ExecProject(ProjectionInfo *projInfo)
 {
     ExprContext *econtext = projInfo->pi_exprContext;
     ExprState  *state = &projInfo->pi_state;
     TupleTableSlot *slot = state->resultslot;
     bool        isnull;
 
     
     ExecClearTuple(slot);
 
     
     (void) ExecEvalExprSwitchContext(state, econtext, &isnull);
 
     
     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中的某个属性值

 Datum
 ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno,
                      bool *isNull)
 {
     Assert(attno > 0);
 
     return slot_getattr(slot, attno, isNull);
 }

10、DatumGetPointer

// 将无符号长整型转换为指针然后进行传递
 
 
 #define DatumGetPointer(X) ((Pointer) (X))

11、AttributeNumberIsValid

//判断属性(号)是否合法
 
 
 #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
 
 #define ItemPointerSetInvalid(pointer) \
 ( \
     AssertMacro(PointerIsValid(pointer)), \
     BlockIdSet(&((pointer)->ip_blkid), InvalidBlockNumber), \
     (pointer)->ip_posid = InvalidOffsetNumber \
 )

15、ExecFilterJunk

//去掉所有的Junk属性

 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;
 
     
     slot_getallattrs(slot);
     old_values = slot->tts_values;
     old_isnull = slot->tts_isnull;
 
     
     cleanTupType = junkfilter->jf_cleanTupType;
     cleanLength = cleanTupType->natts;
     cleanMap = junkfilter->jf_cleanMap;
     resultSlot = junkfilter->jf_resultSlot;
 
     
     ExecClearTuple(resultSlot);
     values = resultSlot->tts_values;
     isnull = resultSlot->tts_isnull;
 
     
     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];
         }
     }
 
     
     return ExecStoreVirtualTuple(resultSlot);
 }

16、ExecPrepareTupleRouting

//分区表使用,后续再行解析
 
 static TupleTableSlot *
 ExecPrepareTupleRouting(ModifyTableState *mtstate,
                         EState *estate,
                         PartitionTupleRouting *proute,
                         ResultRelInfo *targetRelInfo,
                         TupleTableSlot *slot)
 {
     ModifyTable *node;
     int         partidx;
     ResultRelInfo *partrel;
     HeapTuple   tuple;
 
     
     partidx = ExecFindPartition(targetRelInfo,
                                 proute->partition_dispatch_info,
                                 slot,
                                 estate);
     Assert(partidx >= 0 && partidx < proute->num_partitions);
 
     
     partrel = proute->partitions[partidx];
     if (partrel == NULL)
         partrel = ExecInitPartitionInfo(mtstate, targetRelInfo,
                                         proute, estate,
                                         partidx);
 
     
     if (!partrel->ri_PartitionReadyForRouting)
     {
         
         CheckValidResultRel(partrel, CMD_INSERT);
 
         
         ExecInitRoutingInfo(mtstate, estate, proute, partrel, partidx);
     }
 
     
     estate->es_result_relation_info = partrel;
 
     
     tuple = ExecMaterializeSlot(slot);
 
     
     if (mtstate->mt_transition_capture != NULL)
     {
         if (partrel->ri_TrigDesc &&
             partrel->ri_TrigDesc->trig_insert_before_row)
         {
             
             mtstate->mt_transition_capture->tcs_original_insert_tuple = NULL;
             mtstate->mt_transition_capture->tcs_map =
                 TupConvMapForLeaf(proute, targetRelInfo, partidx);
         }
         else
         {
             
             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);
     }
 
     
     ConvertPartitionTupleSlot(proute->parent_child_tupconv_maps[partidx],
                               tuple,
                               proute->partition_tuple_slot,
                               &slot,
                               true);
 
     
     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

//触发执行语句后的触发器(语句级)
 
 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;
     }
 }

二、源码解读



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();//检查中断信号

    
    if (estate->es_epqTuple != NULL)
        elog(ERROR, "ModifyTable should not be called during EvalPlanQual");

    
    //已经完成,返回NULL
    if (node->mt_done)
        return NULL;

    
    //语句级触发器触发
    if (node->fireBSTriggers)
    {
        fireBSTriggers(node);
        node->fireBSTriggers = false;
    }

    
    //预先构造本地变量
    resultRelInfo = node->resultRelInfo + node->mt_whichplan;//设置正在操作的Relation,mt_whichplan是偏移
    subplanstate = node->mt_plans[node->mt_whichplan];//执行计划的State
    junkfilter = resultRelInfo->ri_junkFilter;//额外(无价值)的信息过滤器

    
    //切换当前活跃的Relation
    saved_resultRelInfo = estate->es_result_relation_info;

    estate->es_result_relation_info = resultRelInfo;

    
    for (;;)
    {
        
        ResetPerTupleExprContext(estate);//设置上下文

        planSlot = ExecProcNode(subplanstate);//获取子Plan的Slot(插入数据操作返回值应为NULL)

        if (TupIsNull(planSlot))//没有执行计划(执行计划已全部完成)
        {
            
            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
                
                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)
        {
            Assert(resultRelInfo->ri_projectReturning);

            
            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)//去掉废弃的属性
        {
            
            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);
                    
                    if (isNull)
                        elog(ERROR, "ctid is NULL");

                    tupleid = (ItemPointer) DatumGetPointer(datum);
                    tuple_ctid = *tupleid;  
                    tupleid = &tuple_ctid;
                }

                
                else if (AttributeNumberIsValid(junkfilter->jf_junkAttNo))
                {
                    datum = ExecGetJunkAttribute(slot,
                                                 junkfilter->jf_junkAttNo,
                                                 &isNull);
                    
                    if (isNull)
                        elog(ERROR, "wholerow is NULL");

                    oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
                    oldtupdata.t_len =
                        HeapTupleHeaderGetDatumLength(oldtupdata.t_data);
                    ItemPointerSetInvalid(&(oldtupdata.t_self));
                    
                    oldtupdata.t_tableOid =
                        (relkind == RELKIND_VIEW) ? InvalidOid :
                        RelationGetRelid(resultRelInfo->ri_RelationDesc);

                    oldtuple = &oldtupdata;
                }
                else
                    Assert(relkind == RELKIND_FOREIGN_TABLE);
            }

            
            if (operation != CMD_DELETE)
                slot = ExecFilterJunk(junkfilter, slot);//去掉废弃的属性
        }

        switch (operation)//根据操作类型,执行相应的操作
        {
            case CMD_INSERT://插入
                
                if (proute)
                    slot = ExecPrepareTupleRouting(node, estate, proute,
                                                   resultRelInfo, slot);//准备相应的Tuple Slot
                slot = ExecInsert(node, slot, planSlot,
                                  estate, node->canSetTag);//执行插入
                
                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  );
                break;
            default:
                elog(ERROR, "unknown operation");
                break;
        }

        
        if (slot)
        {
            estate->es_result_relation_info = saved_resultRelInfo;
            return slot;
        }
    }
    
    estate->es_result_relation_info = saved_resultRelInfo;

    
    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函数的实现逻辑是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯