这篇文章将为大家详细讲解有关PostgreSQL中prune_append_rel_partitions->get_matching_partitions函数怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

一、数据结构

PartitionScheme
分区方案,根据设计,分区方案只包含分区方法的一般属性(列表与范围、分区列的数量和每个分区列的类型信息),而不包含特定的分区边界信息。

/**Ifmultiplerelationsarepartitionedthesameway,allsuchpartitions*willhaveapointertothesamePartitionScheme.AlistofPartitionScheme*objectsisattachedtothePlannerInfo.Bydesign,thepartitionscheme*incorporatesonlythegeneralpropertiesofthepartitionmethod(LISTvs.*RANGE,numberofpartitioningcolumnsandthetypeinformationforeach)*andnotthespecificbounds.*如果多个关系以相同的方式分区,那么所有这些分区都将具有指向相同PartitionScheme的指针。*PartitionScheme对象的链表附加到PlannerInfo中。*根据设计,分区方案只包含分区方法的一般属性(列表与范围、分区列的数量和每个分区列的类型信息),*而不包含特定的界限。**Westoretheopclass-declaredinputdatatypesinsteadofthepartitionkey*datatypessincetheformerratherthanthelatterareusedtocompare*partitionbounds.Sincepartitionkeydatatypesandtheopclassdeclared*inputdatatypesareexpectedtobebinarycompatible(perResolveOpClass),*bothofthoseshouldhavesamebyvalandlengthproperties.*我们存储opclass-declared的输入数据类型,而不是分区键数据类型,*因为前者用于比较分区边界,而不是后者。*由于分区键数据类型和opclass-declared的输入数据类型预期是二进制兼容的(每个ResolveOpClass),*所以它们应该具有相同的byval和length属性。*/typedefstructPartitionSchemeData{charstrategy;/*分区策略;partitionstrategy*/int16partnatts;/*分区属性个数;numberofpartitionattributes*/Oid*partopfamily;/*操作符族OIDs;OIDsofoperatorfamilies*/Oid*partopcintype;/*opclass声明的输入数据类型的OIDs;OIDsofopclassdeclaredinputdatatypes*/Oid*partcollation;/*分区排序规则OIDs;OIDsofpartitioningcollations*//*Cachedinformationaboutpartitionkeydatatypes.*///缓存有关分区键数据类型的信息。int16*parttyplen;bool*parttypbyval;/*Cachedinformationaboutpartitioncomparisonfunctions.*///缓存有关分区比较函数的信息。FmgrInfo*partsupfunc;}PartitionSchemeData;typedefstructPartitionSchemeData*PartitionScheme;

PartitionPruneXXX
执行Prune期间需要使用的数据结构,包括PartitionPruneStep/PartitionPruneStepOp/PartitionPruneCombineOp/PartitionPruneStepCombine

/**AbstractNodetypeforpartitionpruningsteps(therearenoconcrete*Nodesofthistype).*用于分区修剪步骤pruning的抽象节点类型(没有这种类型的具体节点)。**step_idistheglobalidentifierofthestepwithinitspruningcontext.*step_id是步骤在其修剪pruning上下文中的全局标识符。*/typedefstructPartitionPruneStep{NodeTagtype;intstep_id;}PartitionPruneStep;/**PartitionPruneStepOp-InformationtopruneusingasetofmutuallyAND'd*OpExprclauses*PartitionPruneStepOp-使用一组AND操作的OpExpr条件子句进行修剪prune的信息**ThiscontainsinformationextractedfromuptopartnattsOpExprclauses,*wherepartnattsisthenumberofpartitionkeycolumns.'opstrategy'isthe*strategyoftheoperatorintheclausematchedtothelastpartitionkey.*'exprs'containsexpressionswhichcomprisethelookupkeytobepassedto*thepartitionboundsearchfunction.'cmpfns'containstheOIDsof*comparisonfunctionsusedtocompareaforementionedexpressionswith*partitionbounds.Both'exprs'and'cmpfns'containthesamenumberof*items,uptopartnattsitems.*它包含从partnattsOpExpr子句中提取的信息,*其中partnatts是分区键列的数量。*“opstrategy”是子句中与最后一个分区键匹配的操作符的策略。*'exprs'包含一些表达式,这些表达式包含要传递给分区绑定搜索函数的查找键。*“cmpfns”包含用于比较上述表达式与分区边界的比较函数的OIDs。*“exprs”和“cmpfns”包含相同数量的条目,最多包含partnatts个条目。**Oncewefindtheoffsetofapartitionboundusingthelookupkey,we*determinewhichpartitionstoincludeintheresultbasedonthevalueof*'opstrategy'.Forexample,ifitwereequality,we'dreturnjustthe*partitionthatwouldcontainthatkeyorasetofpartitionsifthekey*didn'tconsistofallpartitioningcolumns.Fornon-equalitystrategies,*we'dneedtoincludeotherpartitionsasappropriate.*一旦我们使用查找键找到分区绑定的偏移量,*我们将根据“opstrategy”的值确定在结果中包含哪些分区。*例如,如果它是相等的,我们只返回包含该键的分区,或者如果该键不包含所有分区列,*则返回一组分区。*对于非等值的情况,需要适当地包括其他分区。**'nullkeys'isthesetcontainingtheoffsetofthepartitionkeys(0to*partnatts-1)thatwerematchedtoanISNULLclause.Thisisonly*consideredforhashpartitioningasweneedtopasswhichkeysarenull*tothehashpartitionboundsearchfunction.Itisneverpossibleto*haveanexpressionbepresentin'exprs'foragivenpartitionkeyand*thecorrespondingbitsetin'nullkeys'.*'nullkeys'是包含与isNULL子句匹配的分区键(0到partnatts-1)偏移量的集合。*这只适用于哈希分区,因为我们需要将哪些键为null传递给哈希分区绑定搜索函数。*对于给定的分区键和“nullkeys”中设置的相应bit,不可能在“exprs”中出现表达式。*/typedefstructPartitionPruneStepOp{PartitionPruneStepstep;StrategyNumberopstrategy;List*exprs;List*cmpfns;Bitmapset*nullkeys;}PartitionPruneStepOp;/**PartitionPruneStepCombine-InformationtopruneusingaBoolExprclause*PartitionPruneStepCombine-使用BoolExpr条件prune的信息**ForBoolExprclauses,wecombinethesetofpartitionsdeterminedforeach*oftheargumentclauses.*对于BoolExpr子句,我们为每个参数子句确定的分区集进行组合。*/typedefenumPartitionPruneCombineOp{PARTPRUNE_COMBINE_UNION,PARTPRUNE_COMBINE_INTERSECT}PartitionPruneCombineOp;typedefstructPartitionPruneStepCombine{PartitionPruneStepstep;PartitionPruneCombineOpcombineOp;List*source_stepids;}PartitionPruneStepCombine;/*TheresultofperformingonePartitionPruneStep*///执行PartitionPruneStep步骤后的结果typedefstructPruneStepResult{/**Theoffsetsofbounds(inatable'sboundinfo)whosepartitionis*selectedbythepruningstep.*被pruning步骤选中的分区边界(在数据表boundinfo中)偏移*/Bitmapset*bound_offsets;boolscan_default;/*是否扫描默认分区?Scanthedefaultpartition?*/boolscan_null;/*是否为NULL值扫描分区?ScanthepartitionforNULLvalues?*/}PruneStepResult;二、源码解读

get_matching_partitions函数确定在分区pruning后仍然"存活"的分区。

/**get_matching_partitions*Determinepartitionsthatsurvivepartitionpruning*确定在分区修剪pruning后仍然存在的分区.**ReturnsaBitmapsetoftheRelOptInfo->part_relsindexesofthesurviving*partitions.*返回pruning后仍存在的分区的RelOptInfo->part_rels索引位图集。*/Bitmapset*get_matching_partitions(PartitionPruneContext*context,List*pruning_steps){Bitmapset*result;intnum_steps=list_length(pruning_steps),i;PruneStepResult**results,*final_result;ListCell*lc;/*Iftherearenopruningstepsthenallpartitionsmatch.*///没有pruning步骤,则视为保留所有分区if(num_steps==0){Assert(context->nparts>0);returnbms_add_range(NULL,0,context->nparts-1);}/**Allocatespaceforindividualpruningstepstostoreitsresult.Each*slotwillholdaPruneStepResultafterperformingagivenpruningstep.*Laterstepsmayusetheresultofoneormoreearliersteps.The*resultofapplyingallpruningstepsisthevaluecontainedintheslot*ofthelastpruningstep.*为单个修剪步骤分配空间来存储结果。*每个slot将持有pruning后,执行一个给定的pruning步骤。*后面的步骤可以使用前面一个或多个步骤的结果。*应用所有步骤的结果是最后一个步骤的slot中包含的值。*/results=(PruneStepResult**)palloc0(num_steps*sizeof(PruneStepResult*));foreach(lc,pruning_steps)//遍历步骤{PartitionPruneStep*step=lfirst(lc);switch(nodeTag(step)){caseT_PartitionPruneStepOp:results[step->step_id]=perform_pruning_base_step(context,(PartitionPruneStepOp*)step);//执行pruning基础步骤break;caseT_PartitionPruneStepCombine:results[step->step_id]=perform_pruning_combine_step(context,(PartitionPruneStepCombine*)step,results);//执行pruning组合步骤break;default:elog(ERROR,"invalidpruningsteptype:%d",(int)nodeTag(step));}}/**Atthispointweknowtheoffsetsofallthedatumswhosecorresponding*partitionsneedtobeintheresult,includingspecialnull-accepting*anddefaultpartitions.Collecttheactualpartitionindexesnow.*到目前为止,我们已经知道结果中需要的相应分区的所有数据的偏移量,*包括特殊的接受null的分区和默认分区。*现在收集实际的分区索引。*/final_result=results[num_steps-1];//最终结果Assert(final_result!=NULL);i=-1;result=NULL;while((i=bms_next_member(final_result->bound_offsets,i))>=0){intpartindex=context->boundinfo->indexes[i];//分区编号/**Inrangeandhashpartitioningcases,someslotsmaycontain-1,*indicatingthatnopartitionhasbeendefinedtoacceptagiven*rangeofdataorforagivenremainder,respectively.Thedefault*partition,ifany,incaseofrangepartitioning,willbeaddedto*theresult,becausethespecifiedrangestillsatisfiesthequery's*conditions.*范围分区和散列分区,一些slot可能包含-1,*这表示没有定义接受给定范围的数据或给定余数的分区。*在范围分区的情况下,默认分区(如果有的话)将被添加到结果中,*因为指定的范围仍然满足查询的条件。*/if(partindex>=0)result=bms_add_member(result,partindex);}/*Addthenulland/ordefaultpartitionifneededandifpresent.*///如果需要,添加NULL和/或默认分区。if(final_result->scan_null){Assert(context->strategy==PARTITION_STRATEGY_LIST);Assert(partition_bound_accepts_nulls(context->boundinfo));result=bms_add_member(result,context->boundinfo->null_index);}if(final_result->scan_default){Assert(context->strategy==PARTITION_STRATEGY_LIST||context->strategy==PARTITION_STRATEGY_RANGE);Assert(partition_bound_has_default(context->boundinfo));result=bms_add_member(result,context->boundinfo->default_index);}returnresult;}/**perform_pruning_base_step*Determinestheindexesofdatumsthatsatisfyconditionsspecifiedin*'opstep'.*确定满足“opstep”中指定条件的数据索引。**Resultalsocontainswhetherspecialnull-acceptingand/ordefault*partitionneedtobescanned.*结果还包含是否需要扫描特殊的可接受null和/或默认分区。*/staticPruneStepResult*perform_pruning_base_step(PartitionPruneContext*context,PartitionPruneStepOp*opstep){ListCell*lc1,*lc2;intkeyno,nvalues;Datumvalues[PARTITION_MAX_KEYS];FmgrInfo*partsupfunc;intstateidx;/**Therebetterbethesamenumberofexpressionsandcomparefunctions.*最好有相同数量的表达式和比较函数。*/Assert(list_length(opstep->exprs)==list_length(opstep->cmpfns));nvalues=0;lc1=list_head(opstep->exprs);lc2=list_head(opstep->cmpfns);/**Generatethepartitionlookupkeythatwillbeusedbyoneofthe*get_matching_*_boundsfunctionscalledbelow.*生成将由下面调用的get_matching_*_bounds函数使用的分区查找键。*/for(keyno=0;keyno<context->partnatts;keyno++){/**Forhashpartitioning,itispossiblethatvaluesofsomekeysare*notprovidedinoperatorclauses,butinsteadtheplannerfound*thattheyappearedinaISNULLclause.*对于哈希分区,操作符子句中可能没有提供某些键的值,*但是计划器发现它们出现在isNULL子句中。*/if(bms_is_member(keyno,opstep->nullkeys))continue;/**Forrangepartitioning,wemustonlyperformpruningwithvalues*foreitherallpartitionkeysoraprefixthereof.*对于范围分区,我们必须只对所有分区键或其前缀执行值修剪pruning。*/if(keyno>nvalues&&context->strategy==PARTITION_STRATEGY_RANGE)break;if(lc1!=NULL)//步骤的条件表达式不为NULL{Expr*expr;Datumdatum;boolisnull;expr=lfirst(lc1);stateidx=PruneCxtStateIdx(context->partnatts,opstep->step.step_id,keyno);if(partkey_datum_from_expr(context,expr,stateidx,&datum,&isnull)){Oidcmpfn;/**Sinceweonlyallowstrictoperatorsinpruningsteps,any*null-valuedcomparisonvaluemustcausethecomparisonto*fail,sothatnopartitionscouldmatch.*由于我们只允许在修剪pruning步骤中使用严格的操作符,*任何空值比较值都必须导致比较失败,这样就没有分区能够匹配。*/if(isnull){PruneStepResult*result;result=(PruneStepResult*)palloc(sizeof(PruneStepResult));result->bound_offsets=NULL;result->scan_default=false;result->scan_null=false;returnresult;}/*Setupthestepcmpfuncsentry,unlesswealreadydid*///配置stepcmpfuncs(步骤比较函数)入口cmpfn=lfirst_oid(lc2);Assert(OidIsValid(cmpfn));if(cmpfn!=context->stepcmpfuncs[stateidx].fn_oid){/**Iftheneededsupportfunctionisthesameonecached*intherelation'spartitionkey,copythecached*FmgrInfo.Otherwise(i.e.,whenwehaveacross-type*comparison),anactuallookupisrequired.*如果所需的支持函数与关系分区键缓存的支持函数相同,*则复制缓存的FmgrInfo。*否则(比如存在一个跨类型比较时),需要实际的查找。*/if(cmpfn==context->partsupfunc[keyno].fn_oid)fmgr_info_copy(&context->stepcmpfuncs[stateidx],&context->partsupfunc[keyno],context->ppccontext);elsefmgr_info_cxt(cmpfn,&context->stepcmpfuncs[stateidx],context->ppccontext);}values[keyno]=datum;nvalues++;}lc1=lnext(lc1);lc2=lnext(lc2);}}/**Pointpartsupfunctotheentryforthe0thkeyofthisstep;the*additionalsupportfunctions,ifany,followconsecutively.*将partsupfunc指向此步骤第0个键的条目;*附加的支持功能(如果有的话)是连续的。*/stateidx=PruneCxtStateIdx(context->partnatts,opstep->step.step_id,0);partsupfunc=&context->stepcmpfuncs[stateidx];switch(context->strategy){casePARTITION_STRATEGY_HASH:returnget_matching_hash_bounds(context,opstep->opstrategy,values,nvalues,partsupfunc,opstep->nullkeys);casePARTITION_STRATEGY_LIST:returnget_matching_list_bounds(context,opstep->opstrategy,values[0],nvalues,&partsupfunc[0],opstep->nullkeys);casePARTITION_STRATEGY_RANGE:returnget_matching_range_bounds(context,opstep->opstrategy,values,nvalues,partsupfunc,opstep->nullkeys);default:elog(ERROR,"unexpectedpartitionstrategy:%d",(int)context->strategy);break;}returnNULL;}/**perform_pruning_combine_step*Determinestheindexesofdatumsobtainedbycombiningthosegiven*bythestepsidentifiedbycstep->source_stepidsusingthespecified*combinationmethod*使用指定的组合方法将cstep->source_stepids标识的步骤组合在一起得到数据索引**Sincecstepmayrefertotheresultofearliersteps,wealsoreceive*step_resultshere.*因为cstep可能引用前面步骤的结果,所以在这里也会收到step_results。*/staticPruneStepResult*perform_pruning_combine_step(PartitionPruneContext*context,PartitionPruneStepCombine*cstep,PruneStepResult**step_results){ListCell*lc1;PruneStepResult*result=NULL;boolfirststep;/**Acombinestepwithoutanysourcestepsisanindicationtonotperform*anypartitionpruning,wejustreturnallpartitions.*如无源步骤,则不执行分区pruning,返回所有分区*/result=(PruneStepResult*)palloc0(sizeof(PruneStepResult));if(list_length(cstep->source_stepids)==0){PartitionBoundInfoboundinfo=context->boundinfo;result->bound_offsets=bms_add_range(NULL,0,boundinfo->ndatums-1);result->scan_default=partition_bound_has_default(boundinfo);result->scan_null=partition_bound_accepts_nulls(boundinfo);returnresult;}switch(cstep->combineOp)//根据组合操作类型确定相应逻辑{casePARTPRUNE_COMBINE_UNION://PARTPRUNE_COMBINE_UNIONforeach(lc1,cstep->source_stepids){intstep_id=lfirst_int(lc1);PruneStepResult*step_result;/**step_results[step_id]mustcontainavalidresult,whichis*confirmedbythefactthatcstep'sstep_idisgreaterthan*step_idandthefactthatresultsoftheindividualsteps*areevaluatedinsequenceoftheirstep_ids.*step_results[step_id]必须包含一个有效的结果,*cstep的step_id大于step_id,并且各个步骤的结果按其step_id的顺序计算,*这一点是可以确认的。*/if(step_id>=cstep->step.step_id)elog(ERROR,"invalidpruningcombinestepargument");step_result=step_results[step_id];Assert(step_result!=NULL);/*Recordanyadditionaldatumindexesfromthisstep*///记录从该步骤产生的偏移索引result->bound_offsets=bms_add_members(result->bound_offsets,step_result->bound_offsets);/*Updatewhethertoscannullanddefaultpartitions.*///更新扫描null/default分区的标记if(!result->scan_null)result->scan_null=step_result->scan_null;if(!result->scan_default)result->scan_default=step_result->scan_default;}break;casePARTPRUNE_COMBINE_INTERSECT://PARTPRUNE_COMBINE_INTERSECTfirststep=true;foreach(lc1,cstep->source_stepids){intstep_id=lfirst_int(lc1);PruneStepResult*step_result;if(step_id>=cstep->step.step_id)elog(ERROR,"invalidpruningcombinestepargument");step_result=step_results[step_id];Assert(step_result!=NULL);if(firststep)//第一个步骤{/*Copystep'sresultthefirsttime.*///第一次,拷贝步骤的结果result->bound_offsets=bms_copy(step_result->bound_offsets);result->scan_null=step_result->scan_null;result->scan_default=step_result->scan_default;firststep=false;}else{/*Recorddatumindexescommontobothsteps*///记录其他步骤产生的索引result->bound_offsets=bms_int_members(result->bound_offsets,step_result->bound_offsets);/*Updatewhethertoscannullanddefaultpartitions.*///更新扫描null/default分区的标记if(result->scan_null)result->scan_null=step_result->scan_null;if(result->scan_default)result->scan_default=step_result->scan_default;}}break;}returnresult;}/**get_matching_hash_bounds*Determineoffsetofthehashboundmatchingthespecifiedvalues,*consideringthatallthenon-nullvaluescomefromclausescontaining*acompatiblehashequalityoperatorandanykeysthatarenullcome*fromanISNULLclause.*考虑到所有非空值都来自包含兼容的哈希相等操作符的子句,*而所有空键都来自ISNULL子句,因此确定匹配指定值的哈希边界的偏移量。**Generallythisfunctionwillreturnasinglematchingboundoffset,*althoughifapartitionhasnotbeensetupforagivenmodulusthenwemay*returnnomatches.Ifthenumberofclausesfounddon'tcovertheentire*partitionkey,thenwe'llneedtoreturnalloffsets.*通常,这个函数会返回一个匹配的边界偏移量,*但是如果没有为给定的模数设置分区,则可能不返回匹配值。*如果找到的子句数量不包含整个分区键,那么我们需要返回所有偏移量。**'opstrategy'ifnon-zeromustbeHTEqualStrategyNumber.*opstrategy-如非0,则为HTEqualStrategyNumber**'values'containsDatumsindexedbythepartitionkeytouseforpruning.*values包含用于分区键执行pruning的数据值**'nvalues',thenumberofDatumsinthe'values'array.*nvaluesvalues数组大小**'partsupfunc'containspartitionhashingfunctionsthatcanproducecorrect*hashforthetypeofthevaluescontainedin'values'.*partsupfunc存储分区hash函数,可以为values产生hash值**'nullkeys'isthesetofpartitionkeysthatarenull.*nullkeys为null的分区键值集合*/staticPruneStepResult*get_matching_hash_bounds(PartitionPruneContext*context,StrategyNumberopstrategy,Datum*values,intnvalues,FmgrInfo*partsupfunc,Bitmapset*nullkeys){PruneStepResult*result=(PruneStepResult*)palloc0(sizeof(PruneStepResult));PartitionBoundInfoboundinfo=context->boundinfo;int*partindices=boundinfo->indexes;intpartnatts=context->partnatts;boolisnull[PARTITION_MAX_KEYS];inti;uint64rowHash;intgreatest_modulus;Assert(context->strategy==PARTITION_STRATEGY_HASH);/**Forhashpartitioningwecanonlyperformpruningbasedonequality*clausestothepartitionkeyorISNULLclauses.Wealsocanonly*pruneifwegotvaluesforallkeys.*对于Hash分区,只能基于等值条件语句或者是ISNULL条件进行pruning.*当然,如果可以拿到所有键的值,也可以执行prune*/if(nvalues+bms_num_members(nullkeys)==partnatts){/**Ifthereareanyvalues,theymusthavecomefromclauses*containinganequalityoperatorcompatiblewithhashpartitioning.*如存在values,那么这些值必须从包含一个与hash分区兼容的等值操作符的条件语句而来*/Assert(opstrategy==HTEqualStrategyNumber||nvalues==0);for(i=0;i<partnatts;i++)isnull[i]=bms_is_member(i,nullkeys);greatest_modulus=get_hash_partition_greatest_modulus(boundinfo);rowHash=compute_partition_hash_value(partnatts,partsupfunc,values,isnull);if(partindices[rowHash%greatest_modulus]>=0)result->bound_offsets=bms_make_singleton(rowHash%greatest_modulus);}else{/*Gettingheremeansatleastonehashpartitionexists.*///程序执行到这里,意味着至少存在一个hash分区Assert(boundinfo->ndatums>0);result->bound_offsets=bms_add_range(NULL,0,boundinfo->ndatums-1);}/**Thereisneitheraspecialhashnullpartitionorthedefaulthash*partition.*要么存在一个特别的hashnull分区,要么是默认的hash分区*/result->scan_null=result->scan_default=false;returnresult;}三、跟踪分析

测试脚本如下

testdb=#explainverboseselect*fromt_hash_partitionwherec1=1ORc1=2;QUERYPLAN-------------------------------------------------------------------------------------Append(cost=0.00..30.53rows=6width=200)->SeqScanonpublic.t_hash_partition_1(cost=0.00..15.25rows=3width=200)Output:t_hash_partition_1.c1,t_hash_partition_1.c2,t_hash_partition_1.c3Filter:((t_hash_partition_1.c1=1)OR(t_hash_partition_1.c1=2))->SeqScanonpublic.t_hash_partition_3(cost=0.00..15.25rows=3width=200)Output:t_hash_partition_3.c1,t_hash_partition_3.c2,t_hash_partition_3.c3Filter:((t_hash_partition_3.c1=1)OR(t_hash_partition_3.c1=2))(7rows)

启动gdb,设置断点

(gdb)bget_matching_partitionsBreakpoint1at0x804d3b:filepartprune.c,line619.(gdb)cContinuing.Breakpoint1,get_matching_partitions(context=0x7fff4a5e3930,pruning_steps=0x14d5300)atpartprune.c:619619intnum_steps=list_length(pruning_steps),(gdb)n626if(num_steps==0)

查看输入参数,pruning_steps是有3个ITEM的链表

(gdb)p*pruning_steps$1={type=T_List,length=3,head=0x14d52d8,tail=0x14d58d0}

pruning_steps的3个ITEM类型分别是PartitionPruneStepOp/PartitionPruneStepOp/PartitionPruneStepCombine
第1和2个ITEM的expr是Const,即常量1和2

(gdb)p*(Node*)pruning_steps->head->data.ptr_value$2={type=T_PartitionPruneStepOp}-->Node类型(gdb)p*(PartitionPruneStepOp*)pruning_steps->head->data.ptr_value$3={step={type=T_PartitionPruneStepOp,step_id=0},opstrategy=1,exprs=0x14d52a0,cmpfns=0x14d5240,nullkeys=0x0}(gdb)set$ppso=(PartitionPruneStepOp*)pruning_steps->head->data.ptr_value(gdb)p*$ppso->exprs$4={type=T_List,length=1,head=0x14d5278,tail=0x14d5278}(gdb)p*(Node*)$ppso->exprs->head->data.ptr_value$5={type=T_Const}(gdb)p*(Const*)$ppso->exprs->head->data.ptr_value$6={xpr={type=T_Const},consttype=23,consttypmod=-1,constcollid=0,constlen=4,constvalue=1,constisnull=false,constbyval=true,location=42}-->第1个步骤的表达式,常量1(gdb)set$ppso=(PartitionPruneStepOp*)pruning_steps->head->next->data.ptr_value(gdb)p*(Const*)$ppso->exprs->head->data.ptr_value$7={xpr={type=T_Const},consttype=23,consttypmod=-1,constcollid=0,constlen=4,constvalue=2,constisnull=false,constbyval=true,location=52}-->第2个步骤的表达式,常量2(gdb)p*(Node*)pruning_steps->head->next->next->data.ptr_value$8={type=T_PartitionPruneStepCombine}(gdb)set$ppsc=(PartitionPruneStepCombine*)pruning_steps->head->next->next->data.ptr_value(gdb)p*$ppsc$9={step={type=T_PartitionPruneStepCombine,step_id=2},combineOp=PARTPRUNE_COMBINE_UNION,source_stepids=0x14d5480}-->第3个步骤,组合操作是PARTPRUNE_COMBINE_UNION

为步骤结果分配内存

(gdb)n640palloc0(num_steps*sizeof(PruneStepResult*));(gdb)pnum_steps$10=3(gdb)n639results=(PruneStepResult**)(gdb)641foreach(lc,pruning_steps)

开始遍历pruning步骤,进入perform_pruning_base_step函数

(gdb)643PartitionPruneStep*step=lfirst(lc);(gdb)645switch(nodeTag(step))(gdb)648results[step->step_id]=(gdb)step649perform_pruning_base_step(context,(gdb)perform_pruning_base_step(context=0x7fff4a5e3930,opstep=0x14d4e98)atpartprune.c:29952995Assert(list_length(opstep->exprs)==list_length(opstep->cmpfns));

perform_pruning_base_step->查看输入参数,比较函数是OID=425的函数

(gdb)p*opstep->cmpfns$12={type=T_OidList,length=1,head=0x14d5218,tail=0x14d5218}(gdb)popstep->cmpfns->head->data.oid_value$16=425(gdb)n2998lc1=list_head(opstep->exprs);(gdb)2999lc2=list_head(opstep->cmpfns);(gdb)

perform_pruning_base_step->遍历分区键

(gdb)3005for(keyno=0;keyno<context->partnatts;keyno++)(gdb)3012if(bms_is_member(keyno,opstep->nullkeys))-->没有null键(gdb)3019if(keyno>nvalues&&context->strategy==PARTITION_STRATEGY_RANGE)-->nvalues为0(gdb)pnvalues$17=0(gdb)n3022if(lc1!=NULL)(gdb)3028expr=lfirst(lc1);-->获取步骤中的表达式,其实是常量1(gdb)3029stateidx=PruneCxtStateIdx(context->partnatts,-->stateidx为0(gdb)p*expr$18={type=T_Const}(gdb)p*(Const*)expr$19={xpr={type=T_Const},consttype=23,consttypmod=-1,constcollid=0,constlen=4,constvalue=1,constisnull=false,constbyval=true,location=42}(gdb)n3031if(partkey_datum_from_expr(context,expr,stateidx,(gdb)pstateidx$20=0(gdb)n3041if(isnull)-->非NULL(gdb)pisnull$21=false

perform_pruning_base_step->获取比较函数进行处理

(gdb)n3054cmpfn=lfirst_oid(lc2);-->OID=425(gdb)3055Assert(OidIsValid(cmpfn));(gdb)3056if(cmpfn!=context->stepcmpfuncs[stateidx].fn_oid)-->fn_oid为0(gdb)3064if(cmpfn==context->partsupfunc[keyno].fn_oid)-->fn_oid为425(gdb)pcontext->stepcmpfuncs[stateidx].fn_oid$22=0(gdb)pcontext->partsupfunc[keyno].fn_oid$23=425(gdb)n3065fmgr_info_copy(&context->stepcmpfuncs[stateidx],(gdb)3066&context->partsupfunc[keyno],(gdb)3065fmgr_info_copy(&context->stepcmpfuncs[stateidx],(gdb)3066&context->partsupfunc[keyno],(gdb)3065fmgr_info_copy(&context->stepcmpfuncs[stateidx],-->拷贝函数(gdb)3073values[keyno]=datum;-->设置值(gdb)pdatum$24=1(gdb)n3074nvalues++;(gdb)3077lc1=lnext(lc1);(gdb)3078lc2=lnext(lc2);(gdb)

perform_pruning_base_step->完成分区键遍历

(gdb)n3005for(keyno=0;keyno<context->partnatts;keyno++)(gdb)3086stateidx=PruneCxtStateIdx(context->partnatts,opstep->step.step_id,0);(gdb)3087partsupfunc=&context->stepcmpfuncs[stateidx];(gdb)3089switch(context->strategy)(gdb)3092returnget_matching_hash_bounds(context,(gdb)

perform_pruning_base_step->进入get_matching_hash_bounds函数

(gdb)3092returnget_matching_hash_bounds(context,(gdb)step3093opstep->opstrategy,(gdb)3092returnget_matching_hash_bounds(context,(gdb)get_matching_hash_bounds(context=0x7fff4a5e3930,opstrategy=1,values=0x7fff4a5e3750,nvalues=1,partsupfunc=0x14d3068,nullkeys=0x0)atpartprune.c:21562156PruneStepResult*result=(PruneStepResult*)palloc0(sizeof(PruneStepResult));(gdb)

get_matching_hash_bounds->变量赋值

(gdb)n2157PartitionBoundInfoboundinfo=context->boundinfo;(gdb)2158int*partindices=boundinfo->indexes;(gdb)2159intpartnatts=context->partnatts;(gdb)2165Assert(context->strategy==PARTITION_STRATEGY_HASH);(gdb)2172if(nvalues+bms_num_members(nullkeys)==partnatts)(gdb)

get_matching_hash_bounds->分区边界信息,共有6个分区,Index分别是0-5

(gdb)pboundinfo$25=(PartitionBoundInfo)0x14d32a0(gdb)p*boundinfo$26={strategy=104'h',ndatums=6,datums=0x14d32f8,kind=0x0,indexes=0x14d2e00,null_index=-1,default_index=-1}(gdb)p*boundinfo->datums$27=(Datum*)0x14d3350(gdb)p**boundinfo->datums$28=6(gdb)p**boundinfo->indexesCannotaccessmemoryataddress0x0(gdb)p*boundinfo->indexes$29=0(gdb)pboundinfo->indexes[0]$30=0(gdb)pboundinfo->indexes[1]$31=1(gdb)pboundinfo->indexes[5]$32=5(gdb)

get_matching_hash_bounds->分区索引和分区键数

(gdb)p*partindices$33=0(gdb)ppartnatts$34=1(gdb)(gdb)pnvalues$35=1(gdb)pbms_num_members(nullkeys)$36=0

get_matching_hash_bounds->遍历分区键,判断值是否落在分区中

(gdb)n2178Assert(opstrategy==HTEqualStrategyNumber||nvalues==0);(gdb)2180for(i=0;i<partnatts;i++)(gdb)2181isnull[i]=bms_is_member(i,nullkeys);(gdb)2180for(i=0;i<partnatts;i++)(gdb)2183greatest_modulus=get_hash_partition_greatest_modulus(boundinfo);(gdb)2184rowHash=compute_partition_hash_value(partnatts,partsupfunc,(gdb)2187if(partindices[rowHash%greatest_modulus]>=0)

get_matching_hash_bounds->

(gdb)pvalues$43=(Datum*)0x7fff4a5e3750(gdb)p*values$44=1-->约束条件(gdb)pisnull[0]$38=false-->不为NULL(gdb)pgreatest_modulus$39=6-->6个分区(gdb)prowHash$40=11274504255086170040-->values算出的Hash值(gdb)prowHash%greatest_modulus$41=2-->所在的分区(gdb)ppartindices[2]$42=2-->存在该分区(gdb)

get_matching_hash_bounds->返回结果(bound_offsets->words=4,即编号2)

(gdb)n2189bms_make_singleton(rowHash%greatest_modulus);(gdb)2188result->bound_offsets=(gdb)2203result->scan_null=result->scan_default=false;(gdb)2205returnresult;(gdb)2206}(gdb)p*result$45={bound_offsets=0x14d59b8,scan_default=false,scan_null=false}(gdb)p*result->bound_offsets$46={nwords=1,words=0x14d59bc}(gdb)p*result->bound_offsets->words$47=4(gdb)

回到get_matching_partitions

(gdb)nperform_pruning_base_step(context=0x7fff4a5e3930,opstep=0x14d4e98)atpartprune.c:31193119}(gdb)get_matching_partitions(context=0x7fff4a5e3930,pruning_steps=0x14d5300)atpartprune.c:648648results[step->step_id]=(gdb)651break;(gdb)641foreach(lc,pruning_steps)(gdb)643PartitionPruneStep*step=lfirst(lc);(gdb)645switch(nodeTag(step))(gdb)p

继续执行步骤,进入perform_pruning_combine_step

654results[step->step_id]=(gdb)655perform_pruning_combine_step(context,(gdb)stepperform_pruning_combine_step(context=0x7fff4a5e3930,cstep=0x14d5898,step_results=0x14d5958)atpartprune.c:31363136PruneStepResult*result=NULL;(gdb)

perform_pruning_combine_step->进入PARTPRUNE_COMBINE_UNION处理逻辑

(gdb)n3143result=(PruneStepResult*)palloc0(sizeof(PruneStepResult));(gdb)3144if(list_length(cstep->source_stepids)==0)(gdb)3154switch(cstep->combineOp)(gdb)3157foreach(lc1,cstep->source_stepids)(gdb)(gdb)p*cstep$49={step={type=T_PartitionPruneStepCombine,step_id=2},combineOp=PARTPRUNE_COMBINE_UNION,source_stepids=0x14d5480}

perform_pruning_combine_step->遍历组合步骤的源步骤 cstep->source_stepids,合并这些步骤的结果

(gdb)n3159intstep_id=lfirst_int(lc1);...(gdb)3174result->bound_offsets=bms_add_members(result->bound_offsets,(gdb)3178if(!result->scan_null)(gdb)3179result->scan_null=step_result->scan_null;(gdb)3180if(!result->scan_default)(gdb)3181result->scan_default=step_result->scan_default;(gdb)3157foreach(lc1,cstep->source_stepids)(gdb)3183break;(gdb)3223returnresult;(gdb)

perform_pruning_combine_step->最终结果

(gdb)p*result$54={bound_offsets=0x14d5a48,scan_default=false,scan_null=false}(gdb)p*result->bound_offsets$55={nwords=1,words=0x14d5a4c}(gdb)p*result->bound_offsets->words$56=5

perform_pruning_combine_step->回到get_matching_partitions

(gdb)n3224}(gdb)get_matching_partitions(context=0x7fff4a5e3930,pruning_steps=0x14d5300)atpartprune.c:654654results[step->step_id]=

完成所有步骤的处理

(gdb)n658break;(gdb)641foreach(lc,pruning_steps)(gdb)n671final_result=results[num_steps-1];(gdb)672Assert(final_result!=NULL);(gdb)

构造结果位图集

...675while((i=bms_next_member(final_result->bound_offsets,i))>=0)(gdb)n677intpartindex=context->boundinfo->indexes[i];(gdb)687if(partindex>=0)(gdb)688result=bms_add_member(result,partindex);(gdb)

完成调用

gdb)675while((i=bms_next_member(final_result->bound_offsets,i))>=0)(gdb)692if(final_result->scan_null)(gdb)698if(final_result->scan_default)(gdb)706returnresult;(gdb)707}(gdb)

关于“PostgreSQL中prune_append_rel_partitions->get_matching_partitions函数怎么用”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。