千家信息网

PostgreSQL执行查询时获取元组属性值实现方法是什么

发表于:2025-02-02 作者:千家信息网编辑
千家信息网最后更新 2025年02月02日,本篇内容主要讲解"PostgreSQL执行查询时获取元组属性值实现方法是什么",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"PostgreSQL执行查询时获
千家信息网最后更新 2025年02月02日PostgreSQL执行查询时获取元组属性值实现方法是什么

本篇内容主要讲解"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 TABLETime: 12425.991 ms (00:12.426)[local]:5432 pg12@testdb=# insert into t_getattrs values(1,'test','test',1234.45,12345.77777);INSERT 0 1Time: 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 voidtts_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 voidslot_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_tupleBreakpoint 3 at 0x6fdeac: file execTuples.c, line 892.(gdb) cContinuing.Breakpoint 3, slot_deform_heap_tuple (slot=0x12312a0, tuple=0x1231880, offp=0x12312e8, natts=5)    at execTuples.c:892892        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  to continue, or q  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 ,   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) n893        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) n896        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) n910        attnum = slot->tts_nvalid;(gdb) p natts$9 = 5(gdb) n911        if (attnum == 0)(gdb) p attnum$10 = 0(gdb)

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

(gdb) n914            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) n926        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' }, 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) n973            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' }, 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) n938            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) n971            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) n974                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) n969            values[attnum] = fetchatt(thisatt, tp + off);(gdb) p *thisatt$36 = {attrelid = 131110, attname = {data = "col_char", '\000' }, 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) n971            off = att_addlength_pointer(off, thisatt->attlen, tp + off);(gdb) 973            if (thisatt->attlen <= 0)(gdb) p off$38 = 20(gdb) n974                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) n969            values[attnum] = fetchatt(thisatt, tp + off);(gdb) p *thisatt$40 = {attrelid = 131110, attname = {data = "col_double", '\000' }, 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) n971            off = att_addlength_pointer(off, thisatt->attlen, tp + off);(gdb) 973            if (thisatt->attlen <= 0)(gdb) p off$50 = 32(gdb) n926        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' }, 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    0x610x7fe18396e478:    0x1e    0x58    0x1b    0x00    0x00    0x00    0x00    0x000x7fe18396e480:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x000x7fe18396e488:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00(gdb)

完成函数调用

(gdb) n971            off = att_addlength_pointer(off, thisatt->attlen, tp + off);(gdb) p values[attnum]$55 = 140606552073328(gdb) n973            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:677677    }(gdb)

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

0