PostgreSQL中什么函数通过递归调用初始化计划树中的所有Plan节点
这篇文章主要介绍“PostgreSQL中什么函数通过递归调用初始化计划树中的所有Plan节点”,在日常操作中,相信很多人在PostgreSQL中什么函数通过递归调用初始化计划树中的所有Plan节点问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PostgreSQL中什么函数通过递归调用初始化计划树中的所有Plan节点”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
一、数据结构Plan
所有计划节点通过将Plan结构作为第一个字段从Plan结构“派生”。这确保了在将节点转换为计划节点时,一切都能正常工作。(在执行器中以通用方式传递时,节点指针经常被转换为Plan *)
/*----------------*Plannode**Allplannodes"derive"fromthePlanstructurebyhavingthe*Planstructureasthefirstfield.Thisensuresthateverythingworks*whennodesarecasttoPlan's.(nodepointersarefrequentlycasttoPlan**whenpassedaroundgenericallyintheexecutor)*所有计划节点通过将Plan结构作为第一个字段从Plan结构“派生”。*这确保了在将节点转换为计划节点时,一切都能正常工作。*(在执行器中以通用方式传递时,节点指针经常被转换为Plan*)**WeneveractuallyinstantiateanyPlannodes;thisisjustthecommon*abstractsuperclassforallPlan-typenodes.*从未实例化任何Plan节点;这只是所有Plan-type节点的通用抽象超类。*----------------*/typedefstructPlan{NodeTagtype;//节点类型/**成本估算信息;estimatedexecutioncostsforplan(seecostsize.cformoreinfo)*/Coststartup_cost;/*启动成本;costexpendedbeforefetchinganytuples*/Costtotal_cost;/*总成本;totalcost(assumingalltuplesfetched)*//**优化器估算信息;planner'sestimateofresultsizeofthisplanstep*/doubleplan_rows;/*行数;numberofrowsplanisexpectedtoemit*/intplan_width;/*平均行大小(Byte为单位);averagerowwidthinbytes*//**并行执行相关的信息;informationneededforparallelquery*/boolparallel_aware;/*是否参与并行执行逻辑?engageparallel-awarelogic?*/boolparallel_safe;/*是否并行安全;OKtouseaspartofparallelplan?*//**Plan类型节点通用的信息.CommonstructuraldataforallPlantypes.*/intplan_node_id;/*uniqueacrossentirefinalplantree*/List*targetlist;/*targetlisttobecomputedatthisnode*/List*qual;/*implicitly-ANDedqualconditions*/structPlan*lefttree;/*inputplantree(s)*/structPlan*righttree;List*initPlan;/*InitPlannodes(un-correlatedexpr*subselects)*//**Informationformanagementofparameter-change-drivenrescanning*parameter-change-driven重扫描的管理信息.**extParamincludestheparamIDsofallexternalPARAM_EXECparams*affectingthisplannodeoritschildren.setParamparamsfromthe*node'sinitPlansarenotincluded,buttheirextParamsare.**allParamincludesalltheextParamparamIDs,plustheIDsoflocal*paramsthataffectthenode(i.e.,thesetParamsofitsinitplans).*Theseare_all_thePARAM_EXECparamsthataffectthisnode.*/Bitmapset*extParam;Bitmapset*allParam;}Plan;二、源码解读
ExecInitNode函数递归初始化计划树中的所有Plan节点,返回对应给定的Plan Node节点的PlanState节点.
/*------------------------------------------------------------------------*ExecInitNode**Recursivelyinitializesallthenodesintheplantreerooted*at'node'.*递归初始化计划树中的所有Plan节点.**Inputs:*'node'isthecurrentnodeoftheplanproducedbythequeryplanner*'estate'isthesharedexecutionstatefortheplantree*'eflags'isabitwiseORofflagbitsdescribedinexecutor.h*node-查询计划器产生的当前节点*estate-Plan树共享的执行状态信息*eflags-一个位或标记位,在executor.h中描述**ReturnsaPlanStatenodecorrespondingtothegivenPlannode.*返回对应PlanNode节点的PlanState节点*------------------------------------------------------------------------*/PlanState*ExecInitNode(Plan*node,EState*estate,inteflags){PlanState*result;//结果List*subps;//子PlanState链表ListCell*l;//临时变量/**donothingwhenwegettotheendofaleafontree.*如node为NULL则返回NULL*/if(node==NULL)returnNULL;/**Makesurethere'senoughstackavailable.Needtocheckhere,in*additiontoExecProcNode()(viaExecProcNodeFirst()),toensurethe*stackisn'toverrunwhileinitializingthenodetree.*确保有足够的堆栈可用。*除了ExecProcNode()(通过ExecProcNodeFirst()调用),还需要在这里进行检查,以确保在初始化节点树时堆栈没有溢出。*/check_stack_depth();switch(nodeTag(node))//根据节点类型进入相应的逻辑{/**控制节点;controlnodes*/caseT_Result:result=(PlanState*)ExecInitResult((Result*)node,estate,eflags);break;caseT_ProjectSet:result=(PlanState*)ExecInitProjectSet((ProjectSet*)node,estate,eflags);break;caseT_ModifyTable:result=(PlanState*)ExecInitModifyTable((ModifyTable*)node,estate,eflags);break;caseT_Append:result=(PlanState*)ExecInitAppend((Append*)node,estate,eflags);break;caseT_MergeAppend:result=(PlanState*)ExecInitMergeAppend((MergeAppend*)node,estate,eflags);break;caseT_RecursiveUnion:result=(PlanState*)ExecInitRecursiveUnion((RecursiveUnion*)node,estate,eflags);break;caseT_BitmapAnd:result=(PlanState*)ExecInitBitmapAnd((BitmapAnd*)node,estate,eflags);break;caseT_BitmapOr:result=(PlanState*)ExecInitBitmapOr((BitmapOr*)node,estate,eflags);break;/**扫描节点;scannodes*/caseT_SeqScan:result=(PlanState*)ExecInitSeqScan((SeqScan*)node,estate,eflags);break;caseT_SampleScan:result=(PlanState*)ExecInitSampleScan((SampleScan*)node,estate,eflags);break;caseT_IndexScan:result=(PlanState*)ExecInitIndexScan((IndexScan*)node,estate,eflags);break;caseT_IndexOnlyScan:result=(PlanState*)ExecInitIndexOnlyScan((IndexOnlyScan*)node,estate,eflags);break;caseT_BitmapIndexScan:result=(PlanState*)ExecInitBitmapIndexScan((BitmapIndexScan*)node,estate,eflags);break;caseT_BitmapHeapScan:result=(PlanState*)ExecInitBitmapHeapScan((BitmapHeapScan*)node,estate,eflags);break;caseT_TidScan:result=(PlanState*)ExecInitTidScan((TidScan*)node,estate,eflags);break;caseT_SubqueryScan:result=(PlanState*)ExecInitSubqueryScan((SubqueryScan*)node,estate,eflags);break;caseT_FunctionScan:result=(PlanState*)ExecInitFunctionScan((FunctionScan*)node,estate,eflags);break;caseT_TableFuncScan:result=(PlanState*)ExecInitTableFuncScan((TableFuncScan*)node,estate,eflags);break;caseT_ValuesScan:result=(PlanState*)ExecInitValuesScan((ValuesScan*)node,estate,eflags);break;caseT_CteScan:result=(PlanState*)ExecInitCteScan((CteScan*)node,estate,eflags);break;caseT_NamedTuplestoreScan:result=(PlanState*)ExecInitNamedTuplestoreScan((NamedTuplestoreScan*)node,estate,eflags);break;caseT_WorkTableScan:result=(PlanState*)ExecInitWorkTableScan((WorkTableScan*)node,estate,eflags);break;caseT_ForeignScan:result=(PlanState*)ExecInitForeignScan((ForeignScan*)node,estate,eflags);break;caseT_CustomScan:result=(PlanState*)ExecInitCustomScan((CustomScan*)node,estate,eflags);break;/**连接节点/joinnodes*/caseT_NestLoop:result=(PlanState*)ExecInitNestLoop((NestLoop*)node,estate,eflags);break;caseT_MergeJoin:result=(PlanState*)ExecInitMergeJoin((MergeJoin*)node,estate,eflags);break;caseT_HashJoin:result=(PlanState*)ExecInitHashJoin((HashJoin*)node,estate,eflags);break;/**物化节点/materializationnodes*/caseT_Material:result=(PlanState*)ExecInitMaterial((Material*)node,estate,eflags);break;caseT_Sort:result=(PlanState*)ExecInitSort((Sort*)node,estate,eflags);break;caseT_Group:result=(PlanState*)ExecInitGroup((Group*)node,estate,eflags);break;caseT_Agg:result=(PlanState*)ExecInitAgg((Agg*)node,estate,eflags);break;caseT_WindowAgg:result=(PlanState*)ExecInitWindowAgg((WindowAgg*)node,estate,eflags);break;caseT_Unique:result=(PlanState*)ExecInitUnique((Unique*)node,estate,eflags);break;caseT_Gather:result=(PlanState*)ExecInitGather((Gather*)node,estate,eflags);break;caseT_GatherMerge:result=(PlanState*)ExecInitGatherMerge((GatherMerge*)node,estate,eflags);break;caseT_Hash:result=(PlanState*)ExecInitHash((Hash*)node,estate,eflags);break;caseT_SetOp:result=(PlanState*)ExecInitSetOp((SetOp*)node,estate,eflags);break;caseT_LockRows:result=(PlanState*)ExecInitLockRows((LockRows*)node,estate,eflags);break;caseT_Limit:result=(PlanState*)ExecInitLimit((Limit*)node,estate,eflags);break;default:elog(ERROR,"unrecognizednodetype:%d",(int)nodeTag(node));result=NULL;/*避免优化器提示警告信息;keepcompilerquiet*/break;}//设置节点的ExecProcNode函数ExecSetExecProcNode(result,result->ExecProcNode);/**InitializeanyinitPlanspresentinthisnode.Theplannerputthemin*aseparatelistforus.*初始化该Plan节点中的所有initPlans.*计划器把这些信息放到一个单独的链表中*/subps=NIL;//初始化foreach(l,node->initPlan)//遍历initPlan{SubPlan*subplan=(SubPlan*)lfirst(l);//子计划SubPlanState*sstate;//子计划状态Assert(IsA(subplan,SubPlan));sstate=ExecInitSubPlan(subplan,result);//初始化SubPlansubps=lappend(subps,sstate);//添加到链表中}result->initPlan=subps;//赋值/*Setupinstrumentationforthisnodeifrequested*///如需要,配置instrumentationif(estate->es_instrument)result->instrument=InstrAlloc(1,estate->es_instrument);returnresult;}/**IfanodewantstochangeitsExecProcNodefunctionafterExecInitNode()*hasfinished,itshoulddosowiththisfunction.Thatwayanywrapper*functionscanbereinstalled,withoutthenodehavingtoknowhowthat*works.*如果一个节点想要在ExecInitNode()完成之后更改它的ExecProcNode函数,那么它应该使用这个函数。*这样就可以重新安装任何包装器函数,而不必让节点知道它是如何工作的。*/voidExecSetExecProcNode(PlanState*node,ExecProcNodeMtdfunction){/**AddawrapperaroundtheExecProcNodecallbackthatchecksstackdepth*duringthefirstexecutionandmaybeaddsaninstrumentationwrapper.*Whenthecallbackischangedafterexecutionhasalreadybegunthat*meanswe'llsuperfluouslyexecuteExecProcNodeFirst,butthatseemsok.*在ExecProcNode回调函数添加一个包装器,在第一次执行时检查堆栈深度,可能还会添加一个检测包装器。*在执行已经开始之后,当回调函数被更改时,这意味着再次执行ExecProcNodeFirst是多余的,但这似乎是可以的。*/node->ExecProcNodeReal=function;node->ExecProcNode=ExecProcNodeFirst;}/*----------------------------------------------------------------*ExecInitSeqScan*初始化顺序扫描节点*----------------------------------------------------------------*/SeqScanState*ExecInitSeqScan(SeqScan*node,EState*estate,inteflags){SeqScanState*scanstate;/**OnceuponatimeitwaspossibletohaveanouterPlanofaSeqScan,but*notanymore.*先前有可能存在外部的SeqScan计划,但现在该做法已废弃,这里进行校验*/Assert(outerPlan(node)==NULL);Assert(innerPlan(node)==NULL);/**createstatestructure*创建SeqScanState数据结构体*/scanstate=makeNode(SeqScanState);scanstate->ss.ps.plan=(Plan*)node;scanstate->ss.ps.state=estate;scanstate->ss.ps.ExecProcNode=ExecSeqScan;/**Miscellaneousinitialization*初始化*createexpressioncontextfornode*创建表达式上下文*/ExecAssignExprContext(estate,&scanstate->ss.ps);/**openthescanrelation*打开扫描的Relation*/scanstate->ss.ss_currentRelation=ExecOpenScanRelation(estate,node->scanrelid,eflags);/*andcreateslotwiththeappropriaterowtype*///使用合适的rowtype打开slotExecInitScanTupleSlot(estate,&scanstate->ss,RelationGetDescr(scanstate->ss.ss_currentRelation));/**Initializeresulttypeandprojection.*初始化结果类型和投影*/ExecInitResultTypeTL(&scanstate->ss.ps);ExecAssignScanProjectionInfo(&scanstate->ss);/**initializechildexpressions*初始化子表达式*/scanstate->ss.ps.qual=ExecInitQual(node->plan.qual,(PlanState*)scanstate);returnscanstate;}/*----------------------------------------------------------------*ExecOpenScanRelation**Opentheheaprelationtobescannedbyabase-levelscanplannode.*Thisshouldbecalledduringthenode'sExecInitroutine.*打开HeapRelation,由一个基表扫描计划节点扫描。这应该在节点的ExecInit例程中调用。*----------------------------------------------------------------*/RelationExecOpenScanRelation(EState*estate,Indexscanrelid,inteflags){Relationrel;/*Opentherelation.*///打开关系rel=ExecGetRangeTableRelation(estate,scanrelid);/**Complainifwe'reattemptingascanofanunscannablerelation,except*whenthequerywon'tactuallyberun.Thisisaslightlyklugyplace*todothis,perhaps,butthereisnobetterplace.*给出提示信息:试图扫描一个不存在的关系。这可能是一个有点笨拙的地方,但没有更好的提示了。*/if((eflags&(EXEC_FLAG_EXPLAIN_ONLY|EXEC_FLAG_WITH_NO_DATA))==0&&!RelationIsScannable(rel))ereport(ERROR,(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),errmsg("materializedview\"%s\"hasnotbeenpopulated",RelationGetRelationName(rel)),errhint("UsetheREFRESHMATERIALIZEDVIEWcommand.")));returnrel;}/*----------------------------------------------------------------*ExecInitHashJoin*初始化Hash连接节点*InitroutineforHashJoinnode.*Hash连接通过递归调用ExecInitNode函数初始化参与连接的Relation*----------------------------------------------------------------*/HashJoinState*ExecInitHashJoin(HashJoin*node,EState*estate,inteflags){HashJoinState*hjstate;Plan*outerNode;Hash*hashNode;List*lclauses;List*rclauses;List*rhclauses;List*hoperators;TupleDescouterDesc,innerDesc;ListCell*l;/*checkforunsupportedflags*///校验不支持的flagsAssert(!(eflags&(EXEC_FLAG_BACKWARD|EXEC_FLAG_MARK)));/**createstatestructure*创建state数据结构体*/hjstate=makeNode(HashJoinState);hjstate->js.ps.plan=(Plan*)node;hjstate->js.ps.state=estate;/**SeeExecHashJoinInitializeDSM()andExecHashJoinInitializeWorker()*wherethisfunctionmaybereplacedwithaparallelversion,ifwe*managedtolaunchaparallelquery.*请参阅ExecHashJoinInitializeWorker()和ExecHashJoinInitializeWorker(),*如果成功启动了并行查询,这个函数可以用一个并行版本替换。*/hjstate->js.ps.ExecProcNode=ExecHashJoin;hjstate->js.jointype=node->join.jointype;/**Miscellaneousinitialization*初始化*createexpressioncontextfornode*/ExecAssignExprContext(estate,&hjstate->js.ps);/**initializechildnodes*初始化子节点**Note:wecouldsuppresstheREWINDflagfortheinnerinput,which*wouldamounttobettingthatthehashwillbeasinglebatch.Not*clearifthiswouldbeawinornot.*注意:可以禁止内部输入的REWIND标志,这相当于打赌散列将是单个批处理。*不清楚这是否会是一场胜利。*/outerNode=outerPlan(node);hashNode=(Hash*)innerPlan(node);outerPlanState(hjstate)=ExecInitNode(outerNode,estate,eflags);//递归处理外表节点outerDesc=ExecGetResultType(outerPlanState(hjstate));//innerPlanState(hjstate)=ExecInitNode((Plan*)hashNode,estate,eflags);//递归处理内表节点innerDesc=ExecGetResultType(innerPlanState(hjstate));/**Initializeresultslot,typeandprojection.*初始化节点slot/类型和投影*/ExecInitResultTupleSlotTL(&hjstate->js.ps);ExecAssignProjectionInfo(&hjstate->js.ps,NULL);/**tupletableinitialization*元组表初始化*/hjstate->hj_OuterTupleSlot=ExecInitExtraTupleSlot(estate,outerDesc);/**detectwhetherweneedonlyconsiderthefirstmatchinginnertuple*检测是否只需要考虑内表元组的首次匹配*/hjstate->js.single_match=(node->join.inner_unique||node->join.jointype==JOIN_SEMI);/*setupnulltuplesforouterjoins,ifneeded*///配置外连接的NULL元组switch(node->join.jointype){caseJOIN_INNER:caseJOIN_SEMI:break;caseJOIN_LEFT:caseJOIN_ANTI://左连接&半连接hjstate->hj_NullInnerTupleSlot=ExecInitNullTupleSlot(estate,innerDesc);break;caseJOIN_RIGHT:hjstate->hj_NullOuterTupleSlot=ExecInitNullTupleSlot(estate,outerDesc);break;caseJOIN_FULL:hjstate->hj_NullOuterTupleSlot=ExecInitNullTupleSlot(estate,outerDesc);hjstate->hj_NullInnerTupleSlot=ExecInitNullTupleSlot(estate,innerDesc);break;default:elog(ERROR,"unrecognizedjointype:%d",(int)node->join.jointype);}/**nowforsomevoodoo.ourtemporarytupleslotisactuallytheresult*tupleslotoftheHashnode(whichisourinnerplan).wecandothis*becauseHashnodesdon'treturntuplesviaExecProcNode()--instead*thehashjoinnodeusesExecScanHashBucket()togetatthecontentsof*thehashtable.-cim6/9/91*现在来点巫术。*临时tuple槽实际上是散列节点的结果tuple槽(这是我们的内部计划)。*之可以这样做,是因为哈希节点不会通过ExecProcNode()返回元组——*相反,哈希连接节点使用ExecScanHashBucket()来获取哈希表的内容。*bycim6/9/91*/{HashState*hashstate=(HashState*)innerPlanState(hjstate);TupleTableSlot*slot=hashstate->ps.ps_ResultTupleSlot;hjstate->hj_HashTupleSlot=slot;}/**initializechildexpressions*初始化子表达式*/hjstate->js.ps.qual=ExecInitQual(node->join.plan.qual,(PlanState*)hjstate);hjstate->js.joinqual=ExecInitQual(node->join.joinqual,(PlanState*)hjstate);hjstate->hashclauses=ExecInitQual(node->hashclauses,(PlanState*)hjstate);/**initializehash-specificinfo*初始化hash相关的信息*/hjstate->hj_HashTable=NULL;hjstate->hj_FirstOuterTupleSlot=NULL;hjstate->hj_CurHashValue=0;hjstate->hj_CurBucketNo=0;hjstate->hj_CurSkewBucketNo=INVALID_SKEW_BUCKET_NO;hjstate->hj_CurTuple=NULL;/**Deconstructthehashclausesintoouterandinnerargumentvalues,so*thatwecanevaluatethosesubexpressionsseparately.Alsomakealist*ofthehashoperatorOIDs,inpreparationforlookingupthehash*functionstouse.*将哈希子句解构为外部和内部参数值,以便能够分别计算这些子表达式。*还可以列出哈希运算符oid,以便查找要使用的哈希函数。*/lclauses=NIL;rclauses=NIL;rhclauses=NIL;hoperators=NIL;foreach(l,node->hashclauses){OpExpr*hclause=lfirst_node(OpExpr,l);lclauses=lappend(lclauses,ExecInitExpr(linitial(hclause->args),(PlanState*)hjstate));rclauses=lappend(rclauses,ExecInitExpr(lsecond(hclause->args),(PlanState*)hjstate));rhclauses=lappend(rhclauses,ExecInitExpr(lsecond(hclause->args),innerPlanState(hjstate)));hoperators=lappend_oid(hoperators,hclause->opno);}hjstate->hj_OuterHashKeys=lclauses;hjstate->hj_InnerHashKeys=rclauses;hjstate->hj_HashOperators=hoperators;/*childHashnodeneedstoevaluateinnerhashkeys,too*/((HashState*)innerPlanState(hjstate))->hashkeys=rhclauses;hjstate->hj_JoinState=HJ_BUILD_HASHTABLE;hjstate->hj_MatchedOuter=false;hjstate->hj_OuterNotEmpty=false;returnhjstate;}三、跟踪分析
测试脚本如下
testdb=#explainselectdw.*,grjf.grbh,grjf.xm,grjf.ny,grjf.jetestdb-#fromt_dwxxdw,lateral(selectgr.grbh,gr.xm,jf.ny,jf.jetestdb(#fromt_grxxgrinnerjoint_jfxxjftestdb(#ongr.dwbh=dw.dwbhtestdb(#andgr.grbh=jf.grbh)grjftestdb-#orderbydw.dwbh;QUERYPLAN------------------------------------------------------------------------------------------Sort(cost=20070.93..20320.93rows=100000width=47)SortKey:dw.dwbh->HashJoin(cost=3754.00..8689.61rows=100000width=47)HashCond:((gr.dwbh)::text=(dw.dwbh)::text)->HashJoin(cost=3465.00..8138.00rows=100000width=31)HashCond:((jf.grbh)::text=(gr.grbh)::text)->SeqScanont_jfxxjf(cost=0.00..1637.00rows=100000width=20)->Hash(cost=1726.00..1726.00rows=100000width=16)->SeqScanont_grxxgr(cost=0.00..1726.00rows=100000width=16)->Hash(cost=164.00..164.00rows=10000width=20)->SeqScanont_dwxxdw(cost=0.00..164.00rows=10000width=20)(11rows)
启动gdb,设置断点,进入ExecInitNode
(gdb)bExecInitNodeBreakpoint1at0x6e3b90:fileexecProcnode.c,line148.(gdb)cContinuing.Breakpoint1,ExecInitNode(node=0x1b71f90,estate=0x1b78f48,eflags=16)atexecProcnode.c:148warning:Sourcefileismorerecentthanexecutable.148if(node==NULL)
输入参数,node为T_Sort
(gdb)p*node$1={type=T_Sort,startup_cost=20070.931487218411,total_cost=20320.931487218411,plan_rows=100000,plan_width=47,parallel_aware=false,parallel_safe=true,plan_node_id=0,targetlist=0x1b762c0,qual=0x0,lefttree=0x1b75728,righttree=0x0,initPlan=0x0,extParam=0x0,allParam=0x0}(gdb)p*estate$2={type=T_EState,es_direction=ForwardScanDirection,es_snapshot=0x1b31e10,es_crosscheck_snapshot=0x0,es_range_table=0x1b75c00,es_plannedstmt=0x1b77d58,es_sourceText=0x1a8ceb8"selectdw.*,grjf.grbh,grjf.xm,grjf.ny,grjf.je\nfromt_dwxxdw,lateral(selectgr.grbh,gr.xm,jf.ny,jf.je\n",''<repeats24times>,"fromt_grxxgrinnerjoint_jfxxjf\n",''<repeats34times>...,es_junkFilter=0x0,es_output_cid=0,es_result_relations=0x0,es_num_result_relations=0,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=0x0,es_trig_oldtup_slot=0x0,es_trig_newtup_slot=0x0,es_param_list_info=0x0,es_param_exec_vals=0x0,es_queryEnv=0x0,es_query_cxt=0x1b78e30,es_tupleTable=0x0,es_rowMarks=0x0,es_processed=0,es_lastoid=0,es_top_eflags=16,es_instrument=0,es_finished=false,es_exprcontexts=0x0,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,es_jit_worker_instr=0x0}
检查堆栈
(gdb)n156check_stack_depth();
进入相应的处理逻辑
158switch(nodeTag(node))(gdb)313result=(PlanState*)ExecInitSort((Sort*)node,
返回结果
(gdb)p*result$3={type=11084746,plan=0x69900000699,state=0x0,ExecProcNode=0x0,ExecProcNodeReal=0x0,instrument=0x1000000000000,worker_instrument=0x0,worker_jit_instrument=0x200000001,qual=0x0,lefttree=0x0,righttree=0x0,initPlan=0x0,subPlan=0x0,chgParam=0x7f7f7f7f7f7f7f7e,ps_ResultTupleSlot=0x7f7f7f7f7f7f7f7f,ps_ExprContext=0x7f7f7f7f7f7f7f7f,ps_ProjInfo=0x80,scandesc=0x0}
设置断点,进入ExecSetExecProcNode
(gdb)bExecSetExecProcNodeBreakpoint2at0x6e41a1:fileexecProcnode.c,line414.(gdb)cContinuing.
ExecSetExecProcNode->输入参数,function为ExecSeqScan,在实际执行时调用此函数
(gdb)p*function$5={TupleTableSlot*(structPlanState*)}0x714d59<ExecSeqScan>(gdb)p*node$6={type=T_SeqScanState,plan=0x1b74f58,state=0x1b78f48,ExecProcNode=0x714d59<ExecSeqScan>,ExecProcNodeReal=0x0,instrument=0x0,worker_instrument=0x0,worker_jit_instrument=0x0,qual=0x0,lefttree=0x0,righttree=0x0,initPlan=0x0,subPlan=0x0,chgParam=0x0,ps_ResultTupleSlot=0x1b7a9c0,ps_ExprContext=0x1b7a5a8,ps_ProjInfo=0x1b7aa80,scandesc=0x7f07174f5308}
回到最上层的ExecInitNode,initPlan为NULL
(gdb)ExecInitNode(node=0x1b74f58,estate=0x1b78f48,eflags=16)atexecProcnode.c:379379subps=NIL;(gdb)380foreach(l,node->initPlan)(gdb)p*node$7={type=T_SeqScan,startup_cost=0,total_cost=1726,plan_rows=100000,plan_width=16,parallel_aware=false,parallel_safe=true,plan_node_id=5,targetlist=0x1b74e20,qual=0x0,lefttree=0x0,righttree=0x0,initPlan=0x0,extParam=0x0,allParam=0x0}
完成调用
(gdb)n389result->initPlan=subps;(gdb)392if(estate->es_instrument)(gdb)395returnresult;(gdb)396}
下面重点考察ExecInitSeqScan和ExecInitHashJoin,首先是ExecInitHashJoin
ExecInitHashJoin->
(gdb)bExecInitSeqScanBreakpoint3at0x714daf:filenodeSeqscan.c,line148.(gdb)bExecInitHashJoinBreakpoint4at0x701f60:filenodeHashjoin.c,line604.(gdb)cContinuing.Breakpoint4,ExecInitHashJoin(node=0x1b737c0,estate=0x1b78f48,eflags=16)atnodeHashjoin.c:604warning:Sourcefileismorerecentthanexecutable.604Assert(!(eflags&(EXEC_FLAG_BACKWARD|EXEC_FLAG_MARK)));
ExecInitHashJoin->校验并初始化
604Assert(!(eflags&(EXEC_FLAG_BACKWARD|EXEC_FLAG_MARK)));(gdb)n609hjstate=makeNode(HashJoinState);(gdb)610hjstate->js.ps.plan=(Plan*)node;(gdb)611hjstate->js.ps.state=estate;(gdb)618hjstate->js.ps.ExecProcNode=ExecHashJoin;(gdb)619hjstate->js.jointype=node->join.jointype;(gdb)626ExecAssignExprContext(estate,&hjstate->js.ps);
ExecInitHashJoin->初步的数据结构体
(gdb)p*hjstate$8={js={ps={type=T_HashJoinState,plan=0x1b737c0,state=0x1b78f48,ExecProcNode=0x701efa<ExecHashJoin>,ExecProcNodeReal=0x0,instrument=0x0,worker_instrument=0x0,worker_jit_instrument=0x0,qual=0x0,lefttree=0x0,righttree=0x0,initPlan=0x0,subPlan=0x0,chgParam=0x0,ps_ResultTupleSlot=0x0,ps_ExprContext=0x0,ps_ProjInfo=0x0,scandesc=0x0},jointype=JOIN_INNER,single_match=false,joinqual=0x0},hashclauses=0x0,hj_OuterHashKeys=0x0,hj_InnerHashKeys=0x0,hj_HashOperators=0x0,hj_HashTable=0x0,hj_CurHashValue=0,hj_CurBucketNo=0,hj_CurSkewBucketNo=0,hj_CurTuple=0x0,hj_OuterTupleSlot=0x0,hj_HashTupleSlot=0x0,hj_NullOuterTupleSlot=0x0,hj_NullInnerTupleSlot=0x0,hj_FirstOuterTupleSlot=0x0,hj_JoinState=0,hj_MatchedOuter=false,hj_OuterNotEmpty=false}
ExecInitHashJoin->获取HashJoin的outer&inner(PG视为Hash节点)
outerNode为HashJoin,innerNode为Hash
(gdb)n635outerNode=outerPlan(node);gdb)n636hashNode=(Hash*)innerPlan(node);(gdb)638outerPlanState(hjstate)=ExecInitNode(outerNode,estate,eflags);(gdb)p*node$9={join={plan={type=T_HashJoin,startup_cost=3754,total_cost=8689.6112499999999,plan_rows=100000,plan_width=47,parallel_aware=false,parallel_safe=true,plan_node_id=1,targetlist=0x1b74cc8,qual=0x0,lefttree=0x1b73320,righttree=0x1b73728,initPlan=0x0,extParam=0x0,allParam=0x0},jointype=JOIN_INNER,inner_unique=true,joinqual=0x0},hashclauses=0x1b74bb8}(gdb)p*outerNode$12={type=T_HashJoin,startup_cost=3465,total_cost=8138,plan_rows=100000,plan_width=31,parallel_aware=false,parallel_safe=true,plan_node_id=2,targetlist=0x1b75588,qual=0x0,lefttree=0x1b72da0,righttree=0x1b73288,initPlan=0x0,extParam=0x0,allParam=0x0}(gdb)p*hashNode$11={plan={type=T_Hash,startup_cost=164,total_cost=164,plan_rows=10000,plan_width=20,parallel_aware=false,parallel_safe=true,plan_node_id=6,targetlist=0x1b75c08,qual=0x0,lefttree=0x1b73570,righttree=0x0,initPlan=0x0,extParam=0x0,allParam=0x0},skewTable=16742,skewColumn=1,skewInherit=false,rows_total=0}
ExecInitHashJoin->进入outerNode的HashJoin,直接跳过
(gdb)nBreakpoint4,ExecInitHashJoin(node=0x1b73320,estate=0x1b78f48,eflags=16)atnodeHashjoin.c:604604Assert(!(eflags&(EXEC_FLAG_BACKWARD|EXEC_FLAG_MARK)));(gdb)finishRuntillexitfrom#0ExecInitHashJoin(node=0x1b73320,estate=0x1b78f48,eflags=16)atnodeHashjoin.c:604
ExecInitHashJoin->进入innerNode的调用(ExecInitSeqScan)
Breakpoint3,ExecInitSeqScan(node=0x1b72da0,estate=0x1b78f48,eflags=16)atnodeSeqscan.c:148warning:Sourcefileismorerecentthanexecutable.148Assert(outerPlan(node)==NULL);(gdb)
ExecInitSeqScan
ExecInitSeqScan->执行校验,并创建Node.
注意:ExecProcNode=ExecSeqScan
148Assert(outerPlan(node)==NULL);(gdb)n149Assert(innerPlan(node)==NULL);(gdb)154scanstate=makeNode(SeqScanState);(gdb)155scanstate->ss.ps.plan=(Plan*)node;(gdb)156scanstate->ss.ps.state=estate;(gdb)157scanstate->ss.ps.ExecProcNode=ExecSeqScan;(gdb)164ExecAssignExprContext(estate,&scanstate->ss.ps);(gdb)p*scanstate$1={ss={ps={type=T_SeqScanState,plan=0x1b72da0,state=0x1b78f48,ExecProcNode=0x714d59<ExecSeqScan>,ExecProcNodeReal=0x0,instrument=0x0,worker_instrument=0x0,worker_jit_instrument=0x0,qual=0x0,lefttree=0x0,righttree=0x0,initPlan=0x0,subPlan=0x0,chgParam=0x0,ps_ResultTupleSlot=0x0,ps_ExprContext=0x0,ps_ProjInfo=0x0,scandesc=0x0},ss_currentRelation=0x0,ss_currentScanDesc=0x0,ss_ScanTupleSlot=0x0},pscan_len=0}
ExecInitSeqScan->打开Relation
gdb)n173ExecOpenScanRelation(estate,(gdb)172scanstate->ss.ss_currentRelation=(gdb)179RelationGetDescr(scanstate->ss.ss_currentRelation));(gdb)p*scanstate->ss.ss_currentRelation$3={rd_node={spcNode=1663,dbNode=16402,relNode=16747},rd_smgr=0x1b64650,rd_refcnt=1,rd_backend=-1,rd_islocaltemp=false,rd_isnailed=false,rd_isvalid=true,rd_indexvalid=1'\001',rd_statvalid=true,rd_createSubid=0,rd_newRelfilenodeSubid=0,rd_rel=0x7f07174f5c78,rd_att=0x7f07174f5d90,rd_id=16747,rd_lockInfo={lockRelId={relId=16747,dbId=16402}},rd_rules=0x0,rd_rulescxt=0x0,trigdesc=0x0,rd_rsdesc=0x0,rd_fkeylist=0x0,rd_fkeyvalid=false,rd_partkeycxt=0x0,rd_partkey=0x0,rd_pdcxt=0x0,rd_partdesc=0x0,rd_partcheck=0x0,rd_indexlist=0x7f0717447328,rd_oidindex=0,rd_pkindex=0,rd_replidindex=0,rd_statlist=0x0,rd_indexattr=0x0,rd_projindexattr=0x0,rd_keyattr=0x0,rd_pkattr=0x0,rd_idattr=0x0,rd_projidx=0x0,rd_pubactions=0x0,rd_options=0x0,rd_index=0x0,rd_indextuple=0x0,rd_amhandler=0,rd_indexcxt=0x0,rd_amroutine=0x0,rd_opfamily=0x0,rd_opcintype=0x0,rd_support=0x0,rd_supportinfo=0x0,rd_indoption=0x0,rd_indexprs=0x0,rd_indpred=0x0,rd_exclops=0x0,rd_exclprocs=0x0,rd_exclstrats=0x0,rd_amcache=0x0,rd_indcollation=0x0,rd_fdwroutine=0x0,rd_toastoid=0,pgstat_info=0x1b0b6c0}
ExecInitSeqScan->使用合适的rowtype打开slot(初始化ScanTupleSlot)
(gdb)178ExecInitScanTupleSlot(estate,&scanstate->ss,(gdb)184ExecInitResultTupleSlotTL(estate,&scanstate->ss.ps);(gdb)p*scanstate->ss.ss_ScanTupleSlot$4={type=T_TupleTableSlot,tts_isempty=true,tts_shouldFree=false,tts_shouldFreeMin=false,tts_slow=false,tts_tuple=0x0,tts_tupleDescriptor=0x7f07174f5d90,tts_mcxt=0x1b78e30,tts_buffer=0,tts_nvalid=0,tts_values=0x1b79a98,tts_isnull=0x1b79ab0,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)
ExecInitSeqScan->初始化结果类型和投影
(gdb)n185ExecAssignScanProjectionInfo(&scanstate->ss);(gdb)191ExecInitQual(node->plan.qual,(PlanState*)scanstate);(gdb)p*scanstate$5={ss={ps={type=T_SeqScanState,plan=0x1b72da0,state=0x1b78f48,ExecProcNode=0x714d59<ExecSeqScan>,ExecProcNodeReal=0x0,instrument=0x0,worker_instrument=0x0,worker_jit_instrument=0x0,qual=0x0,lefttree=0x0,righttree=0x0,initPlan=0x0,subPlan=0x0,chgParam=0x0,ps_ResultTupleSlot=0x1b79d48,ps_ExprContext=0x1b79978,ps_ProjInfo=0x1b79e08,scandesc=0x7f07174f5d90},ss_currentRelation=0x7f07174f5a60,ss_currentScanDesc=0x0,ss_ScanTupleSlot=0x1b79a38},pscan_len=0}(gdb)p*scanstate->ss.ps.ps_ResultTupleSlot$6={type=T_TupleTableSlot,tts_isempty=true,tts_shouldFree=false,tts_shouldFreeMin=false,tts_slow=false,tts_tuple=0x0,tts_tupleDescriptor=0x1b79b30,tts_mcxt=0x1b78e30,tts_buffer=0,tts_nvalid=0,tts_values=0x1b79da8,tts_isnull=0x1b79dc0,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)p*scanstate->ss.ps.ps_ProjInfo$7={type=T_ProjectionInfo,pi_state={tag={type=T_ExprState},flags=6'\006',resnull=false,resvalue=0,resultslot=0x1b79d48,steps=0x1b79ea0,evalfunc=0x6d104b<ExecInterpExprStillValid>,expr=0x1b72c68,evalfunc_private=0x6cec02<ExecInterpExpr>,steps_len=5,steps_alloc=16,parent=0x1b79860,ext_params=0x0,innermost_caseval=0x0,innermost_casenull=0x0,innermost_domainval=0x0,innermost_domainnull=0x0},pi_exprContext=0x1b79978}
ExecInitSeqScan->初始化子条件表达式(为NULL),返回结果
(gdb)n190scanstate->ss.ps.qual=(gdb)193returnscanstate;(gdb)p*scanstate->ss.ps.qualCannotaccessmemoryataddress0x0
ExecInitSeqScan->回到ExecInitNode for Node SeqScan
(gdb)n194}(gdb)ExecInitNode(node=0x1b72da0,estate=0x1b78f48,eflags=16)atexecProcnode.c:209warning:Sourcefileismorerecentthanexecutable.209break;
ExecInitSeqScan->回到ExecInitNode,结束调用
(gdb)n379subps=NIL;(gdb)380foreach(l,node->initPlan)(gdb)389result->initPlan=subps;(gdb)392if(estate->es_instrument)(gdb)395returnresult;(gdb)396}(gdb)
ExecInitSeqScan->回到ExecInitHashJoin
(gdb)ExecInitHashJoin(node=0x1b73320,estate=0x1b78f48,eflags=16)atnodeHashjoin.c:639639outerDesc=ExecGetResultType(outerPlanState(hjstate));
ExecInitHashJoin
ExecInitHashJoin->完成outer relation的处理,开始处理inner relation(递归调用ExecInitNode)
639outerDesc=ExecGetResultType(outerPlanState(hjstate));(gdb)n640innerPlanState(hjstate)=ExecInitNode((Plan*)hashNode,estate,eflags);(gdb)Breakpoint2,ExecInitSeqScan(node=0x1b72ff0,estate=0x1b78f48,eflags=16)atnodeSeqscan.c:148148Assert(outerPlan(node)==NULL);(gdb)del2(gdb)finishRuntillexitfrom#0ExecInitSeqScan(node=0x1b72ff0,estate=0x1b78f48,eflags=16)atnodeSeqscan.c:1480x00000000006e3cd2inExecInitNode(node=0x1b72ff0,estate=0x1b78f48,eflags=16)atexecProcnode.c:207207result=(PlanState*)ExecInitSeqScan((SeqScan*)node,Valuereturnedis$10=(SeqScanState*)0x1b7a490(gdb)...(gdb)n641innerDesc=ExecGetResultType(innerPlanState(hjstate));
ExecInitHashJoin->查看outerDesc和innerDesc
(gdb)p*outerDesc$14={natts=3,tdtypeid=2249,tdtypmod=-1,tdhasoid=false,tdrefcount=-1,constr=0x0,attrs=0x1b79b50}(gdb)p*innerDesc$15={natts=3,tdtypeid=2249,tdtypmod=-1,tdhasoid=false,tdrefcount=-1,constr=0x0,attrs=0x1b7ab38}(gdb)
ExecInitHashJoin->初始化节点slot/类型/投影/元组表等
(gdb)n647ExecAssignProjectionInfo(&hjstate->js.ps,NULL);(gdb)652hjstate->hj_OuterTupleSlot=ExecInitExtraTupleSlot(estate,outerDesc);(gdb)657hjstate->js.single_match=(node->join.inner_unique||(gdb)658node->join.jointype==JOIN_SEMI);(gdb)p*hjstate$16={js={ps={type=T_HashJoinState,plan=0x1b73320,state=0x1b78f48,ExecProcNode=0x701efa<ExecHashJoin>,ExecProcNodeReal=0x0,instrument=0x0,worker_instrument=0x0,worker_jit_instrument=0x0,qual=0x0,lefttree=0x1b79860,righttree=0x1b7a2b8,initPlan=0x0,subPlan=0x0,chgParam=0x0,ps_ResultTupleSlot=0x1b7ad30,ps_ExprContext=0x1b797a0,ps_ProjInfo=0x1b85bf8,scandesc=0x0},jointype=JOIN_INNER,single_match=false,joinqual=0x0},hashclauses=0x0,hj_OuterHashKeys=0x0,hj_InnerHashKeys=0x0,hj_HashOperators=0x0,hj_HashTable=0x0,hj_CurHashValue=0,hj_CurBucketNo=0,hj_CurSkewBucketNo=0,hj_CurTuple=0x0,hj_OuterTupleSlot=0x1b860a8,hj_HashTupleSlot=0x0,hj_NullOuterTupleSlot=0x0,hj_NullInnerTupleSlot=0x0,hj_FirstOuterTupleSlot=0x0,hj_JoinState=0,hj_MatchedOuter=false,hj_OuterNotEmpty=false}(gdb)p*hjstate->js.ps.ps_ResultTupleSlot#结果TupleSlot$20={type=T_TupleTableSlot,tts_isempty=true,tts_shouldFree=false,tts_shouldFreeMin=false,tts_slow=false,tts_tuple=0x0,tts_tupleDescriptor=0x1b857b8,tts_mcxt=0x1b78e30,tts_buffer=0,tts_nvalid=0,tts_values=0x1b7ad90,tts_isnull=0x1b7adb8,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)p*hjstate->js.ps.ps_ProjInfo#投影$18={type=T_ProjectionInfo,pi_state={tag={type=T_ExprState},flags=6'\006',resnull=false,resvalue=0,resultslot=0x1b7ad30,steps=0x1b85c90,evalfunc=0x6d104b<ExecInterpExprStillValid>,expr=0x1b75588,evalfunc_private=0x6cec02<ExecInterpExpr>,steps_len=8,steps_alloc=16,parent=0x1b79588,ext_params=0x0,innermost_caseval=0x0,innermost_casenull=0x0,innermost_domainval=0x0,innermost_domainnull=0x0},pi_exprContext=0x1b797a0}(gdb)p*hjstate->hj_OuterTupleSlot#元组slot$19={type=T_TupleTableSlot,tts_isempty=true,tts_shouldFree=false,tts_shouldFreeMin=false,tts_slow=false,tts_tuple=0x0,tts_tupleDescriptor=0x1b79b30,tts_mcxt=0x1b78e30,tts_buffer=0,tts_nvalid=0,tts_values=0x1b86108,tts_isnull=0x1b86120,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)n657hjstate->js.single_match=(node->join.inner_unique||(gdb)661switch(node->join.jointype)(gdb)phjstate->js.single_match$21=false
ExecInitHashJoin->配置外连接的NULL元组(不需要)
(gdb)n665break;
ExecInitHashJoin->获取Hash操作的State,注意ExecProcNode是一个包装器(ExecProcNodeFirst),实际的函数是ExecHash
(gdb)694HashState*hashstate=(HashState*)innerPlanState(hjstate);(gdb)695TupleTableSlot*slot=hashstate->ps.ps_ResultTupleSlot;(gdb)n697hjstate->hj_HashTupleSlot=slot;(gdb)(gdb)p*hashstate$22={ps={type=T_HashState,plan=0x1b73288,state=0x1b78f48,ExecProcNode=0x6e41bb<ExecProcNodeFirst>,ExecProcNodeReal=0x6fb5f2<ExecHash>,instrument=0x0,worker_instrument=0x0,worker_jit_instrument=0x0,qual=0x0,lefttree=0x1b7a490,righttree=0x0,initPlan=0x0,subPlan=0x0,chgParam=0x0,ps_ResultTupleSlot=0x1b856f8,ps_ExprContext=0x1b7a3d0,ps_ProjInfo=0x0,scandesc=0x0},hashtable=0x0,hashkeys=0x0,shared_info=0x0,hinstrument=0x0,parallel_state=0x0}
ExecInitHashJoin->初始化(子)表达式,均为NULL
(gdb)n697hjstate->hj_HashTupleSlot=slot;(gdb)704ExecInitQual(node->join.plan.qual,(PlanState*)hjstate);(gdb)n703hjstate->js.ps.qual=(gdb)706ExecInitQual(node->join.joinqual,(PlanState*)hjstate);(gdb)705hjstate->js.joinqual=(gdb)708ExecInitQual(node->hashclauses,(PlanState*)hjstate);(gdb)707hjstate->hashclauses=(gdb)713hjstate->hj_HashTable=NULL;(gdb)714hjstate->hj_FirstOuterTupleSlot=NULL;(gdb)716hjstate->hj_CurHashValue=0;(gdb)717hjstate->hj_CurBucketNo=0;(gdb)718hjstate->hj_CurSkewBucketNo=INVALID_SKEW_BUCKET_NO;(gdb)719hjstate->hj_CurTuple=NULL;(gdb)727lclauses=NIL;(gdb)p*hjstate->js.ps.qualCannotaccessmemoryataddress0x0(gdb)p*hjstate->js.joinqualCannotaccessmemoryataddress0x0(gdb)
ExecInitHashJoin->将哈希子句解构为外部和内部参数值,以便能够分别计算这些子表达式;还可以列出哈希运算符oid,以便查找要使用的哈希函数.
...(gdb)p*hjstate->hj_OuterHashKeys$25={type=T_List,length=1,head=0x1b86f98,tail=0x1b86f98}(gdb)p*hjstate->hj_InnerHashKeys$26={type=T_List,length=1,head=0x1b87708,tail=0x1b87708}(gdb)p*hjstate->hj_HashOperators$27={type=T_OidList,length=1,head=0x1b87768,tail=0x1b87768}(gdb)
ExecInitHashJoin->完成调用
(gdb)n746hjstate->hj_JoinState=HJ_BUILD_HASHTABLE;(gdb)747hjstate->hj_MatchedOuter=false;(gdb)748hjstate->hj_OuterNotEmpty=false;(gdb)750returnhjstate;(gdb)751}
ExecInitHashJoin->最终结果(注意:这是最上层的HashJoin)
(gdb)p*hjstate$28={js={ps={type=T_HashJoinState,plan=0x1b73320,state=0x1b78f48,ExecProcNode=0x701efa<ExecHashJoin>,ExecProcNodeReal=0x0,instrument=0x0,worker_instrument=0x0,worker_jit_instrument=0x0,qual=0x0,lefttree=0x1b79860,righttree=0x1b7a2b8,initPlan=0x0,subPlan=0x0,chgParam=0x0,ps_ResultTupleSlot=0x1b7ad30,ps_ExprContext=0x1b797a0,ps_ProjInfo=0x1b85bf8,scandesc=0x0},jointype=JOIN_INNER,single_match=false,joinqual=0x0},hashclauses=0x1b86168,hj_OuterHashKeys=0x1b86fc0,hj_InnerHashKeys=0x1b87730,hj_HashOperators=0x1b87790,hj_HashTable=0x0,hj_CurHashValue=0,hj_CurBucketNo=0,hj_CurSkewBucketNo=-1,hj_CurTuple=0x0,hj_OuterTupleSlot=0x1b860a8,hj_HashTupleSlot=0x1b856f8,hj_NullOuterTupleSlot=0x0,hj_NullInnerTupleSlot=0x0,hj_FirstOuterTupleSlot=0x0,hj_JoinState=1,hj_MatchedOuter=false,hj_OuterNotEmpty=false}
到此,关于“PostgreSQL中什么函数通过递归调用初始化计划树中的所有Plan节点”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。