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---Typeto 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执行查询时获取元组属性值实现方法是什么"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
属性
内存
物理
数据
分配
情况
指向
数组
上下
上下文
内容
副本
有效
状态
存储
方法
最小
函数
缓存
处理
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
小学生网络安全知识顺口溜
5亿邮箱数据库下载
支付宝线下服务器在哪
怎么样从系统中导入数据库
数据库记录的修改教案
广州采购管理软件开发商
烽火通信的服务器
服务器中央处理器都有多大的
cf手游服务器没有角色
常用生活用品数据库
服务器usb端口禁用怎么解决
数据库 追加0行
多媒体法制主题展厅软件开发
网络安全主题教育手抄报图片
mysql数据库数据同步
大学数据库方面的书籍
南京淳科软件开发有限公司
软件开发店怎么样
敏捷软件开发 pdf高清
网络安全周深圳
三级数据库技术软件安装
打印服务器跨网段
怎么样从系统中导入数据库
北京小荷网络技术有限公司
无锡直播软件开发公司
在线网络技术开发平台
网络安全风险防范方法
数据库数据安全问题
域名服务器错误
网络技术应用操作题江苏