这篇文章主要介绍“PostgreSQL中获取Tuple的分区键值函数是什么”,在日常操作中,相信很多人在PostgreSQL中获取Tuple的分区键值函数是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PostgreSQL中获取Tuple的分区键值函数是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

一、数据结构

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

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

FormPartitionKeyDatum函数获取Tuple的分区键值,返回键值values[]数组和是否为null标记isnull[]数组.

/*----------------*FormPartitionKeyDatum*Constructvalues[]andisnull[]arraysforthepartitionkey*ofatuple.*构造values[]数组和isnull[]数组**pdPartitiondispatchobjectofthepartitionedtable*pd分区表的分区分发器(dispatch)对象**slotHeaptuplefromwhichtoextractpartitionkey*slot从其中提前分区键的heaptuple**estateexecutorstateforevaluatinganypartitionkey*expressions(mustbenon-NULL)*estate解析分区键表达式(必须非NULL)的执行器状态**valuesArrayofpartitionkeyDatums(outputarea)*分区键Datums数组(输出参数)*isnullArrayofis-nullindicators(outputarea)*is-null标记数组(输出参数)**theecxt_scantupleslotofestate'sper-tupleexprcontextmustpointto*theheaptuplepassedin.*estate的per-tuple上下文的ecxt_scantuple必须指向传入的heaptuple*----------------*/staticvoidFormPartitionKeyDatum(PartitionDispatchpd,TupleTableSlot*slot,EState*estate,Datum*values,bool*isnull){ListCell*partexpr_item;inti;if(pd->key->partexprs!=NIL&&pd->keystate==NIL){/*Checkcallerhassetupcontextcorrectly*///检查调用者是否已正确配置内存上下文Assert(estate!=NULL&&GetPerTupleExprContext(estate)->ecxt_scantuple==slot);/*Firsttimethrough,setupexpressionevaluationstate*///第一次进入,配置表达式解析器状态pd->keystate=ExecPrepareExprList(pd->key->partexprs,estate);}partexpr_item=list_head(pd->keystate);//获取分区键表达式状态for(i=0;i<pd->key->partnatts;i++)//循环遍历分区键{AttrNumberkeycol=pd->key->partattrs[i];//分区键属性编号Datumdatum;//typedefuintptr_tDatum;sizeof(Datum)==sizeof(void*)==4or8boolisNull;//是否nullif(keycol!=0)//编号不为0{/*Plaincolumn;getthevaluedirectlyfromtheheaptuple*///扁平列,直接从堆元组中提取值datum=slot_getattr(slot,keycol,&isNull);}else{/*Expression;needtoevaluateit*///表达式,需要解析if(partexpr_item==NULL)//分区键表达式状态为NULL,报错elog(ERROR,"wrongnumberofpartitionkeyexpressions");//获取表达式值datum=ExecEvalExprSwitchContext((ExprState*)lfirst(partexpr_item),GetPerTupleExprContext(estate),&isNull);//切换至下一个partexpr_item=lnext(partexpr_item);}values[i]=datum;//赋值isnull[i]=isNull;}if(partexpr_item!=NULL)//参数设置有误?报错elog(ERROR,"wrongnumberofpartitionkeyexpressions");}/**slot_getattr-fetchoneattributeoftheslot'scontents.*slot_getattr-提取slot中的某个属性值*/staticinlineDatumslot_getattr(TupleTableSlot*slot,intattnum,bool*isnull){AssertArg(attnum>0);if(attnum>slot->tts_nvalid)slot_getsomeattrs(slot,attnum);*isnull=slot->tts_isnull[attnum-1];returnslot->tts_values[attnum-1];}/**Thisfunctionforcestheentriesoftheslot'sDatum/isnullarraystobe*validatleastupthroughtheattnum'thentry.*这个函数强制slot的Datum/isnull数组的条目至少在attnum的第一个条目上是有效的。*/staticinlinevoidslot_getsomeattrs(TupleTableSlot*slot,intattnum){if(slot->tts_nvalid<attnum)slot_getsomeattrs_int(slot,attnum);}/**slot_getsomeattrs_int-workhorseforslot_getsomeattrs()*slot_getsomeattrs_int-slot_getsomeattrs()函数的实际实现*/voidslot_getsomeattrs_int(TupleTableSlot*slot,intattnum){/*Checkforcallererrors*///检查调用者输入参数是否有误Assert(slot->tts_nvalid<attnum);/*slot_getsomeattrchecked*/Assert(attnum>0);//attnum参数判断if(unlikely(attnum>slot->tts_tupleDescriptor->natts))elog(ERROR,"invalidattributenumber%d",attnum);/*Fetchasmanyattributesaspossiblefromtheunderlyingtuple.*///从元组中获取尽可能多的属性。slot->tts_ops->getsomeattrs(slot,attnum);/**Iftheunderlyingtupledoesn'thaveenoughattributes,tupledescriptor*musthavethemissingattributes.*如果底层元组没有足够的属性,那么元组描述符必须具有缺少的属性。*/if(unlikely(slot->tts_nvalid<attnum)){slot_getmissingattrs(slot,slot->tts_nvalid,attnum);slot->tts_nvalid=attnum;}}三、跟踪分析

测试脚本如下

--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(20,'HASH0','HAHS0');

启动gdb,设置断点

(gdb)bFormPartitionKeyDatumBreakpoint5at0x6e30d2:fileexecPartition.c,line1087.(gdb)bslot_getattrBreakpoint6at0x489d9b:fileheaptuple.c,line1510.(gdb)cContinuing.Breakpoint5,FormPartitionKeyDatum(pd=0x2e1bfa0,slot=0x2e1b8a0,estate=0x2e1aeb8,values=0x7fff4e2407a0,isnull=0x7fff4e240780)atexecPartition.c:10871087if(pd->key->partexprs!=NIL&&pd->keystate==NIL)

循环,根据分区键获取相应的键值

1087if(pd->key->partexprs!=NIL&&pd->keystate==NIL)(gdb)n1097partexpr_item=list_head(pd->keystate);(gdb)1098for(i=0;i<pd->key->partnatts;i++)(gdb)1100AttrNumberkeycol=pd->key->partattrs[i];(gdb)1104if(keycol!=0)(gdb)1107datum=slot_getattr(slot,keycol,&isNull);

进入函数slot_getattr

(gdb)stepBreakpoint6,slot_getattr(slot=0x2e1b8a0,attnum=1,isnull=0x7fff4e240735)atheaptuple.c:15101510HeapTupletuple=slot->tts_tuple;

获取结果,分区键值为20

...(gdb)p*isnull$31=false(gdb)pslot->tts_values[attnum-1]$32=20

返回到FormPartitionKeyDatum函数中

(gdb)n1593}(gdb)FormPartitionKeyDatum(pd=0x2e1bfa0,slot=0x2e1b8a0,estate=0x2e1aeb8,values=0x7fff4e2407a0,isnull=0x7fff4e240780)atexecPartition.c:11191119values[i]=datum;

完成调用

1119values[i]=datum;(gdb)n1120isnull[i]=isNull;(gdb)1098for(i=0;i<pd->key->partnatts;i++)(gdb)1123if(partexpr_item!=NULL)(gdb)1125}(gdb)ExecFindPartition(resultRelInfo=0x2e1b108,pd=0x2e1c5b8,slot=0x2e1b8a0,estate=0x2e1aeb8)atexecPartition.c:282282if(partdesc->nparts==0)

到此,关于“PostgreSQL中获取Tuple的分区键值函数是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!