本篇内容主要讲解“怎么理解PostgreSQL的分区表”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么理解PostgreSQL的分区表”吧!

在PG中,分区表通过"继承"的方式实现,这里就会存在一个问题,就是在插入数据时,PG如何确定数据应该插入到哪个目标分区?在PG中,通过函数ExecPrepareTupleRouting为路由待插入的元组做准备,主要的目的是确定元组所在的分区。

一、数据结构

ModifyTable
ModifyTable Node
通过插入、更新或删除,将子计划生成的行应用到结果表。

/*----------------*ModifyTablenode-*Applyrowsproducedbysubplan(s)toresulttable(s),*byinserting,updating,ordeleting.*通过插入、更新或删除,将子计划生成的行应用到结果表。**Iftheoriginallynamedtargettableisapartitionedtable,both*nominalRelationandrootRelationcontaintheRTindexofthepartition*root,whichisnototherwisementionedintheplan.OtherwiserootRelation*iszero.However,nominalRelationwillalwaysbeset,asit'stherelthat*EXPLAINshouldclaimistheINSERT/UPDATE/DELETEtarget.*如果最初命名的目标表是分区表,则nominalRelation和rootRelation都包含分区根的RT索引,计划中没有另外提到这个索引。*否则,根关系为零。但是,总是会设置名义关系,nominalRelation因为EXPLAIN应该声明的rel是INSERT/UPDATE/DELETE目标关系。**NotethatrowMarksandepqParamarepresumedtobevalidforallthe*subplan(s);theycan'tcontainanyinfothatvariesacrosssubplans.*注意,rowMarks和epqParam被假定对所有子计划有效;*它们不能包含任何在子计划中变化的信息。*----------------*/typedefstructModifyTable{Planplan;CmdTypeoperation;/*操作类型;INSERT,UPDATE,orDELETE*/boolcanSetTag;/*是否需要设置tag?dowesetthecommandtag/es_processed?*/IndexnominalRelation;/*用于EXPLAIN的父RT索引;ParentRTindexforuseofEXPLAIN*/IndexrootRelation;/*根RootRT索引(如目标为分区表);RootRTindex,iftargetispartitioned*/boolpartColsUpdated;/*更新了层次结构中的分区关键字;somepartkeyinhierarchyupdated*/List*resultRelations;/*RT索引的整型链表;integerlistofRTindexes*/intresultRelIndex;/*计划链表中第一个resultRel的索引;indexoffirstresultRelinplan'slist*/introotResultRelIndex;/*分区表根索引;indexofthepartitionedtableroot*/List*plans;/*生成源数据的计划链表;plan(s)producingsourcedata*/List*withCheckOptionLists;/*每一个目标表均具备的WCO链表;per-target-tableWCOlists*/List*returningLists;/*每一个目标表均具备的RETURNING链表;per-target-tableRETURNINGtlists*/List*fdwPrivLists;/*每一个目标表的FDW私有数据链表;per-target-tableFDWprivatedatalists*/Bitmapset*fdwDirectModifyPlans;/*FDWDM计划索引位图;indicesofFDWDMplans*/List*rowMarks;/*rowMarks链表;PlanRowMarks(non-lockingonly)*/intepqParam;/*EvalPlanQual再解析使用的参数ID;IDofParamforEvalPlanQualre-eval*/OnConflictActiononConflictAction;/*ONCONFLICTaction*/List*arbiterIndexes;/*冲突仲裁器索引表;ListofONCONFLICTarbiterindexOIDs*/List*onConflictSet;/*SETforINSERTONCONFLICTDOUPDATE*/Node*onConflictWhere;/*WHEREforONCONFLICTUPDATE*/IndexexclRelRTI;/*RTIoftheEXCLUDEDpseudorelation*/List*exclRelTlist;/*已排除伪关系的投影列链表;tlistoftheEXCLUDEDpseudorelation*/}ModifyTable;

ResultRelInfo
ResultRelInfo结构体
每当更新一个现有的关系时,我们必须更新关系上的索引,也许还需要触发触发器。ResultRelInfo保存关于结果关系所需的所有信息,包括索引。

/**ResultRelInfo*ResultRelInfo结构体**Wheneverweupdateanexistingrelation,wehavetoupdateindexesonthe*relation,andperhapsalsofiretriggers.ResultRelInfoholdsallthe*informationneededaboutaresultrelation,includingindexes.*每当更新一个现有的关系时,我们必须更新关系上的索引,也许还需要触发触发器。*ResultRelInfo保存关于结果关系所需的所有信息,包括索引。**Normally,aResultRelInforeferstoatablethatisinthequery's*rangetable;thenri_RangeTableIndexistheRTindexandri_RelationDesc*isjustacopyoftherelevantes_relations[]entry.Butsometimes,*inResultRelInfosusedonlyfortriggers,ri_RangeTableIndexiszero*andri_RelationDescisaseparately-openedrelcachepointerthatneeds*tobeseparatelyclosed.SeeExecGetTriggerResultRel.*通常,ResultRelInfo是指查询范围表中的表;*ri_RangeTableIndex是RT索引,而ri_RelationDesc只是相关es_relations[]条目的副本。*但有时,在只用于触发器的ResultRelInfos中,ri_RangeTableIndex为零(NULL),*而ri_RelationDesc是一个需要单独关闭单独打开的relcache指针。*具体可参考ExecGetTriggerResultRel结构体。*/typedefstructResultRelInfo{NodeTagtype;/*resultrelation'srangetableindex,or0ifnotinrangetable*///RTE索引Indexri_RangeTableIndex;/*relationdescriptorforresultrelation*///结果/目标relation的描述符Relationri_RelationDesc;/*#ofindicesexistingonresultrelation*///目标关系中索引数目intri_NumIndices;/*arrayofrelationdescriptorsforindices*///索引的关系描述符数组(索引视为一个relation)RelationPtrri_IndexRelationDescs;/*arrayofkey/attrinfoforindices*///索引的键/属性数组IndexInfo**ri_IndexRelationInfo;/*triggerstobefired,ifany*///触发的索引TriggerDesc*ri_TrigDesc;/*cachedlookupinfofortriggerfunctions*///触发器函数(缓存)FmgrInfo*ri_TrigFunctions;/*arrayoftriggerWHENexprstates*///WHEN表达式状态的触发器数组ExprState**ri_TrigWhenExprs;/*optionalruntimemeasurementsfortriggers*///可选的触发器运行期度量器Instrumentation*ri_TrigInstrument;/*FDWcallbackfunctions,ifforeigntable*///FDW回调函数structFdwRoutine*ri_FdwRoutine;/*availabletosaveprivatestateofFDW*///可用于存储FDW的私有状态void*ri_FdwState;/*truewhenmodifyingforeigntabledirectly*///直接更新FDW时为Tboolri_usesFdwDirectModify;/*listofWithCheckOption'stobechecked*///WithCheckOption链表List*ri_WithCheckOptions;/*listofWithCheckOptionexprstates*///WithCheckOption表达式链表List*ri_WithCheckOptionExprs;/*arrayofconstraint-checkingexprstates*///约束检查表达式状态数组ExprState**ri_ConstraintExprs;/*forremovingjunkattributesfromtuples*///用于从元组中删除junk属性JunkFilter*ri_junkFilter;/*listofRETURNINGexpressions*///RETURNING表达式链表List*ri_returningList;/*forcomputingaRETURNINGlist*///用于计算RETURNING链表ProjectionInfo*ri_projectReturning;/*listofarbiterindexestousetocheckconflicts*///用于检查冲突的仲裁器索引的列表List*ri_onConflictArbiterIndexes;/*ONCONFLICTevaluationstate*///ONCONFLICT解析状态OnConflictSetState*ri_onConflict;/*partitioncheckexpression*///分区检查表达式链表List*ri_PartitionCheck;/*partitioncheckexpressionstate*///分区检查表达式状态ExprState*ri_PartitionCheckExpr;/*relationdescriptorforrootpartitionedtable*///分区root根表描述符Relationri_PartitionRoot;/*Additionalinformationspecifictopartitiontuplerouting*///额外的分区元组路由信息structPartitionRoutingInfo*ri_PartitionInfo;}ResultRelInfo;

PartitionRoutingInfo
PartitionRoutingInfo结构体
分区路由信息,用于将元组路由到表分区的结果关系信息。

/**PartitionRoutingInfo*PartitionRoutingInfo-分区路由信息**Additionalresultrelationinformationspecifictoroutingtuplestoa*tablepartition.*用于将元组路由到表分区的结果关系信息。*/typedefstructPartitionRoutingInfo{/**Mapforconvertingtuplesinrootpartitionedtableformatinto*partitionformat,orNULLifnoconversionisrequired.*映射,用于将根分区表格式的元组转换为分区格式,如果不需要转换,则转换为NULL。*/TupleConversionMap*pi_RootToPartitionMap;/**Mapforconvertingtuplesinpartitionformatintotherootpartitioned*tableformat,orNULLifnoconversionisrequired.*映射,用于将分区格式的元组转换为根分区表格式,如果不需要转换,则转换为NULL。*/TupleConversionMap*pi_PartitionToRootMap;/**Slottostoretuplesinpartitionformat,orNULLwhennotranslation*isrequiredbetweenrootandpartition.*以分区格式存储元组的slot.在根分区和分区之间不需要转换时为NULL。*/TupleTableSlot*pi_PartitionTupleSlot;}PartitionRoutingInfo;

TupleConversionMap
TupleConversionMap结构体,用于存储元组转换映射信息.

typedefstructTupleConversionMap{TupleDescindesc;/*源行类型的描述符;tupdescforsourcerowtype*/TupleDescoutdesc;/*结果行类型的描述符;tupdescforresultrowtype*/AttrNumber*attrMap;/*输入字段的索引信息,0表示NULL;indexesofinputfields,or0fornull*/Datum*invalues;/*析构源数据的工作空间;workspacefordeconstructingsource*/bool*inisnull;//是否为NULL标记数组Datum*outvalues;/*构造结果的工作空间;workspaceforconstructingresult*/bool*outisnull;//null标记}TupleConversionMap;二、源码解读

ExecPrepareTupleRouting函数确定要插入slot中的tuple所属的分区,同时修改mtstate和estate等相关信息,为后续实际的插入作准备。

/**ExecPrepareTupleRouting---prepareforroutingonetuple*ExecPrepareTupleRouting---为路由一个元组做准备**Determinethepartitioninwhichthetupleinslotistobeinserted,*andmodifymtstateandestatetoprepareforit.*确定要插入slot中tuple的分区,并修改mtstate和estate以为插入作准备。**Callermustreverttheestatechangesafterexecutingtheinsertion!*Inmtstate,transitioncapturechangesmayalsoneedtobereverted.*调用方必须在执行插入之后恢复estate中被修改的属性值!*在mtstate中,转换捕获更改也可能需要恢复。**Returnsaslotholdingthetupleofthepartitionrowtype.*返回包含分区rowtype元组的槽位。*/staticTupleTableSlot*ExecPrepareTupleRouting(ModifyTableState*mtstate,EState*estate,PartitionTupleRouting*proute,ResultRelInfo*targetRelInfo,TupleTableSlot*slot){ModifyTable*node;//ModifyTable节点intpartidx;//分区索引ResultRelInfo*partrel;//ResultRelInfo结构体指针(数组)HeapTupletuple;//元组/**Determinethetargetpartition.IfExecFindPartitiondoesnotfinda*partitionafterall,itdoesn'treturnhere;otherwise,thereturned*valueistobeusedasanindexintothearraysfortheResultRelInfo*andTupleConversionMapforthepartition.*确定目标分区。*如果ExecFindPartition最终没有找到分区,它不会在这里返回;*否则,返回值将用作分区的ResultRelInfo和TupleConversionMap数组的索引。*/partidx=ExecFindPartition(targetRelInfo,proute->partition_dispatch_info,slot,estate);Assert(partidx>=0&&partidx<proute->num_partitions);/**GettheResultRelInfocorrespondingtotheselectedpartition;ifnot*yetthere,initializeit.*获取与所选分区对应的ResultRelInfo;如果还没有,则初始化。*/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.*注意:分区键的更新调用将元组移动到新分区的插入。*此检查将应用于此类更新的子计划分区,该分区被选择为将元组路由到的分区。*在这里而不是在ExecSetupPartitionTupleRouting中执行此检查的原因是为了避免由于无法路由的子计划分区而不必要地中止这样的更新,这些分区可能最终不会被选择用于更新元组移动。*/if(!partrel->ri_PartitionReadyForRouting){/*VerifythepartitionisavalidtargetforINSERT.*///验证分区是否可用于INSERTCheckValidResultRel(partrel,CMD_INSERT);/*Setupinformationneededforroutingtuplestothepartition.*///设置将元组路由到分区所需的信息。ExecInitRoutingInfo(mtstate,estate,proute,partrel,partidx);}/**Makeitlooklikeweareinsertingintothepartition.*让它看起来像是插入到分区中。*/estate->es_result_relation_info=partrel;/*Gettheheaptupleoutofthegivenslot.*///从给定的slot中获取heaptupletuple=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.*如果分区上有BEFORE触发器,必须准备将它们的结果转换回tuplestore格式。*/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);/*InitializeinformationneededtohandleONCONFLICTDOUPDATE.*///如为ONCONFLICTDOUPDATE模式,则初始化相关信息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;}/**ExecFetchSlotHeapTuple-fetchHeapTuplerepresentingtheslot'scontent*ExecFetchSlotHeapTuple-根据slot提取HeapTuple**ThereturnedHeapTuplerepresentstheslot'scontentascloselyas*possible.*返回的HeapTuple尽可能就是slot的内容。**Ifmaterializeistrue,thecontentsoftheslotswillbemadeindependent*fromtheunderlyingstorage(i.e.allbufferpinsarerelease,memoryis*allocatedintheslot'scontext).*如果materialize为T,slot的内容将独立于底层存储(即释放所有缓冲区pin,在slot的上下文中分配内存)。**IfshouldFreeisnot-NULLit'llbesettotrueifthereturnedtuplehas*beenallocatedinthecallingmemorycontext,andmustbefreedbythe*caller(viaexplicitpfree()oramemorycontextreset).*如果shouldFreenot-NULL,那么如果返回的元组已经在调用内存上下文中分配,*并且必须由调用方释放(通过显式pfree()或内存上下文重置)。**NB:Ifmaterializeistrue,modificationsofthereturnedtupleare*allowed.Butitdependsonthetypeoftheslotwhethersuchmodifications*willalsoaffecttheslot'scontents.Whilethatisnotthenicest*behaviour,allsuchmodifcationsareintheprocessofbeingremoved.*注意:如果materialize为T,则允许修改返回的元组。*但这取决于slot的类型,这种修改是否也会影响slot的内容。*虽然这不是最好的行为,但所有这些修改都在被移除的过程中。*/HeapTupleExecFetchSlotHeapTuple(TupleTableSlot*slot,boolmaterialize,bool*shouldFree){/**sanitychecks*安全检查*/Assert(slot!=NULL);Assert(!TTS_EMPTY(slot));/*Materializethetuplesothattheslot"owns"it,ifrequested.*///物化元组,以便slot“拥有”它(如要求)。if(materialize)slot->tts_ops->materialize(slot);if(slot->tts_ops->get_heap_tuple==NULL){if(shouldFree)*shouldFree=true;returnslot->tts_ops->copy_heap_tuple(slot);//返回slot拷贝}else{if(shouldFree)*shouldFree=false;returnslot->tts_ops->get_heap_tuple(slot);//直接返回slot}}三、跟踪分析

测试脚本如下

--HashPartitiondroptableifexistst_hash_partition;createtablet_hash_partition(c1intnotnull,c2varchar(40),c3varchar(40))partitionbyhash(c1);createtablet_hash_partition_1partitionoft_hash_partitionforvalueswith(modulus6,remainder0);createtablet_hash_partition_2partitionoft_hash_partitionforvalueswith(modulus6,remainder1);createtablet_hash_partition_3partitionoft_hash_partitionforvalueswith(modulus6,remainder2);createtablet_hash_partition_4partitionoft_hash_partitionforvalueswith(modulus6,remainder3);createtablet_hash_partition_5partitionoft_hash_partitionforvalueswith(modulus6,remainder4);createtablet_hash_partition_6partitionoft_hash_partitionforvalueswith(modulus6,remainder5);--deletefromt_hash_partitionwherec1=0;insertintot_hash_partition(c1,c2,c3)VALUES(0,'HASH0','HAHS0');

启动gdb,设置断点,进入ExecPrepareTupleRouting

(gdb)bExecPrepareTupleRoutingBreakpoint1at0x710b1e:filenodeModifyTable.c,line1712.(gdb)cContinuing.Breakpoint1,ExecPrepareTupleRouting(mtstate=0x1e4de60,estate=0x1e4daf8,proute=0x1e4eb48,targetRelInfo=0x1e4dd48,slot=0x1e4e4e0)atnodeModifyTable.c:17121712partidx=ExecFindPartition(targetRelInfo,

查看函数调用栈
ExecPrepareTupleRouting在ExecModifyTable Node中被调用,为后续的插入作准备.

(gdb)bt#0ExecPrepareTupleRouting(mtstate=0x1e4de60,estate=0x1e4daf8,proute=0x1e4eb48,targetRelInfo=0x1e4dd48,slot=0x1e4e4e0)atnodeModifyTable.c:1712#10x0000000000711602inExecModifyTable(pstate=0x1e4de60)atnodeModifyTable.c:2157#20x00000000006e4c30inExecProcNodeFirst(node=0x1e4de60)atexecProcnode.c:445#30x00000000006d9974inExecProcNode(node=0x1e4de60)at../../../src/include/executor/executor.h:237#40x00000000006dc22dinExecutePlan(estate=0x1e4daf8,planstate=0x1e4de60,use_parallel_mode=false,operation=CMD_INSERT,sendTuples=false,numberTuples=0,direction=ForwardScanDirection,dest=0x1e67e90,execute_once=true)atexecMain.c:1723#50x00000000006d9f5cinstandard_ExecutorRun(queryDesc=0x1e39d68,direction=ForwardScanDirection,count=0,execute_once=true)atexecMain.c:364#60x00000000006d9d7finExecutorRun(queryDesc=0x1e39d68,direction=ForwardScanDirection,count=0,execute_once=true)atexecMain.c:307#70x00000000008cbdb3inProcessQuery(plan=0x1e67d18,sourceText=0x1d60ec8"insertintot_hash_partition(c1,c2,c3)VALUES(0,'HASH0','HAHS0');",params=0x0,queryEnv=0x0,dest=0x1e67e90,completionTag=0x7ffdcf148b20"")atpquery.c:161#80x00000000008cd6f9inPortalRunMulti(portal=0x1dc6538,isTopLevel=true,setHoldSnapshot=false,dest=0x1e67e90,altdest=0x1e67e90,completionTag=0x7ffdcf148b20"")atpquery.c:1286#90x00000000008cccb9inPortalRun(portal=0x1dc6538,count=9223372036854775807,isTopLevel=true,run_once=true,dest=0x1e67e90,altdest=0x1e67e90,completionTag=0x7ffdcf148b20"")atpquery.c:799#100x00000000008c6b1einexec_simple_query(query_string=0x1d60ec8"insertintot_hash_partition(c1,c2,c3)VALUES(0,'HASH0','HAHS0');")atpostgres.c:1145#110x00000000008cae70inPostgresMain(argc=1,argv=0x1d8aba8,dbname=0x1d8aa10"testdb",username=0x1d5dba8"xdb")atpostgres.c:4182

找到该元组所在的分区

(gdb)n1716Assert(partidx>=0&&partidx<proute->num_partitions);(gdb)ppartidx$1=2

获取与所选分区对应的ResultRelInfo;如果还没有,则初始化

(gdb)n1722partrel=proute->partitions[partidx];(gdb)1723if(partrel==NULL)(gdb)p*partrelCannotaccessmemoryataddress0x0(gdb)n1724partrel=ExecInitPartitionInfo(mtstate,targetRelInfo,

初始化后的partrel

(gdb)p*partrel$2={type=T_ResultRelInfo,ri_RangeTableIndex=1,ri_RelationDesc=0x1e7c940,ri_NumIndices=0,ri_IndexRelationDescs=0x0,ri_IndexRelationInfo=0x0,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=0x1e4f538,ri_PartitionCheckExpr=0x0,ri_PartitionRoot=0x1e7c2f8,ri_PartitionReadyForRouting=true}

目标分区描述符-->t_hash_partition_3

(gdb)p*partrel->ri_RelationDesc$3={rd_node={spcNode=1663,dbNode=16402,relNode=16995},rd_smgr=0x1e34510,rd_refcnt=1,rd_backend=-1,rd_islocaltemp=false,rd_isnailed=false,rd_isvalid=true,rd_indexvalid=0'\000',rd_statvalid=false,rd_createSubid=0,rd_newRelfilenodeSubid=0,rd_rel=0x1e7c1e0,rd_att=0x1e7cb58,rd_id=16995,rd_lockInfo={lockRelId={relId=16995,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=0x1e7aa30,rd_indexlist=0x0,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=0x1de40b0}------------------testdb=#selectoid,relnamefrompg_classwhereoid=16995;oid|relname-------+--------------------16995|t_hash_partition_3(1row)-----------------

该分区是可路由的

(gdb)ppartrel->ri_PartitionReadyForRouting$4=true

设置estate变量(让它看起来像是插入到分区中)/物化tuple

(gdb)n1751estate->es_result_relation_info=partrel;(gdb)1754tuple=ExecMaterializeSlot(slot);(gdb)1760if(mtstate->mt_transition_capture!=NULL)(gdb)ptuple$5=(HeapTuple)0x1e4f4e0(gdb)p*tuple$6={t_len=40,t_self={ip_blkid={bi_hi=65535,bi_lo=65535},ip_posid=0},t_tableOid=0,t_data=0x1e4f4f8}(gdb)(gdb)p*tuple->t_data$7={t_choice={t_heap={t_xmin=160,t_xmax=4294967295,t_field3={t_cid=2249,t_xvac=2249}},t_datum={datum_len_=160,datum_typmod=-1,datum_typeid=2249}},t_ctid={ip_blkid={bi_hi=65535,bi_lo=65535},ip_posid=0},t_infomask2=3,t_infomask=2,t_hoff=24'\030',t_bits=0x1e4f50f""}

mtstate->mt_transition_capture 为NULL,无需处理相关信息

(gdb)pmtstate->mt_transition_capture$8=(structTransitionCaptureState*)0x01783if(mtstate->mt_oc_transition_capture!=NULL)(gdb)

如需要,转换元组

1792ConvertPartitionTupleSlot(proute->parent_child_tupconv_maps[partidx],(gdb)1798Assert(mtstate!=NULL);(gdb)1799node=(ModifyTable*)mtstate->ps.plan;(gdb)p*mtstate$9={ps={type=T_ModifyTableState,plan=0x1e59838,state=0x1e4daf8,ExecProcNode=0x711056<ExecModifyTable>,ExecProcNodeReal=0x711056<ExecModifyTable>,instrument=0x0,worker_instrument=0x0,worker_jit_instrument=0x0,qual=0x0,lefttree=0x0,righttree=0x0,initPlan=0x0,subPlan=0x0,chgParam=0x0,ps_ResultTupleSlot=0x1e4ede8,ps_ExprContext=0x0,ps_ProjInfo=0x0,scandesc=0x0},operation=CMD_INSERT,canSetTag=true,mt_done=false,mt_plans=0x1e4e078,mt_nplans=1,mt_whichplan=0,resultRelInfo=0x1e4dd48,rootResultRelInfo=0x0,mt_arowmarks=0x1e4e098,mt_epqstate={estate=0x0,planstate=0x0,origslot=0x1e4e4e0,plan=0x1e59588,arowMarks=0x0,epqParam=0},fireBSTriggers=false,mt_existing=0x0,mt_excludedtlist=0x0,mt_conflproj=0x0,mt_partition_tuple_routing=0x1e4eb48,mt_transition_capture=0x0,mt_oc_transition_capture=0x0,mt_per_subplan_tupconv_maps=0x0}

返回slot,完成调用

(gdb)n1800if(node->onConflictAction==ONCONFLICT_UPDATE)(gdb)1810returnslot;(gdb)1811}

到此,相信大家对“怎么理解PostgreSQL的分区表”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!