文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

PostgreSQL 源码解读(2)- 插入数据#2(RelationPutHeapTuple)

2024-04-02 19:55

关注

本文简单介绍了PG插入数据部分的源码,主要内容包括RelationPutHeapTuple函数的实现逻辑。

一、数据结构/宏定义/通用函数

RelationPutHeapTuple函数在hio.c文件中,相关的数据结构、宏定义如下:

1、Relation
数据表数据结构封装
 typedef struct RelationData
 {
     RelFileNode rd_node;        
     
     struct SMgrRelationData *rd_smgr;   
     int         rd_refcnt;      
     BackendId   rd_backend;     
     bool        rd_islocaltemp; 
     bool        rd_isnailed;    
     bool        rd_isvalid;     
     char        rd_indexvalid;  
     bool        rd_statvalid;   
 
     
     SubTransactionId rd_createSubid;    
     SubTransactionId rd_newRelfilenodeSubid;    
 
     Form_pg_class rd_rel;       
     TupleDesc   rd_att;         
     Oid         rd_id;          
     LockInfoData rd_lockInfo;   
     RuleLock   *rd_rules;       
     MemoryContext rd_rulescxt;  
     TriggerDesc *trigdesc;      
     
     struct RowSecurityDesc *rd_rsdesc;  
 
     
     List       *rd_fkeylist;    
     bool        rd_fkeyvalid;   
 
     MemoryContext rd_partkeycxt;    
     struct PartitionKeyData *rd_partkey;    
     MemoryContext rd_pdcxt;     
     struct PartitionDescData *rd_partdesc;  
     List       *rd_partcheck;   
 
     
     List       *rd_indexlist;   
     Oid         rd_oidindex;    
     Oid         rd_pkindex;     
     Oid         rd_replidindex; 
 
     
     List       *rd_statlist;    
 
     
     Bitmapset  *rd_indexattr;   
     Bitmapset  *rd_projindexattr;   
     Bitmapset  *rd_keyattr;     
     Bitmapset  *rd_pkattr;      
     Bitmapset  *rd_idattr;      
     Bitmapset  *rd_projidx;     
 
     PublicationActions *rd_pubactions;  
 
     
     bytea      *rd_options;     
 
     
     Form_pg_index rd_index;     
     
     struct HeapTupleData *rd_indextuple;    
 
     
     Oid         rd_amhandler;   
     MemoryContext rd_indexcxt;  
     
     struct IndexAmRoutine *rd_amroutine;    
     Oid        *rd_opfamily;    
     Oid        *rd_opcintype;   
     RegProcedure *rd_support;   
     FmgrInfo   *rd_supportinfo; 
     int16      *rd_indoption;   
     List       *rd_indexprs;    
     List       *rd_indpred;     
     Oid        *rd_exclops;     
     Oid        *rd_exclprocs;   
     uint16     *rd_exclstrats;  
     void       *rd_amcache;     
     Oid        *rd_indcollation;    
 
     
 
     
     struct FdwRoutine *rd_fdwroutine;   
 
     
     Oid         rd_toastoid;    
 
     
     struct PgStat_TableStatus *pgstat_info; 
 } RelationData;
 
typedef struct RelationData *Relation;
2、Buffer
实际类型为整型,共享缓冲区的index,0为非法Buffer。
 
 typedef int Buffer;
 
 #define InvalidBuffer   0

3、HeapTupleHeader
Heap(还有一种是Index)类型Tuple的头部数据,在Page结构中已作详细分析。
 struct HeapTupleHeaderData
 {
     union
     {
         HeapTupleFields t_heap;
         DatumTupleFields t_datum;
     }           t_choice;
      ItemPointerData t_ctid;     
     
  #define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK2 2
     uint16      t_infomask2;    
  #define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK 3
     uint16      t_infomask;     
  #define FIELDNO_HEAPTUPLEHEADERDATA_HOFF 4
     uint8       t_hoff;         
      
  #define FIELDNO_HEAPTUPLEHEADERDATA_BITS 5
     bits8       t_bits[FLEXIBLE_ARRAY_MEMBER];  
      
 };

4、ItemPointerData
数据行指针数据结构,ip_blkid是数据块ID,ip_posid是Tuple在数据块中的偏移(其实是类似数组中的序号)。
typedef struct ItemPointerData
 {
     BlockIdData ip_blkid;
     OffsetNumber ip_posid;
 }  ItemPointerData;
 
 typedef ItemPointerData *ItemPointer;

 typedef struct BlockIdData
 {
     uint16      bi_hi;
     uint16      bi_lo;
 } BlockIdData;
 
 typedef BlockIdData *BlockId; 

5、HeapTuple
存储在Heap中的Tuple(Row)数据结构:

typedef struct HeapTupleData
 {
     uint32      t_len;          
     ItemPointerData t_self;     
     Oid         t_tableOid;     
 #define FIELDNO_HEAPTUPLEDATA_DATA 3
     HeapTupleHeader t_data;     
 } HeapTupleData;
 
 typedef HeapTupleData *HeapTuple;
 
 #define HEAPTUPLESIZE   MAXALIGN(sizeof(HeapTupleData))

