本篇内容介绍了“PostgreSQL中哪个函数为heap tuple找到合适的分区”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一、数据结构

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;二、源码解读

ExecFindPartition函数在以父节点为根的分区树中为包含在*slot中的元组找到目标分区(叶子分区)

/**ExecFindPartition--Findaleafpartitioninthepartitiontreerooted*atparent,fortheheaptuplecontainedin*slot*ExecFindPartition——在以父节点为根的分区树中为包含在*slot中的堆元组找到目标分区(叶子分区)**estatemustbenon-NULL;we'llneedittocomputeanyexpressionsinthe*partitionkey(s)*estate不能为NULL;需要使用它计算分区键上的表达式**Ifnoleafpartitionisfound,thisroutineerrorsoutwiththeappropriate*errormessage,elseitreturnstheleafpartitionsequencenumber*asanindexintothearrayof(ResultRelInfosof)allleafpartitionsin*thepartitiontree.*如果没有找到目标分区,则此例程将输出适当的错误消息,*否则它将分区树中所有叶子分区的数组(ResultRelInfos)的目标分区序列号作为索引返回。*/intExecFindPartition(ResultRelInfo*resultRelInfo,PartitionDispatch*pd,TupleTableSlot*slot,EState*estate){intresult;//结果索引号Datumvalues[PARTITION_MAX_KEYS];//值类型Datumboolisnull[PARTITION_MAX_KEYS];//是否null?Relationrel;//关系PartitionDispatchdispatch;//ExprContext*ecxt=GetPerTupleExprContext(estate);//表达式上下文TupleTableSlot*ecxt_scantuple_old=ecxt->ecxt_scantuple;//原tupleslotTupleTableSlot*myslot=NULL;//临时变量MemoryContextoldcxt;//原内存上下文HeapTupletuple;//tuple/*useper-tuplecontextheretoavoidleakingmemory*///使用每个元组上下文来避免内存泄漏oldcxt=MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));/**Firstchecktheroottable'spartitionconstraint,ifany.Nopointin*routingthetupleifitdoesn'tbelongintheroottableitself.*首先检查根表的分区约束(如果有的话)。如果元组不属于根表本身,则没有必要路由它。*/if(resultRelInfo->ri_PartitionCheck)ExecPartitionCheck(resultRelInfo,slot,estate,true);/*startwiththerootpartitionedtable*///从root分区表开始tuple=ExecFetchSlotTuple(slot);//获取tupledispatch=pd[0];//rootwhile(true){PartitionDescpartdesc;//分区描述符TupleConversionMap*map=dispatch->tupmap;//转换映射intcur_index=-1;//当前索引rel=dispatch->reldesc;//relationpartdesc=RelationGetPartitionDesc(rel);//获取rel描述符/**Convertthetupletothisparent'slayout,ifdifferentfromthe*currentrelation.*如果元组与当前关系不同,则将tuple转换为parent'slayout。*/myslot=dispatch->tupslot;if(myslot!=NULL&&map!=NULL){tuple=do_convert_tuple(tuple,map);ExecStoreTuple(tuple,myslot,InvalidBuffer,true);slot=myslot;}/**Extractpartitionkeyfromtuple.Expressionevaluationmachinery*thatFormPartitionKeyDatum()invokesexpectsecxt_scantupleto*pointtothecorrecttupleslot.Theslotmighthavechangedfrom*whatwasusedfortheparenttableifthetableofthecurrent*partitioninglevelhasdifferenttupledescriptorfromtheparent.*Soupdateecxt_scantupleaccordingly.*从元组中提取分区键。*FormPartitionKeyDatum()调用的表达式计算机制期望ecxt_scantuple指向正确的元组slot。*如果当前分区级别的表与父表具有不同的元组描述符,那么slot可能已经改变了父表使用的slot。*因此相应地更新ecxt_scantuple。*/ecxt->ecxt_scantuple=slot;FormPartitionKeyDatum(dispatch,slot,estate,values,isnull);/**Nothingforget_partition_for_tuple()todoifthereareno*partitionstobeginwith.*如无分区,则退出(无需调用get_partition_for_tuple)*/if(partdesc->nparts==0){result=-1;break;}//调用get_partition_for_tuplecur_index=get_partition_for_tuple(rel,values,isnull);/**cur_index<0meanswefailedtofindapartitionofthisparent.*cur_index>=0meansweeitherfoundtheleafpartition,orthe*nextparenttofindapartitionof.*cur_index<0表示未能找到该父节点的分区。*cur_index>=0表示要么找到叶子分区,要么找到下一个父分区。*/if(cur_index<0){result=-1;break;//找不到,退出}elseif(dispatch->indexes[cur_index]>=0){result=dispatch->indexes[cur_index];/*success!*/break;//找到了,退出循环}else{/*movedownonelevel*///移到下一层查找dispatch=pd[-dispatch->indexes[cur_index]];/**Releasethededicatedslot,ifitwasused.Createacopyof*thetuplefirst,forthenextiteration.*/if(slot==myslot){tuple=ExecCopySlotTuple(myslot);ExecClearTuple(myslot);}}}/*Releasethetupleinthelowestparent'sdedicatedslot.*///释放位于最低父级的专用的slot相对应的元组。if(slot==myslot)ExecClearTuple(myslot);/*Apartitionwasnotfound.*///找不到partitionif(result<0){char*val_desc;val_desc=ExecBuildSlotPartitionKeyDescription(rel,values,isnull,64);Assert(OidIsValid(RelationGetRelid(rel)));ereport(ERROR,(errcode(ERRCODE_CHECK_VIOLATION),errmsg("nopartitionofrelation\"%s\"foundforrow",RelationGetRelationName(rel)),val_desc?errdetail("Partitionkeyofthefailingrowcontains%s.",val_desc):0));}MemoryContextSwitchTo(oldcxt);ecxt->ecxt_scantuple=ecxt_scantuple_old;returnresult;}/**get_partition_for_tuple*Findspartitionofrelationwhichacceptsthepartitionkeyspecified*invaluesandisnull*get_partition_for_tuple*查找参数为values和isnull中指定分区键的关系分区**Returnvalueisindexofthepartition(>=0and<partdesc->nparts)ifone*foundor-1ifnonefound.*返回值是分区的索引(>=0和<partdesc->nparts),*如果找到一个分区,则返回值;如果没有找到,则返回值为-1。*/staticintget_partition_for_tuple(Relationrelation,Datum*values,bool*isnull){intbound_offset;intpart_index=-1;PartitionKeykey=RelationGetPartitionKey(relation);PartitionDescpartdesc=RelationGetPartitionDesc(relation);PartitionBoundInfoboundinfo=partdesc->boundinfo;/*Routeasappropriatebasedonpartitioningstrategy.*///基于分区的策略进行路由switch(key->strategy){casePARTITION_STRATEGY_HASH://HASH分区{intgreatest_modulus;uint64rowHash;greatest_modulus=get_hash_partition_greatest_modulus(boundinfo);rowHash=compute_partition_hash_value(key->partnatts,key->partsupfunc,values,isnull);part_index=boundinfo->indexes[rowHash%greatest_modulus];}break;casePARTITION_STRATEGY_LIST://列表分区if(isnull[0]){if(partition_bound_accepts_nulls(boundinfo))part_index=boundinfo->null_index;}else{boolequal=false;bound_offset=partition_list_bsearch(key->partsupfunc,key->partcollation,boundinfo,values[0],&equal);if(bound_offset>=0&&equal)part_index=boundinfo->indexes[bound_offset];}break;casePARTITION_STRATEGY_RANGE://范围分区{boolequal=false,range_partkey_has_null=false;inti;/**NorangeincludesNULL,sothiswillbeacceptedbythe*defaultpartitionifthereisone,andotherwiserejected.*任何范围都不包含NULL值,因此默认分区将接受该值(如果存在),否则将拒绝该值。*/for(i=0;i<key->partnatts;i++){if(isnull[i]){range_partkey_has_null=true;break;}}if(!range_partkey_has_null){bound_offset=partition_range_datum_bsearch(key->partsupfunc,key->partcollation,boundinfo,key->partnatts,values,&equal);/**Theboundatbound_offsetislessthanorequaltothe*tuplevalue,sotheboundatoffset+1istheupper*boundofthepartitionwe'relookingfor,ifthere*actuallyexistsone.*bound_offset的边界小于或等于元组值,所以offset+1的边界是我们要找的分区的上界,如存在的话。*/part_index=boundinfo->indexes[bound_offset+1];}}break;default:elog(ERROR,"unexpectedpartitionstrategy:%d",(int)key->strategy);//暂不支持其他分区}/**part_index<0meanswefailedtofindapartitionofthisparent.Use*thedefaultpartition,ifthereisone.*part_index<0表示没有找到这个父节点的分区。如存在分区,则使用默认分区。*/if(part_index<0)part_index=boundinfo->default_index;returnpart_index;}

依赖的函数

/**get_hash_partition_greatest_modulus**Returnsthegreatestmodulusofthehashpartitionbound.Thegreatest*moduluswillbeattheendofthedatumsarraybecausehashpartitionsare*arrangedintheascendingorderoftheirmoduliandremainders.*返回哈希分区边界的最大模。*最大模量将位于datums数组的末尾,因为哈希分区按照它们的模块和余数的升序排列。*/intget_hash_partition_greatest_modulus(PartitionBoundInfobound){Assert(bound&&bound->strategy==PARTITION_STRATEGY_HASH);Assert(bound->datums&&bound->ndatums>0);Assert(DatumGetInt32(bound->datums[bound->ndatums-1][0])>0);returnDatumGetInt32(bound->datums[bound->ndatums-1][0]);}/**compute_partition_hash_value**Computethehashvalueforgivenpartitionkeyvalues.*给定分区键值,计算相应的Hash值*/uint64compute_partition_hash_value(intpartnatts,FmgrInfo*partsupfunc,Datum*values,bool*isnull){inti;uint64rowHash=0;//返回结果Datumseed=UInt64GetDatum(HASH_PARTITION_SEED);for(i=0;i<partnatts;i++){/*Nullsarejustignored*/if(!isnull[i]){//不为NULLDatumhash;Assert(OidIsValid(partsupfunc[i].fn_oid));/**Computehashforeachdatumvaluebycallingrespective*datatype-specifichashfunctionsofeachpartitionkey*attribute.*通过调用每个分区键属性的特定于数据类型的哈希函数,计算每个数据值的哈希值。*/hash=FunctionCall2(&partsupfunc[i],values[i],seed);/*Formasingle64-bithashvalue*///组合成一个单独的64bit哈希值rowHash=hash_combine64(rowHash,DatumGetUInt64(hash));}}returnrowHash;}/**Combinetwo64-bithashvalues,resultinginanotherhashvalue,usingthe*samekindoftechniqueashash_combine().Testingshowsthatthisalso*producesgoodbitmixing.*使用与hash_combine()相同的技术组合两个64位哈希值,生成另一个哈希值。*测试表明,该方法也能产生良好的混合效果。*/staticinlineuint64hash_combine64(uint64a,uint64b){/*0x49a0f4dd15e5a8e3is64bitrandomdata*/a^=b+UINT64CONST(0x49a0f4dd15e5a8e3)+(a<<54)+(a>>7);returna;}//两个参数的函数调用宏定义#defineFunctionCall2(flinfo,arg1,arg2)\FunctionCall2Coll(flinfo,InvalidOid,arg1,arg2)三、跟踪分析

测试脚本如下

--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);insertintot_hash_partition(c1,c2,c3)VALUES(0,'HASH0','HAHS0');

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

(gdb)bExecFindPartitionBreakpoint1at0x6e19e7:fileexecPartition.c,line227.(gdb)cContinuing.Breakpoint1,ExecFindPartition(resultRelInfo=0x14299a8,pd=0x142ae58,slot=0x142a140,estate=0x1429758)atexecPartition.c:227227ExprContext*ecxt=GetPerTupleExprContext(estate);

初始化变量,切换内存上下文

227ExprContext*ecxt=GetPerTupleExprContext(estate);(gdb)n228TupleTableSlot*ecxt_scantuple_old=ecxt->ecxt_scantuple;(gdb)229TupleTableSlot*myslot=NULL;(gdb)234oldcxt=MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));(gdb)pecxt_scantuple_old$1=(TupleTableSlot*)0x0

