Postgresql源码(139)vim直接修改postgresql表文件的简单实例 ...

打印 上一主题 下一主题

主题 1019|帖子 1019|积分 3057

1 前言


  • PG可以用pageinspect方便的读取查看表文件。
  • 本篇介绍一种用vim查看、编辑的方法,案例比较简单,主要分享原理。
   修改表文件和controlfile好坏常危险的行为,请不要在生产实验。
  2 用例

简化问题,用简单编码的数据类型。
  1. drop table t1;
  2. create table t1 (a int, b int);
  3. insert into t1 select t.i, t.i/10 from generate_series(1,1000) t(i);
  4. analyze t1;
  5. select relname,relfilenode,relpages from pg_class where relname = 't1';
  6. relname | relfilenode | relpages
  7. ---------+-------------+----------
  8. t1      |       49158 |        5
  9. checkpoint;
  10. select ctid, xmin, * from t1 order by 1 limit 10 offset 220;
  11.   ctid   |   xmin   |  a  | b
  12. ---------+----------+-----+----
  13. (0,221) | 50889007 | 221 | 22
  14. (0,222) | 50889007 | 222 | 22
  15. (0,223) | 50889007 | 223 | 22
  16. (0,224) | 50889007 | 224 | 22
  17. (0,225) | 50889007 | 225 | 22
  18. (0,226) | 50889007 | 226 | 22
  19. (1,1)   | 50889007 | 227 | 22
  20. (1,2)   | 50889007 | 228 | 22
  21. (1,3)   | 50889007 | 229 | 22
  22. (1,4)   | 50889007 | 230 | 23
复制代码
模拟页面损坏,编辑修改a=230 b=23这一行的数据,把xmin编辑成错误的值,制造报错。
3 vim打开表文件找到页面位置

前面查询到表文件49158,使用vim编辑并转换为16进制显示:

转换后

每个页面8KB,一个页面的地点占用0x2000,以是一号页面的地点范围是0x2000 - 0x4000
(ctid得知我们需要修改的数据在1号页面)

4 根据地点定位元组位置

ctid = (1,4)  元组在1号页面的第4个位置,根据PG的页面结构,我们从页面尾部,向上找第四个元组
  1. postgres=# select ctid, xmin, * from t1 where a = 230;
  2. ctid  |   xmin   |  a  | b
  3. -------+----------+-----+----
  4. (1,4) | 50889007 | 230 | 23
复制代码

根据页面地点范围,1号页面的结尾在00004000处,向上找四个元组,因为这里元组都是定长的,可以按规律找四个即可。这里数据简单可以用这种方法。
如果数据类型比较复杂,长短不一,可根据每个元组头部的xmin确定位置。例如我们需要找的元组的xmin是50889007,看到下面第四行的元组头部为
2f81 0803 = 十六进制:0x308812f = 十进制:50889007 即是xmin的值
以是可以根据xmin的值,从末了一个一个向上找。

5 怎样解析表中的二进制数据?

拿到页面1的第四行数据:
  1. 00003f80: 2f81 0803 0000 0000 0000 0000 0000 0100
  2. 00003f90: 0400 0200 0009 1800 e600 0000 1700 0000
复制代码
这段数据可以分成三段来看
  1. 第一部分:HeapTupleHeaderData
  2. -----------------------------------
  3. 第二部分:nulls bitmap 不一定有
  4. -----------------------------------
  5. 第三部分:具体数据值
复制代码
头上肯定是一个HeapTupleHeaderData,而HeapTupleHeaderData的第一个元素是一个uint32记录的xmin。
  1. struct HeapTupleHeaderData
  2. {
  3.         union
  4.         {
  5.                 HeapTupleFields t_heap;
  6.                 DatumTupleFields t_datum;
  7.         }                        t_choice;
  8.         ItemPointerData t_ctid;                /* current TID of this or newer tuple (or a
  9.         uint16                t_infomask2;        /* number of attributes + various flags */
  10.         uint16                t_infomask;                /* various flag bits, see below */
  11.         uint8                t_hoff;                        /* sizeof header incl. bitmap, padding */
  12.         bits8                t_bits[FLEXIBLE_ARRAY_MEMBER];        /* bitmap of NULLs */
  13.         /* MORE DATA FOLLOWS AT END OF STRUCT */
  14. };
  15. typedef struct HeapTupleFields
  16. {
  17.         TransactionId t_xmin;                /* inserting xact ID */
  18.         TransactionId t_xmax;                /* deleting or locking xact ID */
  19.         union
  20.         {
  21.                 CommandId        t_cid;                /* inserting or deleting command ID, or both */
  22.                 TransactionId t_xvac;        /* old-style VACUUM FULL xact ID */
  23.         }                        t_field3;
  24. } HeapTupleFields;
复制代码
以是按次序来解析,因为我这台机器是小端序的(lscpu可以看),以是低地点存低位的值,解析出来:


  • 2f81 0803 = 十六进制:0x308812f = 十进制:50889007 即是xmin的值。
  • 0200 = 十六进制:0x2 = t_infomask2
  • 0009 = 十六进制:0x900 = t_infomask
  • e600 0000 = 十六进制:0xe6 = 十进制:230 = a列的值
  • 1700 0000 = 十六进制:0x17 = 十进制:23  = b列的值
  1. 00003f80:           2f81 0803 0000 0000 0000 0000 0000 0100
  2.                     |        |         |         |         
  3.                     |xmin(4B)| xmax(4B)| cid(4B) |  t_ctid
  4.                     
  5. 00003f90:           0400  0200  0009 1800      e600 0000 1700 0000
  6.                          |mask2|mask|hoff|     这里没有null值数组,所以就是数据了。
  7.    
  8. t_infomask  = 0x900
  9. t_infomask2 = 0x02
  10. t_hoff      = 0x18
复制代码
结果和真实数据对得上:
  1. postgres=# select ctid, xmin, * from t1 where a = 230;
  2. ctid  |   xmin   |  a  | b
  3. -------+----------+-----+----
  4. (1,4) | 50889007 | 230 | 23
复制代码
6 vim修改表文件中的数据

先做个实验,把b列的值也改成230:
  1. select ctid, xmin, * from t1 where a = 230;
  2. ctid  |   xmin   |  a  | b
  3. -------+----------+-----+----
  4. (1,4) | 50889007 | 230 | 23
复制代码

使用vim编辑

转换回二进制:

wq生存退出。
重新查询b照旧23,为什么呢?因为页面已经在shared_buffer中了,现在没有负载以是页面不会重新从磁盘读上来。

这里需要重启一下,注意不要kill -9,redo有大概会把数据盖掉,让数据库正常checkpoint后关闭,在启动:


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

玛卡巴卡的卡巴卡玛

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表