本篇内容主要讲解“PostgreSQL执行查询时获取元组属性值实现方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PostgreSQL执行查询时获取元组属性值实现方法是什么”吧!

测试数据如下:

[local]:5432pg12@testdb=#createtablet_getattrs(idint,col_varcharvarchar(20),col_charchar(10),col_doublefloat,col_numericnumeric);CREATETABLETime:12425.991ms(00:12.426)[local]:5432pg12@testdb=#insertintot_getattrsvalues(1,'test','test',1234.45,12345.77777);INSERT01Time:30.281ms[local]:5432pg12@testdb=#select*fromt_getattrs;一、数据结构

TupleTableSlot

/*----------*Theexecutorstorestuplesina"tupletable"whichisaListof*independentTupleTableSlots.Thereareseveralcasesweneedtohandle:*1.physicaltupleinadiskbufferpage*2.physicaltupleconstructedinpalloc'edmemory*3."minimal"physicaltupleconstructedinpalloc'edmemory*4."virtual"tupleconsistingofDatum/isnullarrays*执行器在"tupletable"中存储元组,这个表是各自独立的TupleTableSlots链表.*有以下情况需要处理:*1.磁盘缓存页中的物理元组*2.在已分配内存中构造的物理元组*3.在已分配内存中构造的"minimal"物理元组*4.含有Datum/isnull数组的"virtual"虚拟元组**Thefirsttwocasesaresimilarinthattheybothdealwith"materialized"*tuples,butresourcemanagementisdifferent.Foratupleinadiskpage*weneedtoholdapinonthebufferuntiltheTupleTableSlot'sreference*tothetupleisdropped;whileforapalloc'dtupleweusuallywantthe*tuplepfree'dwhentheTupleTableSlot'sreferenceisdropped.*最上面2种情况跟"物化"元组的处理方式类似,但资源管理是不同的.*对于在磁盘页中的元组,需要pin在缓存中直至TupleTableSlot依赖的元组被清除,*而对于通过palloc分配的元组在TupleTableSlot依赖被清除后通常希望使用pfree释放**A"minimal"tupleishandledsimilarlytoapalloc'dregulartuple.*Atpresent,minimaltuplesneverarestoredinbuffers,sothereisno*paralleltocase1.Notethataminimaltuplehasno"systemcolumns".*(Actually,itcouldhaveanOID,butwehavenoneedtoaccesstheOID.)*"minimal"元组与通常的palloc分配的元组处理类似.*截止目前为止,"minimal"元组不会存储在缓存中,因此对于第一种情况不会存在并行的问题.*注意"minimal"没有"systemcolumns"系统列*(实际上,可以有OID,但不需要访问OID列)**A"virtual"tupleisanoptimizationusedtominimizephysicaldata*copyinginanestofplannodes.Anypass-by-referenceDatumsinthe*tuplepointtostoragethatisnotdirectlyassociatedwiththe*TupleTableSlot;generallytheywillpointtopartofatuplestoredin*alowerplannode'soutputTupleTableSlot,ortoafunctionresult*constructedinaplannode'sper-tupleecontext.Itistheresponsibility*ofthegeneratingplannodetobesuretheseresourcesarenotreleased*foraslongasthevirtualtupleneedstobevalid.Weonlyusevirtual*tuplesintheresultslotsofplannodes---tuplestobecopiedanywhere*elseneedtobe"materialized"intophysicaltuples.Notealsothata*virtualtupledoesnothaveany"systemcolumns".*"virtual"元组是用于在嵌套计划节点中拷贝时最小化物理数据的优化.*所有通过引用传递指向与TupleTableSlot非直接相关的存储的元组的Datums使用,*通常它们会指向存储在低层节点输出的TupleTableSlot中的元组的一部分,*或者指向在计划节点的per-tuple内存上下文econtext中构造的函数结果.*产生计划节点的时候有责任确保这些资源未被释放,确保virtual元组是有效的.*我们使用计划节点中的结果slots中的虚拟元组---元组会拷贝到其他地方需要"物化"到物理元组中.*注意virtual元组不需要有"systemcolumns"**ItisalsopossibleforaTupleTableSlottoholdbothphysicalandminimal*copiesofatuple.Thisisdonewhentheslotisrequestedtoprovide*theformatotherthantheoneitcurrentlyholds.(Originallyweattempted*tohandlesuchrequestsbyreplacingoneformatwiththeother,butthat*hadthefataldefectofinvalidatinganypass-by-referenceDatumspointing*intotheexistingslotcontents.)Bothcopiesmustcontainidenticaldata*payloadswhenthisisthecase.*TupleTableSlot包含物理和minimal元组拷贝是可能的.*在slot需要提供格式化而不是当前持有的格式时会出现这种情况.*(原始的情况是我们准备通过另外一种格式进行替换来处理这种请求,但在校验引用传递Datums时会出现致命错误)*同时在这种情况下,拷贝必须含有唯一的数据payloads.**TheDatum/isnullarraysofaTupleTableSlotservedoubleduty.Whenthe*slotcontainsavirtualtuple,theyaretheauthoritativedata.Whenthe*slotcontainsaphysicaltuple,thearrayscontaindataextractedfrom*thetuple.(Inthisstate,anypass-by-referenceDatumspointinto*thephysicaltuple.)Theextractedinformationisbuilt"lazily",*ie,onlyasneeded.Thisservestoavoidrepeatedextractionofdata*fromthephysicaltuple.*TupleTableSlot中的Datum/isnull数组有双重职责.*在slot包含虚拟元组时,它们是authoritative(权威)数据.*在slot包含物理元组时,时包含从元组中提取的数据的数组.*(在这种情况下,所有通过引用传递的Datums指向物理元组)*提取的信息通过'lazily'在需要的时候才构建.*这样可以避免从物理元组的重复数据提取.**ATupleTableSlotcanalsobe"empty",holdingnovaliddata.Thisis*theonlyvalidstateforafreshly-createdslotthathasnotyethada*tupledescriptorassignedtoit.Inthisstate,tts_isemptymustbe*true,tts_shouldFreefalse,tts_tupleNULL,tts_bufferInvalidBuffer,*andtts_nvalidzero.*TupleTableSlot可能为"empty",没有有效数据.*对于新鲜创建仍未分配描述的的slot来说这是唯一有效的状态.*在这种状态下,tts_isempty必须为T,tts_shouldFree为F,tts_tuple为NULL,*tts_buffer为InvalidBuffer,tts_nvalid为0.**ThetupleDescriptorissimplyreferenced,notcopied,bytheTupleTableSlot*code.ThecallerofExecSetSlotDescriptor()isresponsibleforproviding*adescriptorthatwillliveaslongastheslotdoes.(Typically,both*slotsanddescriptorsareinper-querymemoryandarefreedbymemory*contextdeallocationatqueryend;soit'snotworthprovidinganyextra*mechanismtodomore.However,theslotwillincrementthetupdesc*referencecountifareference-countedtupdescissupplied.)*tupleDescriptor只是简单的引用并没有通过TupleTableSlot中的代码进行拷贝.*ExecSetSlotDescriptor()的调用者有责任提供与slot生命周期一样的描述符.*(典型的,不管是slots还是描述符会在per-query内存中,*并且会在查询结束时通过内存上下文的析构器释放,因此不需要提供额外的机制来处理.*但是,如果使用了引用计数型tupdesc,slot会增加tupdesc引用计数)**Whentts_shouldFreeistrue,thephysicaltupleis"owned"bytheslot*andshouldbefreedwhentheslot'sreferencetothetupleisdropped.*在tts_shouldFree为T的情况下,物理元组由slot持有,并且在slot引用元组被清除时释放内存.**Iftts_bufferisnotInvalidBuffer,thentheslotisholdingapin*ontheindicatedbufferpage;dropthepinwhenwereleasethe*slot'sreferencetothatbuffer.(tts_shouldFreeshouldalwaysbe*falseinsuchacase,sincepresumablytts_tupleispointingatthe*bufferpage.)*如tts_buffer不是InvalidBuffer,那么slot持有缓存页中的pin,在释放引用该buffer的slot时会清除该pin.*(tts_shouldFree通常来说应为F,因为tts_tuple会指向缓存页)**tts_nvalidindicatesthenumberofvalidcolumnsinthetts_values/isnull*arrays.Whentheslotisholdinga"virtual"tuplethismustbeequal*tothedescriptor'snatts.Whentheslotisholdingaphysicaltuple*thisisequaltothenumberofcolumnswehaveextracted(wealways*extractcolumnsfromlefttoright,sotherearenoholes).*tts_nvalid指示了tts_values/isnull数组中的有效列数.*如果slot含有虚拟元组,该字段必须跟描述符的natts一样.*在slot含有物理元组时,该字段等于我们提取的列数.*(我们通常从左到右提取列,因此不会有空洞存在)**tts_values/tts_isnullareallocatedwhenadescriptorisassignedtothe*slot;theyareoflengthequaltothedescriptor'snatts.*在描述符分配给slot时tts_values/tts_isnull会被分配内存,长度与描述符natts长度一样.**tts_mintuplemustalwaysbeNULLiftheslotdoesnotholda"minimal"*tuple.Whenitdoes,tts_mintuplepointstotheactualMinimalTupleData*object(thethingtobepfree'diftts_shouldFreeMinistrue).Iftheslot*hasonlyaminimalandnotalsoaregularphysicaltuple,thentts_tuple*pointsattts_minhdrandthefieldsofthatstructaresetcorrectly*foraccesstotheminimaltuple;inparticular,tts_minhdr.t_datapoints*MINIMAL_TUPLE_OFFSETbytesbeforetts_mintuple.Thisallowscolumn*extractiontotreatthecaseidenticallytoregularphysicaltuples.*如果slot没有包含minimal元组,tts_mintuple通常必须为NULL.*如含有,则tts_mintuple执行实际的MinimalTupleData对象(如tts_shouldFreeMin为T,则需要通过pfree释放内存).*如果slot只有一个minimal而没有通常的物理元组,那么tts_tuple指向tts_minhdr,*结构体的其他字段会被正确的设置为用于访问minimal元组.*特别的,tts_minhdr.t_data指向tts_mintuple前的MINIMAL_TUPLE_OFFSET字节.*这可以让列提取可以独立处理通常的物理元组.**tts_slow/tts_offaresavedstateforslot_deform_tuple,andshouldnot*betouchedbyanyothercode.*tts_slow/tts_off用于存储slot_deform_tuple状态,不应通过其他代码修改.*----------*/typedefstructTupleTableSlot{NodeTagtype;//Node标记//如slot为空,则为Tbooltts_isempty;/*true=slotisempty*///是否需要pfreetts_tuple?booltts_shouldFree;/*shouldpfreetts_tuple?*///是否需要pfreetts_mintuple?booltts_shouldFreeMin;/*shouldpfreetts_mintuple?*/#defineFIELDNO_TUPLETABLESLOT_SLOW4//为slot_deform_tuple存储状态?booltts_slow;/*savedstateforslot_deform_tuple*/#defineFIELDNO_TUPLETABLESLOT_TUPLE5//物理元组,如为虚拟元组则为NULLHeapTupletts_tuple;/*physicaltuple,orNULLifvirtual*/#defineFIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR6//slot中的元组描述符TupleDesctts_tupleDescriptor;/*slot'stupledescriptor*///slot所在的上下文MemoryContexttts_mcxt;/*slotitselfisinthiscontext*///元组缓存,如无则为InvalidBufferBuffertts_buffer;/*tuple'sbuffer,orInvalidBuffer*/#defineFIELDNO_TUPLETABLESLOT_NVALID9//tts_values中的有效值inttts_nvalid;/*#ofvalidvaluesintts_values*/#defineFIELDNO_TUPLETABLESLOT_VALUES10//当前每个属性的值Datum*tts_values;/*currentper-attributevalues*/#defineFIELDNO_TUPLETABLESLOT_ISNULL11//isnull数组bool*tts_isnull;/*currentper-attributeisnullflags*///minimal元组,如无则为NULLMinimalTupletts_mintuple;/*minimaltuple,orNULLifnone*///在minimal情况下的工作空间HeapTupleDatatts_minhdr;/*workspaceforminimal-tuple-onlycase*/#defineFIELDNO_TUPLETABLESLOT_OFF14//slot_deform_tuple的存储状态uint32tts_off;/*savedstateforslot_deform_tuple*///不能被变更的描述符(固定描述符)booltts_fixedTupleDescriptor;/*descriptorcan'tbechanged*/}TupleTableSlot;/*basetupletableslottype*/typedefstructTupleTableSlot{NodeTagtype;//Node标记#defineFIELDNO_TUPLETABLESLOT_FLAGS1uint16tts_flags;/*布尔状态;Booleanstates*/#defineFIELDNO_TUPLETABLESLOT_NVALID2AttrNumbertts_nvalid;/*在tts_values中有多少有效的values;#ofvalidvaluesintts_values*/constTupleTableSlotOps*consttts_ops;/*slot的实际实现;implementationofslot*/#defineFIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR4TupleDesctts_tupleDescriptor;/*slot的元组描述符;slot'stupledescriptor*/#defineFIELDNO_TUPLETABLESLOT_VALUES5Datum*tts_values;/*当前属性值;currentper-attributevalues*/#defineFIELDNO_TUPLETABLESLOT_ISNULL6bool*tts_isnull;/*当前属性isnull标记;currentper-attributeisnullflags*/MemoryContexttts_mcxt;/*内存上下文;slotitselfisinthiscontext*/}TupleTableSlot;/*routinesforaTupleTableSlotimplementation*///TupleTableSlot的"小程序"structTupleTableSlotOps{/*Minimumsizeoftheslot*///slot的最小化大小size_tbase_slot_size;/*Initialization.*///初始化方法void(*init)(TupleTableSlot*slot);/*Destruction.*///析构方法void(*release)(TupleTableSlot*slot);/**Clearthecontentsoftheslot.Onlythecontentsareexpectedtobe*clearedandnotthetupledescriptor.Typicallyanimplementationof*thiscallbackshouldfreethememoryallocatedforthetuplecontained*intheslot.*清除slot中的内容。*只希望清除内容,而不希望清除元组描述符。*通常,这个回调的实现应该释放为slot中包含的元组分配的内存。*/void(*clear)(TupleTableSlot*slot);/**Fillupfirstnattsentriesoftts_valuesandtts_isnullarrayswith*valuesfromthetuplecontainedintheslot.Thefunctionmaybecalled*withnattsmorethanthenumberofattributesavailableinthetuple,*inwhichcaseitshouldsettts_nvalidtothenumberofreturned*columns.*用slot中包含的元组的值填充tts_values和tts_isnull数组的第一个natts条目。*在调用该函数时,natts可能多于元组中可用属性的数量,在这种情况下,*应该将tts_nvalid设置为返回列的数量。*/void(*getsomeattrs)(TupleTableSlot*slot,intnatts);/**Returnsvalueofthegivensystemattributeasadatumandsetsisnull*tofalse,ifit'snotNULL.Throwsanerroriftheslottypedoesnot*supportsystemattributes.*将给定系统属性的值作为基准返回,如果不为NULL,*则将isnull设置为false。如果slot类型不支持系统属性,则引发错误。*/Datum(*getsysattr)(TupleTableSlot*slot,intattnum,bool*isnull);/**Makethecontentsoftheslotsolelydependontheslot,andnoton*underlyingresources(likeanothermemorycontext,buffers,etc).*使slot的内容完全依赖于slot,而不是底层资源(如另一个内存上下文、缓冲区等)。*/void(*materialize)(TupleTableSlot*slot);/**Copythecontentsofthesourceslotintothedestinationslot'sown*context.Invokedusingcallbackofthedestinationslot.*将源slot的内容复制到目标slot自己的上下文中。*使用目标slot的回调函数调用。*/void(*copyslot)(TupleTableSlot*dstslot,TupleTableSlot*srcslot);/**Returnaheaptuple"owned"bytheslot.Itisslot'sresponsibilityto*freethememoryconsumedbytheheaptuple.Iftheslotcannot"own"a*heaptuple,itshouldnotimplementthiscallbackandshouldsetitas*NULL.*返回slot“拥有”的堆元组。*slot负责释放堆元组分配的内存。*如果slot不能“拥有”堆元组,它不应该实现这个回调函数,应该将它设置为NULL。*/HeapTuple(*get_heap_tuple)(TupleTableSlot*slot);/**Returnaminimaltuple"owned"bytheslot.Itisslot'sresponsibility*tofreethememoryconsumedbytheminimaltuple.Iftheslotcannot*"own"aminimaltuple,itshouldnotimplementthiscallbackandshould*setitasNULL.*返回slot“拥有”的最小元组。*slot负责释放最小元组分配的内存。*如果slot不能“拥有”最小元组,它不应该实现这个回调函数,应该将它设置为NULL。*/MinimalTuple(*get_minimal_tuple)(TupleTableSlot*slot);/**Returnacopyofheaptuplerepresentingthecontentsoftheslot.The*copyneedstobepalloc'dinthecurrentmemorycontext.Theslot*itselfisexpectedtoremainunaffected.Itis*not*expectedtohave*meaningful"systemcolumns"inthecopy.Thecopyisnotbe"owned"by*thesloti.e.thecallerhastotakeresponsibiltytofreememory*consumedbytheslot.*返回表示slot内容的堆元组副本。*需要在当前内存上下文中对副本进行内存分配palloc。*预计slot本身不会受到影响。*它不希望在副本中有有意义的“系统列”。副本不是slot“拥有”的,即调用方必须负责释放slot消耗的内存。*/HeapTuple(*copy_heap_tuple)(TupleTableSlot*slot);/**Returnacopyofminimaltuplerepresentingthecontentsoftheslot.The*copyneedstobepalloc'dinthecurrentmemorycontext.Theslot*itselfisexpectedtoremainunaffected.Itis*not*expectedtohave*meaningful"systemcolumns"inthecopy.Thecopyisnotbe"owned"by*thesloti.e.thecallerhastotakeresponsibiltytofreememory*consumedbytheslot.*返回表示slot内容的最小元组的副本。*需要在当前内存上下文中对副本进行palloc。*预计slot本身不会受到影响。*它不希望在副本中有有意义的“系统列”。副本不是slot“拥有”的,即调用方必须负责释放slot消耗的内存。*/MinimalTuple(*copy_minimal_tuple)(TupleTableSlot*slot);};typedefstructtupleDesc{intnatts;/*tuple中的属性数量;numberofattributesinthetuple*/Oidtdtypeid;/*tuple类型的组合类型ID;compositetypeIDfortupletype*/int32tdtypmod;/*tuple类型的typmode;typmodfortupletype*/inttdrefcount;/*依赖计数,如为-1,则没有依赖;referencecount,or-1ifnotcounting*/TupleConstr*constr;/*约束,如无则为NULL;constraints,orNULLifnone*//*attrs[N]isthedescriptionofAttributeNumberN+1*///attrs[N]是第N+1个属性的描述符FormData_pg_attributeattrs[FLEXIBLE_ARRAY_MEMBER];}*TupleDesc;二、源码解读

staticvoidtts_heap_getsomeattrs(TupleTableSlot*slot,intnatts){HeapTupleTableSlot*hslot=(HeapTupleTableSlot*)slot;Assert(!TTS_EMPTY(slot));slot_deform_heap_tuple(slot,hslot->tuple,&hslot->off,natts);}/**slot_deform_heap_tuple*GivenaTupleTableSlot,extractdatafromtheslot'sphysicaltuple*intoitsDatum/isnullarrays.Dataisextractedupthroughthe*natts'thcolumn(callermustensurethisisalegalcolumnnumber).*给定一个TupleTableSlot,从其中提取数据到Datum/isnull数组中。*数据根据第N个属性列来进行提取。**Thisisessentiallyanincrementalversionofheap_deform_tuple:*oneachcallweextractattributesuptotheoneneeded,without*re-computinginformationaboutpreviouslyextractedattributes.*slot->tts_nvalidisthenumberofattributesalreadyextracted.*slot->tts_nvalid是已完成提取的属性格式。**Thisismarkedasalwaysinline,sothedifferentoffpfordifferenttypes*ofslotsgetsoptimizedaway.*/staticpg_attribute_always_inlinevoidslot_deform_heap_tuple(TupleTableSlot*slot,HeapTupletuple,uint32*offp,intnatts){//元组描述符TupleDesctupleDesc=slot->tts_tupleDescriptor;//列值数组Datum*values=slot->tts_values;//isnull标记数组bool*isnull=slot->tts_isnull;//头部信息HeapTupleHeadertup=tuple->t_data;boolhasnulls=HeapTupleHasNulls(tuple);//属性编号intattnum;//指向元组数据的指针char*tp;/*ptrtotupledata*///偏移uint32off;/*offsetintupledata*///指向tuple中的nullbitmapbits8*bp=tup->t_bits;/*ptrtonullbitmapintuple*///是否可以使用/设置attcacheoffboolslow;/*canweuse/setattcacheoff?*//*Wecanonlyfetchasmanyattributesasthetuplehas.*///只能提取元组中有的属性,获取元组个数natts=Min(HeapTupleHeaderGetNatts(tuple->t_data),natts);/**Checkwhetherthefirstcallforthistuple,andinitializeorrestore*loopstate.*是否第一次调用?*/attnum=slot->tts_nvalid;if(attnum==0){/*Startfromthefirstattribute*///从第一个属性开始off=0;slow=false;}else{/*Restorestatefrompreviousexecution*///从上一次执行中恢复状态off=*offp;slow=TTS_SLOW(slot);}//调整指针位置tp=(char*)tup+tup->t_hoff;for(;attnum<natts;attnum++){//获取列值Form_pg_attributethisatt=TupleDescAttr(tupleDesc,attnum);if(hasnulls&&att_isnull(attnum,bp)){//nullvalues[attnum]=(Datum)0;isnull[attnum]=true;slow=true;/*can'tuseattcacheoffanymore*/continue;}isnull[attnum]=false;if(!slow&&thisatt->attcacheoff>=0)off=thisatt->attcacheoff;elseif(thisatt->attlen==-1){/**Wecanonlycachetheoffsetforavarlenaattributeifthe*offsetisalreadysuitablyaligned,sothattherewouldbeno*padbytesinanycase:thentheoffsetwillbevalidforeither*analignedorunalignedvalue.*varlena:无论是否对齐,偏移都是有效的.*/if(!slow&&off==att_align_nominal(off,thisatt->attalign))thisatt->attcacheoff=off;else{off=att_align_pointer(off,thisatt->attalign,-1,tp+off);slow=true;}}else{/*notvarlena,sosafetouseatt_align_nominal*///非varlena:使用att_align_nominaloff=att_align_nominal(off,thisatt->attalign);if(!slow)thisatt->attcacheoff=off;}//获取列值values[attnum]=fetchatt(thisatt,tp+off);//调整偏移off=att_addlength_pointer(off,thisatt->attlen,tp+off);if(thisatt->attlen<=0)slow=true;/*can'tuseattcacheoffanymore*/}/**Savestatefornextexecution*存储状态*/slot->tts_nvalid=attnum;*offp=off;if(slow)slot->tts_flags|=TTS_FLAG_SLOW;elseslot->tts_flags&=~TTS_FLAG_SLOW;}/*Accessorforthei'thattributeoftupdesc.*/#defineTupleDescAttr(tupdesc,i)(&(tupdesc)->attrs[(i)])#definefetchatt(A,T)fetch_att(T,(A)->attbyval,(A)->attlen)/**Same,butworkfrombyval/lenparametersratherthanForm_pg_attribute.*/#ifSIZEOF_DATUM==8#definefetch_att(T,attbyval,attlen)\(\(attbyval)?\(\(attlen)==(int)sizeof(Datum)?\*((Datum*)(T))\:\(\(attlen)==(int)sizeof(int32)?\Int32GetDatum(*((int32*)(T)))\:\(\(attlen)==(int)sizeof(int16)?\Int16GetDatum(*((int16*)(T)))\:\(\AssertMacro((attlen)==1),\CharGetDatum(*((char*)(T)))\)\)\)\)\:\PointerGetDatum((char*)(T))\)#else/*SIZEOF_DATUM!=8*/#definefetch_att(T,attbyval,attlen)\(\(attbyval)?\(\(attlen)==(int)sizeof(int32)?\Int32GetDatum(*((int32*)(T)))\:\(\(attlen)==(int)sizeof(int16)?\Int16GetDatum(*((int16*)(T)))\:\(\AssertMacro((attlen)==1),\CharGetDatum(*((char*)(T)))\)\)\)\:\PointerGetDatum((char*)(T))\)#endif/*SIZEOF_DATUM==8*//**DatumGetPointer*Returnspointervalueofadatum.*/#defineDatumGetPointer(X)((Pointer)(X))/**PointerGetDatum*Returnsdatumrepresentationforapointer.*/#definePointerGetDatum(X)((Datum)(X))三、跟踪分析

执行SQL:

[local]:5432pg12@testdb=#select*fromt_getattrs;

启动gdb,进入断点

(gdb)bslot_deform_heap_tupleBreakpoint3at0x6fdeac:fileexecTuples.c,line892.(gdb)cContinuing.Breakpoint3,slot_deform_heap_tuple(slot=0x12312a0,tuple=0x1231880,offp=0x12312e8,natts=5)atexecTuples.c:892892TupleDesctupleDesc=slot->tts_tupleDescriptor;(gdb)bt#0slot_deform_heap_tuple(slot=0x12312a0,tuple=0x1231880,offp=0x12312e8,natts=5)atexecTuples.c:892#10x00000000006fd7d6intts_buffer_heap_getsomeattrs(slot=0x12312a0,natts=5)atexecTuples.c:676#20x00000000006ff7a9inslot_getsomeattrs_int(slot=0x12312a0,attnum=5)atexecTuples.c:1877#30x000000000048ed13inslot_getsomeattrs(slot=0x12312a0,attnum=5)at../../../../src/include/executor/tuptable.h:345#40x000000000048ed39inslot_getallattrs(slot=0x12312a0)at../../../../src/include/executor/tuptable.h:357#50x000000000048f88ainprinttup(slot=0x12312a0,self=0x1239a50)atprinttup.c:392#60x00000000006efc3cinExecutePlan(estate=0x1230e38,planstate=0x1231090,use_parallel_mode=false,operation=CMD_SELECT,sendTuples=true,numberTuples=0,direction=ForwardScanDirection,dest=0x1239a50,execute_once=true)atexecMain.c:1685#70x00000000006ed9dfinstandard_ExecutorRun(queryDesc=0x121b978,direction=ForwardScanDirection,count=0,execute_once=true)atexecMain.c:364#80x00000000006ed815inExecutorRun(queryDesc=0x121b978,direction=ForwardScanDirection,count=0,execute_once=true)atexecMain.c:308#90x00000000008f1010inPortalRunSelect(portal=0x11b9c08,forward=true,count=0,dest=0x1239a50)atpquery.c:929#100x00000000008f0caeinPortalRun(portal=0x11b9c08,count=9223372036854775807,isTopLevel=true,run_once=true,dest=0x1239a50,altdest=0x1239a50,completionTag=0x7ffd32962250"")atpquery.c:770#110x00000000008ead35inexec_simple_query(query_string=0x1152d98"select*fromt_getattrs;")atpostgres.c:1215#120x00000000008eefa5inPostgresMain(argc=1,argv=0x117fda8,dbname=0x117fbf0"testdb",username=0x114fab8"pg12")atpostgres.c:4236#130x0000000000845915inBackendRun(port=0x1175bc0)atpostmaster.c:4431#140x00000000008450f3inBackendStartup(port=0x1175bc0)atpostmaster.c:4122---Type<return>tocontinue,orq<return>toquit---#150x000000000084132finServerLoop()atpostmaster.c:1704#160x0000000000840be5inPostmasterMain(argc=1,argv=0x114da70)atpostmaster.c:1377#170x0000000000761469inmain(argc=1,argv=0x114da70)atmain.c:228(gdb)(gdb)

输入参数

(gdb)p*slot-->元组slot$1={type=T_TupleTableSlot,tts_flags=16,tts_nvalid=0,tts_ops=0xc3e780<TTSOpsBufferHeapTuple>,tts_tupleDescriptor=0x7fe1af2fd7a8,tts_values=0x1231310,tts_isnull=0x1231338,tts_mcxt=0x1230d20,tts_tid={ip_blkid={bi_hi=0,bi_lo=0},ip_posid=1},tts_tableOid=131110}(gdb)ptuple$2=(HeapTuple)0x1231880(gdb)p*tuple-->元组$3={t_len=67,t_self={ip_blkid={bi_hi=0,bi_lo=0},ip_posid=1},t_tableOid=131110,t_data=0x7fe18396e438}(gdb)p*offp-->偏移$4=0(gdb)pnatts-->5个属性$5=5(gdb)

初始化相关变量

(gdb)n893Datum*values=slot->tts_values;(gdb)894bool*isnull=slot->tts_isnull;(gdb)895HeapTupleHeadertup=tuple->t_data;(gdb)p*values$6=0(gdb)p*isnull$7=false(gdb)n896boolhasnulls=HeapTupleHasNulls(tuple);(gdb)900bits8*bp=tup->t_bits;/*ptrtonullbitmapintuple*/(gdb)904natts=Min(HeapTupleHeaderGetNatts(tuple->t_data),natts);(gdb)p*bp$8=0'\000'(gdb)n910attnum=slot->tts_nvalid;(gdb)pnatts$9=5(gdb)n911if(attnum==0)(gdb)pattnum$10=0(gdb)

首次执行,设置偏移等信息以及初始化元组数据指针

(gdb)n914off=0;(gdb)915slow=false;(gdb)924tp=(char*)tup+tup->t_hoff;(gdb)ptup->t_hoff$11=24'\030'(gdb)p*tup-->元组头部信息$12={t_choice={t_heap={t_xmin=14764,t_xmax=0,t_field3={t_cid=0,t_xvac=0}},t_datum={datum_len_=14764,datum_typmod=0,datum_typeid=0}},t_ctid={ip_blkid={bi_hi=0,bi_lo=0},ip_posid=1},t_infomask2=5,t_infomask=2306,t_hoff=24'\030',t_bits=0x7fe18396e44f""}(gdb)

开始循环获取每个属性的值

(gdb)n926for(;attnum<natts;attnum++)(gdb)928Form_pg_attributethisatt=TupleDescAttr(tupleDesc,attnum);(gdb)930if(hasnulls&&att_isnull(attnum,bp))

属性信息

(gdb)pthisatt$13=(Form_pg_attribute)0x7fe1af2fd7c0(gdb)p*thisatt$14={attrelid=131110,attname={data="id",'\000'<repeats61times>},atttypid=23,attstattarget=-1,attlen=4,attnum=1,attndims=0,attcacheoff=0,atttypmod=-1,attbyval=true,attstorage=112'p',attalign=105'i',attnotnull=false,atthasdef=false,atthasmissing=false,attidentity=0'\000',attgenerated=0'\000',attisdropped=false,attislocal=true,attinhcount=0,attcollation=0}(gdb)

获取第1个属性值,即id的值。注意:fetchatt执行的逻辑是Int32GetDatum(*((int32 *)(T)))

(gdb)pthisatt->attbyval$18=true(gdb)pthisatt->attlen$19=4(gdb)pSIZEOF_DATUM$24=8(gdb)p(int)sizeof(Datum)$26=8(gdb)p(int)sizeof(int32)$27=4(gdb)(gdb)pInt32GetDatum(*((int32*)(tp+off)))$25=1###(attlen)==(int)sizeof(int32)?\Int32GetDatum(*((int32*)(T)))\###

获取第2个属性值,即col_varchar的值。注意:fetchatt执行的逻辑是PointerGetDatum((char *) (T))

(gdb)n973if(thisatt->attlen<=0)(gdb)926for(;attnum<natts;attnum++)(gdb)928Form_pg_attributethisatt=TupleDescAttr(tupleDesc,attnum);(gdb)930if(hasnulls&&att_isnull(attnum,bp))(gdb)p*thisatt$28={attrelid=131110,attname={data="col_varchar",'\000'<repeats52times>},atttypid=1043,attstattarget=-1,attlen=-1,attnum=2,attndims=0,attcacheoff=4,atttypmod=24,attbyval=false,attstorage=120'x',attalign=105'i',attnotnull=false,atthasdef=false,atthasmissing=false,attidentity=0'\000',attgenerated=0'\000',attisdropped=false,attislocal=true,attinhcount=0,attcollation=100}(gdb)n938isnull[attnum]=false;(gdb)940if(!slow&&thisatt->attcacheoff>=0)(gdb)941off=thisatt->attcacheoff;(gdb)969values[attnum]=fetchatt(thisatt,tp+off);(gdb)poff$29=4(gdb)pPointerGetDatum((char*)(tp+off))$30=140606552073300(gdb)x/5cPointerGetDatum((char*)(tp+off))0x7fe18396e454:11'\v'116't'101'e'115's'116't'(gdb)p(char*)PointerGetDatum((char*)(tp+off))$32=0x7fe18396e454"\vtest\027test"(gdb)

获取第2个属性值,即col_char的值。注意:fetchatt执行的逻辑是PointerGetDatum((char *) (T))

(gdb)n971off=att_addlength_pointer(off,thisatt->attlen,tp+off);(gdb)973if(thisatt->attlen<=0)(gdb)poff$33=9(gdb)pthisatt->attlen$34=-1(gdb)n974slow=true;/*can'tuseattcacheoffanymore*/(gdb)926for(;attnum<natts;attnum++)(gdb)928Form_pg_attributethisatt=TupleDescAttr(tupleDesc,attnum);(gdb)930if(hasnulls&&att_isnull(attnum,bp))(gdb)938isnull[attnum]=false;(gdb)940if(!slow&&thisatt->attcacheoff>=0)(gdb)942elseif(thisatt->attlen==-1)(gdb)950if(!slow&&(gdb)955off=att_align_pointer(off,thisatt->attalign,-1,(gdb)957slow=true;(gdb)poff$35=9(gdb)n969values[attnum]=fetchatt(thisatt,tp+off);(gdb)p*thisatt$36={attrelid=131110,attname={data="col_char",'\000'<repeats55times>},atttypid=1042,attstattarget=-1,attlen=-1,attnum=3,attndims=0,attcacheoff=-1,atttypmod=14,attbyval=false,attstorage=120'x',attalign=105'i',attnotnull=false,atthasdef=false,atthasmissing=false,attidentity=0'\000',attgenerated=0'\000',attisdropped=false,attislocal=true,attinhcount=0,attcollation=100}(gdb)p(char*)PointerGetDatum((char*)(tp+off))$37=0x7fe18396e459"\027test"(gdb)

获取第4个属性值,即col_double的值。注意:fetchatt执行的逻辑是*((Datum *)(T))

(gdb)n971off=att_addlength_pointer(off,thisatt->attlen,tp+off);(gdb)973if(thisatt->attlen<=0)(gdb)poff$38=20(gdb)n974slow=true;/*can'tuseattcacheoffanymore*/(gdb)926for(;attnum<natts;attnum++)(gdb)928Form_pg_attributethisatt=TupleDescAttr(tupleDesc,attnum);(gdb)930if(hasnulls&&att_isnull(attnum,bp))(gdb)938isnull[attnum]=false;(gdb)940if(!slow&&thisatt->attcacheoff>=0)(gdb)942elseif(thisatt->attlen==-1)(gdb)963off=att_align_nominal(off,thisatt->attalign);(gdb)965if(!slow)(gdb)poff$39=24(gdb)n969values[attnum]=fetchatt(thisatt,tp+off);(gdb)p*thisatt$40={attrelid=131110,attname={data="col_double",'\000'<repeats53times>},atttypid=701,attstattarget=-1,attlen=8,attnum=4,attndims=0,attcacheoff=-1,atttypmod=-1,attbyval=true,attstorage=112'p',attalign=100'd',attnotnull=false,atthasdef=false,atthasmissing=false,attidentity=0'\000',attgenerated=0'\000',attisdropped=false,attislocal=true,attinhcount=0,attcollation=0}(gdb)p*((Datum*)(tp+off))$41=4653143983961984205(gdb)p*(double*)((Datum*)(tp+off))$49=1234.45(gdb)

获取第5个属性值,即col_numeric的值。注意:fetchatt执行的逻辑是PointerGetDatum((char *) (T))

(gdb)n971off=att_addlength_pointer(off,thisatt->attlen,tp+off);(gdb)973if(thisatt->attlen<=0)(gdb)poff$50=32(gdb)n926for(;attnum<natts;attnum++)(gdb)928Form_pg_attributethisatt=TupleDescAttr(tupleDesc,attnum);(gdb)930if(hasnulls&&att_isnull(attnum,bp))(gdb)938isnull[attnum]=false;(gdb)940if(!slow&&thisatt->attcacheoff>=0)(gdb)942elseif(thisatt->attlen==-1)(gdb)950if(!slow&&(gdb)955off=att_align_pointer(off,thisatt->attalign,-1,(gdb)957slow=true;(gdb)969values[attnum]=fetchatt(thisatt,tp+off);(gdb)p*thisatt$51={attrelid=131110,attname={data="col_numeric",'\000'<repeats52times>},atttypid=1700,attstattarget=-1,attlen=-1,attnum=5,attndims=0,attcacheoff=-1,atttypmod=-1,attbyval=false,attstorage=109'm',attalign=105'i',attnotnull=false,atthasdef=false,atthasmissing=false,attidentity=0'\000',attgenerated=0'\000',attisdropped=false,attislocal=true,attinhcount=0,attcollation=0}(gdb)pPointerGetDatum((char*)(tp+off))$52=140606552073328(gdb)x/32cPointerGetDatum((char*)(tp+off))0x7fe18396e470:23'\027'-127'\201'-126'\202'1'\001'0'\000'41')'9'\t'97'a'0x7fe18396e478:30'\036'88'X'27'\033'0'\000'0'\000'0'\000'0'\000'0'\000'0x7fe18396e480:0'\000'0'\000'0'\000'0'\000'0'\000'0'\000'0'\000'0'\000'0x7fe18396e488:0'\000'0'\000'0'\000'0'\000'0'\000'0'\000'0'\000'0'\000'(gdb)x/32xPointerGetDatum((char*)(tp+off))0x7fe18396e470:0x170x810x820x010x000x290x090x610x7fe18396e478:0x1e0x580x1b0x000x000x000x000x000x7fe18396e480:0x000x000x000x000x000x000x000x000x7fe18396e488:0x000x000x000x000x000x000x000x00(gdb)

完成函数调用

(gdb)n971off=att_addlength_pointer(off,thisatt->attlen,tp+off);(gdb)pvalues[attnum]$55=140606552073328(gdb)n973if(thisatt->attlen<=0)(gdb)974slow=true;/*can'tuseattcacheoffanymore*/(gdb)926for(;attnum<natts;attnum++)(gdb)980slot->tts_nvalid=attnum;(gdb)981*offp=off;(gdb)982if(slow)(gdb)983slot->tts_flags|=TTS_FLAG_SLOW;(gdb)986}(gdb)tts_buffer_heap_getsomeattrs(slot=0x12312a0,natts=5)atexecTuples.c:677677}(gdb)

到此,相信大家对“PostgreSQL执行查询时获取元组属性值实现方法是什么”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!