提取tuple,获取dispatch

(gdb)n244tuple=ExecFetchSlotTuple(slot);(gdb)245dispatch=pd[0];(gdb)n249TupleConversionMap*map=dispatch->tupmap;(gdb)p*tuple$2={t_len=40,t_self={ip_blkid={bi_hi=65535,bi_lo=65535},ip_posid=0},t_tableOid=0,t_data=0x142b158}(gdb)

查看分发器dispatch信息

(gdb)p*dispatch$3={reldesc=0x7fbfa6900950,key=0x1489860,keystate=0x0,partdesc=0x149b130,tupslot=0x0,tupmap=0x0,indexes=0x142ade8}(gdb)p*dispatch->reldesc$4={rd_node={spcNode=1663,dbNode=16402,relNode=16986},rd_smgr=0x0,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=0x7fbfa6900b68,rd_att=0x7fbfa6900c80,rd_id=16986,rd_lockInfo={lockRelId={relId=16986,dbId=16402}},rd_rules=0x0,rd_rulescxt=0x0,trigdesc=0x0,rd_rsdesc=0x0,rd_fkeylist=0x0,rd_fkeyvalid=false,rd_partkeycxt=0x1489710,rd_partkey=0x1489860,rd_pdcxt=0x149afe0,rd_partdesc=0x149b130,rd_partcheck=0x0,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=0x0}----------------------------------------------------------------------------testdb=#selectrelnamefrompg_classwhereoid=16986;relname------------------t_hash_partition-->hash分区表(1row)----------------------------------------------------------------------------(gdb)p*dispatch->key$5={strategy=104'h',partnatts=1,partattrs=0x14898f8,partexprs=0x0,partopfamily=0x1489918,partopcintype=0x1489938,partsupfunc=0x1489958,partcollation=0x14899b0,parttypid=0x14899d0,parttypmod=0x14899f0,parttyplen=0x1489a10,parttypbyval=0x1489a30,parttypalign=0x1489a50"i~\177\177\177\177\177\177\b",parttypcoll=0x1489a70}(gdb)p*dispatch->partdesc$6={nparts=6,oids=0x149b168,boundinfo=0x149b1a0}(gdb)p*dispatch->partdesc->boundinfo$8={strategy=104'h',ndatums=6,datums=0x149b1f8,kind=0x0,indexes=0x149b288,null_index=-1,default_index=-1}(gdb)p*dispatch->partdesc->boundinfo->datums$9=(Datum*)0x149b2c0(gdb)p**dispatch->partdesc->boundinfo->datums$10=6(gdb)p*dispatch->indexes$15=0