6、HeapTupleHeaderIsSpeculative
 #define HeapTupleHeaderIsSpeculative(tup) \
 ( \
  (ItemPointerGetOffsetNumberNoCheck(&(tup)->t_ctid) == SpecTokenOffsetNumber) \
 )

 #define ItemPointerGetOffsetNumberNoCheck(pointer) \
 ( \
  (pointer)->ip_posid \
 )

7、BufferGetPage
//获取与该buffer(有符号整型)对应的page
 #define BufferGetPage(buffer) ((Page)BufferGetBlock(buffer))
 #define BufferGetBlock(buffer) \
 ( \
  AssertMacro(BufferIsValid(buffer)), \
  BufferIsLocal(buffer) ? \
  LocalBufferBlockPointers[-(buffer) - 1] \
  : \
  (Block) (BufferBlocks + ((Size) ((buffer) - 1)) * BLCKSZ) \
 )
 #define BufferIsLocal(buffer) ((buffer) < 0)
 typedef void *Block;//指向任意类型的指针
 Block *LocalBufferBlockPointers = NULL;//指针的指针

8、BufferGetBlockNumber
 
 BlockNumber
 BufferGetBlockNumber(Buffer buffer)
 {
     BufferDesc *bufHdr;
 
     Assert(BufferIsPinned(buffer));
 
     if (BufferIsLocal(buffer))
         bufHdr = GetLocalBufferDescriptor(-buffer - 1);
     else
         bufHdr = GetBufferDescriptor(buffer - 1);
 
     
     return bufHdr->tag.blockNum;
 }

9、BlockIdSet
 
 #define BlockIdSet(blockId, blockNumber) \
 ( \
     AssertMacro(PointerIsValid(blockId)), \
     (blockId)->bi_hi = (blockNumber) >> 16, \//右移16位,得到高位
     (blockId)->bi_lo = (blockNumber) & 0xffff \//高16位全部置0,得到低位
 )

10、ItemPointerSet
 
 #define ItemPointerSet(pointer, blockNumber, offNum) \
 ( \
  AssertMacro(PointerIsValid(pointer)), \
  BlockIdSet(&((pointer)->ip_blkid), blockNumber), \
  (pointer)->ip_posid = offNum \
 )

11、PageGetItemId
获取行指针(ItemIdData指针) 

 #define PageGetItemId(page, offsetNumber) \
  ((ItemId) (&((PageHeader) (page))->pd_linp[(offsetNumber) - 1]))

12、PageGetItem
根据ItemId获取相应的Item(Tuple)
 
 #define PageGetItem(page, itemId) \
 ( \
     AssertMacro(PageIsValid(page)), \
     AssertMacro(ItemIdHasStorage(itemId)), \
     (Item)(((char *)(page)) + ItemIdGetOffset(itemId)) \
 )

 #define ItemIdGetOffset(itemId) \
  ((itemId)->lp_off)

二、源码解读


void
RelationPutHeapTuple(Relation relation,
                     Buffer buffer,
                     HeapTuple tuple,
                     bool token)
{
    Page        pageHeader;//页头
    OffsetNumber offnum;//行偏移

    
    //TODO token & speculatively有待考究
    Assert(!token || HeapTupleHeaderIsSpeculative(tuple->t_data));

    
    //根据buffer获取相应的page(页头)
    pageHeader = BufferGetPage(buffer);
    //插入数据,PageAddItem函数上一节已介绍,函数成功返回行偏移
   
    offnum = PageAddItem(pageHeader, (Item) tuple->t_data,
                         tuple->t_len, InvalidOffsetNumber, false, true);
    //如果不成功,记录日志
    if (offnum == InvalidOffsetNumber)
        elog(PANIC, "failed to add tuple to page");
    
    
    //&(tuple->t_self)类型为ItemPointer,亦即行指针(ItemPointerData结构体指针)
    //根据buffer获取块号,把块号和行偏移写入行指针中
    ItemPointerSet(&(tuple->t_self), BufferGetBlockNumber(buffer), offnum);

    
    if (!token)
    {
        //获取行指针,ItemId即ItemIdData指针
        ItemId      itemId = PageGetItemId(pageHeader, offnum);
        //获取TupleHeader
        HeapTupleHeader item = (HeapTupleHeader) PageGetItem(pageHeader, itemId);
        //更新TupleHeader中的行指针
        item->t_ctid = tuple->t_self;
    }
}

三、跟踪分析

使用上一节的数据表,回收垃圾后,插入一条记录。

testdb=# vacuum t_insert;
VACUUM
testdb=# 
testdb=# checkpoint;
CHECKPOINT
testdb=#  select pg_backend_pid();
 pg_backend_pid 
----------------
           1582
(1 row)

使用gdb进行跟踪分析:

[root@localhost ~]# gdb -p 1582
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7
...
(gdb) 

插入一条记录:

