您好,登錄后才能下訂單哦!
本篇內容主要講解“PostgreSQL執行查詢時獲取元組屬性值實現方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“PostgreSQL執行查詢時獲取元組屬性值實現方法是什么”吧!
測試數據如下:
[local]:5432 pg12@testdb=# create table t_getattrs(id int,col_varchar varchar(20),col_char char(10),col_double float,col_numeric numeric); CREATE TABLE Time: 12425.991 ms (00:12.426) [local]:5432 pg12@testdb=# insert into t_getattrs values(1,'test','test',1234.45,12345.77777); INSERT 0 1 Time: 30.281 ms [local]:5432 pg12@testdb=# select * from t_getattrs;
TupleTableSlot
/*---------- * The executor stores tuples in a "tuple table" which is a List of * independent TupleTableSlots. There are several cases we need to handle: * 1. physical tuple in a disk buffer page * 2. physical tuple constructed in palloc'ed memory * 3. "minimal" physical tuple constructed in palloc'ed memory * 4. "virtual" tuple consisting of Datum/isnull arrays * 執行器在"tuple table"中存儲元組,這個表是各自獨立的TupleTableSlots鏈表. * 有以下情況需要處理: * 1. 磁盤緩存頁中的物理元組 * 2. 在已分配內存中構造的物理元組 * 3. 在已分配內存中構造的"minimal"物理元組 * 4. 含有Datum/isnull數組的"virtual"虛擬元組 * * The first two cases are similar in that they both deal with "materialized" * tuples, but resource management is different. For a tuple in a disk page * we need to hold a pin on the buffer until the TupleTableSlot's reference * to the tuple is dropped; while for a palloc'd tuple we usually want the * tuple pfree'd when the TupleTableSlot's reference is dropped. * 最上面2種情況跟"物化"元組的處理方式類似,但資源管理是不同的. * 對于在磁盤頁中的元組,需要pin在緩存中直至TupleTableSlot依賴的元組被清除, * 而對于通過palloc分配的元組在TupleTableSlot依賴被清除后通常希望使用pfree釋放 * * A "minimal" tuple is handled similarly to a palloc'd regular tuple. * At present, minimal tuples never are stored in buffers, so there is no * parallel to case 1. Note that a minimal tuple has no "system columns". * (Actually, it could have an OID, but we have no need to access the OID.) * "minimal"元組與通常的palloc分配的元組處理類似. * 截止目前為止,"minimal"元組不會存儲在緩存中,因此對于第一種情況不會存在并行的問題. * 注意"minimal"沒有"system columns"系統列 * (實際上,可以有OID,但不需要訪問OID列) * * A "virtual" tuple is an optimization used to minimize physical data * copying in a nest of plan nodes. Any pass-by-reference Datums in the * tuple point to storage that is not directly associated with the * TupleTableSlot; generally they will point to part of a tuple stored in * a lower plan node's output TupleTableSlot, or to a function result * constructed in a plan node's per-tuple econtext. It is the responsibility * of the generating plan node to be sure these resources are not released * for as long as the virtual tuple needs to be valid. We only use virtual * tuples in the result slots of plan nodes --- tuples to be copied anywhere * else need to be "materialized" into physical tuples. Note also that a * virtual tuple does not have any "system columns". * "virtual"元組是用于在嵌套計劃節點中拷貝時最小化物理數據的優化. * 所有通過引用傳遞指向與TupleTableSlot非直接相關的存儲的元組的Datums使用, * 通常它們會指向存儲在低層節點輸出的TupleTableSlot中的元組的一部分, * 或者指向在計劃節點的per-tuple內存上下文econtext中構造的函數結果. * 產生計劃節點的時候有責任確保這些資源未被釋放,確保virtual元組是有效的. * 我們使用計劃節點中的結果slots中的虛擬元組 --- 元組會拷貝到其他地方需要"物化"到物理元組中. * 注意virtual元組不需要有"system columns" * * It is also possible for a TupleTableSlot to hold both physical and minimal * copies of a tuple. This is done when the slot is requested to provide * the format other than the one it currently holds. (Originally we attempted * to handle such requests by replacing one format with the other, but that * had the fatal defect of invalidating any pass-by-reference Datums pointing * into the existing slot contents.) Both copies must contain identical data * payloads when this is the case. * TupleTableSlot包含物理和minimal元組拷貝是可能的. * 在slot需要提供格式化而不是當前持有的格式時會出現這種情況. * (原始的情況是我們準備通過另外一種格式進行替換來處理這種請求,但在校驗引用傳遞Datums時會出現致命錯誤) * 同時在這種情況下,拷貝必須含有唯一的數據payloads. * * The Datum/isnull arrays of a TupleTableSlot serve double duty. When the * slot contains a virtual tuple, they are the authoritative data. When the * slot contains a physical tuple, the arrays contain data extracted from * the tuple. (In this state, any pass-by-reference Datums point into * the physical tuple.) The extracted information is built "lazily", * ie, only as needed. This serves to avoid repeated extraction of data * from the physical tuple. * TupleTableSlot中的Datum/isnull數組有雙重職責. * 在slot包含虛擬元組時,它們是authoritative(權威)數據. * 在slot包含物理元組時,時包含從元組中提取的數據的數組. * (在這種情況下,所有通過引用傳遞的Datums指向物理元組) * 提取的信息通過'lazily'在需要的時候才構建. * 這樣可以避免從物理元組的重復數據提取. * * A TupleTableSlot can also be "empty", holding no valid data. This is * the only valid state for a freshly-created slot that has not yet had a * tuple descriptor assigned to it. In this state, tts_isempty must be * true, tts_shouldFree false, tts_tuple NULL, tts_buffer InvalidBuffer, * and tts_nvalid zero. * TupleTableSlot可能為"empty",沒有有效數據. * 對于新鮮創建仍未分配描述的的slot來說這是唯一有效的狀態. * 在這種狀態下,tts_isempty必須為T,tts_shouldFree為F, tts_tuple為NULL, * tts_buffer為InvalidBuffer,tts_nvalid為0. * * The tupleDescriptor is simply referenced, not copied, by the TupleTableSlot * code. The caller of ExecSetSlotDescriptor() is responsible for providing * a descriptor that will live as long as the slot does. (Typically, both * slots and descriptors are in per-query memory and are freed by memory * context deallocation at query end; so it's not worth providing any extra * mechanism to do more. However, the slot will increment the tupdesc * reference count if a reference-counted tupdesc is supplied.) * tupleDescriptor只是簡單的引用并沒有通過TupleTableSlot中的代碼進行拷貝. * ExecSetSlotDescriptor()的調用者有責任提供與slot生命周期一樣的描述符. * (典型的,不管是slots還是描述符會在per-query內存中, * 并且會在查詢結束時通過內存上下文的析構器釋放,因此不需要提供額外的機制來處理. * 但是,如果使用了引用計數型tupdesc,slot會增加tupdesc引用計數) * * When tts_shouldFree is true, the physical tuple is "owned" by the slot * and should be freed when the slot's reference to the tuple is dropped. * 在tts_shouldFree為T的情況下,物理元組由slot持有,并且在slot引用元組被清除時釋放內存. * * If tts_buffer is not InvalidBuffer, then the slot is holding a pin * on the indicated buffer page; drop the pin when we release the * slot's reference to that buffer. (tts_shouldFree should always be * false in such a case, since presumably tts_tuple is pointing at the * buffer page.) * 如tts_buffer不是InvalidBuffer,那么slot持有緩存頁中的pin,在釋放引用該buffer的slot時會清除該pin. * (tts_shouldFree通常來說應為F,因為tts_tuple會指向緩存頁) * * tts_nvalid indicates the number of valid columns in the tts_values/isnull * arrays. When the slot is holding a "virtual" tuple this must be equal * to the descriptor's natts. When the slot is holding a physical tuple * this is equal to the number of columns we have extracted (we always * extract columns from left to right, so there are no holes). * tts_nvalid指示了tts_values/isnull數組中的有效列數. * 如果slot含有虛擬元組,該字段必須跟描述符的natts一樣. * 在slot含有物理元組時,該字段等于我們提取的列數. * (我們通常從左到右提取列,因此不會有空洞存在) * * tts_values/tts_isnull are allocated when a descriptor is assigned to the * slot; they are of length equal to the descriptor's natts. * 在描述符分配給slot時tts_values/tts_isnull會被分配內存,長度與描述符natts長度一樣. * * tts_mintuple must always be NULL if the slot does not hold a "minimal" * tuple. When it does, tts_mintuple points to the actual MinimalTupleData * object (the thing to be pfree'd if tts_shouldFreeMin is true). If the slot * has only a minimal and not also a regular physical tuple, then tts_tuple * points at tts_minhdr and the fields of that struct are set correctly * for access to the minimal tuple; in particular, tts_minhdr.t_data points * MINIMAL_TUPLE_OFFSET bytes before tts_mintuple. This allows column * extraction to treat the case identically to regular physical tuples. * 如果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_off are saved state for slot_deform_tuple, and should not * be touched by any other code. * tts_slow/tts_off用于存儲slot_deform_tuple狀態,不應通過其他代碼修改. *---------- */ typedef struct TupleTableSlot { NodeTag type;//Node標記 //如slot為空,則為T bool tts_isempty; /* true = slot is empty */ //是否需要pfree tts_tuple? bool tts_shouldFree; /* should pfree tts_tuple? */ //是否需要pfree tts_mintuple? bool tts_shouldFreeMin; /* should pfree tts_mintuple? */ #define FIELDNO_TUPLETABLESLOT_SLOW 4 //為slot_deform_tuple存儲狀態? bool tts_slow; /* saved state for slot_deform_tuple */ #define FIELDNO_TUPLETABLESLOT_TUPLE 5 //物理元組,如為虛擬元組則為NULL HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */ #define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 6 //slot中的元組描述符 TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */ //slot所在的上下文 MemoryContext tts_mcxt; /* slot itself is in this context */ //元組緩存,如無則為InvalidBuffer Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */ #define FIELDNO_TUPLETABLESLOT_NVALID 9 //tts_values中的有效值 int tts_nvalid; /* # of valid values in tts_values */ #define FIELDNO_TUPLETABLESLOT_VALUES 10 //當前每個屬性的值 Datum *tts_values; /* current per-attribute values */ #define FIELDNO_TUPLETABLESLOT_ISNULL 11 //isnull數組 bool *tts_isnull; /* current per-attribute isnull flags */ //minimal元組,如無則為NULL MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */ //在minimal情況下的工作空間 HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */ #define FIELDNO_TUPLETABLESLOT_OFF 14 //slot_deform_tuple的存儲狀態 uint32 tts_off; /* saved state for slot_deform_tuple */ //不能被變更的描述符(固定描述符) bool tts_fixedTupleDescriptor; /* descriptor can't be changed */ } TupleTableSlot; /* base tuple table slot type */ typedef struct TupleTableSlot { NodeTag type;//Node標記 #define FIELDNO_TUPLETABLESLOT_FLAGS 1 uint16 tts_flags; /* 布爾狀態;Boolean states */ #define FIELDNO_TUPLETABLESLOT_NVALID 2 AttrNumber tts_nvalid; /* 在tts_values中有多少有效的values;# of valid values in tts_values */ const TupleTableSlotOps *const tts_ops; /* slot的實際實現;implementation of slot */ #define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 4 TupleDesc tts_tupleDescriptor; /* slot的元組描述符;slot's tuple descriptor */ #define FIELDNO_TUPLETABLESLOT_VALUES 5 Datum *tts_values; /* 當前屬性值;current per-attribute values */ #define FIELDNO_TUPLETABLESLOT_ISNULL 6 bool *tts_isnull; /* 當前屬性isnull標記;current per-attribute isnull flags */ MemoryContext tts_mcxt; /*內存上下文; slot itself is in this context */ } TupleTableSlot; /* routines for a TupleTableSlot implementation */ //TupleTableSlot的"小程序" struct TupleTableSlotOps { /* Minimum size of the slot */ //slot的最小化大小 size_t base_slot_size; /* Initialization. */ //初始化方法 void (*init)(TupleTableSlot *slot); /* Destruction. */ //析構方法 void (*release)(TupleTableSlot *slot); /* * Clear the contents of the slot. Only the contents are expected to be * cleared and not the tuple descriptor. Typically an implementation of * this callback should free the memory allocated for the tuple contained * in the slot. * 清除slot中的內容。 * 只希望清除內容,而不希望清除元組描述符。 * 通常,這個回調的實現應該釋放為slot中包含的元組分配的內存。 */ void (*clear)(TupleTableSlot *slot); /* * Fill up first natts entries of tts_values and tts_isnull arrays with * values from the tuple contained in the slot. The function may be called * with natts more than the number of attributes available in the tuple, * in which case it should set tts_nvalid to the number of returned * columns. * 用slot中包含的元組的值填充tts_values和tts_isnull數組的第一個natts條目。 * 在調用該函數時,natts可能多于元組中可用屬性的數量,在這種情況下, * 應該將tts_nvalid設置為返回列的數量。 */ void (*getsomeattrs)(TupleTableSlot *slot, int natts); /* * Returns value of the given system attribute as a datum and sets isnull * to false, if it's not NULL. Throws an error if the slot type does not * support system attributes. * 將給定系統屬性的值作為基準返回,如果不為NULL, * 則將isnull設置為false。如果slot類型不支持系統屬性,則引發錯誤。 */ Datum (*getsysattr)(TupleTableSlot *slot, int attnum, bool *isnull); /* * Make the contents of the slot solely depend on the slot, and not on * underlying resources (like another memory context, buffers, etc). * 使slot的內容完全依賴于slot,而不是底層資源(如另一個內存上下文、緩沖區等)。 */ void (*materialize)(TupleTableSlot *slot); /* * Copy the contents of the source slot into the destination slot's own * context. Invoked using callback of the destination slot. * 將源slot的內容復制到目標slot自己的上下文中。 * 使用目標slot的回調函數調用。 */ void (*copyslot) (TupleTableSlot *dstslot, TupleTableSlot *srcslot); /* * Return a heap tuple "owned" by the slot. It is slot's responsibility to * free the memory consumed by the heap tuple. If the slot can not "own" a * heap tuple, it should not implement this callback and should set it as * NULL. * 返回slot“擁有”的堆元組。 * slot負責釋放堆元組分配的內存。 * 如果slot不能“擁有”堆元組,它不應該實現這個回調函數,應該將它設置為NULL。 */ HeapTuple (*get_heap_tuple)(TupleTableSlot *slot); /* * Return a minimal tuple "owned" by the slot. It is slot's responsibility * to free the memory consumed by the minimal tuple. If the slot can not * "own" a minimal tuple, it should not implement this callback and should * set it as NULL. * 返回slot“擁有”的最小元組。 * slot負責釋放最小元組分配的內存。 * 如果slot不能“擁有”最小元組,它不應該實現這個回調函數,應該將它設置為NULL。 */ MinimalTuple (*get_minimal_tuple)(TupleTableSlot *slot); /* * Return a copy of heap tuple representing the contents of the slot. The * copy needs to be palloc'd in the current memory context. The slot * itself is expected to remain unaffected. It is *not* expected to have * meaningful "system columns" in the copy. The copy is not be "owned" by * the slot i.e. the caller has to take responsibilty to free memory * consumed by the slot. * 返回表示slot內容的堆元組副本。 * 需要在當前內存上下文中對副本進行內存分配palloc。 * 預計slot本身不會受到影響。 * 它不希望在副本中有有意義的“系統列”。副本不是slot“擁有”的,即調用方必須負責釋放slot消耗的內存。 */ HeapTuple (*copy_heap_tuple)(TupleTableSlot *slot); /* * Return a copy of minimal tuple representing the contents of the slot. The * copy needs to be palloc'd in the current memory context. The slot * itself is expected to remain unaffected. It is *not* expected to have * meaningful "system columns" in the copy. The copy is not be "owned" by * the slot i.e. the caller has to take responsibilty to free memory * consumed by the slot. * 返回表示slot內容的最小元組的副本。 * 需要在當前內存上下文中對副本進行palloc。 * 預計slot本身不會受到影響。 * 它不希望在副本中有有意義的“系統列”。副本不是slot“擁有”的,即調用方必須負責釋放slot消耗的內存。 */ MinimalTuple (*copy_minimal_tuple)(TupleTableSlot *slot); }; typedef struct tupleDesc { int natts; /* tuple中的屬性數量;number of attributes in the tuple */ Oid tdtypeid; /* tuple類型的組合類型ID;composite type ID for tuple type */ int32 tdtypmod; /* tuple類型的typmode;typmod for tuple type */ int tdrefcount; /* 依賴計數,如為-1,則沒有依賴;reference count, or -1 if not counting */ TupleConstr *constr; /* 約束,如無則為NULL;constraints, or NULL if none */ /* attrs[N] is the description of Attribute Number N+1 */ //attrs[N]是第N+1個屬性的描述符 FormData_pg_attribute attrs[FLEXIBLE_ARRAY_MEMBER]; } *TupleDesc;
static void tts_heap_getsomeattrs(TupleTableSlot *slot, int natts) { HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot; Assert(!TTS_EMPTY(slot)); slot_deform_heap_tuple(slot, hslot->tuple, &hslot->off, natts); } /* * slot_deform_heap_tuple * Given a TupleTableSlot, extract data from the slot's physical tuple * into its Datum/isnull arrays. Data is extracted up through the * natts'th column (caller must ensure this is a legal column number). * 給定一個TupleTableSlot,從其中提取數據到Datum/isnull數組中。 * 數據根據第N個屬性列來進行提取。 * * This is essentially an incremental version of heap_deform_tuple: * on each call we extract attributes up to the one needed, without * re-computing information about previously extracted attributes. * slot->tts_nvalid is the number of attributes already extracted. * slot->tts_nvalid是已完成提取的屬性格式。 * * This is marked as always inline, so the different offp for different types * of slots gets optimized away. */ static pg_attribute_always_inline void slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, int natts) { //元組描述符 TupleDesc tupleDesc = slot->tts_tupleDescriptor; //列值數組 Datum *values = slot->tts_values; //isnull標記數組 bool *isnull = slot->tts_isnull; //頭部信息 HeapTupleHeader tup = tuple->t_data; bool hasnulls = HeapTupleHasNulls(tuple); //屬性編號 int attnum; //指向元組數據的指針 char *tp; /* ptr to tuple data */ //偏移 uint32 off; /* offset in tuple data */ //指向tuple中的null bitmap bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */ //是否可以使用/設置attcacheoff bool slow; /* can we use/set attcacheoff? */ /* We can only fetch as many attributes as the tuple has. */ //只能提取元組中有的屬性,獲取元組個數 natts = Min(HeapTupleHeaderGetNatts(tuple->t_data), natts); /* * Check whether the first call for this tuple, and initialize or restore * loop state. * 是否第一次調用? */ attnum = slot->tts_nvalid; if (attnum == 0) { /* Start from the first attribute */ //從第一個屬性開始 off = 0; slow = false; } else { /* Restore state from previous execution */ //從上一次執行中恢復狀態 off = *offp; slow = TTS_SLOW(slot); } //調整指針位置 tp = (char *) tup + tup->t_hoff; for (; attnum < natts; attnum++) { //獲取列值 Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum); if (hasnulls && att_isnull(attnum, bp)) { //null values[attnum] = (Datum) 0; isnull[attnum] = true; slow = true; /* can't use attcacheoff anymore */ continue; } isnull[attnum] = false; if (!slow && thisatt->attcacheoff >= 0) off = thisatt->attcacheoff; else if (thisatt->attlen == -1) { /* * We can only cache the offset for a varlena attribute if the * offset is already suitably aligned, so that there would be no * pad bytes in any case: then the offset will be valid for either * an aligned or unaligned value. * 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 { /* not varlena, so safe to use att_align_nominal */ //非varlena:使用att_align_nominal off = 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't use attcacheoff anymore */ } /* * Save state for next execution * 存儲狀態 */ slot->tts_nvalid = attnum; *offp = off; if (slow) slot->tts_flags |= TTS_FLAG_SLOW; else slot->tts_flags &= ~TTS_FLAG_SLOW; } /* Accessor for the i'th attribute of tupdesc. */ #define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)]) #define fetchatt(A,T) fetch_att(T, (A)->attbyval, (A)->attlen) /* * Same, but work from byval/len parameters rather than Form_pg_attribute. */ #if SIZEOF_DATUM == 8 #define fetch_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 */ #define fetch_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 * Returns pointer value of a datum. */ #define DatumGetPointer(X) ((Pointer) (X)) /* * PointerGetDatum * Returns datum representation for a pointer. */ #define PointerGetDatum(X) ((Datum) (X))
執行SQL:
[local]:5432 pg12@testdb=# select * from t_getattrs;
啟動gdb,進入斷點
(gdb) b slot_deform_heap_tuple Breakpoint 3 at 0x6fdeac: file execTuples.c, line 892. (gdb) c Continuing. Breakpoint 3, slot_deform_heap_tuple (slot=0x12312a0, tuple=0x1231880, offp=0x12312e8, natts=5) at execTuples.c:892 892 TupleDesc tupleDesc = slot->tts_tupleDescriptor; (gdb) bt #0 slot_deform_heap_tuple (slot=0x12312a0, tuple=0x1231880, offp=0x12312e8, natts=5) at execTuples.c:892 #1 0x00000000006fd7d6 in tts_buffer_heap_getsomeattrs (slot=0x12312a0, natts=5) at execTuples.c:676 #2 0x00000000006ff7a9 in slot_getsomeattrs_int (slot=0x12312a0, attnum=5) at execTuples.c:1877 #3 0x000000000048ed13 in slot_getsomeattrs (slot=0x12312a0, attnum=5) at ../../../../src/include/executor/tuptable.h:345 #4 0x000000000048ed39 in slot_getallattrs (slot=0x12312a0) at ../../../../src/include/executor/tuptable.h:357 #5 0x000000000048f88a in printtup (slot=0x12312a0, self=0x1239a50) at printtup.c:392 #6 0x00000000006efc3c in ExecutePlan (estate=0x1230e38, planstate=0x1231090, use_parallel_mode=false, operation=CMD_SELECT, sendTuples=true, numberTuples=0, direction=ForwardScanDirection, dest=0x1239a50, execute_once=true) at execMain.c:1685 #7 0x00000000006ed9df in standard_ExecutorRun (queryDesc=0x121b978, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:364 #8 0x00000000006ed815 in ExecutorRun (queryDesc=0x121b978, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:308 #9 0x00000000008f1010 in PortalRunSelect (portal=0x11b9c08, forward=true, count=0, dest=0x1239a50) at pquery.c:929 #10 0x00000000008f0cae in PortalRun (portal=0x11b9c08, count=9223372036854775807, isTopLevel=true, run_once=true, dest=0x1239a50, altdest=0x1239a50, completionTag=0x7ffd32962250 "") at pquery.c:770 #11 0x00000000008ead35 in exec_simple_query (query_string=0x1152d98 "select * from t_getattrs;") at postgres.c:1215 #12 0x00000000008eefa5 in PostgresMain (argc=1, argv=0x117fda8, dbname=0x117fbf0 "testdb", username=0x114fab8 "pg12") at postgres.c:4236 #13 0x0000000000845915 in BackendRun (port=0x1175bc0) at postmaster.c:4431 #14 0x00000000008450f3 in BackendStartup (port=0x1175bc0) at postmaster.c:4122 ---Type <return> to continue, or q <return> to quit--- #15 0x000000000084132f in ServerLoop () at postmaster.c:1704 #16 0x0000000000840be5 in PostmasterMain (argc=1, argv=0x114da70) at postmaster.c:1377 #17 0x0000000000761469 in main (argc=1, argv=0x114da70) at main.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) p tuple $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) p natts --> 5個屬性 $5 = 5 (gdb)
初始化相關變量
(gdb) n 893 Datum *values = slot->tts_values; (gdb) 894 bool *isnull = slot->tts_isnull; (gdb) 895 HeapTupleHeader tup = tuple->t_data; (gdb) p *values $6 = 0 (gdb) p *isnull $7 = false (gdb) n 896 bool hasnulls = HeapTupleHasNulls(tuple); (gdb) 900 bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */ (gdb) 904 natts = Min(HeapTupleHeaderGetNatts(tuple->t_data), natts); (gdb) p *bp $8 = 0 '\000' (gdb) n 910 attnum = slot->tts_nvalid; (gdb) p natts $9 = 5 (gdb) n 911 if (attnum == 0) (gdb) p attnum $10 = 0 (gdb)
首次執行,設置偏移等信息以及初始化元組數據指針
(gdb) n 914 off = 0; (gdb) 915 slow = false; (gdb) 924 tp = (char *) tup + tup->t_hoff; (gdb) p tup->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) n 926 for (; attnum < natts; attnum++) (gdb) 928 Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum); (gdb) 930 if (hasnulls && att_isnull(attnum, bp))
屬性信息
(gdb) p thisatt $13 = (Form_pg_attribute) 0x7fe1af2fd7c0 (gdb) p *thisatt $14 = {attrelid = 131110, attname = {data = "id", '\000' <repeats 61 times>}, 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) p thisatt->attbyval $18 = true (gdb) p thisatt->attlen $19 = 4 (gdb) p SIZEOF_DATUM $24 = 8 (gdb) p (int) sizeof(Datum) $26 = 8 (gdb) p (int) sizeof(int32) $27 = 4 (gdb) (gdb) p Int32GetDatum(*((int32 *)(tp+off))) $25 = 1 ### (attlen) == (int) sizeof(int32) ? \ Int32GetDatum(*((int32 *)(T))) \ ###
獲取第2個屬性值,即col_varchar的值。注意:fetchatt執行的邏輯是PointerGetDatum((char *) (T))
(gdb) n 973 if (thisatt->attlen <= 0) (gdb) 926 for (; attnum < natts; attnum++) (gdb) 928 Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum); (gdb) 930 if (hasnulls && att_isnull(attnum, bp)) (gdb) p *thisatt $28 = {attrelid = 131110, attname = {data = "col_varchar", '\000' <repeats 52 times>}, 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) n 938 isnull[attnum] = false; (gdb) 940 if (!slow && thisatt->attcacheoff >= 0) (gdb) 941 off = thisatt->attcacheoff; (gdb) 969 values[attnum] = fetchatt(thisatt, tp + off); (gdb) p off $29 = 4 (gdb) p PointerGetDatum((char *) (tp+off)) $30 = 140606552073300 (gdb) x/5c PointerGetDatum((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) n 971 off = att_addlength_pointer(off, thisatt->attlen, tp + off); (gdb) 973 if (thisatt->attlen <= 0) (gdb) p off $33 = 9 (gdb) p thisatt->attlen $34 = -1 (gdb) n 974 slow = true; /* can't use attcacheoff anymore */ (gdb) 926 for (; attnum < natts; attnum++) (gdb) 928 Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum); (gdb) 930 if (hasnulls && att_isnull(attnum, bp)) (gdb) 938 isnull[attnum] = false; (gdb) 940 if (!slow && thisatt->attcacheoff >= 0) (gdb) 942 else if (thisatt->attlen == -1) (gdb) 950 if (!slow && (gdb) 955 off = att_align_pointer(off, thisatt->attalign, -1, (gdb) 957 slow = true; (gdb) p off $35 = 9 (gdb) n 969 values[attnum] = fetchatt(thisatt, tp + off); (gdb) p *thisatt $36 = {attrelid = 131110, attname = {data = "col_char", '\000' <repeats 55 times>}, 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) n 971 off = att_addlength_pointer(off, thisatt->attlen, tp + off); (gdb) 973 if (thisatt->attlen <= 0) (gdb) p off $38 = 20 (gdb) n 974 slow = true; /* can't use attcacheoff anymore */ (gdb) 926 for (; attnum < natts; attnum++) (gdb) 928 Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum); (gdb) 930 if (hasnulls && att_isnull(attnum, bp)) (gdb) 938 isnull[attnum] = false; (gdb) 940 if (!slow && thisatt->attcacheoff >= 0) (gdb) 942 else if (thisatt->attlen == -1) (gdb) 963 off = att_align_nominal(off, thisatt->attalign); (gdb) 965 if (!slow) (gdb) p off $39 = 24 (gdb) n 969 values[attnum] = fetchatt(thisatt, tp + off); (gdb) p *thisatt $40 = {attrelid = 131110, attname = {data = "col_double", '\000' <repeats 53 times>}, 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) n 971 off = att_addlength_pointer(off, thisatt->attlen, tp + off); (gdb) 973 if (thisatt->attlen <= 0) (gdb) p off $50 = 32 (gdb) n 926 for (; attnum < natts; attnum++) (gdb) 928 Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum); (gdb) 930 if (hasnulls && att_isnull(attnum, bp)) (gdb) 938 isnull[attnum] = false; (gdb) 940 if (!slow && thisatt->attcacheoff >= 0) (gdb) 942 else if (thisatt->attlen == -1) (gdb) 950 if (!slow && (gdb) 955 off = att_align_pointer(off, thisatt->attalign, -1, (gdb) 957 slow = true; (gdb) 969 values[attnum] = fetchatt(thisatt, tp + off); (gdb) p *thisatt $51 = {attrelid = 131110, attname = {data = "col_numeric", '\000' <repeats 52 times>}, 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) p PointerGetDatum((char *) (tp+off)) $52 = 140606552073328 (gdb) x/32c PointerGetDatum((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/32x PointerGetDatum((char *) (tp+off)) 0x7fe18396e470: 0x17 0x81 0x82 0x01 0x00 0x29 0x09 0x61 0x7fe18396e478: 0x1e 0x58 0x1b 0x00 0x00 0x00 0x00 0x00 0x7fe18396e480: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x7fe18396e488: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 (gdb)
完成函數調用
(gdb) n 971 off = att_addlength_pointer(off, thisatt->attlen, tp + off); (gdb) p values[attnum] $55 = 140606552073328 (gdb) n 973 if (thisatt->attlen <= 0) (gdb) 974 slow = true; /* can't use attcacheoff anymore */ (gdb) 926 for (; attnum < natts; attnum++) (gdb) 980 slot->tts_nvalid = attnum; (gdb) 981 *offp = off; (gdb) 982 if (slow) (gdb) 983 slot->tts_flags |= TTS_FLAG_SLOW; (gdb) 986 } (gdb) tts_buffer_heap_getsomeattrs (slot=0x12312a0, natts=5) at execTuples.c:677 677 } (gdb)
到此,相信大家對“PostgreSQL執行查詢時獲取元組屬性值實現方法是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。