分区描述符中的oids(分别对应t_hash_partition_1->6)

(gdb)pdispatch->partdesc->oids[0]$11=16989(gdb)pdispatch->partdesc->oids[1]$12=16992...(gdb)pdispatch->partdesc->oids[5]$13=17004

索引信息

(gdb)pdispatch->indexes[0]$16=0...(gdb)pdispatch->indexes[5]$18=5

设置当前索引(-1),获取relation信息,获取分区描述符

(gdb)n250intcur_index=-1;(gdb)252rel=dispatch->reldesc;(gdb)253partdesc=RelationGetPartitionDesc(rel);(gdb)259myslot=dispatch->tupslot;(gdb)p*partdesc$19={nparts=6,oids=0x149b168,boundinfo=0x149b1a0}(gdb)

myslot为NULL

(gdb)n260if(myslot!=NULL&&map!=NULL)(gdb)pmyslot$20=(TupleTableSlot*)0x0

从元组中提取分区键

(gdb)n275ecxt->ecxt_scantuple=slot;(gdb)276FormPartitionKeyDatum(dispatch,slot,estate,values,isnull);(gdb)282if(partdesc->nparts==0)(gdb)p*partdesc$21={nparts=6,oids=0x149b168,boundinfo=0x149b1a0}(gdb)p*slot$22={type=T_TupleTableSlot,tts_isempty=false,tts_shouldFree=true,tts_shouldFreeMin=false,tts_slow=false,tts_tuple=0x142b140,tts_tupleDescriptor=0x1429f28,tts_mcxt=0x1429640,tts_buffer=0,tts_nvalid=1,tts_values=0x142a1a0,tts_isnull=0x142a1b8,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)pvalues$23={0,7152626,21144656,21144128,7141053,21143088,21144128,16372128,140722434628688,0,0,0,21143872,140722434628736,140461078524324,21141056,21144128,0,21143088,21141056,7152279,0,7421941,21141056,21143088,21614576,140722434628800,7422189,21143872,140722434628839,21143088,21144128}(gdb)pisnull$24={false,91,186,126,252,127,false,false,208,166,71,false,false,false,false,false,2,false<repeats15times>}(gdb)p*estate$25={type=T_EState,es_direction=ForwardScanDirection,es_snapshot=0x1451ee0,es_crosscheck_snapshot=0x0,es_range_table=0x14a71c0,es_plannedstmt=0x14a72b8,es_sourceText=0x13acec8"insertintot_hash_partition(c1,c2,c3)VALUES(0,'HASH0','HAHS0');",es_junkFilter=0x0,es_output_cid=0,es_result_relations=0x14299a8,es_num_result_relations=1,es_result_relation_info=0x14299a8,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=0x142afc0,es_trig_oldtup_slot=0x0,es_trig_newtup_slot=0x0,es_param_list_info=0x0,es_param_exec_vals=0x1429970,es_queryEnv=0x0,es_query_cxt=0x1429640,es_tupleTable=0x142a200,es_rowMarks=0x0,es_processed=0,es_lastoid=0,es_top_eflags=0,es_instrument=0,es_finished=false,es_exprcontexts=0x1429ef0,es_subplanstates=0x0,es_auxmodifytables=0x0,es_per_tuple_exprcontext=0x142b080,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)