testdb=# -- 插入1行
testdb=# insert into t_insert values(10,'10','10','10');
(挂起)

回到gdb:

(gdb) b RelationPutHeapTuple
Breakpoint 1 at 0x4cf492: file hio.c, line 51.
#查看输入参数
(gdb) p *relation
$5 = {rd_node = {spcNode = 1663, dbNode = 16477, relNode = 26731}, rd_smgr = 0x259db68, rd_refcnt = 1, rd_backend = -1, rd_islocaltemp = false, rd_isnailed = false, rd_isvalid = true, 
  rd_indexvalid = 0 '\000', rd_statvalid = false, rd_createSubid = 0, rd_newRelfilenodeSubid = 0, rd_rel = 0x7fa9814589e8, rd_att = 0x7fa981458af8, rd_id = 26731, rd_lockInfo = {lockRelId = {
      relId = 26731, dbId = 16477}}, rd_rules = 0x0, rd_rulescxt = 0x0, trigdesc = 0x0, rd_rsdesc = 0x0, rd_fkeylist = 0x0, rd_fkeyvalid = false, rd_partkeycxt = 0x0, rd_partkey = 0x0, rd_pdcxt = 0x0, 
  rd_partdesc = 0x0, rd_partcheck = 0x0, rd_indexlist = 0x0, rd_oidindex = 0, rd_pkindex = 0, rd_replidindex = 0, rd_statlist = 0x0, rd_indexattr = 0x0, rd_projindexattr = 0x0, rd_keyattr = 0x0, 
  rd_pkattr = 0x0, rd_idattr = 0x0, rd_projidx = 0x0, rd_pubactions = 0x0, rd_options = 0x0, rd_index = 0x0, rd_indextuple = 0x0, rd_amhandler = 0, rd_indexcxt = 0x0, rd_amroutine = 0x0, 
  rd_opfamily = 0x0, rd_opcintype = 0x0, rd_support = 0x0, rd_supportinfo = 0x0, rd_indoption = 0x0, rd_indexprs = 0x0, rd_indpred = 0x0, rd_exclops = 0x0, rd_exclprocs = 0x0, rd_exclstrats = 0x0, 
  rd_amcache = 0x0, rd_indcollation = 0x0, rd_fdwroutine = 0x0, rd_toastoid = 0, pgstat_info = 0x2591850}
(gdb) p buffer
$6 = 95
(gdb) p tuple
$7 = (HeapTuple) 0x2539a20
(gdb) p *tuple  #注:HeapTuple
$8 = {t_len = 61, t_self = {ip_blkid = {bi_hi = 65535, bi_lo = 65535}, ip_posid = 0}, t_tableOid = 26731, t_data = 0x2539a38}
(gdb) p *tuple->t_data #注:HeapTupleHeader
$9 = {t_choice = {t_heap = {t_xmin = 1612851, t_xmax = 0, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {datum_len_ = 1612851, datum_typmod = 0, datum_typeid = 0}}, t_ctid = {ip_blkid = {
      bi_hi = 65535, bi_lo = 65535}, ip_posid = 0}, t_infomask2 = 4, t_infomask = 2050, t_hoff = 24 '\030', t_bits = 0x2539a4f ""}
(gdb) p token
$10 = false
#查看PageHeader信息
(gdb) p *(PageHeader)pageHeader
$11 = {pd_lsn = {xlogid = 1, xrecoff = 3677464616}, pd_checksum = 0, pd_flags = 5, pd_lower = 60, pd_upper = 7680, pd_special = 8192, pd_pagesize_version = 8196, pd_prune_xid = 0, 
  pd_linp = 0x7fa96957d318}
#调用PageAddItem函数后
(gdb) next
56      if (offnum == InvalidOffsetNumber)
(gdb) p offnum #2号Item被删除,在执行vacuum回收后,已可用
$12 = 2
(gdb) p *itemId
$13 = {lp_off = 7616, lp_flags = 1, lp_len = 61}
(gdb) p *item
$14 = {t_choice = {t_heap = {t_xmin = 1612851, t_xmax = 0, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {datum_len_ = 1612851, datum_typmod = 0, datum_typeid = 0}}, t_ctid = {ip_blkid = {
      bi_hi = 65535, bi_lo = 65535}, ip_posid = 0}, t_infomask2 = 4, t_infomask = 2050, t_hoff = 24 '\030', t_bits = 0x7fa96957f0d7 ""}
(gdb) next
74  }
(gdb) p *item
No symbol "item" in current context.
(gdb) p tuple->t_self
$15 = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 2} #0号Block,2号偏移
(gdb) c
Continuing.

可以看到,这行数据“正确”的插入在0号Block,2号偏移的位置上。

四、小结

1、基本理解RelationPutHeapTuple函数的实现逻辑和相关的数据结构;
2、在熟悉数据结构(包括宏定义&通用函数)的基础上,阅读源代码和使用gdb调试可以深入掌握PG处理数据“背后”的逻辑。
下一节,将会讲述调用栈中heap_insert函数。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 资料下载
  • 历年真题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