PostgreSQL在查询分区表时如何确定查询的是哪个分区
这篇文章给大家分享的是有关PostgreSQL在查询分区表时如何确定查询的是哪个分区的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。
在规划阶段,函数set_rel_size中,如RTE为分区表(rte->inh=T),则调用set_append_rel_size函数,在set_append_rel_size中通过prune_append_rel_partitions函数获取“仍存活”的分区,下面介绍了prune_append_rel_partitions函数的主逻辑和依赖的函数gen_partprune_steps。
一、数据结构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;二、源码解读
prune_append_rel_partitions函数返回必须扫描以满足rel约束条件baserestrictinfo quals的最小子分区集的RT索引。
/**prune_append_rel_partitions*ReturnsRTindexesoftheminimumsetofchildpartitionswhichmust*bescannedtosatisfyrel'sbaserestrictinfoquals.*返回必须扫描以满足rel约束条件baserestrictinfoquals的最小子分区集的RT索引。**Callersmustensurethat'rel'isapartitionedtable.*调用者必须确保rel是分区表*/Relidsprune_append_rel_partitions(RelOptInfo*rel){Relidsresult;List*clauses=rel->baserestrictinfo;List*pruning_steps;boolcontradictory;PartitionPruneContextcontext;Bitmapset*partindexes;inti;Assert(clauses!=NIL);Assert(rel->part_scheme!=NULL);/*Iftherearenopartitions,returntheemptyset*///如无分区,则返回NULLif(rel->nparts==0)returnNULL;/**Processclauses.Iftheclausesarefoundtobecontradictory,wecan*returntheemptyset.*处理条件子句.*如果发现约束条件相互矛盾,返回NULL。*/pruning_steps=gen_partprune_steps(rel,clauses,&contradictory);if(contradictory)returnNULL;/*SetupPartitionPruneContext*///配置PartitionPruneContext上下文context.strategy=rel->part_scheme->strategy;context.partnatts=rel->part_scheme->partnatts;context.nparts=rel->nparts;context.boundinfo=rel->boundinfo;context.partcollation=rel->part_scheme->partcollation;context.partsupfunc=rel->part_scheme->partsupfunc;context.stepcmpfuncs=(FmgrInfo*)palloc0(sizeof(FmgrInfo)*context.partnatts*list_length(pruning_steps));context.ppccontext=CurrentMemoryContext;/*Thesearenotvalidwhenbeingcalledfromtheplanner*///如从规划器调用,这些状态变量为NULLcontext.planstate=NULL;context.exprstates=NULL;context.exprhasexecparam=NULL;context.evalexecparams=false;/*Actualpruninghappenshere.*///这是实现逻辑partindexes=get_matching_partitions(&context,pruning_steps);/*Addselectedpartitions'RTindexestoresult.*///把选中的分区RT索引放到结果中i=-1;result=NULL;while((i=bms_next_member(partindexes,i))>=0)result=bms_add_member(result,rel->part_rels[i]->relid);returnresult;}/**gen_partprune_steps*Process'clauses'(arel'sbaserestrictinfolistofclauses)andreturn*alistof"partitionpruningsteps"*处理“子句”(rel的baserestrictinfo子句链表)并返回“分区pruning步骤”链表**Iftheclausesintheinputlistarecontradictoryorthereisa*pseudo-constant"false",*contradictoryissettotrueuponreturn.*如果输入链表中的条件子句是互斥矛盾的,或者有一个伪常量“false”,返回时将*contradictory设置为true。*/staticList*gen_partprune_steps(RelOptInfo*rel,List*clauses,bool*contradictory){GeneratePruningStepsContextcontext;context.next_step_id=0;context.steps=NIL;/*Theclauseslistmaybemodifiedbelow,sobettermakeacopy.*///为确保安全,拷贝一份副本clauses=list_copy(clauses);/**Forsub-partitionedtablesthere'sacornercasewhereifthe*sub-partitionedtablesharesanypartitionkeyswithitsparent,then*it'spossiblethatthepartitioninghierarchyallowstheparent*partitiontoonlycontainanarrowerrangeofvaluesthanthe*sub-partitionedtabledoes.Inthiscaseitispossiblethatwe'd*includepartitionsthatcouldnotpossiblyhaveanytuplesmatching*'clauses'.Thepossibilityofsuchapartitionarrangementisperhaps*unlikelyfornon-defaultpartitions,butitmaybemorelikelyinthe*caseofdefaultpartitions,sowe'lladdtheparentpartitiontable's*partitionqualtotheclauselistinthiscaseonly.Thismayresult*inthedefaultpartitionbeingeliminated.*对于子分区表,如果子分区表与其父分区表共享分区键,*那么分区层次结构可能只允许父分区包含比分区表更窄的值范围。*在这种情况下,可能会包含不可能有任何匹配“子句”的元组的分区。*对于非默认分区,这种分区设计的可能性不大,但是对于默认分区,这种可能性更大,*所以只在这种情况下将父分区表的分区条件qual添加到子句链表中。*这可能会导致默认分区被消除。*/if(partition_bound_has_default(rel->boundinfo)&&rel->partition_qual!=NIL){List*partqual=rel->partition_qual;//分区条件链表partqual=(List*)expression_planner((Expr*)partqual);/*FixVarstohavethedesiredvarno*///修正Vars,以使其具备合适的编号varnoif(rel->relid!=1)ChangeVarNodes((Node*)partqual,1,rel->relid,0);clauses=list_concat(clauses,partqual);//添加到条件链表中}/*Downintotherabbit-hole.*///进入到"兔子洞"中(实际生成步骤)gen_partprune_steps_internal(&context,rel,clauses,contradictory);returncontext.steps;}/**gen_partprune_steps_internal*Processes'clauses'togeneratepartitionpruningsteps.*处理条件“子句”以生成分区修剪(pruning)步骤。**FromOpExprclausesthataremutuallyAND'd,wefindcombinationsofthose*thatmatchtothepartitionkeycolumnsandforeverysuchcombination,*weemitaPartitionPruneStepOpcontainingavectorofexpressionswhose*valuesareusedasalookupkeytosearchpartitionsbycomparingthe*valueswithpartitionbounds.Relevantdetailsoftheoperatoranda*vectorof(possiblycross-type)comparisonfunctionsisalsoincludedwith*eachstep.*From中的OpExpr条件是AND,*我们发现那些匹配的组合分区键列,对于每一个这样的组合,*构造PartitionPruneStepOp结构体,*以包含一个向量表达式的值用作查找关键搜索分区比较值和分区范围。*每个步骤还包括操作符的相关细节和(可能是交叉类型的)比较函数的向量。**ForBoolExprclauses,werecursivelygeneratestepsforeachargument,and*returnaPartitionPruneStepCombineoftheirresults.*对于BoolExpr子句,我们递归地为每个参数生成步骤,*并组合他们的结果返回PartitionPruneStepCombine。**Thereturnvalueisalistofthestepsgenerated,whicharealsoaddedto*thecontext'sstepslist.Eachstepisassignedastepidentifier,unique*evenacrossrecursivecalls.*返回值是生成的步骤链表,这些步骤也被添加到上下文的步骤链表中。*每个步骤都被分配一个步骤标识符,即使在递归调用中也是惟一的。**Ifwefindclausesthataremutuallycontradictory,orapseudoconstant*clausethatcontainsfalse,weset*contradictorytotrueandreturnNIL*(thatis,nopruningsteps).Callershouldconsiderallpartitionsas*prunedinthatcase.Otherwise,*contradictoryissettofalse.*如果我们发现相互矛盾的子句,或包含false的伪常量子句,*我们将*contradictory设置为true并返回NIL(即没有修剪步骤).*在这种情况下,调用方应将所有分区视为pruned的。*否则,*contradictory设置为false。**Note:the'clauses'Listmaybemodifiedinsidethisfunction.Callersmay*liketomakeacopyofitbeforepassingthemtothisfunction.*注:“子句”链表可以在此函数中修改。*调用者可能希望在将其传递给此函数之前复制它。*/staticList*gen_partprune_steps_internal(GeneratePruningStepsContext*context,RelOptInfo*rel,List*clauses,bool*contradictory){PartitionSchemepart_scheme=rel->part_scheme;List*keyclauses[PARTITION_MAX_KEYS];Bitmapset*nullkeys=NULL,*notnullkeys=NULL;boolgenerate_opsteps=false;List*result=NIL;ListCell*lc;*contradictory=false;memset(keyclauses,0,sizeof(keyclauses));foreach(lc,clauses){Expr*clause=(Expr*)lfirst(lc);inti;/*LookthroughRestrictInfo,ifany*///RestrictInfo类型if(IsA(clause,RestrictInfo))clause=((RestrictInfo*)clause)->clause;/*Constant-false-or-nulliscontradictory*///False或者是NULL,设置为互斥,返回NILif(IsA(clause,Const)&&(((Const*)clause)->constisnull||!DatumGetBool(((Const*)clause)->constvalue))){*contradictory=true;returnNIL;}/*GettheBoolExpr'soutoftheway.*///Bool表达式if(IsA(clause,BoolExpr)){/**Generatestepsforarguments.*生成步骤**Whilestepsgeneratedfortheargumentsthemselveswillbe*addedtocontext->stepsduringrecursionandwillbeevaluated*independently,collecttheirstepIDstobestoredinthe*combinestepwe'llbecreating.*在递归过程中,为参数本身生成的步骤将被添加到context->steps中,*并将独立地进行解析,同时收集它们的步骤id,以便存储在我们即将创建的组合步骤中。*/if(or_clause((Node*)clause)){//ORList*arg_stepids=NIL;boolall_args_contradictory=true;ListCell*lc1;/**Getpruningstepforeacharg.Ifwegetcontradictoryfor*allargs,itmeanstheORexpressionisfalseasawhole.*对每个参数进行修剪(pruning)。*如果对条件中的所有arg都是互斥的,这意味着OR表达式作为一个整体是错误的。*/foreach(lc1,((BoolExpr*)clause)->args)//遍历条件参数{Expr*arg=lfirst(lc1);boolarg_contradictory;List*argsteps;argsteps=gen_partprune_steps_internal(context,rel,list_make1(arg),&arg_contradictory);if(!arg_contradictory)all_args_contradictory=false;if(argsteps!=NIL){PartitionPruneStep*step;Assert(list_length(argsteps)==1);step=(PartitionPruneStep*)linitial(argsteps);arg_stepids=lappend_int(arg_stepids,step->step_id);}else{/**Nostepseithermeansthatarg_contradictoryis*trueortheargdidn'tcontainaclausematching*thispartitionkey.*如无steps,则意味着要么arg_contradictory为T要么参数没有*包含匹配分区键的条件子句**Incaseofthelatter,wecannotpruneusingsuch*anarg.Toindicatethattothepruningcode,we*mustconstructadummyPartitionPruneStepCombine*whosesource_stepidsissettoanemptyList.*However,ifwecanproveusingconstraintexclusion*thattheclauserefutesthetable'spartition*constraint(ifit'ssub-partitioned),weneednot*botherwiththat.Thatis,weeffectivelyignore*thisORarm.*如果是后者,我们不能使用这样的参数进行修剪prune。*为了向修剪prune代码表明这一点,我们必须构造一个虚构的PartitionPruneStepCombine,*它的source_stepids设置为一个空列表。*但是,如果我们可以使用约束排除证明条件子句与表的分区约束(如果它是子分区的)互斥,*那么就不需要为此费心了。也就是说,实际上忽略了这个OR。**/List*partconstr=rel->partition_qual;PartitionPruneStep*orstep;/*Justignorethisargument.*///忽略该参数if(arg_contradictory)continue;if(partconstr){partconstr=(List*)expression_planner((Expr*)partconstr);if(rel->relid!=1)ChangeVarNodes((Node*)partconstr,1,rel->relid,0);if(predicate_refuted_by(partconstr,list_make1(arg),false))//没有匹配分区键continue;}//构造PARTPRUNE_COMBINE_UNION步骤orstep=gen_prune_step_combine(context,NIL,PARTPRUNE_COMBINE_UNION);//IDarg_stepids=lappend_int(arg_stepids,orstep->step_id);}}//输出参数赋值*contradictory=all_args_contradictory;/*Checkifanycontradictingclauseswerefound*///检查是否互斥,如是则返回NILif(*contradictory)returnNIL;if(arg_stepids!=NIL){PartitionPruneStep*step;//构造stepstep=gen_prune_step_combine(context,arg_stepids,PARTPRUNE_COMBINE_UNION);result=lappend(result,step);}continue;}elseif(and_clause((Node*)clause)){//ANDList*args=((BoolExpr*)clause)->args;//参数链表List*argsteps,*arg_stepids=NIL;ListCell*lc1;/**argsmayitselfcontainclausesofarbitrarytype,sojust*recurseandlatercombinethecomponentpartitionssets*usingacombinestep.*args本身可能包含任意类型的子句,*因此只需递归,然后使用组合步骤来对组件分区集合进行组合。*/argsteps=gen_partprune_steps_internal(context,rel,args,contradictory);if(*contradictory)returnNIL;//互斥,返回NILforeach(lc1,argsteps)//遍历步骤{PartitionPruneStep*step=lfirst(lc1);arg_stepids=lappend_int(arg_stepids,step->step_id);}if(arg_stepids!=NIL)//组合步骤{PartitionPruneStep*step;step=gen_prune_step_combine(context,arg_stepids,PARTPRUNE_COMBINE_INTERSECT);result=lappend(result,step);}continue;}/**Fall-throughforaNOTclause,whichifit'saBooleanclause,*willbehandledinmatch_clause_to_partition_key().We*currentlydon'tperformanypruningformorecomplexNOT*clauses.*NOT子句的Fall-through(如果是Boolean子句)将在match_clause_to_partition_key()中处理。*目前不为更复杂的NOT子句执行任何修剪pruning。*/}/**Mustbeaclauseforwhichwecancheckifoneofitsargsmatches*thepartitionkey.*必须是一个条件子句,我们才可以检查它的一个参数是否与分区键匹配。*/for(i=0;i<part_scheme->partnatts;i++){Expr*partkey=linitial(rel->partexprs[i]);//分区键boolclause_is_not_null=false;PartClauseInfo*pc=NULL;//分区条件信息List*clause_steps=NIL;//尝试将给定的“条件子句”与指定的分区键匹配。switch(match_clause_to_partition_key(rel,context,clause,partkey,i,&clause_is_not_null,&pc,&clause_steps)){//存在匹配项,输出参数为条件casePARTCLAUSE_MATCH_CLAUSE:Assert(pc!=NULL);/**Sinceweonlyallowstrictoperators,checkforany*contradictingISNULL.*因为我们只允许严格的操作符,所以检查任何互斥条件时都是NULL。*/if(bms_is_member(i,nullkeys)){*contradictory=true;returnNIL;}generate_opsteps=true;keyclauses[i]=lappend(keyclauses[i],pc);break;//存在匹配项,匹配的子句是“aisNULL”或“aisNOTNULL”子句casePARTCLAUSE_MATCH_NULLNESS:if(!clause_is_not_null){/*checkforconflictingISNOTNULL*/if(bms_is_member(i,notnullkeys)){*contradictory=true;returnNIL;}nullkeys=bms_add_member(nullkeys,i);}else{/*checkforconflictingISNULL*/if(bms_is_member(i,nullkeys)){*contradictory=true;returnNIL;}notnullkeys=bms_add_member(notnullkeys,i);}break;//存在匹配项,输出参数是步骤casePARTCLAUSE_MATCH_STEPS:Assert(clause_steps!=NIL);result=list_concat(result,clause_steps);break;casePARTCLAUSE_MATCH_CONTRADICT:/*We'venothingmoretodoifacontradictionwasfound.*/*contradictory=true;returnNIL;//不存在匹配项casePARTCLAUSE_NOMATCH:/**Clausedidn'tmatchthiskey,butitmightmatchthe*nextone.*子句与这个键不匹配,但它可能与下一个键匹配。*/continue;//该子句不能用于pruningcasePARTCLAUSE_UNSUPPORTED:/*Thisclausecannotbeusedforpruning.*/break;}/*done;gocheckthenextclause.*///完成一个子句的处理,继续下一个break;}}/*-----------*Nowgeneratesome(more)pruningsteps.Wehavethreestrategies:*现在生成一些(更多)修剪pruning步骤。有三个策略:**1)GeneratepruningstepsbasedonISNULLclauses:*a)Forlistpartitioning,nullpartitionkeyscanonlybefoundin*thedesignatednull-acceptingpartition,soifthereareISNULL*clausescontainingpartitionkeysweshouldgenerateapruning*stepthatgetsridofallpartitionsbutthatone.Wecan*disregardanyOpExprwemayhavefound.*b)Forrangepartitioning,onlythedefaultpartitioncancontain*NULLvalues,sothesamerationaleapplies.*c)Forhashpartitioning,weonlyapplythisstrategyifwehave*ISNULLclausesforallthekeys.Strategy2belowwilltake*careofthecasewheresomekeyshaveOpExprsandothershave*ISNULLclauses.*1)基于ISNULL子句生成修剪步骤:*a)对于列表分区,空分区键只能在指定的接受null的分区中找到,*因此,如果存在包含分区键的null子句,我们应该生成一个删除步骤,*删除除该分区之外的所有分区。*可以忽略我们可能发现的任何OpExpr。*b)对于范围分区,只有默认分区可以包含NULL值,所以应用相同的原理。*c)对于哈希分区,只有在所有键都有ISNULL子句时才应用这种策略。*下面的策略2将处理一些键具有OpExprs而另一些键具有ISNULL子句的情况。**2)Ifnot,generatestepsbasedonOpExprswehave(ifany).*2)如果没有,根据我们拥有的OpExprs生成步骤(如果有)。**3)Ifthisdoesn'tworkeither,wemaybeabletogeneratestepsto*prunejustthenull-acceptingpartition(ifoneexists),ifwehave*ISNOTNULLclausesforallpartitionkeys.*3)如果这两种方法都不起作用,那么如果我们对所有分区键都有ISNOTNULL子句,*我们就可以生成步骤来只删除接受NULL的分区(如果存在的话)。*/if(!bms_is_empty(nullkeys)&&(part_scheme->strategy==PARTITION_STRATEGY_LIST||part_scheme->strategy==PARTITION_STRATEGY_RANGE||(part_scheme->strategy==PARTITION_STRATEGY_HASH&&bms_num_members(nullkeys)==part_scheme->partnatts))){PartitionPruneStep*step;/*Strategy1*///策略1step=gen_prune_step_op(context,InvalidStrategy,false,NIL,NIL,nullkeys);result=lappend(result,step);}elseif(generate_opsteps){PartitionPruneStep*step;/*Strategy2*///策略2step=gen_prune_steps_from_opexps(part_scheme,context,keyclauses,nullkeys);if(step!=NULL)result=lappend(result,step);}elseif(bms_num_members(notnullkeys)==part_scheme->partnatts){PartitionPruneStep*step;/*Strategy3*///策略3step=gen_prune_step_op(context,InvalidStrategy,false,NIL,NIL,NULL);result=lappend(result,step);}/**Finally,resultsfromallentriesappearinginresultshouldbe*combinedusinganINTERSECTcombinestep,ifmorethanone.*最后,如果结果中出现的所有条目的结果多于一个,则应该使用相交组合步骤组合。*/if(list_length(result)>1){List*step_ids=NIL;foreach(lc,result){PartitionPruneStep*step=lfirst(lc);step_ids=lappend_int(step_ids,step->step_id);}if(step_ids!=NIL){PartitionPruneStep*step;step=gen_prune_step_combine(context,step_ids,PARTPRUNE_COMBINE_INTERSECT);result=lappend(result,step);}}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)bprune_append_rel_partitionsBreakpoint1at0x804b07:filepartprune.c,line555.(gdb)cContinuing.Breakpoint1,prune_append_rel_partitions(rel=0x20faba0)atpartprune.c:555555List*clauses=rel->baserestrictinfo;
获取约束条件
(gdb)n562Assert(clauses!=NIL);(gdb)563Assert(rel->part_scheme!=NULL);(gdb)566if(rel->nparts==0)(gdb)573pruning_steps=gen_partprune_steps(rel,clauses,&contradictory);
进入gen_partprune_steps
(gdb)stepgen_partprune_steps(rel=0x20faba0,clauses=0x21c4d20,contradictory=0x7ffe1953a8d7)atpartprune.c:505505context.next_step_id=0;
gen_partprune_steps->判断是否有默认分区(无)
(gdb)n506context.steps=NIL;(gdb)n509clauses=list_copy(clauses);(gdb)524if(partition_bound_has_default(rel->boundinfo)&&(gdb)
gen_partprune_steps_internal->进入gen_partprune_steps_internal
(gdb)stepgen_partprune_steps_internal(context=0x7ffe1953a830,rel=0x20faba0,clauses=0x21c4e00,contradictory=0x7ffe1953a8d7)atpartprune.c:741741PartitionSchemepart_scheme=rel->part_scheme;
gen_partprune_steps_internal->查看分区方案(PartitionScheme)
(gdb)n743Bitmapset*nullkeys=NULL,(gdb)p*part_scheme$1={strategy=104'h',partnatts=1,partopfamily=0x21c3180,partopcintype=0x21c31a0,partcollation=0x21c31c0,parttyplen=0x21c31e0,parttypbyval=0x21c3200,partsupfunc=0x21c3220}(gdb)p*part_scheme->partopfamily$2=1977(gdb)p*part_scheme->partopcintype$3=23(gdb)p*part_scheme->partcollation$4=0(gdb)p*part_scheme->parttyplen$5=4(gdb)p*part_scheme->parttypbyval$6=true(gdb)p*part_scheme->partsupfunc$7={fn_addr=0x4c85e7<hashint4extended>,fn_oid=425,fn_nargs=2,fn_strict=true,fn_retset=false,fn_stats=2'\002',fn_extra=0x0,fn_mcxt=0x20f8db0,fn_expr=0x0}
gen_partprune_steps_internal->SQL查询结果
opfamily->integer_ops,整型操作
testdb=#select*frompg_opfamilywhereoid=1977;opfmethod|opfname|opfnamespace|opfowner-----------+-------------+--------------+----------405|integer_ops|11|10(1row)
gen_partprune_steps_internal->初始化变量
(gdb)n744*notnullkeys=NULL;(gdb)745boolgenerate_opsteps=false;(gdb)746List*result=NIL;(gdb)749*contradictory=false;(gdb)751memset(keyclauses,0,sizeof(keyclauses));(gdb)
gen_partprune_steps_internal->循环处理条件子句
752foreach(lc,clauses)(gdb)n754Expr*clause=(Expr*)lfirst(lc);(gdb)p*clauses$8={type=T_List,length=1,head=0x21c4dd8,tail=0x21c4dd8}(gdb)p*clause$9={type=T_RestrictInfo}(gdb)n759clause=((RestrictInfo*)clause)->clause;(gdb)762if(IsA(clause,Const)&&(gdb)p*clause$10={type=T_BoolExpr}
gen_partprune_steps_internal->布尔表达式,进入相应的处理逻辑
(gdb)n771if(IsA(clause,BoolExpr))(gdb)781if(or_clause((Node*)clause))(gdb)
gen_partprune_steps_internal->OR子句,进入相应的实现逻辑
(gdb)783List*arg_stepids=NIL;(gdb)(gdb)784boolall_args_contradictory=true;(gdb)791foreach(lc1,((BoolExpr*)clause)->args)(gdb)793Expr*arg=lfirst(lc1);(gdb)798gen_partprune_steps_internal(context,rel,(gdb)
gen_partprune_steps_internal->OR子句的相关信息
(gdb)p*((BoolExpr*)clause)->args$3={type=T_List,length=2,head=0x21bf138,tail=0x21bf198}(gdb)p*(OpExpr*)arg$4={xpr={type=T_OpExpr},opno=96,opfuncid=65,opresulttyp
gen_partprune_steps_internal->递归调用gen_partprune_steps_internal,返回argsteps链表
797argsteps=(gdb)n801if(!arg_contradictory)(gdb)802all_args_contradictory=false;(gdb)804if(argsteps!=NIL)(gdb)808Assert(list_length(argsteps)==1);(gdb)pargsteps$6=(List*)0x21c29b0(gdb)p*argsteps$7={type=T_List,length=1,head=0x21c2988,tail=0x21c2988}(gdb)p*(Node*)argsteps->head->data.ptr_value$8={type=T_PartitionPruneStepOp}(gdb)p*(PartitionPruneStepOp*)argsteps->head->data.ptr_value$9={step={type=T_PartitionPruneStepOp,step_id=0},opstrategy=1,exprs=0x21c2830,cmpfns=0x21c27d0,nullkeys=0x0}
gen_partprune_steps_internal->构造step,继续OR子句的下一个条件
(gdb)n809step=(PartitionPruneStep*)linitial(argsteps);(gdb)810arg_stepids=lappend_int(arg_stepids,step->step_id);(gdb)(gdb)791foreach(lc1,((BoolExpr*)clause)->args)
gen_partprune_steps_internal->递归调用gen_partprune_steps_internal,进入递归调用gen_partprune_steps_internal函数
(gdb)stepgen_partprune_steps_internal(context=0x7ffe1953a830,rel=0x20fab08,clauses=0x21c2a70,contradictory=0x7ffe1953a60f)atpartprune.c:741741PartitionSchemepart_scheme=rel->part_scheme;(gdb)...
递归调用gen_partprune_steps_internal->遍历条件
752foreach(lc,clauses)(gdb)754Expr*clause=(Expr*)lfirst(lc);(gdb)758if(IsA(clause,RestrictInfo))(gdb)p*(Expr*)clause$14={type=T_OpExpr}(gdb)p*(OpExpr*)clause$15={xpr={type=T_OpExpr},opno=96,opfuncid=65,opresulttype=16,opretset=false,opcollid=0,inputcollid=0,args=0x21becf8,location=50}(gdb)n762if(IsA(clause,Const)&&(gdb)771if(IsA(clause,BoolExpr))(gdb)918for(i=0;i<part_scheme->partnatts;i++)
递归调用gen_partprune_steps_internal->遍历分区方案
(gdb)920Expr*partkey=linitial(rel->partexprs[i]);(gdb)921boolclause_is_not_null=false;(gdb)p*(Expr*)partkey$16={type=T_Var}(gdb)p*(Var*)partkey$17={xpr={type=T_Var},varno=1,varattno=1,vartype=23,vartypmod=-1,varcollid=0,varlevelsup=0,varnoold=1,varoattno=1,location=-1}
递归调用gen_partprune_steps_internal->尝试将给定的“条件子句”与指定的分区键匹配,match_clause_to_partition_key函数输出结果为PARTCLAUSE_MATCH_CLAUSE(存在匹配项,输出参数为条件)
(gdb)n922PartClauseInfo*pc=NULL;(gdb)923List*clause_steps=NIL;(gdb)925switch(match_clause_to_partition_key(rel,context,(gdb)931Assert(pc!=NULL);(gdb)937if(bms_is_member(i,nullkeys))942generate_opsteps=true;(gdb)943keyclauses[i]=lappend(keyclauses[i],pc);(gdb)944break;(gdb)pkeyclauses[i]$18=(List*)0x21c2b08(gdb)p*keyclauses[i]$19={type=T_List,length=1,head=0x21c2ae0,tail=0x21c2ae0}(gdb)p*(Node*)keyclauses[i]->head->data.ptr_value$20={type=T_Invalid}
递归调用gen_partprune_steps_internal->完成条件遍历,开始生产pruning步骤,使用第2种策略(根据拥有的OpExprs生成步骤)生成
(gdb)n752foreach(lc,clauses)(gdb)n1019if(!bms_is_empty(nullkeys)&&(gdb)1032elseif(generate_opsteps)(gdb)1037step=gen_prune_steps_from_opexps(part_scheme,context,(gdb)n1039if(step!=NULL)(gdb)p*step$21={type=T_PartitionPruneStepOp,step_id=1}(gdb)n1040result=lappend(result,step);(gdb)1056if(list_length(result)>1)(gdb)p*result$22={type=T_List,length=1,head=0x21c2da0,tail=0x21c2da0}(gdb)n1077returnresult;(gdb)1078}(gdb)
gen_partprune_steps_internal->递归调用返回,完成OR子句的处理
(gdb)801if(!arg_contradictory)(gdb)802all_args_contradictory=false;(gdb)804if(argsteps!=NIL)(gdb)808Assert(list_length(argsteps)==1);(gdb)809step=(PartitionPruneStep*)linitial(argsteps);(gdb)810arg_stepids=lappend_int(arg_stepids,step->step_id);(gdb)791foreach(lc1,((BoolExpr*)clause)->args)(gdb)(gdb)855*contradictory=all_args_contradictory;(gdb)858if(*contradictory)(gdb)pall_args_contradictory$23=false(gdb)n861if(arg_stepids!=NIL)(gdb)865step=gen_prune_step_combine(context,arg_stepids,(gdb)867result=lappend(result,step);(gdb)869continue;(gdb)p*step$24={<textvariable,nodebuginfo>}0x7f4522678be0<__step>(gdb)p*result$25={type=T_List,length=1,head=0x21c2e88,tail=0x21c2e88}
gen_partprune_steps_internal->完成所有条件子句的遍历,返回result
(gdb)n752foreach(lc,clauses)(gdb)1019if(!bms_is_empty(nullkeys)&&(gdb)1032elseif(generate_opsteps)(gdb)1042elseif(bms_num_members(notnullkeys)==part_scheme->partnatts)(gdb)1056if(list_length(result)>1)(gdb)1077returnresult;(gdb)1078}(gdb)
gen_partprune_steps->回到gen_partprune_steps,返回steps链表
(gdb)gen_partprune_steps(rel=0x20fab08,clauses=0x21c2390,contradictory=0x7ffe1953a8d7)atpartprune.c:541541returncontext.steps;(gdb)p*result$26=0'\000'(gdb)pcontext.steps$27=(List*)0x21c2890(gdb)p*context.steps$28={type=T_List,length=3,head=0x21c2868,tail=0x21c2e60}$29={type=T_PartitionPruneStepOp}(gdb)p*(PartitionPruneStepOp*)context.steps->head->data.ptr_value$30={step={type=T_PartitionPruneStepOp,step_id=0},opstrategy=1,exprs=0x21c2830,cmpfns=0x21c27d0,nullkeys=0x0}(gdb)p*(PartitionPruneStepOp*)context.steps->head->next->data.ptr_value$31={step={type=T_PartitionPruneStepOp,step_id=1},opstrategy=1,exprs=0x21c2c28,cmpfns=0x21c2bc8,nullkeys=0x0}(gdb)p*(PartitionPruneStepOp*)context.steps->head->next->next->data.ptr_value$32={step={type=T_PartitionPruneStepCombine,step_id=2},opstrategy=0,exprs=0x21c2a10,cmpfns=0x7e,nullkeys=0x10}(gdb)
gen_partprune_steps->回到prune_append_rel_partitions
(gdb)n542}(gdb)prune_append_rel_partitions(rel=0x20fab08)atpartprune.c:574574if(contradictory)(gdb)
prune_append_rel_partitions->设置上下文环境
(gdb)578context.strategy=rel->part_scheme->strategy;(gdb)579context.partnatts=rel->part_scheme->partnatts;...
prune_append_rel_partitions->调用get_matching_partitions,获取匹配的分区编号(Indexes)
结果为5,即数组下标为0和2的Rel(part_rels数组)
597partindexes=get_matching_partitions(&context,pruning_steps);(gdb)600i=-1;(gdb)ppartindexes$33=(Bitmapset*)0x21c2ff8(gdb)p*partindexes$34={nwords=1,words=0x21c2ffc}(gdb)p*partindexes->words$35=5
prune_append_rel_partitions->生成Relids
结果为40,即8+32,即3号和5号Rel
(gdb)n601result=NULL;(gdb)602while((i=bms_next_member(partindexes,i))>=0)(gdb)603result=bms_add_member(result,rel->part_rels[i]->relid);(gdb)pi$39=0(gdb)n602while((i=bms_next_member(partindexes,i))>=0)(gdb)603result=bms_add_member(result,rel->part_rels[i]->relid);(gdb)pi$40=2(gdb)n602while((i=bms_next_member(partindexes,i))>=0)(gdb)605returnresult;(gdb)presult$41=(Relids)0x21c3018(gdb)p*result$42={nwords=1,words=0x21c301c}(gdb)presult->words[0]$43=40
prune_append_rel_partitions->完成调用
606}(gdb)set_append_rel_size(root=0x2120378,rel=0x20fab08,rti=1,rte=0x20fa3d0)atallpaths.c:922922did_pruning=true;(gdb)
感谢各位的阅读!关于“PostgreSQL在查询分区表时如何确定查询的是哪个分区”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。