进入get_partition_for_tuple函数

(gdb)n288cur_index=get_partition_for_tuple(rel,values,isnull);(gdb)stepget_partition_for_tuple(relation=0x7fbfa6900950,values=0x7ffc7eba5bb0,isnull=0x7ffc7eba5b90)atexecPartition.c:11391139intpart_index=-1;(gdb)

get_partition_for_tuple->获取分区键

1139intpart_index=-1;(gdb)n1140PartitionKeykey=RelationGetPartitionKey(relation);(gdb)1141PartitionDescpartdesc=RelationGetPartitionDesc(relation);(gdb)pkey$26=(PartitionKey)0x1489860(gdb)p*key$27={strategy=104'h',partnatts=1,partattrs=0x14898f8,partexprs=0x0,partopfamily=0x1489918,partopcintype=0x1489938,partsupfunc=0x1489958,partcollation=0x14899b0,parttypid=0x14899d0,parttypmod=0x14899f0,parttyplen=0x1489a10,parttypbyval=0x1489a30,parttypalign=0x1489a50"i~\177\177\177\177\177\177\b",parttypcoll=0x1489a70}

get_partition_for_tuple->获取分区描述符&分区边界信息

(gdb)n1142PartitionBoundInfoboundinfo=partdesc->boundinfo;(gdb)1145switch(key->strategy)(gdb)p*partdesc$28={nparts=6,oids=0x149b168,boundinfo=0x149b1a0}(gdb)p*boundinfo$29={strategy=104'h',ndatums=6,datums=0x149b1f8,kind=0x0,indexes=0x149b288,null_index=-1,default_index=-1}

