相关 《Postgresql源码(56)可扩展类型分析ExpandedObject/ExpandedRecord》 《Postgresql源码(58)元组拼接heap_form_tuple剖析》
PG中的元组有多种表现形式,之前介绍过两种了:
今天介绍第三种元组格式:
执行器对元组格式的要求非常灵活,例如select 1;表达式结果、select a,b,c from t;投影临时结果等等。
各种各样结果都需要返回元组形式的数据,所以PG引入了一种通用格式保存数据:TupleTableSlot。
tts_flags
中设置的标志 TTS_FLAG_EMPTY
表示不包含有效数据。 对于尚未分配元组描述符的新创建的slot,empty是唯一有效的状态。 在这种状态下,不应在 tts_flags 中设置 TTS_SHOULDFREE,tts_tuple 必须为 NULL 并且 tts_nvalid 为零。TupleTableSlot基类型
typedef struct TupleTableSlot
{
NodeTag type;
uint16 tts_flags; /* Boolean states */
AttrNumber tts_nvalid; /* # of valid values in tts_values */
const TupleTableSlotOps *const tts_ops; /* implementation of slot */
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
Datum *tts_values; /* current per-attribute values */
bool *tts_isnull; /* current per-attribute isnull flags */
MemoryContext tts_mcxt; /* slot itself is in this context */
ItemPointerData tts_tid; /* stored tuple's tid */
Oid tts_tableOid; /* table oid of tuple */
} TupleTableSlot;
四个结构体对应了四套成员函数,下面是一些总结:
| TTSOpsHeapTuple | TTSOpsBufferHeapTuple | TTSOpsVirtual | TTSOpsMinimalTuple |
---|---|---|---|---|
init | 空 | 空 | 空 | min结构记录了一个完整的头HeapTupleData非指针,初始化时tuple指向这个头 |
release | 空 | 空 | 空 | 空 |
clear | 如果有TTS_FLAG_SHOULDFREE标记需要释放引用的heaptuple | 如果有TTS_FLAG_SHOULDFREE标记需要释放引用的heaptuple;如果记了buffer还要releasebuffer | 如果有TTS_FLAG_SHOULDFREE标记需要释放下data的内容 | 如果有TTS_FLAG_SHOULDFREE标记需要释放pfree mintuple的值 |
getsomeattrs | slot_deform_heap_tuple从物理tuple把数据拆到Datum/isnull数组注意这里只是引用没有内存拷贝 | 同本行第二列 | 不支持 | 同本行第二列 |
getsysattr | 从物理tuple里面拿系统列 | 同本行第二列 | 不支持 | 不支持 |
materialize | 切到slot自己的内存上下文在重建或复制一遍物理tuple,使slot和物理tuple解绑 | 同左面,不同的是如果是copytuple需要释放之前的buffer | 按desc的格式,tts_values的内容拼接元组,把结果放到data指向新申请的空间中 | 同本行第二列,不同的是只拷贝值的部分,不拷贝元组的两个头 |
copyslot | 调用copy_heap_tuple拷物理元组,再拷贝剩下的 | 同本行第二列 | 把源tts_values拷贝给目标tts_values,然后调用materialize给目标slot构造data即可 | 同本行第二列 |
get_heap_tuple | 返回物理元组 | 同本行第二列 | 空 | 空 |
get_minimal_tuple | 空 | 空 | 空 | 返回mintuple |
copy_heap_tuple | 拷贝物理元组heap_copytuple | 同本行第二列 | heap_form_tuple拼接 | 拷贝并把元组头重建出来返回一个物理元组 |
copy_minimal_tuple | 只把isnull bitmap/Datum拷贝出来 | 同本行第二列 | heap_form_minimal_tuple拼接 | 只拷贝mini元组 |
const TupleTableSlotOps TTSOpsHeapTuple = {
.base_slot_size = sizeof(HeapTupleTableSlot),
.init = tts_heap_init,
.release = tts_heap_release,
.clear = tts_heap_clear,
.getsomeattrs = tts_heap_getsomeattrs,
.getsysattr = tts_heap_getsysattr,
.materialize = tts_heap_materialize,
.copyslot = tts_heap_copyslot,
.get_heap_tuple = tts_heap_get_heap_tuple,
/* A heap tuple table slot can not "own" a minimal tuple. */
.get_minimal_tuple = NULL,
.copy_heap_tuple = tts_heap_copy_heap_tuple,
.copy_minimal_tuple = tts_heap_copy_minimal_tuple
};
const TupleTableSlotOps TTSOpsBufferHeapTuple = {
.base_slot_size = sizeof(BufferHeapTupleTableSlot),
.init = tts_buffer_heap_init,
.release = tts_buffer_heap_release,
.clear = tts_buffer_heap_clear,
.getsomeattrs = tts_buffer_heap_getsomeattrs,
.getsysattr = tts_buffer_heap_getsysattr,
.materialize = tts_buffer_heap_materialize,
.copyslot = tts_buffer_heap_copyslot,
.get_heap_tuple = tts_buffer_heap_get_heap_tuple,
/* A buffer heap tuple table slot can not "own" a minimal tuple. */
.get_minimal_tuple = NULL,
.copy_heap_tuple = tts_buffer_heap_copy_heap_tuple,
.copy_minimal_tuple = tts_buffer_heap_copy_minimal_tuple
};
const TupleTableSlotOps TTSOpsVirtual = {
.base_slot_size = sizeof(VirtualTupleTableSlot),
.init = tts_virtual_init,
.release = tts_virtual_release,
.clear = tts_virtual_clear,
.getsomeattrs = tts_virtual_getsomeattrs,
.getsysattr = tts_virtual_getsysattr,
.materialize = tts_virtual_materialize,
.copyslot = tts_virtual_copyslot,
/*
* A virtual tuple table slot can not "own" a heap tuple or a minimal
* tuple.
*/
.get_heap_tuple = NULL,
.get_minimal_tuple = NULL,
.copy_heap_tuple = tts_virtual_copy_heap_tuple,
.copy_minimal_tuple = tts_virtual_copy_minimal_tuple
};
const TupleTableSlotOps TTSOpsMinimalTuple = {
.base_slot_size = sizeof(MinimalTupleTableSlot),
.init = tts_minimal_init,
.release = tts_minimal_release,
.clear = tts_minimal_clear,
.getsomeattrs = tts_minimal_getsomeattrs,
.getsysattr = tts_minimal_getsysattr,
.materialize = tts_minimal_materialize,
.copyslot = tts_minimal_copyslot,
/* A minimal tuple table slot can not "own" a heap tuple. */
.get_heap_tuple = NULL,
.get_minimal_tuple = tts_minimal_get_minimal_tuple,
.copy_heap_tuple = tts_minimal_copy_heap_tuple,
.copy_minimal_tuple = tts_minimal_copy_minimal_tuple
};
如果只有一列使用一级指针也是OK的,代码里面进入就执行了:
char *data = *dataP;
后面都在对data进行操作,这里的data和*dataP都指向内存的数据区域,最后再执行:
*dataP = data;
也就是让dataP重新指向data。
但是多列时会有循环去调用fill_val函数,每次都需要调整data的位置,所以最后的:
*dataP = data;
会把data的位置向后移动,来放下一个列的数据。
---------------------- <----- tuple
| HeapTuple |
---------------------- <----- td <----- tuple->t_data
| HeapTupleHeader |
---------------------- <----- td + td->t_hoff
| isnull |
---------------------- <------ data <------ *dataP
| Datums | 下一列: <------ data <------ *dataP
| |
| | 下一列: <------ data <------ *dataP
| |
----------------------
fill_val
static inline void
fill_val(Form_pg_attribute att,
bits8 **bit,
int *bitmask,
char **dataP,
uint16 *infomask,
Datum datum,
bool isnull)
{
Size data_length;
char *data = *dataP;
/*
* If we're building a null bitmap, set the appropriate bit for the
* current column value here.
*/
if (bit != NULL)
{
if (*bitmask != HIGHBIT)
*bitmask <<= 1;
else
{
*bit += 1;
**bit = 0x0;
*bitmask = 1;
}
if (isnull)
{
*infomask |= HEAP_HASNULL;
return;
}
**bit |= *bitmask;
}
/*
* XXX we use the att_align macros on the pointer value itself, not on an
* offset. This is a bit of a hack.
*/
if (att->attbyval)
{
/* pass-by-value */
data = (char *) att_align_nominal(data, att->attalign);
store_att_byval(data, datum, att->attlen);
data_length = att->attlen;
}
else if (att->attlen == -1)
{
/* varlena */
Pointer val = DatumGetPointer(datum);
*infomask |= HEAP_HASVARWIDTH;
if (VARATT_IS_EXTERNAL(val))
{
if (VARATT_IS_EXTERNAL_EXPANDED(val))
{
/*
* we want to flatten the expanded value so that the
* constructed tuple doesn't depend on it
*/
ExpandedObjectHeader *eoh = DatumGetEOHP(datum);
data = (char *) att_align_nominal(data,
att->attalign);
data_length = EOH_get_flat_size(eoh);
EOH_flatten_into(eoh, data, data_length);
}
else
{
*infomask |= HEAP_HASEXTERNAL;
/* no alignment, since it's short by definition */
data_length = VARSIZE_EXTERNAL(val);
memcpy(data, val, data_length);
}
}
else if (VARATT_IS_SHORT(val))
{
/* no alignment for short varlenas */
data_length = VARSIZE_SHORT(val);
memcpy(data, val, data_length);
}
else if (VARLENA_ATT_IS_PACKABLE(att) &&
VARATT_CAN_MAKE_SHORT(val))
{
/* convert to short varlena -- no alignment */
data_length = VARATT_CONVERTED_SHORT_SIZE(val);
SET_VARSIZE_SHORT(data, data_length);
memcpy(data + 1, VARDATA(val), data_length - 1);
}
else
{
/* full 4-byte header varlena */
data = (char *) att_align_nominal(data,
att->attalign);
data_length = VARSIZE(val);
memcpy(data, val, data_length);
}
}
else if (att->attlen == -2)
{
/* cstring ... never needs alignment */
*infomask |= HEAP_HASVARWIDTH;
Assert(att->attalign == TYPALIGN_CHAR);
data_length = strlen(DatumGetCString(datum)) + 1;
memcpy(data, DatumGetPointer(datum), data_length);
}
else
{
/* fixed-length pass-by-reference */
data = (char *) att_align_nominal(data, att->attalign);
Assert(att->attlen > 0);
data_length = att->attlen;
memcpy(data, DatumGetPointer(datum), data_length);
}
data += data_length;
*dataP = data;
}