PostgreSQL中ExecModifyTable函数的实现逻辑是什么
这篇文章主要讲解了“PostgreSQL中ExecModifyTable函数的实现逻辑是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PostgreSQL中ExecModifyTable函数的实现逻辑是什么”吧!
一、基础信息ExecModifyTable函数使用的数据结构、宏定义以及依赖的函数等。
数据结构/宏定义
1、PartitionTupleRouting
//分区表相关的数据结构,再续解读分区表处理时再行更新/*-----------------------*PartitionTupleRouting-Encapsulatesallinformationrequiredtoexecute*tuple-routingbetweenpartitions.**partition_dispatch_infoArrayofPartitionDispatchobjectswithone*entryforeverypartitionedtableinthe*partitiontree.*num_dispatchnumberofpartitionedtablesinthepartition*tree(=lengthofpartition_dispatch_info[])*partition_oidsArrayofleafpartitionsOIDswithoneentry*foreveryleafpartitioninthepartitiontree,*initializedinfullby*ExecSetupPartitionTupleRouting.*partitionsArrayofResultRelInfo*objectswithoneentry*foreveryleafpartitioninthepartitiontree,*initializedlazilybyExecInitPartitionInfo.*num_partitionsNumberofleafpartitionsinthepartitiontree*(='partitions_oid'/'partitions'arraylength)*parent_child_tupconv_mapsArrayofTupleConversionMapobjectswithone*entryforeveryleafpartition(requiredto*converttuplefromtheroottable'srowtypeto*aleafpartition'srowtypeaftertuplerouting*isdone)*child_parent_tupconv_mapsArrayofTupleConversionMapobjectswithone*entryforeveryleafpartition(requiredto*convertanupdatedtuplefromtheleaf*partition'srowtypetotheroottable'srowtype*sothattupleroutingcanbedone)*child_parent_map_not_requiredArrayofbool.Truevaluemeansthatamapis*determinedtobenotrequiredforthegiven*partition.Falsemeanseitherwehaven'tyet*checkedifamapisrequired,oritwas*determinedtoberequired.*subplan_partition_offsetsIntegerarrayorderedbyUPDATEsubplans.Each*elementofthisarrayhastheindexintothe*correspondingpartitioninpartitionsarray.*num_subplan_partition_offsetsLengthof'subplan_partition_offsets'array*partition_tuple_slotTupleTableSlottobeusedtomanipulateany*givenleafpartition'srowtypeafterthat*partitionischosenforinsertionby*tuple-routing.*root_tuple_slotTupleTableSlottobeusedtotransientlyhold*copyofatuplethat'sbeingmovedacross*partitionsintherootpartitionedtable's*rowtype*-----------------------*/typedefstructPartitionTupleRouting{PartitionDispatch*partition_dispatch_info;intnum_dispatch;Oid*partition_oids;ResultRelInfo**partitions;intnum_partitions;TupleConversionMap**parent_child_tupconv_maps;TupleConversionMap**child_parent_tupconv_maps;bool*child_parent_map_not_required;int*subplan_partition_offsets;intnum_subplan_partition_offsets;TupleTableSlot*partition_tuple_slot;TupleTableSlot*root_tuple_slot;}PartitionTupleRouting;
2、CmdType
//命令类型,DDL命令都归到CMD_UTILITY里面了/**CmdType-*enumsfortypeofoperationrepresentedbyaQueryorPlannedStmt**Thisisneededinbothparsenodes.handplannodes.h,soputithere...*/typedefenumCmdType{CMD_UNKNOWN,CMD_SELECT,/*selectstmt*/CMD_UPDATE,/*updatestmt*/CMD_INSERT,/*insertstmt*/CMD_DELETE,CMD_UTILITY,/*cmdslikecreate,destroy,copy,vacuum,*etc.*/CMD_NOTHING/*dummycommandforinsteadnothingrules*withqual*/}CmdType;
3、JunkFilter
//运行期需要的Attribute成为junkattribute,实际的Tuple需要使用JunkFilter过滤这些属性/*----------------*JunkFilter**Thisclassisusedtostoreinformationregardingjunkattributes.*Ajunkattributeisanattributeinatuplethatisneededonlyfor*storingintermediateinformationintheexecutor,anddoesnotbelong*inemittedtuples.Forexample,whenwedoanUPDATEquery,*theplanneraddsa"junk"entrytothetargetlistsothatthetuples*returnedtoExecutePlan()containanextraattribute:thectidof*thetupletobeupdated.Thisisneededtodotheupdate,butwe*don'twantthectidtobepartofthestorednewtuple!So,we*applya"junkfilter"toremovethejunkattributesandformthe*realoutputtuple.Thejunkfiltercodealsoprovidesroutinesto*extractthevaluesofthejunkattribute(s)fromtheinputtuple.**targetList:theoriginaltargetlist(includingjunkattributes).*cleanTupType:thetupledescriptorforthe"clean"tuple(with*junkattributesremoved).*cleanMap:Amapwiththecorrespondencebetweenthenon-junk*attributenumbersofthe"original"tupleandthe*attributenumbersofthe"clean"tuple.*resultSlot:tupleslotusedtoholdcleanedtuple.*junkAttNo:notusedbyjunkfiltercode.Canbeusedbycaller*toremembertheattnoofaspecificjunkattribute*(nodeModifyTable.ckeepsthe"ctid"or"wholerow"*attnohere).*----------------*/typedefstructJunkFilter{NodeTagtype;List*jf_targetList;TupleDescjf_cleanTupType;AttrNumber*jf_cleanMap;TupleTableSlot*jf_resultSlot;AttrNumberjf_junkAttNo;}JunkFilter;
4、Datum
//实际类型为unsignedlongint,无符号长整型整数/**ADatumcontainseitheravalueofapass-by-valuetypeorapointertoa*valueofapass-by-referencetype.Therefore,werequire:**sizeof(Datum)==sizeof(void*)==4or8**Themacrosbelowandtheanalogousmacrosforothertypesshouldbeusedto*convertbetweenaDatumandtheappropriateCtype.*/typedefuintptr_tDatum;uintptr_tunsignedintegertypecapableofholdingapointer,definedinheader<stdint.h>typedefunsignedlongintuintptr_t;
5、CHECK_FOR_INTERRUPTS
/*TheCHECK_FOR_INTERRUPTS()macroiscalledatstrategicallylocatedspots*whereitisnormallysafetoacceptacancelordieinterrupt.Insome*cases,weinvokeCHECK_FOR_INTERRUPTS()insidelow-levelsubroutinesthat*mightsometimesbecalledincontextsthatdo*not*wanttoallowacancel*ordieinterrupt.TheHOLD_INTERRUPTS()andRESUME_INTERRUPTS()macros*allowcodetoensurethatnocancelordieinterruptwillbeaccepted,*evenifCHECK_FOR_INTERRUPTS()getscalledinasubroutine.Theinterrupt*willbeheldoffuntilCHECK_FOR_INTERRUPTS()isdoneoutsideany*HOLD_INTERRUPTS()...RESUME_INTERRUPTS()section.*/#defineCHECK_FOR_INTERRUPTS()\do{\if(InterruptPending)\ProcessInterrupts();\}while(0)
依赖的函数
1、fireBSTriggers
//触发语句级的触发器/**ProcessBEFOREEACHSTATEMENTtriggers*/staticvoidfireBSTriggers(ModifyTableState*node){ModifyTable*plan=(ModifyTable*)node->ps.plan;ResultRelInfo*resultRelInfo=node->resultRelInfo;/**Ifthenodemodifiesapartitionedtable,wemustfireitstriggers.*Notethatinthatcase,node->resultRelInfopointstothefirstleaf*partition,nottheroottable.*/if(node->rootResultRelInfo!=NULL)resultRelInfo=node->rootResultRelInfo;switch(node->operation){caseCMD_INSERT:ExecBSInsertTriggers(node->ps.state,resultRelInfo);if(plan->onConflictAction==ONCONFLICT_UPDATE)ExecBSUpdateTriggers(node->ps.state,resultRelInfo);break;caseCMD_UPDATE:ExecBSUpdateTriggers(node->ps.state,resultRelInfo);break;caseCMD_DELETE:ExecBSDeleteTriggers(node->ps.state,resultRelInfo);break;default:elog(ERROR,"unknownoperation");break;}}
2、ResetPerTupleExprContext
/*ResetanEState'sper-output-tupleexprcontext,ifone'sbeencreated*/#defineResetPerTupleExprContext(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--isaTupleTableSlotempty?*/#defineTupIsNull(slot)\((slot)==NULL||(slot)->tts_isempty)
5、EvalPlanQualSetPlan
//TODO/**EvalPlanQualSetPlan--setorchangesubplanofanEPQState.**WeneedthissothatModifyTablecandealwithmultiplesubplans.*/voidEvalPlanQualSetPlan(EPQState*epqstate,Plan*subplan,List*auxrowmarks){/*IfwehavealiveEPQquery,shutitdown*/EvalPlanQualEnd(epqstate);/*Andset/changetheplanpointer*/epqstate->plan=subplan;/*Therowmarksdependontheplan,too*/epqstate->arowMarks=auxrowmarks;}
6、tupconv_map_for_subplan
//分区表使用,后续再行解析//TODO/**Foragivensubplanindex,getthetupleconversionmap.*/staticTupleConversionMap*tupconv_map_for_subplan(ModifyTableState*mtstate,intwhichplan){/**Ifapartition-indextupleconversionmaparrayisallocated,weneed*tofirstgettheindexintothepartitionarray.Exactly*one*ofthe*twoarraysisallocated.Thisisbecauseifthereisapartitionarray*required,wedon'trequiresubplan-indexedarraysincewecantranslate*subplanindexintopartitionindex.And,wecreateasubplan-indexed*array*only*ifpartition-indexedarrayisnotrequired.*/if(mtstate->mt_per_subplan_tupconv_maps==NULL){intleaf_index;PartitionTupleRouting*proute=mtstate->mt_partition_tuple_routing;/**Ifsubplan-indexedarrayisNULL,thingsshouldhavebeenarranged*toconvertthesubplanindextopartitionindex.*/Assert(proute&&proute->subplan_partition_offsets!=NULL&&whichplan<proute->num_subplan_partition_offsets);leaf_index=proute->subplan_partition_offsets[whichplan];returnTupConvMapForLeaf(proute,getTargetResultRelInfo(mtstate),leaf_index);}else{Assert(whichplan>=0&&whichplan<mtstate->mt_nplans);returnmtstate->mt_per_subplan_tupconv_maps[whichplan];}}
7、ExecProcessReturning
//返回结果Tuple//更详细的解读有待执行查询语句时的分析解读/**ExecProcessReturning---evaluateaRETURNINGlist**resultRelInfo:currentresultrel*tupleSlot:slotholdingtupleactuallyinserted/updated/deleted*planSlot:slotholdingtuplereturnedbytopsubplannode**Note:IftupleSlotisNULL,theFDWshouldhavealreadyprovidedecontext's*scantuple.**Returnsaslotholdingtheresulttuple*/staticTupleTableSlot*ExecProcessReturning(ResultRelInfo*resultRelInfo,TupleTableSlot*tupleSlot,TupleTableSlot*planSlot){ProjectionInfo*projectReturning=resultRelInfo->ri_projectReturning;ExprContext*econtext=projectReturning->pi_exprContext;/**Resetper-tuplememorycontexttofreeanyexpressionevaluation*storageallocatedinthepreviouscycle.*/ResetExprContext(econtext);/*MaketupleandanyneededjoinvariablesavailabletoExecProject*/if(tupleSlot)econtext->ecxt_scantuple=tupleSlot;else{HeapTupletuple;/**RETURNINGexpressionsmightreferencethetableoidcolumn,so*initializet_tableOidbeforeevaluatingthem.*/Assert(!TupIsNull(econtext->ecxt_scantuple));tuple=ExecMaterializeSlot(econtext->ecxt_scantuple);tuple->t_tableOid=RelationGetRelid(resultRelInfo->ri_RelationDesc);}econtext->ecxt_outertuple=planSlot;/*ComputetheRETURNINGexpressions*/returnExecProject(projectReturning);}/*----------------*ProjectionInfonodeinformation**Thisisalltheinformationneededtoperformprojections---*thatis,formnewtuplesbyevaluationoftargetlistexpressions.*Nodeswhichneedtodoprojectionscreateoneofthese.**ThetargettupleslotiskeptinProjectionInfo->pi_state.resultslot.*ExecProject()evaluatesthetlist,formsatuple,andstoresit*inthegivenslot.Notethattheresultwillbea"virtual"tuple*unlessExecMaterializeSlot()isthencalledtoforceittobe*convertedtoaphysicaltuple.Theslotmusthaveatupledesc*thatmatchestheoutputofthetlist!*----------------*/typedefstructProjectionInfo{NodeTagtype;/*instructionstoevaluateprojection*/ExprStatepi_state;/*expressioncontextinwhichtoevaluateexpression*/ExprContext*pi_exprContext;}ProjectionInfo;/*----------------*ExprContext**Thisclassholdsthe"currentcontext"information*neededtoevaluateexpressionsfordoingtuplequalifications*andtupleprojections.Forexample,ifanexpressionrefers*toanattributeinthecurrentinnertuplethenweneedtoknow*whatthecurrentinnertupleisandsowelookattheexpression*context.**TherearetwomemorycontextsassociatedwithanExprContext:**ecxt_per_query_memoryisaquery-lifespancontext,typicallythesame*contexttheExprContextnodeitselfisallocatedin.Thiscontext*canbeusedforpurposessuchasstoringfunctioncallcacheinfo.**ecxt_per_tuple_memoryisashort-termcontextforexpressionresults.*Asthenamesuggests,itwilltypicallyberesetoncepertuple,*beforewebegintoevaluateexpressionsforthattuple.Each*ExprContextnormallyhasitsveryownper-tuplememorycontext.**CurrentMemoryContextshouldbesettoecxt_per_tuple_memorybefore*callingExecEvalExpr()---seeExecEvalExprSwitchContext().*----------------*/typedefstructExprContext{NodeTagtype;/*TuplesthatVarnodesinexpressionmayreferto*/#defineFIELDNO_EXPRCONTEXT_SCANTUPLE1TupleTableSlot*ecxt_scantuple;#defineFIELDNO_EXPRCONTEXT_INNERTUPLE2TupleTableSlot*ecxt_innertuple;#defineFIELDNO_EXPRCONTEXT_OUTERTUPLE3TupleTableSlot*ecxt_outertuple;/*Memorycontextsforexpressionevaluation---seenotesabove*/MemoryContextecxt_per_query_memory;MemoryContextecxt_per_tuple_memory;/*ValuestosubstituteforParamnodesinexpression*/ParamExecData*ecxt_param_exec_vals;/*forPARAM_EXECparams*/ParamListInfoecxt_param_list_info;/*forotherparamtypes*//**ValuestosubstituteforAggrefnodesintheexpressionsofanAgg*node,orforWindowFuncnodeswithinaWindowAggnode.*/#defineFIELDNO_EXPRCONTEXT_AGGVALUES8Datum*ecxt_aggvalues;/*precomputedvaluesforaggs/windowfuncs*/#defineFIELDNO_EXPRCONTEXT_AGGNULLS9bool*ecxt_aggnulls;/*nullflagsforaggs/windowfuncs*//*ValuetosubstituteforCaseTestExprnodesinexpression*/#defineFIELDNO_EXPRCONTEXT_CASEDATUM10DatumcaseValue_datum;#defineFIELDNO_EXPRCONTEXT_CASENULL11boolcaseValue_isNull;/*ValuetosubstituteforCoerceToDomainValuenodesinexpression*/#defineFIELDNO_EXPRCONTEXT_DOMAINDATUM12DatumdomainValue_datum;#defineFIELDNO_EXPRCONTEXT_DOMAINNULL13booldomainValue_isNull;/*LinktocontainingEState(NULLifastandaloneExprContext)*/structEState*ecxt_estate;/*FunctionstocallbackwhenExprContextisshutdownorrescanned*/ExprContext_CB*ecxt_callbacks;}ExprContext;/**ExecProject**Projectsatuplebasedonprojectioninfoandstoresitintheslotpassed*toExecBuildProjectInfo().**Note:theresultisalwaysavirtualtuple;thereforeitmayreference*thecontentsoftheexprContext'sscantuplesand/ortemporaryresults*constructedintheexprContext.Ifthecallerwishestheresulttobe*validlongerthanthatdatawillbevalid,hemustcallExecMaterializeSlot*ontheresultslot.*/#ifndefFRONTENDstaticinlineTupleTableSlot*ExecProject(ProjectionInfo*projInfo){ExprContext*econtext=projInfo->pi_exprContext;ExprState*state=&projInfo->pi_state;TupleTableSlot*slot=state->resultslot;boolisnull;/**Clearanyformercontentsoftheresultslot.Thismakesitsafefor*ustousetheslot'sDatum/isnullarraysasworkspace.*/ExecClearTuple(slot);/*Runtheexpression,discardingscalarresultfromthelastcolumn.*/(void)ExecEvalExprSwitchContext(state,econtext,&isnull);/**Successfullyformedaresultrow.Marktheresultslotascontaininga*validvirtualtuple(inlinedversionofExecStoreVirtualTuple()).*/slot->tts_isempty=false;slot->tts_nvalid=slot->tts_tupleDescriptor->natts;returnslot;}#endif
8、EvalPlanQualSetSlot
#defineEvalPlanQualSetSlot(epqstate,slot)((epqstate)->origslot=(slot))
9、ExecGetJunkAttribute
//获取Junk中的某个属性值/**ExecGetJunkAttribute**Givenajunkfilter'sinputtuple(slot)andajunkattribute'snumber*previouslyfoundbyExecFindJunkAttribute,extract&returnthevalueand*isNullflagoftheattribute.*/DatumExecGetJunkAttribute(TupleTableSlot*slot,AttrNumberattno,bool*isNull){Assert(attno>0);returnslot_getattr(slot,attno,isNull);}
10、DatumGetPointer
//将无符号长整型转换为指针然后进行传递/**DatumGetPointer*Returnspointervalueofadatum.*/#defineDatumGetPointer(X)((Pointer)(X))
11、AttributeNumberIsValid
//判断属性(号)是否合法/*----------------*supportmacros*----------------*//**AttributeNumberIsValid*Trueifftheattributenumberisvalid.*/#defineAttributeNumberIsValid(attributeNumber)\((bool)((attributeNumber)!=InvalidAttrNumber))
12、DatumGetHeapTupleHeader
#defineDatumGetHeapTupleHeader(X)((HeapTupleHeader)PG_DETOAST_DATUM(X))
13、HeapTupleHeaderGetDatumLength
#defineHeapTupleHeaderGetDatumLength(tup)\VARSIZE(tup)
14、ItemPointerSetInvalid
//块号&块内偏移设置为Invalid/**ItemPointerSetInvalid*Setsadiskitempointertobeinvalid.*/#defineItemPointerSetInvalid(pointer)\(\AssertMacro(PointerIsValid(pointer)),\BlockIdSet(&((pointer)->ip_blkid),InvalidBlockNumber),\(pointer)->ip_posid=InvalidOffsetNumber\)
15、ExecFilterJunk
//去掉所有的Junk属性/**ExecFilterJunk**Constructandreturnaslotwithallthejunkattributesremoved.*/TupleTableSlot*ExecFilterJunk(JunkFilter*junkfilter,TupleTableSlot*slot){TupleTableSlot*resultSlot;AttrNumber*cleanMap;TupleDesccleanTupType;intcleanLength;inti;Datum*values;bool*isnull;Datum*old_values;bool*old_isnull;/**Extractallthevaluesoftheoldtuple.*/slot_getallattrs(slot);old_values=slot->tts_values;old_isnull=slot->tts_isnull;/**getinfofromthejunkfilter*/cleanTupType=junkfilter->jf_cleanTupType;cleanLength=cleanTupType->natts;cleanMap=junkfilter->jf_cleanMap;resultSlot=junkfilter->jf_resultSlot;/**Preparetobuildavirtualresulttuple.*/ExecClearTuple(resultSlot);values=resultSlot->tts_values;isnull=resultSlot->tts_isnull;/**Transposedataintoproperfieldsofthenewtuple.*/for(i=0;i<cleanLength;i++){intj=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];}}/**Andreturnthevirtualtuple.*/returnExecStoreVirtualTuple(resultSlot);}
16、ExecPrepareTupleRouting
//分区表使用,后续再行解析/**ExecPrepareTupleRouting---prepareforroutingonetuple**Determinethepartitioninwhichthetupleinslotistobeinserted,*andmodifymtstateandestatetoprepareforit.**Callermustreverttheestatechangesafterexecutingtheinsertion!*Inmtstate,transitioncapturechangesmayalsoneedtobereverted.**Returnsaslotholdingthetupleofthepartitionrowtype.*/staticTupleTableSlot*ExecPrepareTupleRouting(ModifyTableState*mtstate,EState*estate,PartitionTupleRouting*proute,ResultRelInfo*targetRelInfo,TupleTableSlot*slot){ModifyTable*node;intpartidx;ResultRelInfo*partrel;HeapTupletuple;/**Determinethetargetpartition.IfExecFindPartitiondoesnotfinda*partitionafterall,itdoesn'treturnhere;otherwise,thereturned*valueistobeusedasanindexintothearraysfortheResultRelInfo*andTupleConversionMapforthepartition.*/partidx=ExecFindPartition(targetRelInfo,proute->partition_dispatch_info,slot,estate);Assert(partidx>=0&&partidx<proute->num_partitions);/**GettheResultRelInfocorrespondingtotheselectedpartition;ifnot*yetthere,initializeit.*/partrel=proute->partitions[partidx];if(partrel==NULL)partrel=ExecInitPartitionInfo(mtstate,targetRelInfo,proute,estate,partidx);/**Checkwhetherthepartitionisroutableifwedidn'tyet**Note:anUPDATEofapartitionkeyinvokesanINSERTthatmovesthe*tupletoanewpartition.Thischeckwouldbeappliedtoasubplan*partitionofsuchanUPDATEthatischosenasthepartitiontoroute*thetupleto.Thereasonwedothischeckhereratherthanin*ExecSetupPartitionTupleRoutingistoavoidabortingsuchanUPDATE*unnecessarilyduetonon-routablesubplanpartitionsthatmaynotbe*chosenforupdatetuplemovementafterall.*/if(!partrel->ri_PartitionReadyForRouting){/*VerifythepartitionisavalidtargetforINSERT.*/CheckValidResultRel(partrel,CMD_INSERT);/*Setupinformationneededforroutingtuplestothepartition.*/ExecInitRoutingInfo(mtstate,estate,proute,partrel,partidx);}/**Makeitlooklikeweareinsertingintothepartition.*/estate->es_result_relation_info=partrel;/*Gettheheaptupleoutofthegivenslot.*/tuple=ExecMaterializeSlot(slot);/**Ifwe'recapturingtransitiontuples,wemightneedtoconvertfromthe*partitionrowtypetoparentrowtype.*/if(mtstate->mt_transition_capture!=NULL){if(partrel->ri_TrigDesc&&partrel->ri_TrigDesc->trig_insert_before_row){/**IfthereareanyBEFOREtriggersonthepartition,we'llhave*tobereadytoconverttheirresultbacktotuplestoreformat.*/mtstate->mt_transition_capture->tcs_original_insert_tuple=NULL;mtstate->mt_transition_capture->tcs_map=TupConvMapForLeaf(proute,targetRelInfo,partidx);}else{/**Otherwise,justremembertheoriginalunconvertedtuple,to*avoidaneedlessroundtripconversion.*/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);}/**Convertthetuple,ifnecessary.*/ConvertPartitionTupleSlot(proute->parent_child_tupconv_maps[partidx],tuple,proute->partition_tuple_slot,&slot,true);/*InitializeinformationneededtohandleONCONFLICTDOUPDATE.*/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);}returnslot;}
17、ExecInsert
//上一节已解读
18、ExecUpdate/ExecDelete
//执行更新/删除,在后续实验Update/Delete时再行解析
19、fireASTriggers
//触发执行语句后的触发器(语句级)/**ProcessAFTEREACHSTATEMENTtriggers*/staticvoidfireASTriggers(ModifyTableState*node){ModifyTable*plan=(ModifyTable*)node->ps.plan;ResultRelInfo*resultRelInfo=getTargetResultRelInfo(node);switch(node->operation){caseCMD_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;caseCMD_UPDATE:ExecASUpdateTriggers(node->ps.state,resultRelInfo,node->mt_transition_capture);break;caseCMD_DELETE:ExecASDeleteTriggers(node->ps.state,resultRelInfo,node->mt_transition_capture);break;default:elog(ERROR,"unknownoperation");break;}}二、源码解读
/*----------------------------------------------------------------*ExecModifyTable**Performtablemodificationsasrequired,andreturnRETURNINGresults*ifneeded.*----------------------------------------------------------------*//*输入:pstate-SQL语句执行计划的State(各种状态信息)输出:TupleTableSlot指针,存储执行后Tuple的Slot指针*/staticTupleTableSlot*ExecModifyTable(PlanState*pstate){ModifyTableState*node=castNode(ModifyTableState,pstate);//强制类型转换为ModifyTableState类型PartitionTupleRouting*proute=node->mt_partition_tuple_routing;//分区表执行EState*estate=node->ps.state;//执行器Executor状态CmdTypeoperation=node->operation;//命令类型ResultRelInfo*saved_resultRelInfo;//结果RelationInfoResultRelInfo*resultRelInfo;//同上PlanState*subplanstate;//子执行计划状态JunkFilter*junkfilter;//无用属性过滤器TupleTableSlot*slot;//Tuple存储的Slot指针TupleTableSlot*planSlot;//Plan存储的Slot指针ItemPointertupleid;//Tuple行指针ItemPointerDatatuple_ctid;//Tuple行指针数据(Block号、偏移等)HeapTupleDataoldtupdata;//原Tuple数据HeapTupleoldtuple;//原Tuple数据指针CHECK_FOR_INTERRUPTS();//检查中断信号/**ThisshouldNOTgetcalledduringEvalPlanQual;weshouldhavepasseda*subplantreetoEvalPlanQual,instead.Usearuntimetestnotjust*Assertbecausethisconditioniseasytomissintesting.(Note:*althoughModifyTableshouldnotgetexecutedwithinanEvalPlanQual*operation,wedohavetoallowittobeinitializedandshutdownin*caseitiswithinaCTEsubplan.Hencethistestmustbehere,notin*ExecInitModifyTable.)*/if(estate->es_epqTuple!=NULL)elog(ERROR,"ModifyTableshouldnotbecalledduringEvalPlanQual");/**Ifwe'vealreadycompletedprocessing,don'ttrytodomore.Weneed*thistestbecauseExecPostprocessPlanmightcallusanextratime,and*oursubplan'snodesaren'tnecessarilyrobustagainstbeingcalled*extratimes.*///已经完成,返回NULLif(node->mt_done)returnNULL;/**Onfirstcall,fireBEFORESTATEMENTtriggersbeforeproceeding.*///语句级触发器触发if(node->fireBSTriggers){fireBSTriggers(node);node->fireBSTriggers=false;}/*Preloadlocalvariables*///预先构造本地变量resultRelInfo=node->resultRelInfo+node->mt_whichplan;//设置正在操作的Relation,mt_whichplan是偏移subplanstate=node->mt_plans[node->mt_whichplan];//执行计划的Statejunkfilter=resultRelInfo->ri_junkFilter;//额外(无价值)的信息过滤器/**es_result_relation_infomustpointtothecurrentlyactiveresult*relationwhilewearewithinthisModifyTablenode.Eventhough*ModifyTablenodescan'tbenestedstatically,theycanbenested*dynamically(sinceoursubplancouldincludeareferencetoamodifying*CTE).Sowehavetosaveandrestorethecaller'svalue.*///切换当前活跃的Relationsaved_resultRelInfo=estate->es_result_relation_info;estate->es_result_relation_info=resultRelInfo;/**Fetchrowsfromsubplan(s),andexecutetherequiredtablemodification*foreachrow.*/for(;;){/**Resettheper-output-tupleexprcontext.Thisisneededbecause*triggersexpecttousethatcontextasworkspace.It'sabitugly*todothisbelowthetopleveloftheplan,however.Wemightneed*torethinkthislater.*/ResetPerTupleExprContext(estate);//设置上下文planSlot=ExecProcNode(subplanstate);//获取子Plan的Slot(插入数据操作返回值应为NULL)if(TupIsNull(planSlot))//没有执行计划(执行计划已全部完成){/*advancetonextsubplanifany*/node->mt_whichplan++;//移至下一个执行Planif(node->mt_whichplan<node->mt_nplans){resultRelInfo++;//切换至相应的Relation,如果是插入操作,只有一个subplanstate=node->mt_plans[node->mt_whichplan];//切换至相应的子Plan状态junkfilter=resultRelInfo->ri_junkFilter;//切换至相应的junkfilterestate->es_result_relation_info=resultRelInfo;//切换EvalPlanQualSetPlan(&node->mt_epqstate,subplanstate->plan,node->mt_arowmarks[node->mt_whichplan]);//设置子Plan/*Preparetoconverttransitiontuplesfromthischild.*/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;}elsebreak;//所有的Plan均已执行,跳出循环}//存在子Plan/**IfresultRelInfo->ri_usesFdwDirectModifyistrue,allweneedtodo*hereiscomputetheRETURNINGexpressions.*/if(resultRelInfo->ri_usesFdwDirectModify){Assert(resultRelInfo->ri_projectReturning);/**Ascanslotcontainingthedatathatwasactuallyinserted,*updatedordeletedhasalreadybeenmadeavailableto*ExecProcessReturningbyIterateDirectModify,sononeedto*provideithere.*/slot=ExecProcessReturning(resultRelInfo,NULL,planSlot);//FDW,直接返回estate->es_result_relation_info=saved_resultRelInfo;returnslot;}EvalPlanQualSetSlot(&node->mt_epqstate,planSlot);//设置相应的Slot(子PlanSlot)slot=planSlot;tupleid=NULL;//TupleIDoldtuple=NULL;//原Tupleif(junkfilter!=NULL)//去掉废弃的属性{/**extractthe'ctid'or'wholerow'junkattribute.*/if(operation==CMD_UPDATE||operation==CMD_DELETE)//更新或者删除操作{charrelkind;Datumdatum;boolisNull;relkind=resultRelInfo->ri_RelationDesc->rd_rel->relkind;if(relkind==RELKIND_RELATION||relkind==RELKIND_MATVIEW){datum=ExecGetJunkAttribute(slot,junkfilter->jf_junkAttNo,&isNull);/*shouldn'tevergetanullresult...*/if(isNull)elog(ERROR,"ctidisNULL");tupleid=(ItemPointer)DatumGetPointer(datum);tuple_ctid=*tupleid;/*besurewedon'tfreectid!!*/tupleid=&tuple_ctid;}/**Usethewholerowattribute,whenavailable,toreconstruct*theoldrelationtuple.**Foreigntableupdateshaveawholerowattributewhenthe*relationhasarow-leveltrigger.Notethatthewholerow*attributedoesnotcarrysystemcolumns.Foreigntable*triggersmissseeingthose,exceptthatweknowenoughhere*tosett_tableOid.Quiteseparatelyfromthis,theFDWmay*fetchitsownjunkattrstoidentifytherow.**Otherrelevantrelkinds,currentlylimitedtoviews,always*haveawholerowattribute.*/elseif(AttributeNumberIsValid(junkfilter->jf_junkAttNo)){datum=ExecGetJunkAttribute(slot,junkfilter->jf_junkAttNo,&isNull);/*shouldn'tevergetanullresult...*/if(isNull)elog(ERROR,"wholerowisNULL");oldtupdata.t_data=DatumGetHeapTupleHeader(datum);oldtupdata.t_len=HeapTupleHeaderGetDatumLength(oldtupdata.t_data);ItemPointerSetInvalid(&(oldtupdata.t_self));/*Historically,viewtriggersseeinvalidt_tableOid.*/oldtupdata.t_tableOid=(relkind==RELKIND_VIEW)?InvalidOid:RelationGetRelid(resultRelInfo->ri_RelationDesc);oldtuple=&oldtupdata;}elseAssert(relkind==RELKIND_FOREIGN_TABLE);}/**applythejunkfilterifneeded.*/if(operation!=CMD_DELETE)slot=ExecFilterJunk(junkfilter,slot);//去掉废弃的属性}switch(operation)//根据操作类型,执行相应的操作{caseCMD_INSERT://插入/*Preparefortupleroutingifneeded.*/if(proute)slot=ExecPrepareTupleRouting(node,estate,proute,resultRelInfo,slot);//准备相应的TupleSlotslot=ExecInsert(node,slot,planSlot,estate,node->canSetTag);//执行插入/*RevertExecPrepareTupleRouting'sstatechange.*/if(proute)estate->es_result_relation_info=resultRelInfo;break;caseCMD_UPDATE:slot=ExecUpdate(node,tupleid,oldtuple,slot,planSlot,&node->mt_epqstate,estate,node->canSetTag);break;caseCMD_DELETE:slot=ExecDelete(node,tupleid,oldtuple,planSlot,&node->mt_epqstate,estate,NULL,true,node->canSetTag,false/*changingPart*/);break;default:elog(ERROR,"unknownoperation");break;}/**IfwegotaRETURNINGresult,returnittocaller.We'llcontinue*theworkonnextcall.*/if(slot){estate->es_result_relation_info=saved_resultRelInfo;returnslot;}}/*Restorees_result_relation_infobeforeexiting*/estate->es_result_relation_info=saved_resultRelInfo;/**We'redone,butfireAFTERSTATEMENTtriggersbeforeexiting.*/fireASTriggers(node);node->mt_done=true;//Insert语句,返回NULLreturnNULL;}三、跟踪分析
执行测试脚本:
altertablet_insertaltercolumnc1typevarchar(40);altertablet_insertaltercolumnc2typevarchar(40);altertablet_insertaltercolumnc3typevarchar(40);testdb=#selectpg_backend_pid();pg_backend_pid----------------1570(1row)testdb=#insertintot_insertvalues(13,'ExecModifyTable','ExecModifyTable','ExecModifyTable');(挂起)
启动gdb跟踪:
[root@localhost~]#gdb-p1570GNUgdb(GDB)RedHatEnterpriseLinux7.6.1-100.el7Copyright(C)2013FreeSoftwareFoundation,Inc....(gdb)bExecModifyTableBreakpoint1at0x6c2498:filenodeModifyTable.c,line1915.(gdb)cContinuing.Breakpoint1,ExecModifyTable(pstate=0x2cde640)atnodeModifyTable.c:19151915ModifyTableState*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"insertintot_insertvalues(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}#结果TupleSlot(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)pproute$7=(PartitionTupleRouting*)0x0#插入操作(gdb)poperation$9=CMD_INSERT#查看本地变量(gdb)presultRelInfo$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)psubplanstate$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)pjunkfilter$18=(JunkFilter*)0x830(gdb)p*junkfilterCannotaccessmemoryataddress0x830gdb)next1974saved_resultRelInfo=estate->es_result_relation_info;(gdb)1976estate->es_result_relation_info=resultRelInfo;(gdb)1990ResetPerTupleExprContext(estate);(gdb)1992planSlot=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)next1994if(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)next2027if(resultRelInfo->ri_usesFdwDirectModify)(gdb)2043EvalPlanQualSetSlot(&node->mt_epqstate,planSlot);(gdb)2044slot=planSlot;(gdb)2046tupleid=NULL;(gdb)2047oldtuple=NULL;(gdb)2048if(junkfilter!=NULL)(gdb)2119switch(operation)(gdb)pjunkfilter$21=(JunkFilter*)0x0(gdb)next2123if(proute)(gdb)2127estate,node->canSetTag);(gdb)2126slot=ExecInsert(node,slot,planSlot,(gdb)2129if(proute)(gdb)p*slotCannotaccessmemoryataddress0x0(gdb)next2151if(slot)(gdb)2156}#进入第2轮循环(gdb)1990ResetPerTupleExprContext(estate);(gdb)1992planSlot=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)next1994if(TupIsNull(planSlot))(gdb)1997node->mt_whichplan++;(gdb)next1998if(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)next2020break;(gdb)p*slotCannotaccessmemoryataddress0x0(gdb)next2159estate->es_result_relation_info=saved_resultRelInfo;(gdb)psaved_resultRelInfo$24=(ResultRelInfo*)0x0(gdb)next2164fireASTriggers(node);(gdb)next2166node->mt_done=true;(gdb)#执行成功,返回NULL(Insert语句)2168returnNULL;
感谢各位的阅读,以上就是“PostgreSQL中ExecModifyTable函数的实现逻辑是什么”的内容了,经过本文的学习后,相信大家对PostgreSQL中ExecModifyTable函数的实现逻辑是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。