get_partition_for_tuple->进入Hash分区处理分支

(gdb)n1152greatest_modulus=get_hash_partition_greatest_modulus(boundinfo);(gdb)pkey->strategy$30=104'h'

get_partition_for_tuple->计算模块数&行hash值,获得分区编号(index)

(gdb)n1153rowHash=compute_partition_hash_value(key->partnatts,(gdb)n1157part_index=boundinfo->indexes[rowHash%greatest_modulus];(gdb)1159break;(gdb)ppart_index$31=2(gdb)

get_partition_for_tuple->返回

(gdb)n1228if(part_index<0)(gdb)1231returnpart_index;(gdb)1232}(gdb)ExecFindPartition(resultRelInfo=0x14299a8,pd=0x142ae58,slot=0x142a140,estate=0x1429758)atexecPartition.c:295295if(cur_index<0)(gdb)

已取得分区信息(分区索引编号=2)

(gdb)n300elseif(dispatch->indexes[cur_index]>=0)(gdb)302result=dispatch->indexes[cur_index];(gdb)pdispatch->indexes[cur_index]$32=2(gdb)n304break;(gdb)324if(slot==myslot)(gdb)328if(result<0)(gdb)342MemoryContextSwitchTo(oldcxt);(gdb)343ecxt->ecxt_scantuple=ecxt_scantuple_old;(gdb)345returnresult;(gdb)

完成函数调用

(gdb)n346}(gdb)ExecPrepareTupleRouting(mtstate=0x1429ac0,estate=0x1429758,proute=0x142a7a8,targetRelInfo=0x14299a8,slot=0x142a140)atnodeModifyTable.c:17161716Assert(partidx>=0&&partidx<proute->num_partitions);

“PostgreSQL中哪个函数为heap tuple找到合适的分区”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!