Postgresql内核源码分析-tuple是如何拼装的

 


目录

前言

概述

整体流程

接口说明

整体流程

详细流程

结尾


前言

本文是基于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]
如有错误或者疏漏欢迎指出,互相学习。

注:未经同意,不得转载!

猜你喜欢

转载自blog.csdn.net/senllang/article/details/129783410
今日推荐