- 专栏内容:postgresql内核源码分析
- 个人主页:我的主页
- 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
目录
前言
本文是基于postgresql 15的代码进行分析解读,演示是在centos8系统上进行。
概述
在SQL中我们可以查看表的每个字段的数据,在数据库中字段的数据如何组成一个行数据,也叫tuple,又如何从磁盘文件中获取某张表的字段数据。本文就来看看行数据的拼装/拆解接口,以及对应的流程。
整体流程
涉及tuple拼装/拆解的过程有很多,就拿我们常见的insert来看。
在执行阶段,此时我们拿到了表的字段信息,各字段的数据,然后就需要拼装成一个HeapTupleData结构的数据,再然后查找表文件的空闲空间,找到位置后,将HeapTupleHeaderData结构的数据放到Page中。
这样我们的insert 的数据就在表中了。
接口说明
在insert/update时,需要将字段数据拼装成一个tuple并放到page中时,用下面接口进行拼装。
HeapTuple
heap_form_tuple(TupleDesc tupleDescriptor,
Datum *values,
bool *isnull)
有拼装接口,就有获取tuple每个字段数据的拆解接口。
void
heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
Datum *values, bool *isnull)
整体流程
调用heap_form_tuple进行组装,其中传入了tuple描述信息,也就是表头信息,有多少列,列的类型等;同时也传入了每列的值,如果是空值对应的isnull为true;这是调用者需要准备好的;
详细流程
- 首先检查是否有空值,如果有空值,分配空间时需要带有null值的bitmap;
- 计算tuple的长度,包括tupleheader和data;其中tupleheader的结构为HeapTupleHeaderData,如果有空值时追加t_bits,每个属性占一个bit位;数据长度由
heap_compute_data_size 按values中各数据的真实长度计算;
struct HeapTupleHeaderData
{
union
{
HeapTupleFields t_heap;
DatumTupleFields t_datum;
} t_choice;
ItemPointerData t_ctid; /* current TID of this or newer tuple (or a
* speculative insertion token) */
/* Fields below here must match MinimalTupleData! */
#define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK2 2
uint16 t_infomask2; /* number of attributes + various flags */
#define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK 3
uint16 t_infomask; /* various flag bits, see below */
#define FIELDNO_HEAPTUPLEHEADERDATA_HOFF 4
uint8 t_hoff; /* sizeof header incl. bitmap, padding */
/* ^ - 23 bytes - ^ */
#define FIELDNO_HEAPTUPLEHEADERDATA_BITS 5
bits8 t_bits[FLEXIBLE_ARRAY_MEMBER]; /* bitmap of NULLs */
/* MORE DATA FOLLOWS AT END OF STRUCT */
};
- 计算长度时分了三种情况
第一,对于长度可压缩为一字节的,此时attstorage不为'p',attlen为-1;长度空间按uint8存储,数据仍为原始长度;
第二,对于外部存储或扩展存储内容,此时attlen为-1;由
EOH_get_flat_size来计算大小,再加上保留长度,也就是varattrib_1b_e结构中的data长度;
第三,对于其它,如果长度需要对齐存放,则按对应类型对齐;然后按实际长度存储;如果长度为-1,则按外部存储方式存放;如果为-2,则按字符串长度strlen+1存放;
- 分配内存空间
这里分配空间时,实际分配的是HeapTupleData结构的空间,它是tuple在内存中时的记录结构,其中成员t_data为实际tuple的内容,也是真正要写到page中的数据;
typedef struct HeapTupleData
{
uint32 t_len; /* length of *t_data */
ItemPointerData t_self; /* SelfItemPointer */
Oid t_tableOid; /* table the tuple came from */
#define FIELDNO_HEAPTUPLEDATA_DATA 3
HeapTupleHeader t_data; /* -> tuple header and data */
} HeapTupleData;
所以分配空间时,要加上t_data前面几个字段的长度;
- 填充字段值
在HeapTupleHeader中,在没有放到page中时,使用的是DatumTupleFields结构,在填充
datum_len_时,将len左移了两位,因为低两位存储标志位;
- 填充数据内容
按数据类型进行依次填充,对于空值,对bitmap进行赋值。其中除字符类型 ,其它都需要对offset进行类型对齐。
在填充的过程中,对于变长类型,infomask增加标志HEAP_HASVARWIDTH
结尾
作者邮箱:[email protected]
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!