久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

PostgreSQL執行查詢時獲取元組屬性值實現方法是什么

153次閱讀
沒有評論

共計 29982 個字符,預計需要花費 75 分鐘才能閱讀完成。

本篇內容主要講解“PostgreSQL 執行查詢時獲取元組屬性值實現方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓丸趣 TV 小編來帶大家學習“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 執行查詢時獲取元組屬性值實現方法是什么”有了更深的了解,不妨來實際操作一番吧!這里是丸趣 TV 網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-07-26發表,共計29982字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 岳普湖县| 丹凤县| 南阳市| 昌宁县| 朔州市| 克什克腾旗| 旬邑县| 温州市| 思南县| 高州市| 镇沅| 特克斯县| 万山特区| 汉中市| 武邑县| 桑日县| 江津市| 宁陵县| 简阳市| 江永县| 阿城市| 桐城市| 昌吉市| 岳普湖县| 诸暨市| 山丹县| 东阿县| 伊川县| 龙海市| 渭南市| 邓州市| 大港区| 灵台县| 武汉市| 衡阳市| 西峡县| 乐亭县| 嘉兴市| 襄城县| 平顶山市| 肥西县|