溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

PostgreSQL執(zhí)行查詢時獲取元組屬性值實現(xiàn)方法是什么

發(fā)布時間:2021-11-08 15:22:50 來源:億速云 閱讀:172 作者:iii 欄目:關(guān)系型數(shù)據(jù)庫

本篇內(nèi)容主要講解“PostgreSQL執(zhí)行查詢時獲取元組屬性值實現(xiàn)方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“PostgreSQL執(zhí)行查詢時獲取元組屬性值實現(xiàn)方法是什么”吧!

測試數(shù)據(jù)如下:

[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;

一、數(shù)據(jù)結(jié)構(gòu)

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
 * 執(zhí)行器在"tuple table"中存儲元組,這個表是各自獨立的TupleTableSlots鏈表.
 * 有以下情況需要處理:
 *      1. 磁盤緩存頁中的物理元組
 *      2. 在已分配內(nèi)存中構(gòu)造的物理元組
 *      3. 在已分配內(nèi)存中構(gòu)造的"minimal"物理元組
 *      4. 含有Datum/isnull數(shù)組的"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"系統(tǒng)列
 * (實際上,可以有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"元組是用于在嵌套計劃節(jié)點中拷貝時最小化物理數(shù)據(jù)的優(yōu)化.
 * 所有通過引用傳遞指向與TupleTableSlot非直接相關(guān)的存儲的元組的Datums使用,
 *   通常它們會指向存儲在低層節(jié)點輸出的TupleTableSlot中的元組的一部分,
 *   或者指向在計劃節(jié)點的per-tuple內(nèi)存上下文econtext中構(gòu)造的函數(shù)結(jié)果.
 * 產(chǎn)生計劃節(jié)點的時候有責任確保這些資源未被釋放,確保virtual元組是有效的.
 * 我們使用計劃節(jié)點中的結(jié)果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需要提供格式化而不是當前持有的格式時會出現(xiàn)這種情況.
 * (原始的情況是我們準備通過另外一種格式進行替換來處理這種請求,但在校驗引用傳遞Datums時會出現(xiàn)致命錯誤)
 * 同時在這種情況下,拷貝必須含有唯一的數(shù)據(jù)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數(shù)組有雙重職責.
 * 在slot包含虛擬元組時,它們是authoritative(權(quán)威)數(shù)據(jù).
 * 在slot包含物理元組時,時包含從元組中提取的數(shù)據(jù)的數(shù)組.
 * (在這種情況下,所有通過引用傳遞的Datums指向物理元組)
 * 提取的信息通過'lazily'在需要的時候才構(gòu)建.
 * 這樣可以避免從物理元組的重復數(shù)據(jù)提取.
 *
 * 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",沒有有效數(shù)據(jù).
 * 對于新鮮創(chuàng)建仍未分配描述的的slot來說這是唯一有效的狀態(tài).
 * 在這種狀態(tài)下,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()的調(diào)用者有責任提供與slot生命周期一樣的描述符.
 * (典型的,不管是slots還是描述符會在per-query內(nèi)存中,
 *  并且會在查詢結(jié)束時通過內(nèi)存上下文的析構(gòu)器釋放,因此不需要提供額外的機制來處理.
 *  但是,如果使用了引用計數(shù)型tupdesc,slot會增加tupdesc引用計數(shù))
 *
 * 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引用元組被清除時釋放內(nèi)存.
 *
 * 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數(shù)組中的有效列數(shù).
 * 如果slot含有虛擬元組,該字段必須跟描述符的natts一樣.
 * 在slot含有物理元組時,該字段等于我們提取的列數(shù).
 * (我們通常從左到右提取列,因此不會有空洞存在)
 *
 * 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會被分配內(nèi)存,長度與描述符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執(zhí)行實際的MinimalTupleData對象(如tts_shouldFreeMin為T,則需要通過pfree釋放內(nèi)存).
 * 如果slot只有一個minimal而沒有通常的物理元組,那么tts_tuple指向tts_minhdr,
 *   結(jié)構(gòu)體的其他字段會被正確的設(shè)置為用于訪問minimal元組.
 *   特別的, tts_minhdr.t_data指向tts_mintuple前的MINIMAL_TUPLE_OFFSET字節(jié).
 * 這可以讓列提取可以獨立處理通常的物理元組.
 *
 * 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狀態(tài),不應通過其他代碼修改.
 *----------
 */
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存儲狀態(tài)?
    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數(shù)組
    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的存儲狀態(tài)
    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;      /* 布爾狀態(tài);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的實際實現(xiàn);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;     /*內(nèi)存上下文; 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. */
    //析構(gòu)方法
    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中的內(nèi)容。
     * 只希望清除內(nèi)容,而不希望清除元組描述符。
     * 通常,這個回調(diào)的實現(xiàn)應該釋放為slot中包含的元組分配的內(nèi)存。
     */
    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數(shù)組的第一個natts條目。
     * 在調(diào)用該函數(shù)時,natts可能多于元組中可用屬性的數(shù)量,在這種情況下,
     *   應該將tts_nvalid設(shè)置為返回列的數(shù)量。
     */
    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.
     * 將給定系統(tǒng)屬性的值作為基準返回,如果不為NULL,
     *   則將isnull設(shè)置為false。如果slot類型不支持系統(tǒng)屬性,則引發(fā)錯誤。
     */
    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的內(nèi)容完全依賴于slot,而不是底層資源(如另一個內(nèi)存上下文、緩沖區(qū)等)。
     */
    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的內(nèi)容復制到目標slot自己的上下文中。
     * 使用目標slot的回調(diào)函數(shù)調(diào)用。
     */
    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負責釋放堆元組分配的內(nèi)存。
     * 如果slot不能“擁有”堆元組,它不應該實現(xiàn)這個回調(diào)函數(shù),應該將它設(shè)置為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負責釋放最小元組分配的內(nèi)存。
     * 如果slot不能“擁有”最小元組,它不應該實現(xiàn)這個回調(diào)函數(shù),應該將它設(shè)置為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內(nèi)容的堆元組副本。
     * 需要在當前內(nèi)存上下文中對副本進行內(nèi)存分配palloc。
     * 預計slot本身不會受到影響。
     * 它不希望在副本中有有意義的“系統(tǒng)列”。副本不是slot“擁有”的,即調(diào)用方必須負責釋放slot消耗的內(nèi)存。
     */
    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內(nèi)容的最小元組的副本。
     * 需要在當前內(nèi)存上下文中對副本進行palloc。
     * 預計slot本身不會受到影響。
     * 它不希望在副本中有有意義的“系統(tǒng)列”。副本不是slot“擁有”的,即調(diào)用方必須負責釋放slot消耗的內(nèi)存。
     */
    MinimalTuple (*copy_minimal_tuple)(TupleTableSlot *slot);
};
typedef struct tupleDesc
{
    int         natts;          /* tuple中的屬性數(shù)量;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;     /* 依賴計數(shù),如為-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,從其中提取數(shù)據(jù)到Datum/isnull數(shù)組中。
 *        數(shù)據(jù)根據(jù)第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;
    //列值數(shù)組
    Datum       *values = slot->tts_values;
    //isnull標記數(shù)組
    bool       *isnull = slot->tts_isnull;
    //頭部信息
    HeapTupleHeader tup = tuple->t_data;
    bool        hasnulls = HeapTupleHasNulls(tuple);
    //屬性編號
    int            attnum;
    //指向元組數(shù)據(jù)的指針
    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 */
    //是否可以使用/設(shè)置attcacheoff
    bool        slow;            /* can we use/set attcacheoff? */
    /* We can only fetch as many attributes as the tuple has. */
    //只能提取元組中有的屬性,獲取元組個數(shù)
    natts = Min(HeapTupleHeaderGetNatts(tuple->t_data), natts);
    /*
     * Check whether the first call for this tuple, and initialize or restore
     * loop state.
     * 是否第一次調(diào)用?
     */
    attnum = slot->tts_nvalid;
    if (attnum == 0)
    {
        /* Start from the first attribute */
        //從第一個屬性開始
        off = 0;
        slow = false;
    }
    else
    {
        /* Restore state from previous execution */
        //從上一次執(zhí)行中恢復狀態(tài)
        off = *offp;
        slow = TTS_SLOW(slot);
    }
    //調(diào)整指針位置
    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);
        //調(diào)整偏移
        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
     * 存儲狀態(tài)
     */
    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))

三、跟蹤分析

執(zhí)行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)

輸入?yún)?shù)

(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)

初始化相關(guān)變量

(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)

首次執(zhí)行,設(shè)置偏移等信息以及初始化元組數(shù)據(jù)指針

(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)

開始循環(huán)獲取每個屬性的值

(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執(zhí)行的邏輯是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執(zhí)行的邏輯是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執(zhí)行的邏輯是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執(zhí)行的邏輯是*((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執(zhí)行的邏輯是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)

完成函數(shù)調(diào)用

(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執(zhí)行查詢時獲取元組屬性值實現(xiàn)方法是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI