diff --git a/doc/he3db/safeutility/images/media/image1.jpeg b/doc/he3db/safeutility/images/media/image1.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..69182aa5b727b2efceb722c164c43dc99cef139f Binary files /dev/null and b/doc/he3db/safeutility/images/media/image1.jpeg differ diff --git a/doc/he3db/safeutility/images/media/image10.png b/doc/he3db/safeutility/images/media/image10.png new file mode 100644 index 0000000000000000000000000000000000000000..05ed41f91d4d52bb848f65f0a46b7d2623d17bd4 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image10.png differ diff --git a/doc/he3db/safeutility/images/media/image11.png b/doc/he3db/safeutility/images/media/image11.png new file mode 100644 index 0000000000000000000000000000000000000000..ab5cdc1b6d366a7c512eb8d8b8bed2e1b777f7f1 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image11.png differ diff --git a/doc/he3db/safeutility/images/media/image12.png b/doc/he3db/safeutility/images/media/image12.png new file mode 100644 index 0000000000000000000000000000000000000000..2fe9b75db1508d60c7c2c3d8ff1062bb4c39f455 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image12.png differ diff --git a/doc/he3db/safeutility/images/media/image13.png b/doc/he3db/safeutility/images/media/image13.png new file mode 100644 index 0000000000000000000000000000000000000000..81b4ff21925b2d1b3aceed12b0cb4fecaab775b5 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image13.png differ diff --git a/doc/he3db/safeutility/images/media/image14.png b/doc/he3db/safeutility/images/media/image14.png new file mode 100644 index 0000000000000000000000000000000000000000..ec039bcc6598fdabdeb4e162b903fe77382317d3 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image14.png differ diff --git a/doc/he3db/safeutility/images/media/image15.png b/doc/he3db/safeutility/images/media/image15.png new file mode 100644 index 0000000000000000000000000000000000000000..98cc06b78d179b48ef6f0037afa17bf12117a486 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image15.png differ diff --git a/doc/he3db/safeutility/images/media/image16.png b/doc/he3db/safeutility/images/media/image16.png new file mode 100644 index 0000000000000000000000000000000000000000..481d9272f6076bd4a5912614c7186a7811c98363 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image16.png differ diff --git a/doc/he3db/safeutility/images/media/image17.png b/doc/he3db/safeutility/images/media/image17.png new file mode 100644 index 0000000000000000000000000000000000000000..e72ba4fa7f145329e7b58c501d1c0392415b4718 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image17.png differ diff --git a/doc/he3db/safeutility/images/media/image18.png b/doc/he3db/safeutility/images/media/image18.png new file mode 100644 index 0000000000000000000000000000000000000000..5b4d5d600de45dee749f3e1f796adf2fd76ba4d1 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image18.png differ diff --git a/doc/he3db/safeutility/images/media/image19.png b/doc/he3db/safeutility/images/media/image19.png new file mode 100644 index 0000000000000000000000000000000000000000..c54aa7bd5a5dead0bfc671be6c97a24ed872a516 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image19.png differ diff --git a/doc/he3db/safeutility/images/media/image2.png b/doc/he3db/safeutility/images/media/image2.png new file mode 100644 index 0000000000000000000000000000000000000000..140333e63271103a7f2077746a638b56c044f6de Binary files /dev/null and b/doc/he3db/safeutility/images/media/image2.png differ diff --git a/doc/he3db/safeutility/images/media/image20.png b/doc/he3db/safeutility/images/media/image20.png new file mode 100644 index 0000000000000000000000000000000000000000..031b2e1457f0875e0cffb0038eed9610d08b4ba0 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image20.png differ diff --git a/doc/he3db/safeutility/images/media/image21.png b/doc/he3db/safeutility/images/media/image21.png new file mode 100644 index 0000000000000000000000000000000000000000..99b19b1dbd68281fcc5ada0ccea9a88f61008206 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image21.png differ diff --git a/doc/he3db/safeutility/images/media/image22.png b/doc/he3db/safeutility/images/media/image22.png new file mode 100644 index 0000000000000000000000000000000000000000..36bf311a3acd44ce8152316cbdef1246f9eb54a4 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image22.png differ diff --git a/doc/he3db/safeutility/images/media/image23.png b/doc/he3db/safeutility/images/media/image23.png new file mode 100644 index 0000000000000000000000000000000000000000..7c34eb935d9b92e34b86e7b8f19589950bb5c68a Binary files /dev/null and b/doc/he3db/safeutility/images/media/image23.png differ diff --git a/doc/he3db/safeutility/images/media/image24.png b/doc/he3db/safeutility/images/media/image24.png new file mode 100644 index 0000000000000000000000000000000000000000..62faaf0e3140b8783c03588dac216d95421c524e Binary files /dev/null and b/doc/he3db/safeutility/images/media/image24.png differ diff --git a/doc/he3db/safeutility/images/media/image25.png b/doc/he3db/safeutility/images/media/image25.png new file mode 100644 index 0000000000000000000000000000000000000000..510aea9e36cd4bf814ee1f9a0a04ff6fbb533971 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image25.png differ diff --git a/doc/he3db/safeutility/images/media/image26.png b/doc/he3db/safeutility/images/media/image26.png new file mode 100644 index 0000000000000000000000000000000000000000..b90aa5c8f7b435743c7677a8a89d4f3d85137935 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image26.png differ diff --git a/doc/he3db/safeutility/images/media/image27.png b/doc/he3db/safeutility/images/media/image27.png new file mode 100644 index 0000000000000000000000000000000000000000..8b516cf39e1460e67f2019b70bacb69abb6eeac0 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image27.png differ diff --git a/doc/he3db/safeutility/images/media/image28.png b/doc/he3db/safeutility/images/media/image28.png new file mode 100644 index 0000000000000000000000000000000000000000..fb6189c662aae532bf995c225883cc143814ec6f Binary files /dev/null and b/doc/he3db/safeutility/images/media/image28.png differ diff --git a/doc/he3db/safeutility/images/media/image29.png b/doc/he3db/safeutility/images/media/image29.png new file mode 100644 index 0000000000000000000000000000000000000000..47bcb8b4b3a986c75b9f3b30992a89a6f1df2463 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image29.png differ diff --git a/doc/he3db/safeutility/images/media/image3.png b/doc/he3db/safeutility/images/media/image3.png new file mode 100644 index 0000000000000000000000000000000000000000..c95f9c6262d8c7f18c95179b3d5a93d7dde73b8a Binary files /dev/null and b/doc/he3db/safeutility/images/media/image3.png differ diff --git a/doc/he3db/safeutility/images/media/image30.png b/doc/he3db/safeutility/images/media/image30.png new file mode 100644 index 0000000000000000000000000000000000000000..255b1822f2fb188c45da8805c7c8598364865e55 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image30.png differ diff --git a/doc/he3db/safeutility/images/media/image31.png b/doc/he3db/safeutility/images/media/image31.png new file mode 100644 index 0000000000000000000000000000000000000000..93d8e8a8c10ce6776a2e7f575f0af6b05ba7a8b4 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image31.png differ diff --git a/doc/he3db/safeutility/images/media/image32.png b/doc/he3db/safeutility/images/media/image32.png new file mode 100644 index 0000000000000000000000000000000000000000..d794f6e5207cfa35ae50be5934775eeafed9f3ff Binary files /dev/null and b/doc/he3db/safeutility/images/media/image32.png differ diff --git a/doc/he3db/safeutility/images/media/image33.png b/doc/he3db/safeutility/images/media/image33.png new file mode 100644 index 0000000000000000000000000000000000000000..d3b5506e7826620d89d203a8eb4916283f93f574 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image33.png differ diff --git a/doc/he3db/safeutility/images/media/image34.png b/doc/he3db/safeutility/images/media/image34.png new file mode 100644 index 0000000000000000000000000000000000000000..d8ebcd94bb32c31c858026c8090c27eac1a4edf6 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image34.png differ diff --git a/doc/he3db/safeutility/images/media/image35.png b/doc/he3db/safeutility/images/media/image35.png new file mode 100644 index 0000000000000000000000000000000000000000..da0d2fa0d158cf5b51e3fe3cff0f86d9192d7caa Binary files /dev/null and b/doc/he3db/safeutility/images/media/image35.png differ diff --git a/doc/he3db/safeutility/images/media/image36.png b/doc/he3db/safeutility/images/media/image36.png new file mode 100644 index 0000000000000000000000000000000000000000..de3044f4e8092dd42e708f87f983a1ce83010ad7 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image36.png differ diff --git a/doc/he3db/safeutility/images/media/image37.png b/doc/he3db/safeutility/images/media/image37.png new file mode 100644 index 0000000000000000000000000000000000000000..9af301a00c95fa723f15eebaed9e4e5062cbda35 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image37.png differ diff --git a/doc/he3db/safeutility/images/media/image38.png b/doc/he3db/safeutility/images/media/image38.png new file mode 100644 index 0000000000000000000000000000000000000000..956115b94ffb31fa209dd84daebe222ec0ed5615 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image38.png differ diff --git a/doc/he3db/safeutility/images/media/image39.png b/doc/he3db/safeutility/images/media/image39.png new file mode 100644 index 0000000000000000000000000000000000000000..3bc7643424d8df55171969140466ab9e753d9bc4 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image39.png differ diff --git a/doc/he3db/safeutility/images/media/image4.png b/doc/he3db/safeutility/images/media/image4.png new file mode 100644 index 0000000000000000000000000000000000000000..45e574dfd977c8f10852bbaf2a5fb2c785ee34ee Binary files /dev/null and b/doc/he3db/safeutility/images/media/image4.png differ diff --git a/doc/he3db/safeutility/images/media/image40.png b/doc/he3db/safeutility/images/media/image40.png new file mode 100644 index 0000000000000000000000000000000000000000..f34fd548cf26e21ca6191749a83d91a5c1f07b5f Binary files /dev/null and b/doc/he3db/safeutility/images/media/image40.png differ diff --git a/doc/he3db/safeutility/images/media/image41.png b/doc/he3db/safeutility/images/media/image41.png new file mode 100644 index 0000000000000000000000000000000000000000..4a47652a113eb44b91d278535f9b74a0db218c40 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image41.png differ diff --git a/doc/he3db/safeutility/images/media/image42.png b/doc/he3db/safeutility/images/media/image42.png new file mode 100644 index 0000000000000000000000000000000000000000..ad5f544cb08abcebd8df5c52f87aef6beca72939 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image42.png differ diff --git a/doc/he3db/safeutility/images/media/image43.png b/doc/he3db/safeutility/images/media/image43.png new file mode 100644 index 0000000000000000000000000000000000000000..7753dd0f71606a635759ff40444c64e21c6b7fac Binary files /dev/null and b/doc/he3db/safeutility/images/media/image43.png differ diff --git a/doc/he3db/safeutility/images/media/image44.png b/doc/he3db/safeutility/images/media/image44.png new file mode 100644 index 0000000000000000000000000000000000000000..d137be5a47a48f0aa088d8f048e455b7c27b2bc8 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image44.png differ diff --git a/doc/he3db/safeutility/images/media/image45.png b/doc/he3db/safeutility/images/media/image45.png new file mode 100644 index 0000000000000000000000000000000000000000..3d7f5436e6e68bff95f98e24b8df3682b2412c7b Binary files /dev/null and b/doc/he3db/safeutility/images/media/image45.png differ diff --git a/doc/he3db/safeutility/images/media/image46.png b/doc/he3db/safeutility/images/media/image46.png new file mode 100644 index 0000000000000000000000000000000000000000..34b11f491dbcec1c6a0978205526069e8b49201e Binary files /dev/null and b/doc/he3db/safeutility/images/media/image46.png differ diff --git a/doc/he3db/safeutility/images/media/image5.png b/doc/he3db/safeutility/images/media/image5.png new file mode 100644 index 0000000000000000000000000000000000000000..0596bec8e5eaa36a2acc4d0f592c62a2b2c9e9b1 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image5.png differ diff --git a/doc/he3db/safeutility/images/media/image6.png b/doc/he3db/safeutility/images/media/image6.png new file mode 100644 index 0000000000000000000000000000000000000000..c501341d849aca91d7b13039277a5fb37a016045 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image6.png differ diff --git a/doc/he3db/safeutility/images/media/image7.png b/doc/he3db/safeutility/images/media/image7.png new file mode 100644 index 0000000000000000000000000000000000000000..af5dd1159fff8d04cba0386a8692857a63697f3f Binary files /dev/null and b/doc/he3db/safeutility/images/media/image7.png differ diff --git a/doc/he3db/safeutility/images/media/image8.png b/doc/he3db/safeutility/images/media/image8.png new file mode 100644 index 0000000000000000000000000000000000000000..32049f8d491ffc9adc74fdd45cc3ba7730dcedbf Binary files /dev/null and b/doc/he3db/safeutility/images/media/image8.png differ diff --git a/doc/he3db/safeutility/images/media/image9.png b/doc/he3db/safeutility/images/media/image9.png new file mode 100644 index 0000000000000000000000000000000000000000..0bb050aa92250e1d88ccc81400b8978a2a514437 Binary files /dev/null and b/doc/he3db/safeutility/images/media/image9.png differ diff --git a/doc/he3db/safeutility/readme.md b/doc/he3db/safeutility/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..2e698e3f7b145fa3df2ce2c9df1ef5570b046351 --- /dev/null +++ b/doc/he3db/safeutility/readme.md @@ -0,0 +1,11753 @@ +# 工具箱 + +目录 + +[工具箱 [1](#工具箱)](#工具箱) + +[1. 系统表(新增) [3](#系统表新增)](#系统表新增) + +[确定系统表、属性列OID [3](#确定系统表属性列oid)](#确定系统表属性列oid) + +[新建he3_example.h [3](#新建he3_example.h)](#新建he3_example.h) + +[新建he3_example.c [6](#新建he3_example.c)](#新建he3_example.c) + +[新建he3_example.dat(可选) +[11](#新建he3_example.dat可选)](#新建he3_example.dat可选) + +[动态更新系统表数据 [12](#动态更新系统表数据)](#动态更新系统表数据) + +[修改Makefile [12](#修改makefile)](#修改makefile) + +[重新编译安装 [14](#重新编译安装)](#重新编译安装) + +[重新初始化或升级 [14](#重新初始化或升级)](#重新初始化或升级) + +[测试 [14](#测试)](#测试) + +[新建表示例二 [14](#新建表示例二)](#新建表示例二) + +[新建表示例三 [22](#新建表示例三)](#新建表示例三) + +[2. 系统表(修改) [52](#系统表修改)](#系统表修改) + +[3. 视图(新增) [56](#视图新增)](#视图新增) + +[修改system_views.sql文件 +[56](#修改system_views.sql文件)](#修改system_views.sql文件) + +[重新编译安装 [56](#重新编译安装-1)](#重新编译安装-1) + +[4. 新增内置函数 [56](#新增内置函数)](#新增内置函数) + +[5. 新增参数 [58](#新增参数)](#新增参数) + +[6. 共享内存 [63](#共享内存)](#共享内存) + +[7. pg关键字说明 [66](#pg关键字说明)](#pg关键字说明) + +[8. 字符串操作 [66](#字符串操作)](#字符串操作) + +[string标准库 [66](#string标准库)](#string标准库) + +[ctype标准库 [68](#ctype标准库)](#ctype标准库) + +[string.h标准库高级用法 +[69](#string.h标准库高级用法)](#string.h标准库高级用法) + +[正则表达式 [70](#正则表达式)](#正则表达式) + +[Sscanf(format) [70](#sscanfformat)](#sscanfformat) + +[ASCALL码 [73](#ascall码)](#ascall码) + +[pg密码校验demo [74](#pg密码校验demo)](#pg密码校验demo) + +[9. 随机数 [80](#随机数)](#随机数) + +[生成随机数 [80](#生成随机数)](#生成随机数) + +[打乱数组顺序 [81](#打乱数组顺序)](#打乱数组顺序) + +[10. 系统时间 [81](#系统时间)](#系统时间) + +[pg自定义 [81](#pg自定义)](#pg自定义) + +[用户使用 [82](#用户使用)](#用户使用) + +[c语言自带 [86](#c语言自带)](#c语言自带) + +[时间类型相关操作 [87](#时间类型相关操作)](#时间类型相关操作) + +[11. 用户相关 [102](#用户相关)](#用户相关) + +[12. Libpq [102](#libpq)](#libpq) + +[13. 用其他账号访问 [104](#用其他账号访问)](#用其他账号访问) + +[14. 自定义语法 [104](#自定义语法)](#自定义语法) + +[15. 用户认证 [105](#用户认证)](#用户认证) + +[16. 授权 [105](#授权)](#授权) + +[17. 新增一种权限(一) [105](#新增一种权限一)](#新增一种权限一) + +[18. 新增一种权限(二)security label方式 +[106](#新增一种权限二security-label方式)](#新增一种权限二security-label方式) + +[19. 系统表定义可空列 [112](#系统表定义可空列)](#系统表定义可空列) + +[20. 语法(表)from [113](#语法表from)](#语法表from) + +[21. 语法(函数表达式) [116](#语法函数表达式)](#语法函数表达式) + +[22. 语法(列名)及相关操作(list类型) +[119](#语法列名及相关操作list类型)](#语法列名及相关操作list类型) + +[23. 新增一种语法(一) [122](#新增一种语法一)](#新增一种语法一) + +[Checkpoint(简单) [122](#checkpoint简单)](#checkpoint简单) + +[Drop database(中等) [133](#drop-database中等)](#drop-database中等) + +[24. Security label操作 [136](#security-label操作)](#security-label操作) + +[原理 [136](#原理)](#原理) + +[示例 [138](#示例)](#示例) + +[25. 权限继承 [143](#权限继承)](#权限继承) + +[26. 鉴权 [143](#鉴权)](#鉴权) + +[27. 干预查询树 [156](#干预查询树)](#干预查询树) + +[28. Superuser [156](#superuser)](#superuser) + +[29. Shell [156](#shell)](#shell) + +[30. 生成随机数据 [159](#生成随机数据)](#生成随机数据) + +[31. 加密、解密函数 [161](#加密解密函数)](#加密解密函数) + +[32. M4 [162](#m4)](#m4) + +[33. Encode [162](#encode)](#encode) + +[34. Hash [163](#hash)](#hash) + +[35. 秘钥 [164](#秘钥)](#秘钥) + +[36. 审计日志 [164](#审计日志)](#审计日志) + +[37. Spi [165](#spi)](#spi) + +[38. SQL引擎关键数据结构 +[173](#sql引擎关键数据结构)](#sql引擎关键数据结构) + +[Port [173](#port)](#port) + +[RawStmt(SelectStmt) [174](#rawstmtselectstmt)](#rawstmtselectstmt) + +[RawStmt(SelectStmt-相关操作样本代码) +[175](#rawstmtselectstmt-相关操作样本代码)](#rawstmtselectstmt-相关操作样本代码) + +[Query [177](#query)](#query) + +[PlannedStmt [179](#plannedstmt)](#plannedstmt) + +[PlannedStmt(相关操作样本代码) +[180](#plannedstmt相关操作样本代码)](#plannedstmt相关操作样本代码) + +[39. oid和数据库对象名相互转换 +[180](#oid和数据库对象名相互转换)](#oid和数据库对象名相互转换) + +[现有收集到的转换函数 +[180](#现有收集到的转换函数)](#现有收集到的转换函数) + +[通过系统表获取oid或者对象名 +[181](#通过系统表获取oid或者对象名)](#通过系统表获取oid或者对象名) + +[40. 类型转换 [181](#类型转换)](#类型转换) + +[41. 新增语法(二) [195](#新增语法二)](#新增语法二) + +[42. 行级访问控制(含security label相关操作) +[195](#行级访问控制含security-label相关操作)](#行级访问控制含security-label相关操作) + +[读规则(原有policy) [195](#读规则原有policy)](#读规则原有policy) + +[手动构造where过滤条件(修改语法树) +[195](#手动构造where过滤条件修改语法树)](#手动构造where过滤条件修改语法树) + +[读检查(读取slot中的字段,并检查,主要是做行数据访问控制,依赖sl) +[196](#读检查读取slot中的字段并检查主要是做行数据访问控制依赖sl)](#读检查读取slot中的字段并检查主要是做行数据访问控制依赖sl) + +[读访问控制实例:年级信息表,包括高一、高二、高三三个年级,高一的老师只能读取高一的年级信息,高二的老师只能读取高一、高二的年级信息,高三的老师可以读取所有年级的信息。 +[196](#读访问控制实例年级信息表包括高一高二高三三个年级高一的老师只能读取高一的年级信息高二的老师只能读取高一高二的年级信息高三的老师可以读取所有年级的信息)](#读访问控制实例年级信息表包括高一高二高三三个年级高一的老师只能读取高一的年级信息高二的老师只能读取高一高二的年级信息高三的老师可以读取所有年级的信息) + +[写规则(原有policy) [204](#写规则原有policy)](#写规则原有policy) + +[写检查 [204](#写检查)](#写检查) + +[insert(包含示例) [204](#insert包含示例)](#insert包含示例) + +[Update(包含示例) [206](#update包含示例)](#update包含示例) + +[43. 列级访问控制 [208](#列级访问控制)](#列级访问控制) + +[列过滤(参照ExecFilterJunk重构返回元祖) +[208](#列过滤参照execfilterjunk重构返回元祖)](#列过滤参照execfilterjunk重构返回元祖) + +[示例:如果用户读取的列没有权限则不返回(只返回有权限的列) +[209](#示例如果用户读取的列没有权限则不返回只返回有权限的列)](#示例如果用户读取的列没有权限则不返回只返回有权限的列) + +[44. Initdb [211](#initdb)](#initdb) + +[45. Hook [211](#hook)](#hook) + +[46. 演练1 [212](#演练1)](#演练1) + +[47. 演练2 [223](#演练2)](#演练2) + +[48. 演练3 [234](#演练3)](#演练3) + +## 系统表(新增) + +### 确定系统表、属性列OID + +为避免与现有oid重复,需要在src/include/catalog/目录下执行perl +./unused_oids来获取未被占用的oid(也会同时输出已被占用的oid)。 + ++-----------------------------------------------------------------------+ +| \$ perl src/include/catalog/unused_oids | +| | +| 6 - 9 | +| | +| 111 | +| | +| 。。。 | +| | +| 6273 - 9999 | +| | +| 10001 - 16382 | +| | +| Patches should use a more-or-less consecutive range of OIDs. | +| | +| Best practice is to start with a random choice in the range | +| 8000-9999. | +| | +| Suggested random unused OID: 9295 (705 consecutive OID(s) available | +| starting here) | ++-----------------------------------------------------------------------+ + +后续我们优先选择8000-9999范围的OID。 + +### 新建he3_example.h + +在src/include/catalog/目录下面新建he3_example.h文件,内容如下: + ++------------------------------------------------------------------------------------------------------------------------------------------------------+ +| /\*\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* | +| | +| \* he3_example.h | +| | +| \* definition of the \"access method operator\" system catalog | +| | +| \*(he3_example) | +| | +| \* | +| | +| \* src/include/catalog/he3_example.h | +| | +| \* | +| | +| \* NOTES | +| | +| \* The Catalog.pm module reads this file and derives schema | +| | +| \* information. | +| | +| \* | +| | +| \*\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| #ifndef HE3_EXAMPLE_H | +| | +| #define HE3_EXAMPLE_H | +| | +| #include \"catalog/he3_example_d.h\" | +| | +| #include \"catalog/genbki.h\" | +| | +| /\* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* he3_example definition. cpp turns this into | +| | +| \* typedef struct FormData_he3_example | +| | +| \* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| CATALOG(he3_example,8200,He3ExampleRelationId) { | +| | +| Oid oid; /\* get by executing ./unused_oids in src/include/catalog/ \*/ | +| | +| int32 number BKI_DEFAULT(0); | +| | +| text tname BKI_DEFAULT(\_null\_); | +| | +| } | +| | +| FormData_he3_example; | +| | +| /\* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* Form_he3_example corresponds to a pointer to a tuple with | +| | +| \* the format of he3_example relation. | +| | +| \* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| typedef FormData_he3_example \*Form_he3_example; | +| | +| DECLARE_UNIQUE_INDEX_PKEY(he3_example_oid_index, 8201, HE3ExampleOidIndexId, on he3_example using btree(oid oid_ops)); | +| | +| DECLARE_INDEX(he3_example_number_index, 8202, HE3ExampleNumberIndexId, on he3_example using btree(number int4_ops)); | +| | +| extern void InsertHe3ExampleTuple(int number, char \*tname); | +| | +| extern void UpdateHe3ExampleTuple(int number, int new_number); | +| | +| extern void DeleteHe3ExampleTuple(int number); | +| | +| #endif /\* HE3_EXAMPLE_H \*/ | ++------------------------------------------------------------------------------------------------------------------------------------------------------+ + +注意事项: + +1\. 注释格式仅能使用/\* \*/,不允许使用//。 + +### 新建he3_example.c + +在src/backend/catalog目录中新建he3_example.c文件: + ++------------------------------------------------------------------------------------------------------------------------------------------------------+ +| /\*\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* | +| | +| \* he3_example.c | +| | +| \* routines to support manipulation of the he3_example relation | +| | +| \* | +| | +| \* IDENTIFICATION | +| | +| \* src/backend/catalog/he3_example.c | +| | +| \* | +| | +| \*\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| #include \"postgres.h\" | +| | +| #include \"access/table.h\" | +| | +| #include \"access/genam.h\" | +| | +| #include \"access/heapam.h\" | +| | +| #include \"catalog/catalog.h\" | +| | +| #include \"catalog/he3_example.h\" | +| | +| #include \"utils/builtins.h\" | +| | +| #include \"utils/fmgroids.h\" | +| | +| #include \"utils/rel.h\" | +| | +| #include \"utils/syscache.h\" | +| | +| #include \"utils/snapmgr.h\" | +| | +| /\* | +| | +| \* insert new tuple into he3_exmaple | +| | +| \*/ | +| | +| void InsertHe3ExampleTuple(int number, char \*tname) { | +| | +| Relation rel; | +| | +| bool nulls\[Natts_he3_example\] = {0}; | +| | +| Datum values\[Natts_he3_example\]; | +| | +| int i = 0; | +| | +| values\[i++\] = GetNewObjectId(); | +| | +| values\[i++\] = number; | +| | +| values\[i++\] = CStringGetTextDatum(tname); | +| | +| rel = table_open(He3ExampleRelationId, RowExclusiveLock); | +| | +| HeapTuple tuple = heap_form_tuple(rel-\>rd_att, values, nulls); | +| | +| // 该函数内部会查找 Buffer 读取及可见性处理 | +| | +| CatalogTupleInsert(rel, tuple); | +| | +| elog(INFO, \"insert one tuple in he3_example \[oid = %d\] success\", He3ExampleRelationId); | +| | +| heap_freetuple(tuple); | +| | +| table_close(rel, RowExclusiveLock); | +| | +| } | +| | +| /\* | +| | +| \* update he3_exmaple tuple by column number | +| | +| \*/ | +| | +| void UpdateHe3ExampleTuple(int number, int new_number) { | +| | +| Relation rel; | +| | +| HeapTuple tuple, new_tuple; | +| | +| SysScanDesc scan; | +| | +| ScanKeyData key\[1\]; | +| | +| rel = table_open(He3ExampleRelationId, RowExclusiveLock); | +| | +| // 扫描条件:number列 等于 入参number | +| | +| ScanKeyInit(&key\[0\], Anum_he3_example_number, | +| | +| BTEqualStrategyNumber /\* Equality strategy number \*/, | +| | +| F_INT4EQ /\* Comparison operator \*/, number); | +| | +| scan = systable_beginscan(rel, HE3ExampleNumberIndexId, true, SnapshotSelf, 1, key); | +| | +| while (HeapTupleIsValid((tuple = systable_getnext(scan)))) { | +| | +| // 检查元组是否被其他事务修改 | +| | +| if (!HeapTupleHeaderXminCommitted(tuple-\>t_data)) { | +| | +| continue; | +| | +| } | +| | +| // 拷贝新 tuple,修改其值,然后更新 | +| | +| new_tuple = heap_copytuple(tuple); // 原 tuple 无需 free | +| | +| Form_he3_example form = (Form_he3_example)GETSTRUCT(new_tuple); | +| | +| form-\>number = new_number; | +| | +| // 该函数内部会查找 Buffer 读取及可见性判断 | +| | +| CatalogTupleUpdate(rel, &new_tuple-\>t_self, new_tuple); | +| | +| elog(INFO, \"update one tuple in he3_example \[oid = %d\] success\", He3ExampleRelationId); | +| | +| heap_freetuple(new_tuple); | +| | +| } | +| | +| systable_endscan(scan); | +| | +| table_close(rel, RowExclusiveLock); | +| | +| } | +| | +| /\* | +| | +| \* delete tuple from he3_example by column number | +| | +| \*/ | +| | +| void DeleteHe3ExampleTuple(int number) { | +| | +| HeapTuple tuple; | +| | +| Relation rel; | +| | +| SysScanDesc scan; | +| | +| ScanKeyData key\[1\]; | +| | +| rel = table_open(He3ExampleRelationId, RowExclusiveLock); | +| | +| // 扫描条件:number列 等于 入参number | +| | +| ScanKeyInit(&key\[0\], Anum_he3_example_number, | +| | +| BTEqualStrategyNumber /\* Equality strategy number \*/, | +| | +| F_INT4EQ /\* Comparison operator \*/, number); | +| | +| scan = systable_beginscan(rel, HE3ExampleNumberIndexId, true, NULL, 1, key); | +| | +| while (HeapTupleIsValid((tuple = systable_getnext(scan)))) { | +| | +| // 检查元组是否被其他事务修改 | +| | +| // if (!HeapTupleHeaderXminCommitted(tuple-\>t_data)) { | +| | +| // continue; | +| | +| // } | +| | +| // 该函数内部会查找 Buffer 读取及可见性判断 | +| | +| CatalogTupleDelete(rel, &tuple-\>t_self); | +| | +| elog(INFO, \"delete one tuple in he3_example \[oid = %d\] success\", He3ExampleRelationId); | +| | +| } | +| | +| systable_endscan(scan); | +| | +| table_close(rel, RowExclusiveLock); | +| | +| } | ++------------------------------------------------------------------------------------------------------------------------------------------------------+ + +### **新建he3_example.dat(可选)** + +新建系统表原始数据文件,即src/include/catalog/he3_example.dat + ++----------------------------------------------------------------------------------------------------------------------------------------------+ +| #\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \# | +| | +| \# he3_example.dat | +| | +| \# Initial contents of the he3_example system catalog. | +| | +| \# | +| | +| \# src/include/catalog/he3_example.dat | +| | +| \# | +| | +| #\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \[ | +| | +| \# Note: only bootstrap catalogs, ie those marked BKI_BOOTSTRAP, need to | +| | +| \# have entries here. Be sure that the OIDs listed here match those given in | +| | +| \# their CATALOG and BKI_ROWTYPE_OID macros. | +| | +| { oid =\> \'8301\', number =\> \'1\', tname =\> \'Allen\' }, | +| | +| { oid =\> \'8302\', number =\> \'2\', tname =\> \'Jack\' }, | +| | +| \] | ++----------------------------------------------------------------------------------------------------------------------------------------------+ + +### 动态更新系统表数据 + +如果所添加系统表为动态系统表,会涉及数据动态更新: + ++-----------------------------------------------------------------------+ +| // 工作进程一 | +| | +| InsertHe3ExampleTuple(100, \"str_100\"); | +| | +| InsertHe3ExampleTuple(100, \"str_100\"); | +| | +| InsertHe3ExampleTuple(200, \"str_200\"); | +| | +| InsertHe3ExampleTuple(200, \"str_200\"); | +| | +| InsertHe3ExampleTuple(500, \"str_500\"); | +| | +| InsertHe3ExampleTuple(300, \"str_300\"); | +| | +| InsertHe3ExampleTuple(300, \"str_300\"); | +| | +| // 工作进程二 | +| | +| UpdateHe3ExampleTuple(300, 800); | +| | +| // 工作进程三 | +| | +| DeleteHe3ExampleTuple(800); | ++-----------------------------------------------------------------------+ + +### 修改Makefile + +修改src/backend/catalog/Makefile文件,添加编译he3_example系统表的代码: + ++-----------------------------------------------------------------------+ +| \# **第一部分** | +| | +| OBJS = \\ | +| | +| aclchk.o \\ | +| | +| catalog.o \\ | +| | +| dependency.o \\ | +| | +| \...\... | +| | +| pg_type.o \\ | +| | +| storage.o \\ | +| | +| toasting.o | +| | +| OBJS += he3_example.o | +| | +| \...\... | +| | +| \# **第二部分** | +| | +| \# Note: the order of this list determines the order in which the | +| catalog | +| | +| \# header files are assembled into postgres.bki. BKI_BOOTSTRAP | +| catalogs | +| | +| \# must appear first, and pg_statistic before pg_statistic_ext_data, | +| and | +| | +| \# there are reputedly other, undocumented ordering dependencies. | +| | +| CATALOG_HEADERS := \\ | +| | +| pg_proc.h pg_type.h pg_attribute.h pg_class.h \\ | +| | +| pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h | +| \\ | +| | +| pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \\ | +| | +| pg_language.h pg_largeobject_metadata.h pg_largeobject.h | +| pg_aggregate.h \\ | +| | +| pg_statistic.h pg_statistic_ext.h pg_statistic_ext_data.h \\ | +| | +| pg_rewrite.h pg_trigger.h pg_event_trigger.h pg_description.h \\ | +| | +| pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \\ | +| | +| pg_database.h pg_db_role_setting.h pg_tablespace.h \\ | +| | +| pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \\ | +| | +| pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \\ | +| | +| pg_ts_parser.h pg_ts_template.h pg_extension.h \\ | +| | +| pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \\ | +| | +| pg_foreign_table.h pg_policy.h pg_replication_origin.h \\ | +| | +| pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \\ | +| | +| pg_collation.h pg_parameter_acl.h pg_partitioned_table.h \\ | +| | +| pg_range.h pg_transform.h \\ | +| | +| pg_sequence.h pg_publication.h pg_publication_namespace.h \\ | +| | +| pg_publication_rel.h pg_subscription.h pg_subscription_rel.h | +| | +| CATALOG_HEADERS += he3_example.h | +| | +| \...\... | +| | +| \# **第三部分** | +| | +| \# The .dat files we need can just be listed alphabetically. | +| | +| POSTGRES_BKI_DATA = \$(addprefix | +| \$(top_srcdir)/src/include/catalog/,\\ | +| | +| pg_aggregate.dat pg_am.dat pg_amop.dat pg_amproc.dat pg_authid.dat \\ | +| | +| pg_cast.dat pg_class.dat pg_collation.dat pg_conversion.dat \\ | +| | +| pg_database.dat pg_language.dat \\ | +| | +| pg_namespace.dat pg_opclass.dat pg_operator.dat pg_opfamily.dat \\ | +| | +| pg_proc.dat pg_range.dat pg_tablespace.dat \\ | +| | +| pg_ts_config.dat pg_ts_config_map.dat pg_ts_dict.dat pg_ts_parser.dat | +| \\ | +| | +| pg_ts_template.dat pg_type.dat \\ | +| | +| ) | +| | +| POSTGRES_BKI_DATA += \$(addprefix | +| \$(top_srcdir)/src/include/catalog/,\\ | +| | +| he3_example.dat \\ | +| | +| ) | +| | +| \...\... | ++-----------------------------------------------------------------------+ + +### 重新编译安装 + +重新编译以启用新系统表,完整示例: + ++--------------------------------------------------------------------------------------------------------------------+ +| \# | +| 即使在新增系统表后,重新编译时也不会重新生成postgres.bki文件,依然会使用旧版,需要先清理系统表缓存数据(主要是清理 | +| postgres.bki 等文件)后重新编译 | +| | +| cd src/backend/catalog/ && make maintainer-clean | +| | +| cd - | +| | +| \# 编译安装 | +| | +| ./configure \--prefix=/home/wslu/work/pg/he3pg \--enable-debug \--enable-cassert \--enable-depend CFLAGS=-O0 | +| | +| make -j4 | +| | +| make install | ++--------------------------------------------------------------------------------------------------------------------+ + +### 重新初始化或升级 + +重新初始化数据库目录,或通过升级更新工作目录系统表。 + +### 测试 + +登录数据库,查看新系统表: + ++-----------------------------------------------------------------------+ +| postgres=# select \* from he3_example; | +| | +| oid \| number \| tname | +| | +| \-\-\-\-\--+\-\-\-\-\-\-\--+\-\-\-\-\-\-- | +| | +| 8301 \| 1 \| Allen | +| | +| 8302 \| 2 \| Jack | +| | +| (2 rows) | ++-----------------------------------------------------------------------+ + +若为动态系统表,则需触发系统表更新,并多次查询,确保系统表数据符合预期。 + +### 新建表示例二 + ++-------------------------------------------------------------------------------------------------------------------------------------------------------+ +| **+++ b/src/backend/catalog/Makefile** | +| | +| @@ -43,7 +43,8 @@ OBJS = \\ | +| | +|         pg_subscription.o \\ | +| | +|         pg_type.o \\ | +| | +|         storage.o \\ | +| | +| \-       toasting.o | +| | +| \+       toasting.o \\ | +| | +| \+       pg_hot_data.o | +| | +| @@ -68,7 +68,7 @@ CATALOG_HEADERS := \\ | +| | +|         pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \\ | +| | +|         pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \\ | +| | +|         pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \\ | +| | +| \-       pg_subscription_rel.h pg_stat_share_storage.h | +| | +| \+       pg_subscription_rel.h pg_stat_share_storage.h pg_hot_data.h | +| | +|   | +| | +|  GENERATED_HEADERS := \$(CATALOG_HEADERS:%.h=%\_d.h) schemapg.h system_fk_info.h | +| | +|   | +| | +| **diff \--git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c** | +| | +| **index 7d8c1d5..2711458 100644** | +| | +| **\-\-- a/src/backend/catalog/catalog.c** | +| | +| **+++ b/src/backend/catalog/catalog.c** | +| | +| @@ -40,6 +40,7 @@ | +| | +|  #include \"catalog/pg_stat_share_storage.h\" | +| | +|  #include \"catalog/pg_tablespace.h\" | +| | +|  #include \"catalog/pg_type.h\" | +| | +| +#include \"catalog/pg_hot_data.h\" | +| | +|  #include \"miscadmin.h\" | +| | +|  #include \"storage/fd.h\" | +| | +|  #include \"utils/fmgroids.h\" | +| | +| @@ -247,6 +248,7 @@ IsSharedRelation(Oid relationId) | +| | +|         if (relationId == AuthIdRelationId \|\| | +| | +|                 relationId == AuthMemRelationId \|\| | +| | +|                 relationId == DatabaseRelationId \|\| | +| | +| \+               relationId == HotDataRelationId \|\| | +| | +|                 relationId == SharedDescriptionRelationId \|\| | +| | +|                 relationId == SharedDependRelationId \|\| | +| | +|                 relationId == SharedSecLabelRelationId \|\| | +| | +| @@ -263,6 +265,7 @@ IsSharedRelation(Oid relationId) | +| | +|                 relationId == AuthMemMemRoleIndexId \|\| | +| | +|                 relationId == DatabaseNameIndexId \|\| | +| | +|                 relationId == DatabaseOidIndexId \|\| | +| | +| \+               relationId == HotDataDatnameRelnameIndexId \|\| | +| | +|                 relationId == SharedDescriptionObjIndexId \|\| | +| | +|                 relationId == SharedDependDependerIndexId \|\| | +| | +|                 relationId == SharedDependReferenceIndexId \|\| | +| | +| **diff \--git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c** | +| | +| **index 1664ee5..7e925d0 100644** | +| | +| **\-\-- a/src/backend/utils/cache/syscache.c** | +| | +| **+++ b/src/backend/utils/cache/syscache.c** | +| | +| @@ -74,6 +74,7 @@ | +| | +|  #include \"catalog/pg_ts_template.h\" | +| | +|  #include \"catalog/pg_type.h\" | +| | +|  #include \"catalog/pg_user_mapping.h\" | +| | +| +#include \"catalog/pg_hot_data.h\" | +| | +|  #include \"lib/qunique.h\" | +| | +|  #include \"utils/catcache.h\" | +| | +|  #include \"utils/rel.h\" | +| | +| @@ -475,6 +476,17 @@ static const struct cachedesc cacheinfo\[\] = { | +| | +|                 }, | +| | +|                 4 | +| | +|         }, | +| | +| \+       {HotDataRelationId, /\* HOTDATADATNAMERELNAME \*/ | +| | +| \+               HotDataDatnameRelnameIndexId, | +| | +| \+               2, | +| | +| \+               { | +| | +| \+                       Anum_pg_hot_data_datname, | +| | +| \+                       Anum_pg_hot_data_relname, | +| | +| \+                       0, | +| | +| \+                       0 | +| | +| \+               }, | +| | +| \+               4 | +| | +| \+       }, | +| | +|         {IndexRelationId,                       /\* INDEXRELID \*/ | +| | +|                 IndexRelidIndexId, | +| | +|                 1, | +| | +| **diff \--git a/src/include/catalog/pg_hot_data.h b/src/include/catalog/pg_hot_data.h** | +| | +| **new file mode 100644** | +| | +| **index 0000000..54667a4** | +| | +| **\-\-- /dev/null** | +| | +| **+++ b/src/include/catalog/pg_hot_data.h** | +| | +| @@ -0,0 +1,61 @@ | +| | +| +/\*\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \+ \* | +| | +| \+ \* pg_hot_data.h | +| | +| \+ \*        definition of the \"hot_data\" system catalog (pg_hot_data) | +| | +| \+ \* | +| | +| \+ \* | +| | +| \+ \* Portions Copyright (c) 2022, He3DB Global Development Group | +| | +| \+ \* | +| | +| \+ \* src/include/catalog/pg_hot_data.h | +| | +| \+ \* | +| | +| \+ \* NOTES | +| | +| \+ \*        The Catalog.pm module reads this file and derives schema | +| | +| \+ \*        information. | +| | +| \+ \* | +| | +| \+ | +| \*\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \+ \*/ | +| | +| +#ifndef PG_HOT_DATA_H | +| | +| +#define PG_HOT_DATA_H | +| | +| \+ | +| | +| +#include \"catalog/genbki.h\" | +| | +| +#include \"catalog/pg_hot_data_d.h\" | +| | +| \+ | +| | +| +/\* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \+ \*              pg_hot_data definition.  cpp turns this into | +| | +| \+ \*              typedef struct FormData_pg_hot_data | +| | +| \+ \* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \+ \*/ | +| | +| +CATALOG(pg_hot_data,4790,HotDataRelationId) BKI_SHARED_RELATION BKI_ROWTYPE_OID(4793,HotDataRelation_Rowtype_Id) BKI_SCHEMA_MACRO | +| | +| +{ | +| | +| +    /\* database name \*/ | +| | +| +    NameData        datname; | +| | +| \+ | +| | +| +    /\* relation name \*/ | +| | +| +    NameData        relname; | +| | +| \+ | +| | +| +    /\* caching rules \*/ | +| | +| +    char    crules; | +| | +| \+ | +| | +| +    /\* client addr \*/ | +| | +| +    NameData   clientaddr; | +| | +| \+ | +| | +| +#ifdef CATALOG_VARLEN                  /\* variable-length fields start here \*/ | +| | +| +    /\* cache rules schedule time \*/ | +| | +| +    timestamptz crulessettime; | +| | +| +    | +| | +| +    /\* hot data cache time \*/ | +| | +| +    timestamptz cachetime; | +| | +| +#endif | +| | +| +}FormData_pg_hot_data; | +| | +| \+ | +| | +| +/\* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \+ \*              Form_pg_hot_data corresponds to a pointer to a tuple with | +| | +| \+ \*              the format of pg_hot_data relation. | +| | +| \+ \* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \+ \*/ | +| | +| +typedef FormData_pg_hot_data \*Form_pg_hot_data; | +| | +| \+ | +| | +| +DECLARE_UNIQUE_INDEX(pg_hot_data_datname_relname_index, 4791, on pg_hot_data using btree(datname name_ops, relname name_ops)); | +| | +| +#define HotDataDatnameRelnameIndexId  4791 | +| | +| \+ | +| | +| +#endif | +| | +| \\ No newline at end of file | +| | +| **diff \--git a/src/include/utils/syscache.h b/src/include/utils/syscache.h** | +| | +| **index d74a348..e922edb 100644** | +| | +| **\-\-- a/src/include/utils/syscache.h** | +| | +| **+++ b/src/include/utils/syscache.h** | +| | +| @@ -63,6 +63,7 @@ enum SysCacheIdentifier | +| | +|         FOREIGNSERVERNAME, | +| | +|         FOREIGNSERVEROID, | +| | +|         FOREIGNTABLEREL, | +| | +| \+       HOTDATADATNAMERELNAME, | +| | +|         INDEXRELID, | +| | +|         LANGNAME, | +| | +|         LANGOID, | ++-------------------------------------------------------------------------------------------------------------------------------------------------------+ + +### 新建表示例三 + +he3_login_refuse.h + ++-----------------------------------------------------------------------+ +| #ifndef HE3_LOGIN_REFUSE_H | +| | +| #define HE3_LOGIN_REFUSE_H | +| | +| #include \"catalog/he3_login_refuse_d.h\" | +| | +| #include \"catalog/genbki.h\" | +| | +| /\* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* he3_login_refuse definition. cpp turns this into | +| | +| \* typedef struct FormData_he3_login_refuse | +| | +| \* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| typedef int64 timestamptz; | +| | +| CATALOG(he3_login_refuse,4790,He3LoginRefuseRelationId) | +| | +| { | +| | +| /\* user name \*/ | +| | +| NameData username; | +| | +| /\* db name \*/ | +| | +| NameData dbname; | +| | +| /\* fail login time \*/ | +| | +| timestamptz lastfailedtime; | +| | +| } | +| | +| FormData_he3_login_refuse; | +| | +| /\* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* Form_he3_login_refuse corresponds to a pointer to a tuple with | +| | +| \* the format of he3_login_refuse relation. | +| | +| \* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| typedef FormData_he3_login_refuse \*Form_he3_login_refuse; | +| | +| DECLARE_UNIQUE_INDEX(he3_login_refuse_username_dbname_index, 4791, | +| HE3LoginRefuseUsernameDbnameIndexId, on he3_login_refuse using | +| btree(username name_ops, dbname name_ops)); | +| | +| extern void InsertHe3LoginRefuseTuple(char \*username, char | +| \*dbname); | +| | +| extern void DeleteHe3LoginRefuseTuple(char \*username, char | +| \*dbname); | +| | +| extern void UpdateHe3LoginRefuseTuple(char \*username, char | +| \*dbname); | +| | +| extern timestamptz SelectHe3LoginRefuseTuple(char \*username, char | +| \*dbname); | +| | +| #endif /\* HE3_LOGIN_REFUSE_H \*/ | ++-----------------------------------------------------------------------+ + +he3_login_refuse.c + ++------------------------------------------------------------------------------------------------------------------------------------------------------+ +| /\*\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* | +| | +| \* he3_login_refuse.c | +| | +| \* routines to support access defend of the he3_login_refuse relation | +| | +| \* | +| | +| \* IDENTIFICATION | +| | +| \* src/backend/catalog/he3_login_refuse.c | +| | +| \* | +| | +| \*\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| #include \"postgres.h\" | +| | +| #include \"access/table.h\" | +| | +| #include \"access/genam.h\" | +| | +| #include \"access/heapam.h\" | +| | +| #include \"catalog/catalog.h\" | +| | +| #include \"catalog/he3_login_refuse.h\" | +| | +| #include \"utils/builtins.h\" | +| | +| #include \"utils/fmgroids.h\" | +| | +| #include \"utils/rel.h\" | +| | +| #include \"utils/syscache.h\" | +| | +| #include \"utils/snapmgr.h\" | +| | +| #include \"utils/timestamp.h\" | +| | +| /\* | +| | +| \* insert new tuple into he3_login_refuse | +| | +| \*/ | +| | +| void InsertHe3LoginRefuseTuple(char \*username, char \*dbname) | +| | +| { | +| | +| Relation rel; | +| | +| bool nulls\[Natts_he3_login_refuse\] = {0}; | +| | +| Datum values\[Natts_he3_login_refuse\]; | +| | +| int i = 0; | +| | +| NameData username1, dbname1; | +| | +| namestrcpy(&username1, username); | +| | +| namestrcpy(&dbname1, dbname); | +| | +| values\[i++\] = NameGetDatum(&username1); | +| | +| values\[i++\] = NameGetDatum(&dbname1); | +| | +| values\[i++\] = TimestampTzGetDatum(GetCurrentTimestamp()); | +| | +| rel = table_open(He3LoginRefuseRelationId, RowExclusiveLock); | +| | +| HeapTuple tuple = heap_form_tuple(rel-\>rd_att, values, nulls); | +| | +| CatalogTupleInsert(rel, tuple); | +| | +| elog(INFO, \"insert one tuple in he3_login_refuse success!\"); | +| | +| heap_freetuple(tuple); | +| | +| table_close(rel, RowExclusiveLock); | +| | +| } | +| | +| /\* | +| | +| \* delete tuple from he3_login_refuse by column username and dbname | +| | +| \*/ | +| | +| void DeleteHe3LoginRefuseTuple(char \*username, char \*dbname) | +| | +| { | +| | +| HeapTuple tuple; | +| | +| Relation rel; | +| | +| SysScanDesc scan; | +| | +| ScanKeyData key\[2\]; | +| | +| rel = table_open(He3LoginRefuseRelationId, RowExclusiveLock); | +| | +| NameData username1, dbname1; | +| | +| namestrcpy(&username1, username); | +| | +| namestrcpy(&dbname1, dbname); | +| | +| // 扫描条件:number列 等于 入参number | +| | +| ScanKeyInit(&key\[0\], Anum_he3_login_refuse_username, | +| | +| BTEqualStrategyNumber /\* Equality strategy number \*/, | +| | +| F_NAMEEQ /\* Comparison operator \*/, NameGetDatum(&username1)); | +| | +| ScanKeyInit(&key\[1\], Anum_he3_login_refuse_dbname, | +| | +| BTEqualStrategyNumber /\* Equality strategy number \*/, | +| | +| F_NAMEEQ /\* Comparison operator \*/, NameGetDatum(&dbname1)); | +| | +| scan = systable_beginscan(rel, HE3LoginRefuseUsernameDbnameIndexId, true, NULL, 2, key); | +| | +| while (HeapTupleIsValid((tuple = systable_getnext(scan)))) | +| | +| { | +| | +| // 检查元组是否被其他事务修改 | +| | +| // if (!HeapTupleHeaderXminCommitted(tuple-\>t_data)) { | +| | +| // continue; | +| | +| // } | +| | +| // 该函数内部会查找 Buffer 读取及可见性判断 | +| | +| CatalogTupleDelete(rel, &tuple-\>t_self); | +| | +| elog(INFO, \"delete one tuple in he3_login_refuse \[oid = %d\] success\", HE3LoginRefuseUsernameDbnameIndexId); | +| | +| } | +| | +| systable_endscan(scan); | +| | +| CommandCounterIncrement(); | +| | +| table_close(rel, RowExclusiveLock); | +| | +| } | +| | +| timestamptz SelectHe3LoginRefuseTuple(char \*username, char \*dbname) | +| | +| { | +| | +| HeapTuple tuple; | +| | +| Relation rel; | +| | +| SysScanDesc scan; | +| | +| ScanKeyData key\[2\]; | +| | +| timestamptz lastfailedtime; | +| | +| lastfailedtime = 0; | +| | +| rel = table_open(He3LoginRefuseRelationId, RowExclusiveLock); | +| | +| NameData username1, dbname1; | +| | +| namestrcpy(&username1, username); | +| | +| namestrcpy(&dbname1, dbname); | +| | +| // 扫描条件:number列 等于 入参number | +| | +| ScanKeyInit(&key\[0\], Anum_he3_login_refuse_username, | +| | +| BTEqualStrategyNumber /\* Equality strategy number \*/, | +| | +| F_NAMEEQ /\* Comparison operator \*/, NameGetDatum(&username1)); | +| | +| ScanKeyInit(&key\[1\], Anum_he3_login_refuse_dbname, | +| | +| BTEqualStrategyNumber /\* Equality strategy number \*/, | +| | +| F_NAMEEQ /\* Comparison operator \*/, NameGetDatum(&dbname1)); | +| | +| scan = systable_beginscan(rel, HE3LoginRefuseUsernameDbnameIndexId, true, NULL, 2, key); | +| | +| if (HeapTupleIsValid((tuple = systable_getnext(scan)))) | +| | +| { | +| | +| Form_he3_login_refuse relForm = (Form_he3_login_refuse) GETSTRUCT(tuple); | +| | +| lastfailedtime = relForm-\>lastfailedtime; | +| | +| } | +| | +| systable_endscan(scan); | +| | +| table_close(rel, RowExclusiveLock); | +| | +| return lastfailedtime; | +| | +| } | +| | +| void UpdateHe3LoginRefuseTuple(char \*username, char \*dbname) | +| | +| { | +| | +| HeapTuple tuple; | +| | +| Relation rel; | +| | +| SysScanDesc scan; | +| | +| ScanKeyData key\[2\]; | +| | +| rel = table_open(He3LoginRefuseRelationId, RowExclusiveLock); | +| | +| NameData username1, dbname1; | +| | +| namestrcpy(&username1, username); | +| | +| namestrcpy(&dbname1, dbname); | +| | +| // 扫描条件:number列 等于 入参number | +| | +| ScanKeyInit(&key\[0\], Anum_he3_login_refuse_username, | +| | +| BTEqualStrategyNumber /\* Equality strategy number \*/, | +| | +| F_NAMEEQ /\* Comparison operator \*/, NameGetDatum(&username1)); | +| | +| ScanKeyInit(&key\[1\], Anum_he3_login_refuse_dbname, | +| | +| BTEqualStrategyNumber /\* Equality strategy number \*/, | +| | +| F_NAMEEQ /\* Comparison operator \*/, NameGetDatum(&dbname1)); | +| | +| scan = systable_beginscan(rel, HE3LoginRefuseUsernameDbnameIndexId, true, NULL, 2, key); | +| | +| if (HeapTupleIsValid((tuple = systable_getnext(scan)))) | +| | +| { | +| | +| //lastfailedtime = relForm-\>lastfailedtime; | +| | +| HeapTuple new_Tuple; | +| | +| new_Tuple = heap_copytuple(tuple); | +| | +| Form_he3_login_refuse relForm = (Form_he3_login_refuse) GETSTRUCT(new_Tuple); | +| | +| relForm-\>lastfailedtime = GetCurrentTimestamp(); | +| | +| CatalogTupleUpdate(rel, &new_Tuple-\>t_self, new_Tuple); | +| | +| heap_freetuple(new_Tuple); | +| | +| } | +| | +| // ReleaseSysCache(pg_class_tuple); | +| | +| systable_endscan(scan); | +| | +| table_close(rel, RowExclusiveLock); | +| | +| } | ++------------------------------------------------------------------------------------------------------------------------------------------------------+ + +Makefile + ++----------------------------------------------------------------------------------------------------------------------------------------------------+ +| #\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \# | +| | +| \# Makefile for backend/catalog | +| | +| \# | +| | +| \# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group | +| | +| \# Portions Copyright (c) 1994, Regents of the University of California | +| | +| \# | +| | +| \# src/backend/catalog/Makefile | +| | +| \# | +| | +| #\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| subdir = src/backend/catalog | +| | +| top_builddir = ../../.. | +| | +| include \$(top_builddir)/src/Makefile.global | +| | +| #增加he3_login_refuse.o | +| | +| OBJS = \\ | +| | +| aclchk.o \\ | +| | +| catalog.o \\ | +| | +| dependency.o \\ | +| | +| heap.o \\ | +| | +| index.o \\ | +| | +| indexing.o \\ | +| | +| namespace.o \\ | +| | +| objectaccess.o \\ | +| | +| objectaddress.o \\ | +| | +| partition.o \\ | +| | +| pg_aggregate.o \\ | +| | +| pg_attrdef.o \\ | +| | +| pg_cast.o \\ | +| | +| pg_class.o \\ | +| | +| pg_collation.o \\ | +| | +| pg_constraint.o \\ | +| | +| pg_conversion.o \\ | +| | +| pg_db_role_setting.o \\ | +| | +| pg_depend.o \\ | +| | +| pg_enum.o \\ | +| | +| pg_inherits.o \\ | +| | +| pg_largeobject.o \\ | +| | +| pg_namespace.o \\ | +| | +| pg_operator.o \\ | +| | +| pg_parameter_acl.o \\ | +| | +| pg_proc.o \\ | +| | +| pg_publication.o \\ | +| | +| pg_range.o \\ | +| | +| pg_shdepend.o \\ | +| | +| pg_subscription.o \\ | +| | +| pg_type.o \\ | +| | +| storage.o \\ | +| | +| toasting.o \\ | +| | +| he3_login_refuse.o | +| | +| ifeq (\$(enable_extra_catalog), yes) | +| | +| OBJS += he3_btree_index.o \\ | +| | +| he3_failed_connection.o \\ | +| | +| \# he3_package.o \\ | +| | +| \# he3_example.o | +| | +| endif | +| | +| ifeq (\$(enable_ut), yes) | +| | +| #unit test | +| | +| TESTPGCLASS = -g -O0 -lcheck -lm -pthread -lrt -lsubunit -Wl,\--wrap=tearDown \\ | +| | +| -Wl,\--wrap=setUp \\ | +| | +| -Wl,\--wrap=errfinish \\ | +| | +| -Wl,\--wrap=errdetail \\ | +| | +| -Wl,\--wrap=errstart_cold \\ | +| | +| -Wl,\--wrap=errmsg_internal | +| | +| TESTPARAMETERACL= -g -O0 -lcheck -lm -pthread -lrt -lsubunit -Wl,\--wrap=tearDown \\ | +| | +| -Wl,\--wrap=setUp \\ | +| | +| -Wl,\--wrap=convert_GUC_name_for_parameter_acl\\ | +| | +| -Wl,\--wrap=cstring_to_text\\ | +| | +| -Wl,\--wrap=GetSysCacheOid\\ | +| | +| -Wl,\--wrap=cstring_to_text\\ | +| | +| -Wl,\--wrap=errcode\\ | +| | +| -Wl,\--wrap=errmsg\\ | +| | +| -Wl,\--wrap=pfree\\ | +| | +| -Wl,\--wrap=errfinish \\ | +| | +| -Wl,\--wrap=errdetail \\ | +| | +| -Wl,\--wrap=errstart_cold \\ | +| | +| -Wl,\--wrap=errmsg_internal\\ | +| | +| -Wl,\--wrap=check_GUC_name_for_parameter_acl\\ | +| | +| -Wl,\--wrap=table_open\\ | +| | +| -Wl,\--wrap=memset\\ | +| | +| -Wl,\--wrap=GetNewOidWithIndex\\ | +| | +| -Wl,\--wrap=heap_form_tuple\\ | +| | +| -Wl,\--wrap=CatalogTupleInsert\\ | +| | +| -Wl,\--wrap=table_close\\ | +| | +| -Wl,\--wrap=heap_freetuple | +| | +| TESTPGCLASSOBJS = \\ | +| | +| test_pg_class.o | +| | +| TESTPARAMETERACLOBJS = \\ | +| | +| test_pg_parameter_acl.o | +| | +| all: test_pg_class test_pg_parameter_acl | +| | +| test_pg_class: \$(TESTPGCLASSOBJS) \| submake-libpgport | +| | +| \$(CC) \$(CFLAGS) \$(TESTPGCLASSOBJS) \$(LDFLAGS) \$(TESTPGCLASS) \$(LDFLAGS_EX) -L \$(rpathdir) \$(LIBS) -o \$@\$(X) | +| | +| test_pg_parameter_acl: \$(TESTPARAMETERACLOBJS) \| submake-libpgport | +| | +| \$(CC) \$(CFLAGS) \$(TESTPARAMETERACLOBJS) \$(LDFLAGS) \$(TESTPARAMETERACL) \$(LDFLAGS_EX) -L \$(rpathdir) \$(LIBS) -o \$@\$(X) | +| | +| clean: | +| | +| rm -rf test_pg_class\$(X) | +| | +| rm -rf test_pg_parameter_acl\$(X) | +| | +| rm -rf \$(TESTPGCLASSOBJS) | +| | +| rm -rf \$(TESTPARAMETERACLOBJS) | +| | +| endif | +| | +| ifeq (\$(enable_ut), yes) | +| | +| \# jit unit test | +| | +| TESTCFLAGS = -g -O0 -lcheck -lm -pthread -lrt -lsubunit \\ | +| | +| -Wl,\--wrap=setUp \\ | +| | +| -Wl,\--wrap=tearDown \\ | +| | +| -Wl,\--wrap=errcode \\ | +| | +| -Wl,\--wrap=errmsg \\ | +| | +| -Wl,\--wrap=pfree \\ | +| | +| -Wl,\--wrap=errfinish \\ | +| | +| -Wl,\--wrap=errmsg_internal \\ | +| | +| -Wl,\--wrap=errstart \\ | +| | +| -Wl,\--wrap=errdetail_internal \\ | +| | +| -Wl,\--wrap=errmsg_plural\\ | +| | +| -Wl,\--wrap=check_valid_polymorphic_signature\\ | +| | +| -Wl,\--wrap=memcpy_s \\ | +| | +| -Wl,\--wrap=NameListToString \\ | +| | +| -Wl,\--wrap=format_type_be \\ | +| | +| -Wl,\--wrap=SearchSysCache1 \\ | +| | +| -Wl,\--wrap=IsBinaryCoercible \\ | +| | +| -Wl,\--wrap=ReleaseSysCache \\ | +| | +| -Wl,\--wrap=func_strict \\ | +| | +| -Wl,\--wrap=LookupOperName \\ | +| | +| -Wl,\--wrap=pg_type_aclcheck \\ | +| | +| -Wl,\--wrap=GetUserId \\ | +| | +| -Wl,\--wrap=aclcheck_error_type \\ | +| | +| -Wl,\--wrap=ProcedureCreate \\ | +| | +| -Wl,\--wrap=table_open \\ | +| | +| -Wl,\--wrap=heap_modify_tuple \\ | +| | +| -Wl,\--wrap=CatalogTupleUpdate \\ | +| | +| -Wl,\--wrap=heap_form_tuple \\ | +| | +| -Wl,\--wrap=CatalogTupleInsert \\ | +| | +| -Wl,\--wrap=table_close \\ | +| | +| -Wl,\--wrap=new_object_addresses \\ | +| | +| -Wl,\--wrap=add_exact_object_address \\ | +| | +| -Wl,\--wrap=record_object_address_dependencies \\ | +| | +| -Wl,\--wrap=free_object_addresses \\ | +| | +| -Wl,\--wrap=func_get_detail \\ | +| | +| -Wl,\--wrap=func_signature_string \\ | +| | +| -Wl,\--wrap=enforce_generic_type_consistency \\ | +| | +| -Wl,\--wrap=pg_proc_aclcheck \\ | +| | +| -Wl,\--wrap=aclcheck_error \\ | +| | +| -Wl,\--wrap=errstart_cold \\ | +| | +| -Wl,\--wrap=cstring_to_text \\ | +| | +| -Wl,\--wrap=errdetail \\ | +| | +| -Wl,\--wrap=get_func_name \\ | +| | +| -Wl,\--wrap=check_valid_internal_signature \\ | +| | +| TESTAGGREGATEOBJS = \\ | +| | +| test_pg_aggregate.o | +| | +| all: test_pg_aggregate | +| | +| test_pg_aggregate: \$(TESTAGGREGATEOBJS) \| submake-libpgport | +| | +| \$(CC) \$(CFLAGS) \$(TESTAGGREGATEOBJS) \$(LDFLAGS) \$(TESTCFLAGS) \$(LDFLAGS_EX) -L \$(rpathdir) \$(LIBS) -o \$@\$(X) | +| | +| clean: | +| | +| rm -rf test_pg_aggregate\$(X) | +| | +| rm -rf \$(TESTAGGREGATEOBJS) | +| | +| endif | +| | +| ifeq (\$(enable_ut), yes) | +| | +| \# jit unit test | +| | +| TESTSHDEPEND = -g -O0 -lcheck -lm -pthread -lrt -lsubunit -I\$(UNITYDIR) \\ | +| | +| -Wl,\--wrap=setUp \\ | +| | +| -Wl,\--wrap=tearDown \\ | +| | +| -Wl,\--wrap=errstart_cold \\ | +| | +| -Wl,\--wrap=errcode \\ | +| | +| -Wl,\--wrap=errmsg \\ | +| | +| -Wl,\--wrap=pfree \\ | +| | +| -Wl,\--wrap=errfinish \\ | +| | +| -Wl,\--wrap=errmsg_internal \\ | +| | +| -Wl,\--wrap=errstart \\ | +| | +| -Wl,\--wrap=errdetail_internal \\ | +| | +| -Wl,\--wrap=errmsg_plural\\ | +| | +| -Wl,\--wrap=table_open \\ | +| | +| -Wl,\--wrap=IsPinnedObject \\ | +| | +| -Wl,\--wrap=table_close \\ | +| | +| -Wl,\--wrap=ScanKeyInit \\ | +| | +| -Wl,\--wrap=systable_beginscan \\ | +| | +| -Wl,\--wrap=systable_getnext \\ | +| | +| -Wl,\--wrap=heap_copytuple \\ | +| | +| -Wl,\--wrap=systable_endscan \\ | +| | +| -Wl,\--wrap=CatalogTupleDelete \\ | +| | +| -Wl,\--wrap=CatalogTupleUpdate \\ | +| | +| -Wl,\--wrap=memset_s \\ | +| | +| -Wl,\--wrap=heap_form_tuple \\ | +| | +| -Wl,\--wrap=CatalogTupleInsert \\ | +| | +| -Wl,\--wrap=heap_freetuple \\ | +| | +| -Wl,\--wrap=getObjectDescription \\ | +| | +| -Wl,\--wrap=palloc \\ | +| | +| -Wl,\--wrap=initStringInfo \\ | +| | +| -Wl,\--wrap=repalloc \\ | +| | +| -Wl,\--wrap=lappend \\ | +| | +| -Wl,\--wrap=list_free_deep \\ | +| | +| -Wl,\--wrap=appendStringInfo \\ | +| | +| -Wl,\--wrap=CatalogOpenIndexes \\ | +| | +| -Wl,\--wrap=MakeSingleTupleTableSlot \\ | +| | +| -Wl,\--wrap=ExecClearTuple \\ | +| | +| -Wl,\--wrap=ExecStoreVirtualTuple \\ | +| | +| -Wl,\--wrap=CatalogTuplesMultiInsertWithInfo \\ | +| | +| -Wl,\--wrap=CatalogCloseIndexes \\ | +| | +| -Wl,\--wrap=ExecDropSingleTupleTableSlot \\ | +| | +| -Wl,\--wrap=IsSharedRelation \\ | +| | +| -Wl,\--wrap=LockSharedObject \\ | +| | +| -Wl,\--wrap=get_tablespace_name \\ | +| | +| -Wl,\--wrap=get_database_name \\ | +| | +| -Wl,\--wrap=appendStringInfoChar \\ | +| | +| -Wl,\--wrap=new_object_addresses \\ | +| | +| -Wl,\--wrap=RemoveRoleFromObjectACL \\ | +| | +| -Wl,\--wrap=RemoveRoleFromObjectPolicy \\ | +| | +| -Wl,\--wrap=AcquireDeletionLock \\ | +| | +| -Wl,\--wrap=systable_recheck_tupl \\ | +| | +| -Wl,\--wrap=ReleaseDeletionLock\\ | +| | +| -Wl,\--wrap=add_exact_object_address \\ | +| | +| -Wl,\--wrap=sort_object_addresses \\ | +| | +| -Wl,\--wrap=performMultipleDeletions \\ | +| | +| -Wl,\--wrap=free_object_addresses \\ | +| | +| -Wl,\--wrap=MemoryContextSwitchTo \\ | +| | +| -Wl,\--wrap=AlterTypeOwner_oid \\ | +| | +| -Wl,\--wrap=AlterSchemaOwner_oid \\ | +| | +| -Wl,\--wrap=ATExecChangeOwner \\ | +| | +| -Wl,\--wrap=AlterForeignServerOwner_oid \\ | +| | +| -Wl,\--wrap=AlterForeignDataWrapperOwner_oid \\ | +| | +| -Wl,\--wrap=AlterEventTriggerOwner_oid \\ | +| | +| -Wl,\--wrap=AlterPublicationOwner_oid \\ | +| | +| -Wl,\--wrap=AlterSubscriptionOwner_oid \\ | +| | +| -Wl,\--wrap=AlterObjectOwner_internal \\ | +| | +| -Wl,\--wrap=MemoryContextDelete \\ | +| | +| -Wl,\--wrap=CommandCounterIncrement \\ | +| | +| -Wl,\--wrap=SearchSysCacheExists \\ | +| | +| -Wl,\--wrap=systable_recheck_tuple \\ | +| | +| TESTSHDEPENDOBJS = \\ | +| | +| test_pg_shdepend.o \\ | +| | +| all: test_pg_shdepend | +| | +| test_pg_shdepend: \$(TESTSHDEPENDOBJS) \| submake-libpgport | +| | +| \$(CC) \$(CFLAGS) \$(TESTSHDEPENDOBJS) \$(LDFLAGS) \$(TESTSHDEPEND) \$(LDFLAGS_EX) -L \$(rpathdir) \$(LIBS) -o \$@\$(X) | +| | +| clean: | +| | +| rm -rf test_pg_shdepend\$(X) | +| | +| rm -rf \$(TESTSHDEPENDOBJS) | +| | +| endif | +| | +| ifeq (\$(enable_ut), yes) | +| | +| #unit test | +| | +| TESTPGCONSTRAINT = -g -O0 -lcheck -lm -pthread -lrt -lsubunit -Wl,\--wrap=tearDown \\ | +| | +| -Wl,\--wrap=setUp \\ | +| | +| -Wl,\--wrap=table_open \\ | +| | +| -Wl,\--wrap=systable_beginscan \\ | +| | +| -Wl,\--wrap=systable_getnext \\ | +| | +| -Wl,\--wrap=endscan \\ | +| | +| -Wl,\--wrap=close \\ | +| | +| -Wl,\--wrap=ConstraintNameIsUsed \\ | +| | +| -Wl,\--wrap=ConstraintNameExists \\ | +| | +| -Wl,\--wrap=makeObjectName \\ | +| | +| -Wl,\--wrap=ScanKeyInit \\ | +| | +| -Wl,\--wrap=systable_endscan \\ | +| | +| -Wl,\--wrap=pfree \\ | +| | +| -Wl,\--wrap=snprintf_s \\ | +| | +| -Wl,\--wrap=CatalogTupleUpdate \\ | +| | +| -Wl,\--wrap=heap_freetuple \\ | +| | +| -Wl,\--wrap=table_close \\ | +| | +| -Wl,\--wrap=CatalogTupleDelete \\ | +| | +| -Wl,\--wrap=ReleaseSysCache \\ | +| | +| -Wl,\--wrap=get_rel_name \\ | +| | +| -Wl,\--wrap=format_type_be \\ | +| | +| -Wl,\--wrap=format_type_extended | +| | +| TESTPGCONSTRAINTOBJS = \\ | +| | +| test_pg_constraint.o \\ | +| | +| override CPPFLAGS := \$(CPPFLAGS) | +| | +| all: test_pg_constraint | +| | +| test_pg_constraint: \$(TESTPGCONSTRAINTOBJS) \| submake-libpgport | +| | +| \$(CC) \$(CFLAGS) \$(TESTPGCONSTRAINTOBJS) \$(LDFLAGS) \$(TESTPGCONSTRAINT) \$(LDFLAGS_EX) -L \$(rpathdir) \$(LIBS) -o \$@\$(X) | +| | +| clean: | +| | +| rm -rf test_pg_constraint\$(X) | +| | +| rm -rf \$(TESTPGCONSTRAINTOBJS) | +| | +| endif | +| | +| ifeq (\$(enable_ut), yes) | +| | +| \################################ | +| | +| \# unit tests for indexing.c | +| | +| \################################ | +| | +| TEST_INDEXING_FLAGS = -g -O0 -lcheck -lm -pthread -lrt -lsubunit \\ | +| | +| -Wl,\--wrap=ExceptionalCondition \\ | +| | +| -Wl,\--wrap=errcode \\ | +| | +| -Wl,\--wrap=errmsg \\ | +| | +| -Wl,\--wrap=errfinish \\ | +| | +| -Wl,\--wrap=errmsg_internal \\ | +| | +| -Wl,\--wrap=errstart \\ | +| | +| -Wl,\--wrap=errstart_cold \\ | +| | +| -Wl,\--wrap=errhint \\ | +| | +| -Wl,\--wrap=pfree \\ | +| | +| -Wl,\--wrap=simple_heap_update \\ | +| | +| -Wl,\--wrap=MemoryContextAllocZeroAligned \\ | +| | +| -Wl,\--wrap=ExecOpenIndices \\ | +| | +| -Wl,\--wrap=ExecCloseIndices \\ | +| | +| -Wl,\--wrap=MakeSingleTupleTableSlot \\ | +| | +| -Wl,\--wrap=ExecStoreHeapTuple \\ | +| | +| -Wl,\--wrap=ReindexIsProcessingIndex \\ | +| | +| -Wl,\--wrap=FormIndexDatum \\ | +| | +| -Wl,\--wrap=index_insert \\ | +| | +| -Wl,\--wrap=ExecDropSingleTupleTableSlot \\ | +| | +| -Wl,\--wrap=simple_heap_insert \\ | +| | +| -Wl,\--wrap=GetCurrentCommandId \\ | +| | +| -Wl,\--wrap=heap_multi_insert \\ | +| | +| -Wl,\--wrap=ExecFetchSlotHeapTuple \\ | +| | +| -Wl,\--wrap=heap_freetuple \\ | +| | +| -Wl,\--wrap=simple_heap_delete \\ | +| | +| -Wl,\--wrap=MakeTupleTableSlot \\ | +| | +| -Wl,\--wrap=tts_heap_store_tuple \\ | +| | +| -Wl,\--wrap=table_open \\ | +| | +| -Wl,\--wrap=table_close \\ | +| | +| -Wl,\--wrap=relation_open \\ | +| | +| -Wl,\--wrap=CreateTemplateTupleDesc | +| | +| TEST_INDEXING_OBJS = \\ | +| | +| test_indexing.o | +| | +| all: test_indexing | +| | +| test_indexing: \$(TEST_INDEXING_OBJS) \| submake-libpgport | +| | +| \$(CC) \$(CFLAGS) \$(TEST_INDEXING_OBJS) \$(LDFLAGS) \$(TEST_INDEXING_FLAGS) \$(LDFLAGS_EX) -L \$(rpathdir) \$(LIBS) -o \$@\$(X) | +| | +| clean: | +| | +| rm -rf test_indexing\$(X) | +| | +| rm -rf \$(TEST_INDEXING_OBJS) | +| | +| endif | +| | +| include \$(top_srcdir)/src/backend/common.mk | +| | +| \# Note: the order of this list determines the order in which the catalog | +| | +| \# header files are assembled into postgres.bki. BKI_BOOTSTRAP catalogs | +| | +| \# must appear first, and pg_statistic before pg_statistic_ext_data, and | +| | +| \# there are reputedly other, undocumented ordering dependencies. | +| | +| \# 增加 he3_login_refuse.h | +| | +| CATALOG_HEADERS := \\ | +| | +| pg_proc.h pg_type.h pg_attribute.h pg_class.h \\ | +| | +| pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \\ | +| | +| pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \\ | +| | +| pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \\ | +| | +| pg_statistic.h pg_statistic_ext.h pg_statistic_ext_data.h \\ | +| | +| pg_rewrite.h pg_trigger.h pg_event_trigger.h pg_description.h \\ | +| | +| pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \\ | +| | +| pg_database.h pg_db_role_setting.h pg_tablespace.h \\ | +| | +| pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \\ | +| | +| pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \\ | +| | +| pg_ts_parser.h pg_ts_template.h pg_extension.h \\ | +| | +| pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \\ | +| | +| pg_foreign_table.h pg_policy.h pg_replication_origin.h \\ | +| | +| pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \\ | +| | +| pg_collation.h pg_parameter_acl.h pg_partitioned_table.h \\ | +| | +| pg_range.h pg_transform.h \\ | +| | +| pg_sequence.h pg_publication.h pg_publication_namespace.h \\ | +| | +| pg_publication_rel.h pg_subscription.h pg_subscription_rel.h \\ | +| | +| he3_masking_policy.h he3_masking_policy_actions.h he3_masking_policy_filters.h he3_login_refuse.h | +| | +| ifeq (\$(enable_extra_catalog), yes) | +| | +| CATALOG_HEADERS += \\ | +| | +| he3_btree_index.h \\ | +| | +| he3_failed_connection.h \\ | +| | +| \# he3_package.h \\ | +| | +| \# he3_example.h | +| | +| endif | +| | +| GENERATED_HEADERS := \$(CATALOG_HEADERS:%.h=%\_d.h) schemapg.h system_fk_info.h | +| | +| POSTGRES_BKI_SRCS := \$(addprefix \$(top_srcdir)/src/include/catalog/, \$(CATALOG_HEADERS)) | +| | +| \# The .dat files we need can just be listed alphabetically. | +| | +| POSTGRES_BKI_DATA = \$(addprefix \$(top_srcdir)/src/include/catalog/,\\ | +| | +| pg_aggregate.dat pg_am.dat pg_amop.dat pg_amproc.dat pg_authid.dat \\ | +| | +| pg_cast.dat pg_class.dat pg_collation.dat pg_conversion.dat \\ | +| | +| pg_database.dat pg_language.dat \\ | +| | +| pg_namespace.dat pg_opclass.dat pg_operator.dat pg_opfamily.dat \\ | +| | +| pg_proc.dat pg_range.dat pg_tablespace.dat \\ | +| | +| pg_ts_config.dat pg_ts_config_map.dat pg_ts_dict.dat pg_ts_parser.dat \\ | +| | +| pg_ts_template.dat pg_type.dat \\ | +| | +| ) | +| | +| ifeq (\$(enable_extra_catalog), yes) | +| | +| POSTGRES_BKI_DATA += \$(addprefix \$(top_srcdir)/src/include/catalog/,\\ | +| | +| he3_failed_connection.dat \\ | +| | +| ) | +| | +| \# POSTGRES_BKI_DATA += \$(addprefix \$(top_srcdir)/src/include/catalog/,\\ | +| | +| \# he3_example.dat \\ | +| | +| \# ) | +| | +| endif | +| | +| all: distprep generated-header-symlinks | +| | +| distprep: bki-stamp | +| | +| .PHONY: generated-header-symlinks | +| | +| generated-header-symlinks: \$(top_builddir)/src/include/catalog/header-stamp | +| | +| \# bki-stamp records the last time we ran genbki.pl. We don\'t rely on | +| | +| \# the timestamps of the individual output files, because the Perl script | +| | +| \# won\'t update them if they didn\'t change (to avoid unnecessary recompiles). | +| | +| \# Technically, this should depend on Makefile.global which supplies | +| | +| \# \$(MAJORVERSION); but then genbki.pl would need to be re-run after every | +| | +| \# configure run, even in distribution tarballs. So depending on configure.ac | +| | +| \# instead is cheating a bit, but it will achieve the goal of updating the | +| | +| \# version number when it changes. | +| | +| bki-stamp: genbki.pl Catalog.pm \$(POSTGRES_BKI_SRCS) \$(POSTGRES_BKI_DATA) \$(top_srcdir)/configure.ac | +| \$(top_srcdir)/src/include/access/transam.h | +| | +| \$(PERL) \$\< \--include-path=\$(top_srcdir)/src/include/ \\ | +| | +| \--set-version=\$(MAJORVERSION) \$(POSTGRES_BKI_SRCS) | +| | +| touch \$@ | +| | +| \# The generated headers must all be symlinked into builddir/src/include/, | +| | +| \# using absolute links for the reasons explained in src/backend/Makefile. | +| | +| \# We use header-stamp to record that we\'ve done this because the symlinks | +| | +| \# themselves may appear older than bki-stamp. | +| | +| \$(top_builddir)/src/include/catalog/header-stamp: bki-stamp | +| | +| prereqdir=\`cd \'\$(dir \$\<)\' \>/dev/null && pwd\` && \\ | +| | +| cd \'\$(dir \$@)\' && for file in \$(GENERATED_HEADERS); do \\ | +| | +| rm -f \$\$file && \$(LN_S) \"\$\$prereqdir/\$\$file\" . ; \\ | +| | +| done | +| | +| touch \$@ | +| | +| \# Note: installation of generated headers is handled elsewhere | +| | +| .PHONY: install-data | +| | +| install-data: bki-stamp installdirs | +| | +| \$(INSTALL_DATA) \$(call vpathsearch,postgres.bki) \'\$(DESTDIR)\$(datadir)/postgres.bki\' | +| | +| \$(INSTALL_DATA) \$(call vpathsearch,system_constraints.sql) \'\$(DESTDIR)\$(datadir)/system_constraints.sql\' | +| | +| \$(INSTALL_DATA) \$(srcdir)/system_functions.sql \'\$(DESTDIR)\$(datadir)/system_functions.sql\' | +| | +| \$(INSTALL_DATA) \$(srcdir)/system_views.sql \'\$(DESTDIR)\$(datadir)/system_views.sql\' | +| | +| \$(INSTALL_DATA) \$(srcdir)/information_schema.sql \'\$(DESTDIR)\$(datadir)/information_schema.sql\' | +| | +| \$(INSTALL_DATA) \$(srcdir)/sql_features.txt \'\$(DESTDIR)\$(datadir)/sql_features.txt\' | +| | +| \$(INSTALL_DATA) \$(srcdir)/fix-CVE-2024-4317.sql \'\$(DESTDIR)\$(datadir)/fix-CVE-2024-4317.sql\' | +| | +| \$(INSTALL_DATA) \$(srcdir)/he3_views.sql \'\$(DESTDIR)\$(datadir)/he3_views.sql\' | +| | +| installdirs: | +| | +| \$(MKDIR_P) \'\$(DESTDIR)\$(datadir)\' | +| | +| .PHONY: uninstall-data | +| | +| uninstall-data: | +| | +| rm -f \$(addprefix \'\$(DESTDIR)\$(datadir)\'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql | +| sql_features.txt fix-CVE-2024-4317.sql init_catalog.sql he3_views.sql) | +| | +| \# postgres.bki, system_constraints.sql, and the generated headers are | +| | +| \# in the distribution tarball, so they are not cleaned here. | +| | +| clean: | +| | +| maintainer-clean: clean | +| | +| rm -f bki-stamp postgres.bki system_constraints.sql \$(GENERATED_HEADERS) | ++----------------------------------------------------------------------------------------------------------------------------------------------------+ + +## 系统表(修改) + +以修改系统表pg_authid为例展示如何修改系统表:新增rolpassword2 + +1. 修改表定义src/include/catalog/pg_authid.h + ++-----------------------------------------------------------------------+ +| /\* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* pg_authid definition. cpp turns this into | +| | +| \* typedef struct FormData_pg_authid | +| | +| \* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| CATALOG(pg_authid,1260,AuthIdRelationId) BKI_SHARED_RELATION | +| BKI_ROWTYPE_OID(2842,AuthIdRelation_Rowtype_Id) BKI_SCHEMA_MACRO | +| | +| { | +| | +| Oid oid; /\* oid \*/ | +| | +| NameData rolname; /\* name of role \*/ | +| | +| bool rolsuper; /\* read this field via superuser() only! \*/ | +| | +| bool rolinherit; /\* inherit privileges from other roles? \*/ | +| | +| bool rolcreaterole; /\* allowed to create more roles? \*/ | +| | +| bool rolcreatedb; /\* allowed to create databases? \*/ | +| | +| bool rolcanlogin; /\* allowed to log in as session user? \*/ | +| | +| bool rolreplication; /\* role used for streaming replication \*/ | +| | +| bool rolbypassrls; /\* bypasses row-level security? \*/ | +| | +| int32 rolconnlimit; /\* max connections allowed (-1=no limit) \*/ | +| | +| /\* remaining fields may be null; use heap_getattr to read them! \*/ | +| | +| #ifdef CATALOG_VARLEN /\* variable-length fields start here \*/ | +| | +| text rolpassword; /\* password, if any \*/ | +| | +| timestamptz rolvaliduntil; /\* password expiration time, if any \*/ | +| | +| **text rolpassword2; /\* password2, if any \*/** | +| | +| #endif | +| | +| } FormData_pg_authid; | ++-----------------------------------------------------------------------+ + +2. 修改相关宏定义src/backend/catalog/pg_authid_d.h + ++-----------------------------------------------------------------------+ +| #define Anum_pg_authid_oid 1 | +| | +| #define Anum_pg_authid_rolname 2 | +| | +| #define Anum_pg_authid_rolsuper 3 | +| | +| #define Anum_pg_authid_rolinherit 4 | +| | +| #define Anum_pg_authid_rolcreaterole 5 | +| | +| #define Anum_pg_authid_rolcreatedb 6 | +| | +| #define Anum_pg_authid_rolcanlogin 7 | +| | +| #define Anum_pg_authid_rolreplication 8 | +| | +| #define Anum_pg_authid_rolbypassrls 9 | +| | +| #define Anum_pg_authid_rolconnlimit 10 | +| | +| #define Anum_pg_authid_rolpassword 11 | +| | +| #define Anum_pg_authid_rolvaliduntil 12 | +| | +| #define Anum_pg_authid_rolpassword 13 | +| | +| #define Natts_pg_authid 13 | ++-----------------------------------------------------------------------+ + +3. 修改初始化值src/include/catalog/pg_authid.dat + ++-----------------------------------------------------------------------+ +| \# The bootstrap superuser is named POSTGRES according to this data | +| and | +| | +| \# according to BKI_DEFAULT entries in other catalogs. However, | +| initdb | +| | +| \# will replace that at database initialization time. | +| | +| { oid =\> \'10\', oid_symbol =\> \'BOOTSTRAP_SUPERUSERID\', | +| | +| rolname =\> \'POSTGRES\', rolsuper =\> \'t\', rolinherit =\> \'t\', | +| | +| rolcreaterole =\> \'t\', rolcreatedb =\> \'t\', rolcanlogin =\> | +| \'t\', | +| | +| rolreplication =\> \'t\', rolbypassrls =\> \'t\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'6171\', oid_symbol =\> \'ROLE_PG_DATABASE_OWNER\', | +| | +| rolname =\> \'pg_database_owner\', rolsuper =\> \'f\', rolinherit =\> | +| \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'f\', | +| | +| rolreplication =\> \'f\', rolbypassrls =\> \'f\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'6181\', oid_symbol =\> \'ROLE_PG_READ_ALL_DATA\', | +| | +| rolname =\> \'pg_read_all_data\', rolsuper =\> \'f\', rolinherit =\> | +| \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'f\', | +| | +| rolreplication =\> \'f\', rolbypassrls =\> \'f\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'6182\', oid_symbol =\> \'ROLE_PG_WRITE_ALL_DATA\', | +| | +| rolname =\> \'pg_write_all_data\', rolsuper =\> \'f\', rolinherit =\> | +| \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'f\', | +| | +| rolreplication =\> \'f\', rolbypassrls =\> \'f\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'3373\', oid_symbol =\> \'ROLE_PG_MONITOR\', | +| | +| rolname =\> \'pg_monitor\', rolsuper =\> \'f\', rolinherit =\> \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'f\', | +| | +| rolreplication =\> \'f\', rolbypassrls =\> \'f\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'3374\', oid_symbol =\> \'ROLE_PG_READ_ALL_SETTINGS\', | +| | +| rolname =\> \'pg_read_all_settings\', rolsuper =\> \'f\', rolinherit | +| =\> \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'f\', | +| | +| rolreplication =\> \'f\', rolbypassrls =\> \'f\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'3375\', oid_symbol =\> \'ROLE_PG_READ_ALL_STATS\', | +| | +| rolname =\> \'pg_read_all_stats\', rolsuper =\> \'f\', rolinherit =\> | +| \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'f\', | +| | +| rolreplication =\> \'f\', rolbypassrls =\> \'f\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'3377\', oid_symbol =\> \'ROLE_PG_STAT_SCAN_TABLES\', | +| | +| rolname =\> \'pg_stat_scan_tables\', rolsuper =\> \'f\', rolinherit | +| =\> \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'f\', | +| | +| rolreplication =\> \'f\', rolbypassrls =\> \'f\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'4569\', oid_symbol =\> \'ROLE_PG_READ_SERVER_FILES\', | +| | +| rolname =\> \'pg_read_server_files\', rolsuper =\> \'f\', rolinherit | +| =\> \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'f\', | +| | +| rolreplication =\> \'f\', rolbypassrls =\> \'f\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'4570\', oid_symbol =\> \'ROLE_PG_WRITE_SERVER_FILES\', | +| | +| rolname =\> \'pg_write_server_files\', rolsuper =\> \'f\', rolinherit | +| =\> \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'f\', | +| | +| rolreplication =\> \'f\', rolbypassrls =\> \'f\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'4571\', oid_symbol =\> | +| \'ROLE_PG_EXECUTE_SERVER_PROGRAM\', | +| | +| rolname =\> \'pg_execute_server_program\', rolsuper =\> \'f\', | +| rolinherit =\> \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'f\', | +| | +| rolreplication =\> \'f\', rolbypassrls =\> \'f\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'4200\', oid_symbol =\> \'ROLE_PG_SIGNAL_BACKEND\', | +| | +| rolname =\> \'pg_signal_backend\', rolsuper =\> \'f\', rolinherit =\> | +| \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'f\', | +| | +| rolreplication =\> \'f\', rolbypassrls =\> \'f\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'4544\', oid_symbol =\> \'ROLE_PG_CHECKPOINT\', | +| | +| rolname =\> \'pg_checkpoint\', rolsuper =\> \'f\', rolinherit =\> | +| \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'f\', | +| | +| rolreplication =\> \'f\', rolbypassrls =\> \'f\', rolconnlimit =\> | +| \'-1\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| \] | ++-----------------------------------------------------------------------+ + +## 视图(新增) + +下面将详细介绍如何新建系统视图,以视图mytest_view为例。 + +### 修改system_views.sql文件 + +修改system_views.sql文件,添加定义新视图的SQL语句: + ++-----------------------------------------------------------------------+ +| CREATE VIEW mytest_view AS   | +| | +|     SELECT number,  tname FROM pg_mytest; | ++-----------------------------------------------------------------------+ + +### 重新编译安装 + +重新对源码进行编译和安装。 + +## 新增内置函数 + ++-----------------------------------------------------------------------+ +| 分配oid | +| | +| src/backend/utils/fmgroids.h | +| | +| 查看oid | +| | +| ./src/include/catalog/unused_oids | +| | +| #define F_HELLO_TEXT 6273 | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/backend/utils/fmgrprotos.h | +| | +| extern Datum hello_text(PG_FUNCTION_ARGS); | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/backend/utils/adt/cryptohashfuncs.c | +| | +| Datum | +| | +| hello_text(PG_FUNCTION_ARGS) | +| | +| { | +| | +| char str\[\] = \"Hello PostgreSQL!\"; | +| | +| PG_RETURN_TEXT_P(cstring_to_text(str)); | +| | +| } | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/include/catalog/pg_proc.dat | +| | +| { oid =\> 6273, descr =\> \'Hello World\', | +| | +| proname =\> \'hello\', proleakproof =\> \'t\', prorettype =\> | +| \'text\', | +| | +| proargtypes =\> \'text\', prosrc =\> \'hello_text\' }, | +| | +| 其中含义如下: | +| | +| oid:对象id,唯一不重复 | +| | +| descr:函数描述信息 | +| | +| proname:函数名称 | +| | +| prorettype:返回值类型 | +| | +| proargtypes:参数列表 | +| | +| prosrc:函数名称 | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/backend/utils/fmgrtab.c | +| | +| { 6273, 1, true, false, \"hello_text\", hello_text }, | +| | +| const Oid fmgr_last_builtin_oid = 6273; | +| | +| const uint16 fmgr_builtin_oid_index\[6274\] = { | +| | +| 6273 | ++-----------------------------------------------------------------------+ + +## 新增参数 + ++-----------------------------------------------------------------------+ +| **/src/backend/utils/misc/guc.c** | +| | +| @@ -605,6 +605,7 @@ char           \*pgstat_temp_directory; | +| | +|   | +| | +|  char      \*application_name; | +| | +|  bool           push_standby = false; | +| | +| +bool           he3_point_in_time_recovery; | +| | +|   | +| | +|  int                    tcp_keepalives_idle; | +| | +|  int                    tcp_keepalives_interval; | +| | +| @@ -2120,6 +2121,15 @@ static struct config_bool | +| ConfigureNamesBool\[\] = | +| | +|                 true, | +| | +|                 NULL, NULL, NULL | +| | +|         }, | +| | +| \+ | +| | +| \+       { | +| | +| \+               {\"he3_point_in_time_recovery\", PGC_SIGHUP, | +| WAL_ARCHIVE_RECOVERY, | +| | +| \+                       gettext_noop(\"Sets whether we are in he3 | +| pitr\"), | +| | +| \+               }, | +| | +| \+               &he3_point_in_time_recovery, | +| | +| \+               false, | +| | +| \+               NULL, NULL, NULL | +| | +| \+       }, | +| | +|          | +| | +|         /\* End-of-list marker \*/ | +| | +|         { | +| | +| **diff \--git a/src/include/utils/guc.h b/src/include/utils/guc.h** | +| | +| **index 0c927d0..442c99f 100644** | +| | +| **\-\-- a/src/include/utils/guc.h** | +| | +| **+++ b/src/include/utils/guc.h** | +| | +| @@ -273,6 +273,7 @@ extern char \*external_pid_file; | +| | +|   | +| | +|  extern PGDLLIMPORT char \*application_name; | +| | +|  extern PGDLLIMPORT bool push_standby; | +| | +| +extern PGDLLIMPORT bool he3_point_in_time_recovery; | +| | +|   | +| | +|  extern int     tcp_keepalives_idle; | +| | +|  extern int     tcp_keepalives_interval; | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| **/src/backend/utils/misc/guc.c** | +| | +| @@ -3972,6 +3972,17 @@ static struct config_string | +| ConfigureNamesString\[\] = | +| | +|                 NULL, NULL, NULL | +| | +|         }, | +| | +|   | +| | +| \+       { | +| | +| \+               {\"he3_meta_conninfo\", PGC_SIGHUP, CONN_AUTH_AUTH, | +| | +| \+                       gettext_noop(\"Sets the connection string to | +| be used to connect to the meta server.\"), | +| | +| \+                       NULL, | +| | +| \+                       GUC_SUPERUSER_ONLY | +| | +| \+               }, | +| | +| \+               &he3_meta_conninfo, | +| | +| \+               \"\", | +| | +| \+               NULL, NULL, NULL | +| | +| \+       }, | +| | +| \+ | +| | +|         { | +| | +|                 {\"primary_slot_name\", PGC_SIGHUP, | +| REPLICATION_STANDBY, | +| | +|                         gettext_noop(\"Sets the name of the | +| replication slot to use on the sending server.\"), | +| | +| **diff \--git a/src/include/access/xlog.h | +| b/src/include/access/xlog.h** | +| | +| **index 98a0a06..a53cdd2 100644** | +| | +| **\-\-- a/src/include/access/xlog.h** | +| | +| **+++ b/src/include/access/xlog.h** | +| | +| @@ -142,6 +142,8 @@ extern char \*PrimarySlotName; | +| | +|  extern bool wal_receiver_create_temp_slot; | +| | +|  extern bool track_wal_io_timing; | +| | +|   | +| | +| +extern char    \*he3_meta_conninfo; | +| | +| \+ | +| | +|  /\* indirectly set via GUC system \*/ | +| | +|  extern TransactionId recoveryTargetXid; | +| | +|  extern char \*recovery_target_time_string; | +| | +| **/src/backend/access/transam/xlog.c** | +| | +| @@ -43,7 +43,6 @@ | +| | +|  #include \"catalog/catversion.h\" | +| | +|  #include \"catalog/pg_control.h\" | +| | +|  #include \"catalog/pg_database.h\" | +| | +| -#include \"catalog/pg_hot_data.h\" | +| | +|  #include \"commands/progress.h\" | +| | +|  #include \"commands/tablespace.h\" | +| | +|  #include \"common/controldata_utils.h\" | +| | +| @@ -96,6 +95,13 @@ | +| | +|  #include \"access/ringbuffer.h\" | +| | +|  #include \"access/pthreadpool.h\" | +| | +|  #include \"storage/he3db_logindex.h\" | +| | +| +/\* precache table \*/ | +| | +| +#include \"libpq-fe.h\" | +| | +| +#include \"lib/stringinfo.h\" | +| | +| +#include \"utils/timestamp.h\" | +| | +| +#include \"access/xlog.h\" | +| | +| +#include \"postmaster/postmaster.h\" | +| | +| +#include \ | +| | +|   | +| | +|  extern uint32 bootstrap_data_checksum_version; | +| | +|   | +| | +| @@ -333,6 +339,8 @@ char \*PrimarySlotName = NULL; | +| | +|  char \*PromoteTriggerFile = NULL; | +| | +|  bool wal_receiver_create_temp_slot = false; | +| | +|   | +| | +| +char \*he3_meta_conninfo; | +| | +| \+ | +| | +|  /\* are we currently in standby mode? \*/ | +| | +|  bool StandbyMode = false; | +| | +|   | +| | +| **/src/include/access/xlog.h** | +| | +| @@ -142,6 +142,8 @@ extern char \*PrimarySlotName; | +| | +|  extern bool wal_receiver_create_temp_slot; | +| | +|  extern bool track_wal_io_timing; | +| | +|   | +| | +| +extern char    \*he3_meta_conninfo; | +| | +| \+ | +| | +|  /\* indirectly set via GUC system \*/ | +| | +|  extern TransactionId recoveryTargetXid; | ++-----------------------------------------------------------------------+ + +## 共享内存 + ++-----------------------------------------------------------------------+ +| **/src/backend/storage/buffer/buf_init.c** | +| | +| @@ -141,6 +141,17 @@ InitBufferPool(void) | +| | +|         /\* Init other shared buffer-management stuff \*/ | +| | +|         StrategyInitialize(!foundDescs); | +| | +|   | +| | +| \+       /\* Init preCacheNodes arrays \*/ | +| | +| \+       preCacheNodesPtr = (Oid \*) | +| | +| \+               ShmemInitStruct(\"preCacheNodesPtr\", | +| | +| \+                                               NPreCacheNodes \* | +| sizeof(Oid), &foundBufCkpt); | +| | +| \+       memset(preCacheNodesPtr, 0, NPreCacheNodes \* sizeof(Oid)); | +| | +| \+ | +| | +| \+       preCacheNodesCountPtr = (uint16 \*) | +| | +| \+               ShmemInitStruct(\"preCacheNodesCountPtr\", | +| | +| \+                                               sizeof(uint16), | +| &foundBufCkpt); | +| | +| \+       memset(preCacheNodesCountPtr, 0, sizeof(uint16)); | +| | +| \+ | +| | +|         /\* Initialize per-backend file flush context \*/ | +| | +|         WritebackContextInit(&BackendWritebackContext, | +| | +|                                                  | +| &backend_flush_after); | +| | +| @@ -177,5 +188,8 @@ BufferShmemSize(void) | +| | +|         /\* size of checkpoint sort array in bufmgr.c \*/ | +| | +|         size = add_size(size, mul_size(NBuffers, | +| sizeof(CkptSortItem))); | +| | +|   | +| | +| \+       /\* size of preCacheNodes \*/ | +| | +| \+       size = add_size(size, mul_size(NPreCacheNodes, | +| sizeof(Oid)) + sizeof(int)); | +| | +| \+ | +| | +|         return size; | +| | +|  } | +| | +| **/src/backend/tcop/postgres.c** | +| | +| @@ -90,7 +90,9 @@ bool          isPreCacheTable = false; | +| | +|  bool           isPreCacheIndex = false; | +| | +|  bool           isPreCacheIndexDone = false; | +| | +|  bool           needPreCacheEscape = false; | +| | +| +uint16         \*preCacheNodesCountPtr = NULL; | +| | +| +Oid                    \*preCacheNodesPtr = NULL; | +| | +|  const char \*debug_query_string; /\* client-supplied query string | +| \*/ | +| | +|   | +| | +|  /\* Note: whereToSendOutput is initialized for the | +| bootstrap/standalone case \*/ | +| | +| **/src/include/storage/bufmgr.h** | +| | +| @@ -83,7 +83,9 @@ extern bool           isPreCacheTable; | +| | +|  extern bool            isPreCacheIndex; | +| | +|  extern bool            isPreCacheIndexDone; | +| | +|  extern bool            needPreCacheEscape; | +| | +| +extern uint16  \*preCacheNodesCountPtr; | +| | +| +extern Oid        \*preCacheNodesPtr; | +| | +|   | +| | +|  /\* in buf_init.c \*/ | +| | +|  extern PGDLLIMPORT char \*BufferBlocks; | +| | +| @@ -308,4 +310,7 @@ TestForOldSnapshot(Snapshot snapshot, Relation | +| relation, Page page) | +| | +|   \*/ | +| | +|  #define PAGEXLOG_BLCKSZ        49152 | +| | +|   | +| | +| +/\* Max preCacheNodes \*/ | +| | +| +#define NPreCacheNodes 128 | +| | +| \+ | +| | +|  #endif                                                 /\* BUFMGR_H | +| \*/ | ++-----------------------------------------------------------------------+ + +新增 + + ----------------------------------------------------------------------- + src/backend/tcop/postgres.c + + ----------------------------------------------------------------------- + +## pg关键字说明 + +​​RESERVED_KEYWORD​​:保留关键字(如 SELECT),不能作为标识符 + +​​UNRESERVED_KEYWORD​​:非保留关键字(如 mapping),可用作列名/表名 + +​​COL_NAME_KEYWORD​​:特殊列名关键字(如 status 在 CREATE TABLE 中) + +​​TYPE_FUNC_NAME_KEYWORD​​:类型/函数名关键字(如 current_date) + +## 字符串操作 + +### string标准库 + ++-----------------------------------------------------------------------+ +| #include \ | +| | +| 1 void \*memchr(const void \*str, int c, size_t n) | +| | +| 在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 | +| c(一个无符号字符)的位置。 | +| | +| 2 int memcmp(const void \*str1, const void \*str2, size_t n) | +| | +| 把 str1 和 str2 的前 n 个字节进行比较。 | +| | +| 3 void \*memcpy(void \*dest, const void \*src, size_t n) | +| | +| 从 src 复制 n 个字符到 dest。 | +| | +| 4 void \*memmove(void \*dest, const void \*src, size_t n) | +| | +| 另一个用于从 src 复制 n 个字符到 dest 的函数。 | +| | +| 5 void \*memset(void \*str, int c, size_t n) | +| | +| 将指定的值 c 复制到 str 所指向的内存区域的前 n 个字节中。 | +| | +| 6 char \*strcat(char \*dest, const char \*src) | +| | +| 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。 | +| | +| 7 char \*strncat(char \*dest, const char \*src, size_t n) | +| | +| 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n | +| 字符长度为止。 | +| | +| 8 char \*strchr(const char \*str, int c) | +| | +| 在参数 str 所指向的字符串中搜索第一次出现字符 | +| c(一个无符号字符)的位置。 | +| | +| 9 int strcmp(const char \*str1, const char \*str2) | +| | +| 把 str1 所指向的字符串和 str2 所指向的字符串进行比较。 | +| | +| 10 int strncmp(const char \*str1, const char \*str2, size_t n) | +| | +| 把 str1 和 str2 进行比较,最多比较前 n 个字节。 | +| | +| 11 int strcoll(const char \*str1, const char \*str2) | +| | +| 把 str1 和 str2 进行比较,结果取决于 LC_COLLATE 的位置设置。 | +| | +| 12 char \*strcpy(char \*dest, const char \*src) | +| | +| 把 src 所指向的字符串复制到 dest。 | +| | +| 13 char \*strncpy(char \*dest, const char \*src, size_t n) | +| | +| 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。 | +| | +| 14 size_t strcspn(const char \*str1, const char \*str2) | +| | +| 检索字符串 str1 开头连续有几个字符都不含字符串 str2 中的字符。 | +| | +| 15 char \*strerror(int errnum) | +| | +| 从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。 | +| | +| 16 size_t strlen(const char \*str) | +| | +| 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。 | +| | +| 17 char \*strpbrk(const char \*str1, const char \*str2) | +| | +| 检索字符串 str1 中第一个匹配字符串 str2 | +| 中字符的字符,不包含空结束字符。也就是说,依次检验字符串 str1 | +| 中的字符,当被检验字符在字符串 str2 | +| 中也包含时,则停止检验,并返回该字符位置。 | +| | +| 18 char \*strrchr(const char \*str, int c) | +| | +| 在参数 str 所指向的字符串中搜索最后一次出现字符 | +| c(一个无符号字符)的位置。 | +| | +| 19 size_t strspn(const char \*str1, const char \*str2) | +| | +| 检索字符串 str1 中第一个不在字符串 str2 中出现的字符下标。 | +| | +| 20 char \*strstr(const char \*haystack, const char \*needle) | +| | +| 在字符串 haystack 中查找第一次出现字符串 | +| needle(不包含空结束字符)的位置。 | +| | +| 21 char \*strtok(char \*str, const char \*delim) | +| | +| 分解字符串 str 为一组字符串,delim 为分隔符。 | +| | +| 22 size_t strxfrm(char \*dest, const char \*src, size_t n) | +| | +| 根据程序当前的区域选项中的 LC_COLLATE 来转换字符串 src 的前 n | +| 个字符,并把它们放置在字符串 dest 中。 | ++-----------------------------------------------------------------------+ + +### ctype标准库 + +C 标准库的 ctype.h +头文件提供了一些函数,可用于测试和转换字符,这些函数主要用于检查字符的类型(如字母、数字、空白字符等)以及进行字符大小写转换。 + +\ 提供了一组方便的函数,用于处理字符的分类和转换操作,是 C +标准库中处理字符操作的重要工具。 + +1 数字 + +完整的数字集合 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } + +2 十六进制数字 + +集合 { 0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f } + +3 小写字母 + +集合 { a b c d e f g h i j k l m n o p q r s t u v w x y z } + +4 大写字母 + +集合 {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z } + +5 字母 + +小写字母和大写字母的集合 + +6 字母数字字符 + +数字、小写字母和大写字母的集合 + +7 标点符号字符 + +集合 ! \" \# \$ % & \' ( ) \* + , - . / : ; \< = \> ? @ \[ \\ \] \^ \_ +\` { \| } \~ + +8 图形字符 + +字母数字字符和标点符号字符的集合 + +9 空格字符 + +制表符、换行符、垂直制表符、换页符、回车符、空格符的集合。 + +10 可打印字符 + +字母数字字符、标点符号字符和空格字符的集合。 + +11 控制字符 + +在 ASCII 编码中,这些字符的八进制代码是从 000 到 037,以及 177(DEL)。 + +12 空白字符 + +包括空格符和制表符。 + +13 字母字符 + +小写字母和大写字母的集合。 + ++-----------------------------------------------------------------------+ +| #include \ | +| | +| 1 int isalnum(int c) | +| | +| 该函数检查所传的字符是否是字母和数字。 | +| | +| 2 int isalpha(int c) | +| | +| 该函数检查所传的字符是否是字母。 | +| | +| 3 int iscntrl(int c) | +| | +| 该函数检查所传的字符是否是控制字符。 | +| | +| 4 int isdigit(int c) | +| | +| 该函数检查所传的字符是否是十进制数字。 | +| | +| 5 int isgraph(int c) | +| | +| 该函数检查所传的字符是否有图形表示法。 | +| | +| 6 int islower(int c) | +| | +| 该函数检查所传的字符是否是小写字母。 | +| | +| 7 int isprint(int c) | +| | +| 该函数检查所传的字符是否是可打印的。 | +| | +| 8 int ispunct(int c) | +| | +| 该函数检查所传的字符是否是标点符号字符。 | +| | +| 9 int isspace(int c) | +| | +| 该函数检查所传的字符是否是空白字符。 | +| | +| 10 int isupper(int c) | +| | +| 该函数检查所传的字符是否是大写字母。 | +| | +| 11 int isxdigit(int c) | +| | +| 该函数检查所传的字符是否是十六进制数字。 | +| | +| 12 int tolower(int c) | +| | +| 该函数把大写字母转换为小写字母。 | +| | +| 13 int toupper(int c) | +| | +| 该函数把小写字母转换为大写字母。 | ++-----------------------------------------------------------------------+ + +### string.h标准库高级用法 + ++-----------------------------------------------------------------------+ +| StringInfoData command; | +| | +| initStringInfo(&command); | +| | +| appendStringInfoString(&command, \"select secret,now() from | +| ak_sys_key where extract(epoch from | +| (now()-createtime)\*1000000)%1000=no\"); | +| | +| resetStringInfo(&command); | ++-----------------------------------------------------------------------+ + +### 正则表达式 + +\ + ++----------------------------------------------------------------------------+ +| 基本语法:正则表达式的基础语法包括字符匹配、量词、边界符等。常见的字符有: | +| | +| .:匹配任意单个字符。 | +| | +| \*:匹配前面的字符零次或多次。 | +| | +| +:匹配前面的字符一次或多次。 | +| | +| ?:匹配前面的字符零次或一次。 | +| | +| \[abc\]:匹配a、b或c中的任意一个。 | +| | +| \[\^abc\]:匹配除了a、b、c之外的任意字符。 | +| | +| \\d:匹配数字字符,等价于\[0-9\]。 | +| | +| \\w:匹配字母、数字或下划线,等价于\[a-zA-Z0-9\_\]。 | +| | +| \\s:匹配空白字符。 | ++----------------------------------------------------------------------------+ + +### Sscanf(format) + + ----------------------------------------------------------------------- + int sscanf(const char \*buffer, const char \*format, \[argument\]\...); + + ----------------------------------------------------------------------- + + ----------------------------------------------------------------------- + format \-- 这是字符串,包含了要被写入到标准输出 stdout + 的文本。它可以包含嵌入的 format 标签,format + 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。format + 标签属性是 + %\[flags\]\[width\]\[.precision\]\[length\]specifier,具体讲解如下: + + ----------------------------------------------------------------------- + ++--------------------------------------------------------------------------------------------------------------------------------+ +| 格式字符 意义 | +| | +| a, A | +| | +| 以十六进制形式输出浮点数(C99 新增)。 | +| | +| 实例 printf(\"pi=%a\\n\", 3.14); 输出 pi=0x1.91eb86p+1。 | +| | +| d 以十进制形式输出带符号整数(正数不输出符号) | +| | +| o 以八进制形式输出无符号整数(不输出前缀0) | +| | +| x,X 以十六进制形式输出无符号整数(不输出前缀Ox) | +| | +| u 以十进制形式输出无符号整数 | +| | +| f 以小数形式输出单、双精度实数 | +| | +| e,E 以指数形式输出单、双精度实数 | +| | +| g,G 以%f或%e中较短的输出宽度输出单、双精度实数 | +| | +| c 输出单个字符 | +| | +| s 输出字符串 | +| | +| p 输出指针地址 | +| | +| lu 32位无符号整数 | +| | +| llu 64位无符号整数 | +| | +| flags(标识) 描述 | +| | +| \- 在给定的字段宽度内左对齐,默认是右对齐(参见 width 子说明符)。 | +| | +| \+ 强制在结果之前显示加号或减号(+ 或 -),即正数前面会显示 + 号。默认情况下,只有负数前面会显示一个 - 号。 | +| | +| 空格 如果没有写入任何符号,则在该值前面插入一个空格。 | +| | +| \# 与 o、x 或 X 说明符一起使用时,非零值前面会分别显示 0、0x 或 0X。 | +| | +| 与 e、E 和 f | +| 一起使用时,会强制输出包含一个小数点,即使后边没有数字时也会显示小数点。默认情况下,如果后边没有数字时候,不会显示显示小数点。 | +| | +| 与 g 或 G 一起使用时,结果与使用 e 或 E 时相同,但是尾部的零不会被移除。 | +| | +| 0 在指定填充 padding 的数字左边放置零(0),而不是空格(参见 width 子说明符)。 | +| | +| width(宽度) 描述 | +| | +| (number) 要输出的字符的最小数目。如果输出的值短于该数,结果会用空格填充。如果输出的值长于该数,结果不会被截断。 | +| | +| \* 宽度在 format 字符串中未指定,但是会作为附加整数值参数放置于要被格式化的参数之前。 | +| | +| .precision(精度) 描述 | +| | +| .number 对于整数说明符(d、i、o、u、x、X):precision | +| 指定了要写入的数字的最小位数。如果写入的值短于该数,结果会用前导零来填充。如果写入的值长于该数,结果不会被截断。精度为 0 | +| 意味着不写入任何字符。 | +| | +| 对于 e、E 和 f 说明符:要在小数点后输出的小数位数。 | +| | +| 对于 g 和 G 说明符:要输出的最大有效位数。 | +| | +| 对于 s: 要输出的最大字符数。默认情况下,所有字符都会被输出,直到遇到末尾的空字符。 | +| | +| 对于 c 类型:没有任何影响。 | +| | +| 当未指定任何精度时,默认为 1。如果指定时不带有一个显式值,则假定为 0。 | +| | +| .\* 精度在 format 字符串中未指定,但是会作为附加整数值参数放置于要被格式化的参数之前。 | +| | +| length(长度) 描述 | +| | +| h 参数被解释为短整型或无符号短整型(仅适用于整数说明符:i、d、o、u、x 和 X)。 | +| | +| l 参数被解释为长整型或无符号长整型,适用于整数说明符(i、d、o、u、x 和 X)及说明符 c(表示一个宽字符)和 | +| s(表示宽字符字符串)。 | +| | +| L 参数被解释为长双精度型(仅适用于浮点数说明符:e、E、f、g 和 G)。 | ++--------------------------------------------------------------------------------------------------------------------------------+ +| 规定符 | +| | +| %d 十进制有符号整数 | +| | +| %u 十进制无符号整数 | +| | +| %f 浮点数 | +| | +| %s 字符串 | +| | +| %c 单个字符 | +| | +| %p 指针的值 | +| | +| %e 指数形式的浮点数 | +| | +| %x, %X 无符号以十六进制表示的整数 | +| | +| %o 无符号以八进制表示的整数 | +| | +| %g 把输出的值按照 %e 或者 %f 类型中输出长度较小的方式输出 | +| | +| %p 输出地址符 | +| | +| %lu 32位无符号整数 | +| | +| %llu 64位无符号整数 | +| | +| %% 输出百分号字符本身。 | +| | +| 除了格式化说明符之外,printf() 函数还支持一些标志和选项,用于控制输出的精度、宽度、填充字符和对齐方式等。例如: | +| | +| %-10s:左对齐并占用宽度为 10 的字符串; | +| | +| %5.2f:右对齐并占用宽度为 5,保留两位小数的浮点数; | +| | +| %#x:输出带有 0x 前缀的十六进制数。 | ++--------------------------------------------------------------------------------------------------------------------------------+ + +配合正则 + ++-----------------------------------------------------------------------+ +| sscanf 支持以下正则表达式片段,用于定义匹配规则: | +| | +| 格式符 作用 示例 | +| | +| %\[a-z\] 匹配小写字母,直到遇到非小写字母停止(贪婪匹配) \"abc123\" | +| → \"abc\" | +| | +| %\[0-9A-F\] 匹配数字和大写十六进制字符 \"1A3f\" → \"1A3\" | +| | +| %\[\^;\] 匹配除分号外的所有字符(常用于提取字段) | +| \"name:John;age:30\" → \"name:John\" | +| | +| %\*\[a-z\] ​​跳过​​所有小写字母(不存储结果) \"abc123\" → 跳过 \"abc\" | +| 直接处理 123 | +| | +| ​​2. 典型应用场景​​ | +| | +| ​​(1) 提取结构化数据​​ | +| | +| ​​示例:解析日志中的时间戳和消息​​ | +| | +| char log\[\] = \"\[2023-10-05 14:30:22\] ERROR: File not found\"; | +| | +| char date\[11\], time\[9\], message\[50\]; | +| | +| sscanf(log, \"\[%10\[0-9-\] %8\[0-9:\]\] %\*s %\[\^\\n\]\", date, | +| time, message); | +| | +| // date=\"2023-10-05\", time=\"14:30:22\", message=\"ERROR: File not | +| found\" \[2,5\](@ref) | +| | +| %10\[0-9-\]:匹配 10 位日期(防止溢出) | +| | +| %8\[0-9:\]:匹配 8 位时间 | +| | +| %\*s:跳过日志级别标记(如 ERROR:) | +| | +| %\[\^\\n\]:提取剩余内容直到换行符 | +| | +| ​​(2) 处理复杂分隔符​​ | +| | +| ​​示例:解析 CSV 中的带引号字段​​ | +| | +| char csv\[\] = \"John,\\\"New, York\\\",30\"; | +| | +| char name\[20\], city\[20\]; | +| | +| int age; | +| | +| sscanf(csv, \"%\[\^,\],\\\"%\[\^\\\"\]\\\",%d\", name, city, &age); | +| | +| // name=\"John\", city=\"New, York\", age=30 \[3,6\](@ref) | +| | +| %\[\^,\]:匹配逗号前的所有字符 | +| | +| \\\"%\[\^\\\"\]\\\":匹配双引号内的内容(含逗号) | +| | +| ​​3. 高级技巧与注意事项​​ | +| | +| ​​(1) 宽度限制与溢出防护​​ | +| | +| 在格式符中指定最大宽度,避免缓冲区溢出: | +| | +| char str\[\] = \"ABCDEFGHIJKL\"; | +| | +| char buf\[5\]; | +| | +| sscanf(str, \"%4s\", buf); // buf=\"ABCD\"(自动补 \\0)\[3,6\](@ref) | +| | +| ​​(2) 联合使用 %\* 跳过无关数据​​ | +| | +| ​​示例:提取 MAC 地址​​ | +| | +| char mac_str\[\] = \"MAC: 00:1a:2b:3c:4d:5e\"; | +| | +| unsigned int seg1, seg2, seg3, seg4, seg5, seg6; | +| | +| sscanf(mac_str, \"%\*\[\^0-9a-fA-F\]%2x:%2x:%2x:%2x:%2x:%2x\", | +| | +| &seg1, &seg2, &seg3, &seg4, &seg5, &seg6); // 跳过非十六进制字符 | +| \[2,5\](@ref) | +| | +| ​​(3) 处理符号扩展问题​​ | +| | +| 当匹配带符号的数值时,需显式转换避免符号扩展: | +| | +| signed char ch = -1; | +| | +| unsigned char uch; | +| | +| sscanf(\"ff\", \"%2hhx\", &uch); // | +| uch=0xFF(正确解析负值)\[3,6\](@ref) | ++-----------------------------------------------------------------------+ + +### ASCALL码 + +**​编码范围** + +​标准ASCII码:使用7位二进制数(0\~127)表示128个字符,包括33个控制字符和95个可打印字符 + +​扩展ASCII码:通过8位二进制数扩展为256个字符(0\~255),包含更多符号和部分非英语字符(如拉丁字母变体),但不同系统实现可能不同 + +**字符分类** + +​控制字符(0\~31、127)​:用于设备控制,如换行(LF, 10)、回车(CR, +13)、删除(DEL, 127) + +​可打印字符(32\~126) + +​空格:32(十进制) + +​数字:48(0)\~57(9) + +​大写字母:65(A)\~90(Z) + +​小写字母:97(a)\~122(z) + +​标点符号:如!(33)、@(64)、#(35) + +![IMG_256](./images/media/image1.jpeg){width="5.820138888888889in" +height="3.8333333333333335in"} + +### pg密码校验demo + +#### Passworecheck示例一,比较简单,包括长度、是否包括用户名、是否全字符 + ++-------------------------------------------------------------------------+ +| **Passworecheck示例一,比较简单,包括长度、是否包括用户名、是否全字符** | +| | +| static void | +| | +| check_password(const char \*username, | +| | +| const char \*shadow_pass, | +| | +| PasswordType password_type, | +| | +| Datum validuntil_time, | +| | +| bool validuntil_null) | +| | +| { | +| | +| if (prev_check_password_hook) | +| | +| prev_check_password_hook(username, shadow_pass, | +| | +| password_type, validuntil_time, | +| | +| validuntil_null); | +| | +| if (password_type != PASSWORD_TYPE_PLAINTEXT) | +| | +| { | +| | +| /\* | +| | +| \* Unfortunately we cannot perform exhaustive checks on encrypted | +| | +| \* passwords - we are restricted to guessing. (Alternatively, we could | +| | +| \* insist on the password being presented non-encrypted, but that has | +| | +| \* its own security disadvantages.) | +| | +| \* | +| | +| \* We only check for username = password. | +| | +| \*/ | +| | +| const char \*logdetail = NULL; | +| | +| if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == | +| STATUS_OK) | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_INVALID_PARAMETER_VALUE), | +| | +| errmsg(\"password must not equal user name\"))); | +| | +| } | +| | +| else | +| | +| { | +| | +| /\* | +| | +| \* For unencrypted passwords we can perform better checks | +| | +| \*/ | +| | +| const char \*password = shadow_pass; | +| | +| int pwdlen = strlen(password); | +| | +| int i; | +| | +| bool pwd_has_letter, | +| | +| pwd_has_nonletter; | +| | +| #ifdef USE_CRACKLIB | +| | +| const char \*reason; | +| | +| #endif | +| | +| /\* enforce minimum length \*/ | +| | +| if (pwdlen \< MIN_PWD_LENGTH) | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_INVALID_PARAMETER_VALUE), | +| | +| errmsg(\"password is too short\"))); | +| | +| /\* check if the password contains the username \*/ | +| | +| if (strstr(password, username)) | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_INVALID_PARAMETER_VALUE), | +| | +| errmsg(\"password must not contain user name\"))); | +| | +| /\* check if the password contains both letters and non-letters \*/ | +| | +| pwd_has_letter = false; | +| | +| pwd_has_nonletter = false; | +| | +| for (i = 0; i \< pwdlen; i++) | +| | +| { | +| | +| /\* | +| | +| \* isalpha() does not work for multibyte encodings but let\'s | +| | +| \* consider non-ASCII characters non-letters | +| | +| \*/ | +| | +| if (isalpha((unsigned char) password\[i\])) | +| | +| pwd_has_letter = true; | +| | +| else | +| | +| pwd_has_nonletter = true; | +| | +| } | +| | +| if (!pwd_has_letter \|\| !pwd_has_nonletter) | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_INVALID_PARAMETER_VALUE), | +| | +| errmsg(\"password must contain both letters and nonletters\"))); | +| | +| #ifdef USE_CRACKLIB | +| | +| /\* call cracklib to check password \*/ | +| | +| if ((reason = FascistCheck(password, CRACKLIB_DICTPATH))) | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_INVALID_PARAMETER_VALUE), | +| | +| errmsg(\"password is easily cracked\"), | +| | +| errdetail_log(\"cracklib diagnostic: %s\", reason))); | +| | +| #endif | +| | +| } | +| | +| /\* all checks passed, password is ok \*/ | +| | +| } | ++-------------------------------------------------------------------------+ + +#### Passworecheck示例二,ctype + ++-------------------------------------------------------------------------+ +| **Passworecheck示例二,比较简单,包括长度、是否包括用户名、是否全字符** | +| | +| #include \ | +| | +| #include \ | +| | +| #include \ | +| | +| int check_password_strength(char \*password) { | +| | +| int length = strlen(password); | +| | +| int has_lower = 0; | +| | +| int has_upper = 0; | +| | +| int has_digit = 0; | +| | +| int has_special = 0; | +| | +| for (int i = 0; i \< length; i++) { | +| | +| if (islower(password\[i\])) { | +| | +| has_lower = 1; | +| | +| } | +| | +| if (isupper(password\[i\])) { | +| | +| has_upper = 1; | +| | +| } | +| | +| if (isdigit(password\[i\])) { | +| | +| has_digit = 1; | +| | +| } | +| | +| if (!isalnum(password\[i\])) { | +| | +| has_special = 1; | +| | +| } | +| | +| } | +| | +| if (length \< 8 \|\| !(has_lower && has_upper && has_digit && | +| has_special)) { | +| | +| return 0; // 弱 | +| | +| } else if (length \< 12 \|\| !(has_lower && has_upper && has_digit && | +| has_special)) { | +| | +| return 1; // 中等 | +| | +| } else { | +| | +| return 2; // 强 | +| | +| } | +| | +| } | +| | +| int main() { | +| | +| char password\[100\]; | +| | +| printf(\"请输入密码:\"); | +| | +| scanf(\"%s\", password); | +| | +| int strength = check_password_strength(password); | +| | +| if (strength == 0) { | +| | +| printf(\"密码强度为弱\\n\"); | +| | +| } else if (strength == 1) { | +| | +| printf(\"密码强度为中等\\n\"); | +| | +| } else { | +| | +| printf(\"密码强度为强\\n\"); | +| | +| } | +| | +| return 0; | +| | +| } | ++-------------------------------------------------------------------------+ + +#### Passworecheck示例三,自实现ctype + +自定义判断包括任意字符任意次数 + ++-----------------------------------------------------------------------+ +| #include \ | +| | +| #include \ | +| | +| // 统计字符出现次数的辅助函数 | +| | +| int count_char(const char \*str, char ch) { | +| | +| if (str == NULL) return 0; | +| | +| int count = 0; | +| | +| for (int i = 0; str\[i\] != \'\\0\'; i++) { | +| | +| if (str\[i\] == ch) count++; | +| | +| } | +| | +| return count; | +| | +| } | +| | +| // 主函数:检查集合中是否有字符出现次数达标 | +| | +| bool contains_any_char_with_count(const char \*str, const char | +| \*chars, int required_count) { | +| | +| if (str == NULL \|\| chars == NULL \|\| required_count \<= 0) return | +| false; | +| | +| bool checked\[256\] = {false}; // 记录已检查的ASCII字符,避免重复统计 | +| | +| for (const char \*c = chars; \*c != \'\\0\'; c++) { | +| | +| unsigned char current = \*c; | +| | +| if (!checked\[current\]) { // 仅处理未检查过的字符 | +| | +| checked\[current\] = true; | +| | +| int cnt = count_char(str, current); | +| | +| if (cnt \>= required_count) return true; | +| | +| } | +| | +| } | +| | +| return false; | +| | +| } | +| | +| // 测试示例 | +| | +| int main() { | +| | +| const char \*str = \"hello world! welcome to coding.\"; | +| | +| const char \*chars = \"aeiou!\"; | +| | +| int required = 3; | +| | +| bool result = contains_any_char_with_count(str, chars, required); | +| | +| printf(\"Result: %s\\n\", result ? \"true\" : \"false\"); // | +| 输出:true(字符 \'o\' 出现4次) | +| | +| return 0; | +| | +| } | ++-----------------------------------------------------------------------+ + +## 随机数 + +### 生成随机数 + ++-----------------------------------------------------------------------+ +| #include \ | +| | +| #include \ | +| | +| #include \ | +| | +| int main() { | +| | +| [srand(time(NULL));]{.mark} | +| | +| printf(\"10 个随机数 (0\~99):\\n\"); | +| | +| for (int i = 0; i \< 10; i++) { | +| | +| printf(\"%d \", [rand() % 100]{.mark}); | +| | +| } | +| | +| return 0; | +| | +| } | ++-----------------------------------------------------------------------+ + +### 打乱数组顺序 + ++-----------------------------------------------------------------------+ +| void generateRandomSequence(int arr\[\], int n) { | +| | +| srand(time(NULL)); | +| | +| // 初始化数组 \[1, 2, \..., N\] | +| | +| for (int i = 0; i \< n; i++) { | +| | +| arr\[i\] = i + 1; | +| | +| } | +| | +| // Fisher-Yates 洗牌 | +| | +| for (int i = n - 1; i \> 0; i\--) { | +| | +| int j = rand() % (i + 1); | +| | +| int temp = arr\[i\]; | +| | +| arr\[i\] = arr\[j\]; | +| | +| arr\[j\] = temp; | +| | +| } | +| | +| } | ++-----------------------------------------------------------------------+ + +## 系统时间 + +### pg自定义 + ++-----------------------------------------------------------------------+ +| #include \"utils/timestamp.h\" | +| | +| timestamp 日期和时间 格式为 YYYY-MM-DD HH:MM:SS ,支持到微秒级别(6 | +| 位小数) 同时包含日期和时间信息 timestamp: \'2024-08-08 | +| 12:30:00.123456\' | +| | +| /\* Set at postmaster start \*/ | +| | +| extern PGDLLIMPORT TimestampTz PgStartTime; | +| | +| /\* Set at configuration reload \*/ | +| | +| extern PGDLLIMPORT TimestampTz PgReloadTime; | +| | +| TimestampTz GetCurrentTimestamp(void); | +| | +| TimestampTz GetSQLCurrentTimestamp(int32 typmod); | +| | +| Timestamp GetSQLLocalTimestamp(int32 typmod); | +| | +| pg_time_t timestamptz_to_time_t(TimestampTz t); | +| | +| TimestampTz time_t_to_timestamptz(pg_time_t tm); | +| | +| bool TimestampDifferenceExceeds(TimestampTz start_time, | +| | +| TimestampTz stop_time, | +| | +| int msec); | +| | +| char \*timestamptz_to_str(TimestampTz t); | +| | +| int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2); | ++-----------------------------------------------------------------------+ + +### 用户使用 + ++-------------------------------------------------------------------------------------------------------------------------+ +| **PostgreSQL 时间戳数据类型** | +| | +| PostgreSQL 时间戳类型用来存储日期和时间组合的数据类型。PostgreSQL 支持两种类型的时间戳数据类型,包括 TIMESTAMP 和 | +| TIMESTAMPTZ: | +| | +| TIMESTAMP 全写为: TIMESTAMP WITHOUT TIME ZONE, 用来存储没有时区的时间戳。 | +| | +| TIMESTAMPTZ 全写为: TIMESTAMP WITH TIME ZONE, 用来存储带时区的时间戳。 | +| | +| TIMESTAMP 和 TIMESTAMPTZ 都使用 8 个字节来存储时间戳值。 | +| | +| 在 PostgreSQL 内部,TIMESTAMPTZ 类型的值被存储为它对应的 UTC 值。当您从数据库中查询 TIMESTAMPTZ 值时,PostgreSQL 会将 | +| UTC 值转换回数据库服务器、用户或当前数据库连接设置的时区的时间值。 | +| | +| PostgreSQL 时间戳语法 | +| | +| 要创建一个不带时区的时间戳类型的列,请使用如下语法: | +| | +| column_name TIMESTAMP column_constraint | +| | +| 要创建一个带时区的时间戳类型的列,请使用如下语法: | +| | +| column_name TIMESTAMPTZ column_constraint | +| | +| 时间戳数据类型使用格式: YYYY-MM-DD HH:MI:SS\[.ssssss\]。 | +| | +| PostgreSQL 时间戳实例 | +| | +| 使用下面的语句创建一个新表,名称为 test_timestamp: | +| | +| CREATE TABLE test_timestamp ( | +| | +| id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, | +| | +| timestamp_v TIMESTAMP NOT NULL DEFAULT now(), | +| | +| timestamptz_v TIMESTAMPTZ NOT NULL DEFAULT now() | +| | +| ); | +| | +| 该 test_timestamp 表由三列组成: | +| | +| 该 id 列是标识每行的主键列,它是一个标识列。 | +| | +| 该 timestamp_v 列是不带时区的时间戳类型,它不能为 null,并且默认为由 now() 函数产生的当前时间。 | +| | +| 该 timestamptz_v 列是带时区的时间戳类型,它不能为 null,并且默认为由 now() 函数产生的当前时间。 | +| | +| 插入时间戳数据 | +| | +| 要将数据插入时间戳列,您必须确保数据采用正确的格式。以下 INSERT 语句向 test_timestamp 表中插入一个新行。 | +| | +| INSERT INTO test_timestamp (timestamp_v, timestamptz_v) | +| | +| VALUES | +| | +| (\'2022-08-30 10:10:10\', \'2022-08-30 10:10:10\'), | +| | +| (\'2022-08-30\', \'2022-08-30\') | +| | +| RETURNING \*; | +| | +| id \| timestamp_v \| timestamptz_v | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 \| 2022-08-30 10:10:10 \| 2022-08-30 10:10:10+08 | +| | +| 2 \| 2022-08-30 00:00:00 \| 2022-08-30 00:00:00+08 | +| | +| (2 rows) | +| | +| 虽然我们在插入时没有为 TIMESTAMPTZ 列指定带有时区的值,但是 PostgreSQL 自动为此列的值添加了时区信息。 | +| | +| 要查看当前会话的时区,请使用 SHOW TIMEZONE 语句,如下: | +| | +| SHOW TIMEZONE; | +| | +| TimeZone | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| Asia/Shanghai | +| | +| (1 row) | +| | +| 您不能为时间戳类型指定一个时间类型的值,PostgreSQL 将会给出一个错误,如下: | +| | +| INSERT INTO test_timestamp (timestamp_v, timestamptz_v) | +| | +| VALUES (\'10:10:10\', \'10:10:10\') | +| | +| RETURNING \*; | +| | +| ERROR: invalid input syntax for type timestamp: \"10:10:10\" | +| | +| LINE 2: VALUES (\'10:10:10\', \'10:10:10\') | +| | +| 更新 TIMESTAMP 数据 | +| | +| 要更新 TIMESTAMP 数据,使用该 UPDATE 语句并传入一个正确格式的值: | +| | +| UPDATE | +| | +| test_timestamp | +| | +| SET | +| | +| timestamp_v = \'2022-08-30 11:11:11\', | +| | +| timestamptz_v = \'2022-08-30 11:11:11\' | +| | +| WHERE id = 1 | +| | +| RETURNING \*; | +| | +| id \| timestamp_v \| timestamptz_v | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 \| 2022-08-30 11:11:11 \| 2022-08-30 11:11:11+08 | +| | +| (1 row) | +| | +| 在 WHERE 条件中使用 TIMESTAMP 数据 | +| | +| 您可以在 WHERE 子句中使用 TIMESTAMP 列过滤数据。 | +| | +| 要查找日期为 2022-08-30 的所有的行,如下: | +| | +| SELECT \* | +| | +| FROM test_timestamp | +| | +| WHERE to_char(timestamp_v, \'YYYY-MM-DD\') = \'2022-08-30\'; | +| | +| id \| timestamp_v \| timestamptz_v | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2 \| 2022-08-30 00:00:00 \| 2022-08-30 00:00:00+08 | +| | +| 1 \| 2022-08-30 11:11:11 \| 2022-08-30 11:11:11+08 | +| | +| (2 rows) | +| | +| 您还可以查找日期大于 2022-08-30 的所有的行,如下: | +| | +| SELECT \* | +| | +| FROM test_timestamp | +| | +| WHERE timestamp_v \> \'2022-08-30\'; | +| | +| id \| timestamp_v \| timestamptz_v | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 \| 2022-08-30 11:11:11 \| 2022-08-30 11:11:11+08 | +| | +| (1 row) | +| | +| 输出指定格式的时间值 | +| | +| PostgreSQL 提供了 TO_CHAR() 函数以按照指定格式输出时间值。 TO_CHAR() | +| 函数接受两个参数。第一个参数是要格式化的时间值,第二个参数是格式。 | +| | +| 要以 yyyy/mm/dd 格式显示时间,请使用以下语句: | +| | +| SELECT | +| | +| id, | +| | +| to_char(timestamp_v, \'YYYY/MM/DD HH24:MI:SS\'), | +| | +| to_char(timestamptz_v, \'YYYY/MM/DD HH24:MI:SS TZH\') | +| | +| FROM | +| | +| test_timestamp; | +| | +| id \| to_char \| to_char | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2 \| 2022/08/30 00:00:00 \| 2022/08/30 00:00:00 +08 | +| | +| 1 \| 2022/08/30 11:11:11 \| 2022/08/30 11:11:11 +08 | +| | +| (2 rows) | +| | +| PostgreSQL 时间戳函数 | +| | +| 为了有效地处理时间戳数据,PostgreSQL 提供了一些方便的函数。 | +| | +| 要获取当前时间戳,请使用 now() 或者 current_timestamp,如下: | +| | +| SELECT now(), current_timestamp; | +| | +| now \| current_timestamp | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2022-09-02 10:14:14.06204+08 \| 2022-09-02 10:14:14.06204+08 | +| | +| 除此之外,您还可以使用 transaction_timestamp(), statement_timestamp(), localimestamp(), 或 clock_timestamp() | +| 获取当前的时间戳。 | +| | +| 您可以使用 current_timestamp 指定时间戳的小数秒精度,如下: | +| | +| SELECT current_timestamp, current_timestamp(2); | +| | +| current_timestamp \| current_timestamp | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2022-09-02 10:15:22.670007+08 \| 2022-09-02 10:15:22.67+08 | +| | +| 要获取时间值中的年、月、日、时、分、秒的值,请使用 date_part() 函数: | +| | +| SELECT | +| | +| date_part(\'year\', now()) \"year\", | +| | +| date_part(\'month\', now()) \"month\", | +| | +| date_part(\'day\', now()) \"day\", | +| | +| date_part(\'hour\', now()) \"hour\", | +| | +| date_part(\'minute\', now()) \"minute\", | +| | +| date_part(\'second\', now()) \"second\"; | +| | +| year \| month \| day \| hour \| minute \| second | +| | +| \-\-\-\-\--+\-\-\-\-\-\--+\-\-\-\--+\-\-\-\-\--+\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-- | +| | +| 2022 \| 9 \| 2 \| 10 \| 17 \| 14.520472 | +| | +| 结论 | +| | +| PostgreSQL 支持的时间戳数据类型包括 TIMESTAMP 和 TIMESTAMPTZ。 TIMESTAMP 用来存储没有时区的时间戳, TIMESTAMPTZ | +| 用来存储带时区的时间戳。 | ++-------------------------------------------------------------------------------------------------------------------------+ + +### c语言自带 + ++-----------------------------------------------------------------------+ +| #include \ | +| | +| struct tm { | +| | +| int tm_sec; /\* 秒,范围从 0 到 59 \*/ | +| | +| int tm_min; /\* 分,范围从 0 到 59 \*/ | +| | +| int tm_hour; /\* 小时,范围从 0 到 23 \*/ | +| | +| int tm_mday; /\* 一月中的第几天,范围从 1 到 31 \*/ | +| | +| int tm_mon; /\* 月,范围从 0 到 11 \*/ | +| | +| int tm_year; /\* 自 1900 年起的年数 \*/ | +| | +| int tm_wday; /\* 一周中的第几天,范围从 0 到 6 \*/ | +| | +| int tm_yday; /\* 一年中的第几天,范围从 0 到 365 \*/ | +| | +| int tm_isdst; /\* 夏令时 \*/ | +| | +| }; | +| | +| 1 char \*asctime(const struct tm \*timeptr) | +| | +| 返回一个指向字符串的指针,它代表了结构 timeptr 的日期和时间。 | +| | +| 2 clock_t clock(void) | +| | +| 返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。 | +| | +| 3 char \*ctime(const time_t \*timer) | +| | +| 返回一个表示当地时间的字符串,当地时间是基于参数 timer。 | +| | +| 4 double difftime(time_t time1, time_t time2) | +| | +| 返回 time1 和 time2 之间相差的秒数 (time1-time2)。 | +| | +| 5 struct tm \*gmtime(const time_t \*timer) | +| | +| timer 的值被分解为 tm | +| 结构,并用协调世界时(UTC)也被称为格林尼治标准时间(GMT)表示。 | +| | +| 6 struct tm \*localtime(const time_t \*timer) | +| | +| timer 的值被分解为 tm 结构,并用本地时区表示。 | +| | +| 7 time_t mktime(struct tm \*timeptr) | +| | +| 把 timeptr 所指向的结构转换为一个依据本地时区的 time_t 值。 | +| | +| 8 size_t strftime(char \*str, size_t maxsize, const char \*format, | +| const struct tm \*timeptr) | +| | +| 根据 format 中定义的格式化规则,格式化结构 timeptr | +| 表示的时间,并把它存储在 str 中。 | +| | +| 9 time_t time(time_t \*timer) | +| | +| 计算当前日历时间,并把它编码成 time_t 格式。 | +| | +| 10 int timespec_get(struct timespec \*ts, int base); | +| | +| 获取当前时间(C11)。 | ++-----------------------------------------------------------------------+ + +### 时间类型相关操作 + +data + ++-----------------------------------------------------------------------+ +| PostgreSQL DATE 数据类型介绍 | +| | +| 本文介绍了在 PostgreSQL 中如何使用 DATE 类型以及一些用于处理 DATE | +| 数据的有用的函数。 | +| | +| PostgreSQL 支持 DATE 数据类型,以存储日期值。 | +| | +| PostgreSQL DATE 数据类型使用 yyyy-mm-dd | +| 格式存储日期值,并且每个日期值占用 4 个字节。 | +| | +| PostgreSQL DATE 语法 | +| | +| 要创建一个 DATE 数据类型的列,请使用如下语法: | +| | +| column_name DATE column_constraint | +| | +| 您可以将 yyyy-mm-dd 格式的文本日期值插入到日期列中。 | +| | +| 您可以使用类型转换将一个文本格式的日期值转为日期类型,如下: | +| | +| \'2022-08-30\'::DATE | +| | +| PostgreSQL DATE 实例 | +| | +| 使用下面的语句创建一个新表,名称为 test_date: | +| | +| CREATE TABLE test_date ( | +| | +| id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, | +| | +| date_value DATE NOT NULL DEFAULT CURRENT_DATE | +| | +| ); | +| | +| 该 test_date 表由两列组成: | +| | +| 该 id 列是标识每行的主键列,它是一个标识列。 | +| | +| 该 date_value 列是 DATE 数据类型,它不能为 | +| null,并且具有以当前日期为默认值。 | +| | +| 插入 DATE 数据 | +| | +| 要将数据插入 DATE 列,您必须确保数据采用 yyyy-mm-dd 格式。以下 INSERT | +| 语句向 test_date 表中插入几个新行。 | +| | +| INSERT INTO test_date (date_value) | +| | +| VALUES | +| | +| (\'2022-08-29\'), | +| | +| (\'2022-08-30\'), | +| | +| (\'2022-08-31\') | +| | +| RETURNING \*; | +| | +| id \| date_value | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 \| 2022-08-29 | +| | +| 2 \| 2022-08-30 | +| | +| 3 \| 2022-08-31 | +| | +| (3 rows) | +| | +| 更新 DATE 数据 | +| | +| 要更新 DATE 数据,使用该 UPDATE 语句并传入一个 yyyy-mm-dd 格式的值: | +| | +| UPDATE test_date | +| | +| SET date_value = \'2022-09-01\' | +| | +| WHERE id = 3 | +| | +| RETURNING \*; | +| | +| id \| date_value | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 3 \| 2022-09-01 | +| | +| (1 row) | +| | +| 在 WHERE 条件中使用 DATE 数据 | +| | +| 您可以在 WHERE 子句中使用 DATE 列过滤数据。 | +| | +| 要查找日期为 2022-08-30,请使用以下语句: | +| | +| SELECT \* | +| | +| FROM test_date | +| | +| WHERE date_value = \'2022-08-30\'; | +| | +| id \| date_value | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2 \| 2022-08-30 | +| | +| (1 row) | +| | +| 您还可以查找日期小于 2022-09-01 的所有的行,如下: | +| | +| SELECT \* | +| | +| FROM test_date | +| | +| WHERE date_value \< \'2022-09-01\'; | +| | +| id \| date_value | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 \| 2022-08-29 | +| | +| 2 \| 2022-08-30 | +| | +| (2 rows) | +| | +| 输出指定格式的日期值 | +| | +| PostgreSQL 提供了 TO_CHAR() 函数以按照指定格式输出日期值。 TO_CHAR() | +| 函数接受两个参数。第一个参数是要格式化的日期值,第二个参数是格式。 | +| | +| 要以 yyyy/mm/dd 格式显示日期,请使用以下语句: | +| | +| SELECT | +| | +| id, | +| | +| to_char(date_value, \'yyyy/mm/dd\') | +| | +| FROM | +| | +| test_date; | +| | +| id \| to_char | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 \| 2022/08/29 | +| | +| 2 \| 2022/08/30 | +| | +| 3 \| 2022/09/01 | +| | +| (3 rows) | +| | +| PostgreSQL 日期函数 | +| | +| PostgreSQL 提供了很多 日期 相关的函数。 | +| | +| 要获取当前的日期,请使用 current_date: | +| | +| SELECT current_date; | +| | +| current_date | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2022-08-31 | +| | +| 要计算距离某个日期的年龄,请是 age() 函数: | +| | +| SELECT age(\'2001-01-01\'); | +| | +| age | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 21 years 7 mons 30 days | +| | +| 要将一个指定格式的日期字符串转为日期值,请使用 to_date() 函数: | +| | +| SELECT to_date(\'2022-08-31\', \'YYYY-MM-DD\'); | +| | +| to_date | +| | +| \-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2022-08-31 | +| | +| 要获取日期值中的年、月、日部分,请使用 date_part() 函数: | +| | +| SELECT | +| | +| date_part(\'year\', current_date) \"year\", | +| | +| date_part(\'month\', current_date) \"month\", | +| | +| date_part(\'day\', current_date) \"day\"; | +| | +| year \| month \| day | +| | +| \-\-\-\-\--+\-\-\-\-\-\--+\-\-\-\-- | +| | +| 2022 \| 8 \| 31 | ++-----------------------------------------------------------------------+ + +Time + ++-----------------------------------------------------------------------+ +| PostgreSQL TIME 数据类型介绍 | +| | +| 本文介绍在 PostgreSQL 中如何使用 TIME 类型以及一些用于处理 TIME | +| 数据的有用的函数。 | +| | +| PostgreSQL 支持 TIME 数据类型,以存储时间值。 | +| | +| PostgreSQL 存储 TIME 数据类型值使用 8 个字节。TIME | +| 数据类型允许的范围是从 00:00:00 到 24:00:00。 | +| | +| TIME 数据类型采用以下格式存储 | +| | +| HH:MI:SS.ssssss | +| | +| 其中: | +| | +| HH 表示小时 | +| | +| MI 表示分钟 | +| | +| SS 表示秒 | +| | +| ssssss 表示小数秒 | +| | +| PostgreSQL TIME 语法 | +| | +| 要创建一个 TIME 数据类型的列,请使用如下语法: | +| | +| column_name TIME column_constraint | +| | +| 要将一个实际值插入到时间列中,您可以使用以下格式: | +| | +| HH:MI:SS\[.ssssss\] | +| | +| HHMISS\[.ssssss\] | +| | +| MI:SS\[.ssssss\] | +| | +| HH:MI | +| | +| 您可以使用类型转换将一个文本格式的时间值转为时间类型,如下: | +| | +| \'2022-08-30\'::TIME | +| | +| PostgreSQL TIME 实例 | +| | +| 使用下面的语句创建一个新表,名称为 test_time: | +| | +| CREATE TABLE test_time ( | +| | +| id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, | +| | +| time_value TIME NOT NULL DEFAULT current_time | +| | +| ); | +| | +| 该 test_time 表由两列组成: | +| | +| 该 id 列是标识每行的主键列,它是一个标识列。 | +| | +| 该 time_value 列是 TIME 数据类型,它不能为 | +| null,并且具有以当前时间为默认值。 | +| | +| 插入 TIME 数据 | +| | +| 要将数据插入 TIME 列,您必须确保数据采用正确的格式。以下 INSERT | +| 语句向 test_time 表中插入几个新行。 | +| | +| INSERT INTO test_time (time_value) | +| | +| VALUES | +| | +| (\'10:10:10\'), | +| | +| (\'10:10:11\'), | +| | +| (\'10:10:12\'), | +| | +| (\'10:10\'), | +| | +| (\'101010\'), | +| | +| (\'10:10:10.111111\') | +| | +| RETURNING \*; | +| | +| id \| time_value | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 \| 10:10:10 | +| | +| 2 \| 10:10:11 | +| | +| 3 \| 10:10:12 | +| | +| 4 \| 10:10:00 | +| | +| 5 \| 10:10:10 | +| | +| 6 \| 10:10:10.111111 | +| | +| (6 rows) | +| | +| 更新 TIME 数据 | +| | +| 要更新 TIME 数据,使用该 UPDATE 语句并传入一个正确格式的值: | +| | +| UPDATE test_time | +| | +| SET time_value = \'10:10:09\' | +| | +| WHERE id = 1 | +| | +| RETURNING \*; | +| | +| id \| time_value | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 \| 10:10:09 | +| | +| (1 row) | +| | +| 在 WHERE 条件中使用 TIME 数据 | +| | +| 您可以在 WHERE 子句中使用 TIME 列过滤数据。 | +| | +| 要查找时间为 10:10:10,请使用以下语句: | +| | +| SELECT \* | +| | +| FROM test_time | +| | +| WHERE time_value = \'10:10:10\'; | +| | +| id \| time_value | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 5 \| 10:10:10 | +| | +| (1 row) | +| | +| 您还可以查找时间大于 10:10:10 的所有的行,如下: | +| | +| SELECT \* | +| | +| FROM test_time | +| | +| WHERE time_value \> \'10:10:10\'; | +| | +| id \| time_value | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2 \| 10:10:11 | +| | +| 3 \| 10:10:12 | +| | +| 6 \| 10:10:10.111111 | +| | +| (3 rows) | +| | +| 输出指定格式的时间值 | +| | +| PostgreSQL 提供了 TO_CHAR() 函数以按照指定格式输出时间值。 TO_CHAR() | +| 函数接受两个参数。第一个参数是要格式化的时间值,第二个参数是格式。 | +| | +| 要以 yyyy/mm/dd 格式显示时间,请使用以下语句: | +| | +| SELECT | +| | +| id, | +| | +| to_char(time_value, \'HHMISS\') | +| | +| FROM | +| | +| test_time; | +| | +| id \| to_char | +| | +| \-\-\--+\-\-\-\-\-\-\-\-- | +| | +| 2 \| 101011 | +| | +| 3 \| 101012 | +| | +| 4 \| 101000 | +| | +| 5 \| 101010 | +| | +| 1 \| 101009 | +| | +| (5 rows) | +| | +| PostgreSQL 时间函数 | +| | +| PostgreSQL 提供了很多 时间 相关的函数。 | +| | +| 要获取当前的时间,请使用 current_time: | +| | +| SELECT current_time; | +| | +| current_time | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 17:18:49.94353+08 | +| | +| 您可以使用 current_time 指定时间的小数秒精度,如下: | +| | +| SELECT current_time(1); | +| | +| current_time | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 17:19:00.3+08 | +| | +| 要获取时间值中的时、分、秒部分,请使用 date_part() 函数: | +| | +| SELECT | +| | +| date_part(\'hour\', \'10:11:12\'::time) \"hour\", | +| | +| date_part(\'minute\', \'10:11:12\'::time) \"minute\", | +| | +| date_part(\'second\', \'10:11:12\'::time) \"second\"; | +| | +| hour \| minute \| second | +| | +| \-\-\-\-\--+\-\-\-\-\-\-\--+\-\-\-\-\-\-\-- | +| | +| 10 \| 11 \| 12 | ++-----------------------------------------------------------------------+ + +时间戳 + ++-------------------------------------------------------------------------------------------------------------------------+ +| PostgreSQL 时间戳数据类型 | +| | +| 本文介绍在 PostgreSQL 中如何使用时间戳类型以及一些用于处理时间戳数据的有用的函数。 | +| | +| PostgreSQL 时间戳类型用来存储日期和时间组合的数据类型。PostgreSQL 支持两种类型的时间戳数据类型,包括 TIMESTAMP 和 | +| TIMESTAMPTZ: | +| | +| TIMESTAMP 全写为: TIMESTAMP WITHOUT TIME ZONE, 用来存储没有时区的时间戳。 | +| | +| TIMESTAMPTZ 全写为: TIMESTAMP WITH TIME ZONE, 用来存储带时区的时间戳。 | +| | +| TIMESTAMP 和 TIMESTAMPTZ 都使用 8 个字节来存储时间戳值。 | +| | +| 在 PostgreSQL 内部,TIMESTAMPTZ 类型的值被存储为它对应的 UTC 值。当您从数据库中查询 TIMESTAMPTZ 值时,PostgreSQL 会将 | +| UTC 值转换回数据库服务器、用户或当前数据库连接设置的时区的时间值。 | +| | +| PostgreSQL 时间戳语法 | +| | +| 要创建一个不带时区的时间戳类型的列,请使用如下语法: | +| | +| column_name TIMESTAMP column_constraint | +| | +| 要创建一个带时区的时间戳类型的列,请使用如下语法: | +| | +| column_name TIMESTAMPTZ column_constraint | +| | +| 时间戳数据类型使用格式: YYYY-MM-DD HH:MI:SS\[.ssssss\]。 | +| | +| PostgreSQL 时间戳实例 | +| | +| 使用下面的语句创建一个新表,名称为 test_timestamp: | +| | +| CREATE TABLE test_timestamp ( | +| | +| id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, | +| | +| timestamp_v TIMESTAMP NOT NULL DEFAULT now(), | +| | +| timestamptz_v TIMESTAMPTZ NOT NULL DEFAULT now() | +| | +| ); | +| | +| 该 test_timestamp 表由三列组成: | +| | +| 该 id 列是标识每行的主键列,它是一个标识列。 | +| | +| 该 timestamp_v 列是不带时区的时间戳类型,它不能为 null,并且默认为由 now() 函数产生的当前时间。 | +| | +| 该 timestamptz_v 列是带时区的时间戳类型,它不能为 null,并且默认为由 now() 函数产生的当前时间。 | +| | +| 插入时间戳数据 | +| | +| 要将数据插入时间戳列,您必须确保数据采用正确的格式。以下 INSERT 语句向 test_timestamp 表中插入一个新行。 | +| | +| INSERT INTO test_timestamp (timestamp_v, timestamptz_v) | +| | +| VALUES | +| | +| (\'2022-08-30 10:10:10\', \'2022-08-30 10:10:10\'), | +| | +| (\'2022-08-30\', \'2022-08-30\') | +| | +| RETURNING \*; | +| | +| id \| timestamp_v \| timestamptz_v | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 \| 2022-08-30 10:10:10 \| 2022-08-30 10:10:10+08 | +| | +| 2 \| 2022-08-30 00:00:00 \| 2022-08-30 00:00:00+08 | +| | +| (2 rows) | +| | +| 虽然我们在插入时没有为 TIMESTAMPTZ 列指定带有时区的值,但是 PostgreSQL 自动为此列的值添加了时区信息。 | +| | +| 要查看当前会话的时区,请使用 SHOW TIMEZONE 语句,如下: | +| | +| SHOW TIMEZONE; | +| | +| TimeZone | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| Asia/Shanghai | +| | +| (1 row) | +| | +| 您不能为时间戳类型指定一个时间类型的值,PostgreSQL 将会给出一个错误,如下: | +| | +| INSERT INTO test_timestamp (timestamp_v, timestamptz_v) | +| | +| VALUES (\'10:10:10\', \'10:10:10\') | +| | +| RETURNING \*; | +| | +| ERROR: invalid input syntax for type timestamp: \"10:10:10\" | +| | +| LINE 2: VALUES (\'10:10:10\', \'10:10:10\') | +| | +| 更新 TIMESTAMP 数据 | +| | +| 要更新 TIMESTAMP 数据,使用该 UPDATE 语句并传入一个正确格式的值: | +| | +| UPDATE | +| | +| test_timestamp | +| | +| SET | +| | +| timestamp_v = \'2022-08-30 11:11:11\', | +| | +| timestamptz_v = \'2022-08-30 11:11:11\' | +| | +| WHERE id = 1 | +| | +| RETURNING \*; | +| | +| id \| timestamp_v \| timestamptz_v | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 \| 2022-08-30 11:11:11 \| 2022-08-30 11:11:11+08 | +| | +| (1 row) | +| | +| 在 WHERE 条件中使用 TIMESTAMP 数据 | +| | +| 您可以在 WHERE 子句中使用 TIMESTAMP 列过滤数据。 | +| | +| 要查找日期为 2022-08-30 的所有的行,如下: | +| | +| SELECT \* | +| | +| FROM test_timestamp | +| | +| WHERE to_char(timestamp_v, \'YYYY-MM-DD\') = \'2022-08-30\'; | +| | +| id \| timestamp_v \| timestamptz_v | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2 \| 2022-08-30 00:00:00 \| 2022-08-30 00:00:00+08 | +| | +| 1 \| 2022-08-30 11:11:11 \| 2022-08-30 11:11:11+08 | +| | +| (2 rows) | +| | +| 您还可以查找日期大于 2022-08-30 的所有的行,如下: | +| | +| SELECT \* | +| | +| FROM test_timestamp | +| | +| WHERE timestamp_v \> \'2022-08-30\'; | +| | +| id \| timestamp_v \| timestamptz_v | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 \| 2022-08-30 11:11:11 \| 2022-08-30 11:11:11+08 | +| | +| (1 row) | +| | +| 输出指定格式的时间值 | +| | +| PostgreSQL 提供了 TO_CHAR() 函数以按照指定格式输出时间值。 TO_CHAR() | +| 函数接受两个参数。第一个参数是要格式化的时间值,第二个参数是格式。 | +| | +| 要以 yyyy/mm/dd 格式显示时间,请使用以下语句: | +| | +| SELECT | +| | +| id, | +| | +| to_char(timestamp_v, \'YYYY/MM/DD HH24:MI:SS\'), | +| | +| to_char(timestamptz_v, \'YYYY/MM/DD HH24:MI:SS TZH\') | +| | +| FROM | +| | +| test_timestamp; | +| | +| id \| to_char \| to_char | +| | +| \-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2 \| 2022/08/30 00:00:00 \| 2022/08/30 00:00:00 +08 | +| | +| 1 \| 2022/08/30 11:11:11 \| 2022/08/30 11:11:11 +08 | +| | +| (2 rows) | +| | +| PostgreSQL 时间戳函数 | +| | +| 为了有效地处理时间戳数据,PostgreSQL 提供了一些方便的函数。 | +| | +| 要获取当前时间戳,请使用 now() 或者 current_timestamp,如下: | +| | +| SELECT now(), current_timestamp; | +| | +| now \| current_timestamp | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2022-09-02 10:14:14.06204+08 \| 2022-09-02 10:14:14.06204+08 | +| | +| 除此之外,您还可以使用 transaction_timestamp(), statement_timestamp(), localimestamp(), 或 clock_timestamp() | +| 获取当前的时间戳。 | +| | +| 您可以使用 current_timestamp 指定时间戳的小数秒精度,如下: | +| | +| SELECT current_timestamp, current_timestamp(2); | +| | +| current_timestamp \| current_timestamp | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2022-09-02 10:15:22.670007+08 \| 2022-09-02 10:15:22.67+08 | +| | +| 要获取时间值中的年、月、日、时、分、秒的值,请使用 date_part() 函数: | +| | +| SELECT | +| | +| date_part(\'year\', now()) \"year\", | +| | +| date_part(\'month\', now()) \"month\", | +| | +| date_part(\'day\', now()) \"day\", | +| | +| date_part(\'hour\', now()) \"hour\", | +| | +| date_part(\'minute\', now()) \"minute\", | +| | +| date_part(\'second\', now()) \"second\"; | +| | +| year \| month \| day \| hour \| minute \| second | +| | +| \-\-\-\-\--+\-\-\-\-\-\--+\-\-\-\--+\-\-\-\-\--+\-\-\-\-\-\-\--+\-\-\-\-\-\-\-\-\-\-- | +| | +| 2022 \| 9 \| 2 \| 10 \| 17 \| 14.520472 | ++-------------------------------------------------------------------------------------------------------------------------+ + +INTERVAL + ++---------------------------------------------------------------------------------------------+ +| PostgreSQL INTERVAL 数据类型介绍 | +| | +| 本文介绍在 PostgreSQL 中如何使用 INTERVAL 类型。 | +| | +| 在 PostgreSQL 中, INTERVAL 类型用于存储时间间隔值,比如 N 年 N 月 N 日 N 时 N 分 N | +| 秒。时间间隔不是一个时间点,而是一个时间段。 | +| | +| PostgreSQL INTERVAL 类型占用 16 个字节的存储空间,其取值范围从 -178000000 years 到 | +| 178000000 years。 | +| | +| PostgreSQL INTERVAL 语法 | +| | +| 要定义一个 PostgreSQL INTERVAL 类型的值,请使用如下法: | +| | +| @ INTERVAL \[ fields \] \[ (p) \] | +| | +| 说明: | +| | +| @ 是可选的。 | +| | +| fields 定义一个时间段。您可以采用以下两种输入格式: | +| | +| quantity unit \[quantity unit\...\] \[direction\] | +| | +| 其中, quantity 是一个数字,可能有符号; unit 是 microsecond, millisecond, second, minute, | +| hour, day, week, month, year, decade, century, millennium | +| 中的值,或者他们的缩写或者复数形式; direction 可以是 ago 或者空。 | +| | +| 例如: INTERVAL \'1 years 2 months 3 days 4 hours 5 minutes 6 seconds\' | +| | +| ISO 8601 时间间隔格式 | +| | +| P quantity unit \[ quantity unit \...\] \[ T \[ quantity unit \...\]\] | +| | +| 或者 | +| | +| P \[ years-months-days \] \[ T hours:minutes:seconds \] | +| | +| 其中, unit 您可以使用以下表中的值: | +| | +| 缩写 含义 | +| | +| Y 年 | +| | +| M 月 (日期部分) | +| | +| W 周 | +| | +| D 天 | +| | +| H 小时 | +| | +| M 分钟 (时间部分) | +| | +| S 秒 | +| | +| 例如: | +| | +| INTERVAL \'P1Y2M3DT4H5M6S\' | +| | +| INTERVAL \'P1-2-3T4:5:6\' | +| | +| p 是小数秒的精度。 p 的允许范围是从 0 到 6。 | +| | +| 比如, interval \'1 years 2 months 3 days\' 意思是 1 年 2 月零 3 天。 | +| | +| PostgreSQL INTERVAL 输出格式 | +| | +| PostgreSQL INTERVAL 类型的输出格式可以被设置为 sql_standard, postgres(默认), | +| postgres_verbose ,或 iso_8601。 | +| | +| 您可以使用 SET intervalstyle 命令设置INTERVAL 类型的输出格式。 | +| | +| 下面的示例展示了不同的输出格式: | +| | +| 以下代表 6 years 5 months 4 days 3 hours 2 minutes 1 second 四种风格中的区间: | +| | +| sql_standard | +| | +| SET intervalstyle = \'sql_standard\'; | +| | +| SELECT INTERVAL \'P1Y2M3DT4H5M6S\'; | +| | +| interval | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| +1-2 +3 +4:05:06 | +| | +| sql_standard | +| | +| SET intervalstyle = \'postgres\'; | +| | +| SELECT INTERVAL \'P1Y2M3DT4H5M6S\'; | +| | +| interval | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 year 2 mons 3 days 04:05:06 | +| | +| postgres_verbose | +| | +| SET intervalstyle = \'postgres_verbose\'; | +| | +| SELECT INTERVAL \'P1Y2M3DT4H5M6S\'; | +| | +| interval | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| @ 1 year 2 mons 3 days 4 hours 5 mins 6 secs | +| | +| iso_8601 | +| | +| SET intervalstyle = \'iso_8601\'; | +| | +| SELECT INTERVAL \'P1Y2M3DT4H5M6S\'; | +| | +| interval | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| P1Y2M3DT4H5M6S | +| | +| PostgreSQL 区间相关的运算符和函数 | +| | +| 算数运算 | +| | +| 您可以将算术运算符(+ 、 -)应用于区间值,例如: | +| | +| SELECT INTERVAL \'1 day 12h 50m\' + INTERVAL \'10m\'; | +| | +| ?column? | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 day 13:00:00 | +| | +| 您可以将算术运算符(+ 、 -)应用于时间值和区间值,例如: | +| | +| SELECT | +| | +| current_timestamp \"Now\", | +| | +| current_timestamp - INTERVAL \'10m\' | +| | +| AS \"Ten minutes ago\"; | +| | +| -\[ RECORD 1 \]\-\--+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| Now \| 2022-09-02 16:08:29.933843+08 | +| | +| Ten minutes ago \| 2022-09-02 15:58:29.933843+08 | +| | +| 从 PostgreSQL 间隔中提取数据 | +| | +| 你可以使用 date_part() | +| 函数从一个间隔值的提取指定的字段。下面语句提取间隔值中天数、小时数和分钟数: | +| | +| SELECT | +| | +| date_part(\'day\', INTERVAL \'2 days 10 minutes\') \"day\", | +| | +| date_part(\'hour\', INTERVAL \'2 days 10 minutes\') \"hour\", | +| | +| date_part(\'minutes\', INTERVAL \'2 days 10 minutes\') \"minutes\"; | +| | +| day \| hour \| minutes | +| | +| \-\-\-\--+\-\-\-\-\--+\-\-\-\-\-\-\-\-- | +| | +| 2 \| 0 \| 10 | +| | +| 转换时间值 | +| | +| PostgreSQL 提供三个函数用于转换时间值: | +| | +| justify_days() 将超过 30 天的天数转为月数。 | +| | +| justify_hours() 将超过 24 小时的小时数转为天数。 | +| | +| justify_interval() 将超过 24 小时的小时数转为天数,并且将超过 30 天的天数转为月数。 | +| | +| 比如: | +| | +| SELECT justify_days(INTERVAL \'1 year 40 days\'); | +| | +| justify_days | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 1 year 1 mon 10 days | +| | +| SELECT justify_hours(INTERVAL \'60 hours\'); | +| | +| justify_hours | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 2 days 12:00:00 | +| | +| SELECT justify_interval(INTERVAL \'6000 hours\'); | +| | +| justify_interval | +| | +| \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| 8 mons 10 days | ++---------------------------------------------------------------------------------------------+ + +## 用户相关 + +GetUserId() + +if (roleId == ownerId \|\| superuser_arg(roleId)) + +## Libpq + ++-----------------------------------------------------------------------+ +| #include \"libpq-fe.h\" | +| | +| //通过libpq操作数据库对象 | +| | +| bool SelectHe3LoginRefuseTuple1(char \*username, char \*dbname) | +| | +| { | +| | +| char \*interadminConn=\"host=127.0.0.1 user=ptw port=9432 | +| dbname=postgres\"; | +| | +| PGconn \*metaConn = PQconnectdb(interadminConn); | +| | +| if (PQstatus(metaConn) != CONNECTION_OK) | +| | +| { | +| | +| PQfinish(metaConn); | +| | +| return false; | +| | +| } | +| | +| char command\[256\]=\"select \* from he3_login_refuse where | +| current_timestamp - interval \'2 hours\' \< lastfailedtime AND | +| username=\'\"; | +| | +| strcat(command,username); | +| | +| strcat(command,\"\' AND dbname=\'\"); | +| | +| strcat(command,dbname); | +| | +| strcat(command,\"\'\"); | +| | +| //Query | +| | +| PGresult \*tmRes = PQexec(metaConn, command); | +| | +| if (PQresultStatus(tmRes) != PGRES_TUPLES_OK) | +| | +| { | +| | +| PQclear(tmRes); | +| | +| PQfinish(metaConn); | +| | +| return false; | +| | +| } | +| | +| if (PQntuples(tmRes) \> 0) | +| | +| { | +| | +| PQclear(tmRes); | +| | +| PQfinish(metaConn); | +| | +| return true; | +| | +| } | +| | +| else | +| | +| { | +| | +| PQclear(tmRes); | +| | +| PQfinish(metaConn); | +| | +| return false; | +| | +| } | +| | +| } | ++-----------------------------------------------------------------------+ +| #引入libpq头文件 | +| | +| CFLAGS += -I\$(libpq_srcdir) | ++-----------------------------------------------------------------------+ + +## 用其他账号访问 + +## 自定义语法 + +## 用户认证 + +## 授权 + +## 新增一种权限(一) + ++------------------------------------------------------------------------------------------+ +| ![](./images/media/image2.png){width="5.7652777777777775in" | +| height="1.3125in"}![](./images/media/image3.png){width="5.760416666666667in" | +| height="1.7319444444444445in"}![](./images/media/image4.png){width="5.7625in" | +| height="1.2152777777777777in"}![](./images/media/image5.png){width="5.761805555555555in" | +| height="1.1645833333333333in"}![](./images/media/image6.png){width="5.763888888888889in" | +| height="1.4375in"} | +| | +| ![](./images/media/image7.png){width="5.760416666666667in" | +| height="3.7756944444444445in"} | ++------------------------------------------------------------------------------------------+ + +## 新增一种权限(二)security label方式 + +添加权限:src/backend/catalog/aclchk.c:ExecuteGrantStmt(可以对表grant或者revoke新权限akenc) + ++-----------------------------------------------------------------------+ +| if (stmt-\>privileges == NIL) | +| | +| { | +| | +| istmt.all_privs = true; | +| | +| /\* | +| | +| \* will be turned into ACL_ALL_RIGHTS\_\* by the internal routines | +| | +| \* depending on the object type | +| | +| \*/ | +| | +| istmt.privileges = ACL_NO_RIGHTS; | +| | +| } | +| | +| else | +| | +| { | +| | +| istmt.all_privs = false; | +| | +| istmt.privileges = ACL_NO_RIGHTS; | +| | +| foreach(cell, stmt-\>privileges) | +| | +| { | +| | +| AccessPriv \*privnode = (AccessPriv \*) lfirst(cell); | +| | +| AclMode priv; | +| | +| //haha | +| | +| if(strcmp(privnode-\>priv_name,\"akenc\")==0) | +| | +| { | +| | +| ObjectAddress object; | +| | +| StringInfoData labelrule; | +| | +| initStringInfo(&labelrule); | +| | +| appendStringInfoString(&labelrule, \"akenc:\"); | +| | +| //object.objectId =GetUserId(); | +| | +| object.classId = AuthIdRelationId; | +| | +| object.objectSubId = 0; | +| | +| foreach(cell, istmt.objects) | +| | +| { | +| | +| appendStringInfoString(&labelrule, get_rel_name(lfirst_oid(cell))); | +| | +| appendStringInfoString(&labelrule, \",\"); | +| | +| } | +| | +| labelrule.data\[strlen(labelrule.data)-1\] = \"\\0\"; | +| | +| if(istmt.is_grant) | +| | +| { | +| | +| ListCell \*j; | +| | +| foreach(j, istmt.grantees) | +| | +| { | +| | +| Oid ai_grantee; | +| | +| ai_grantee = lfirst_oid(j); | +| | +| object.objectId =ai_grantee; | +| | +| SetSecurityLabel(&object, \"systemer\", labelrule.data); | +| | +| } | +| | +| } | +| | +| else | +| | +| { | +| | +| ListCell \*j; | +| | +| foreach(j, istmt.grantees) | +| | +| { | +| | +| Oid ai_grantee; | +| | +| ai_grantee = lfirst_oid(j); | +| | +| object.objectId =ai_grantee; | +| | +| SetSecurityLabel(&object, \"systemer\", \"\"); | +| | +| } | +| | +| } | +| | +| resetStringInfo(&labelrule); | +| | +| continue; | +| | +| } | +| | +| //haha end | ++-----------------------------------------------------------------------+ + +鉴权(这里尝试在函数调用的地方进行鉴权,在加密函数Select +pgp_sym_encrypt(\'text\',\'secret\',\'cipher-algo=aes128\');进行鉴权) + ++-----------------------------------------------------------------------+ +| Datum | +| | +| pgp_sym_encrypt_text(PG_FUNCTION_ARGS) | +| | +| { | +| | +| bytea \*data, | +| | +| \*key; | +| | +| text \*arg = NULL; | +| | +| text \*res; | +| | +| //haha | +| | +| //step1 get sl | +| | +| ObjectAddress object; | +| | +| char \*label; | +| | +| object.objectId =GetUserId(); | +| | +| object.classId = AuthIdRelationId; | +| | +| object.objectSubId = 0; | +| | +| label = GetSecurityLabel(&object, \"systemer\"); | +| | +| //规则最长49个字符,每个规则最多6个表名,每个表名为最长8个字符 | +| akenc:table1,table2 | +| | +| //step2 parse label | +| | +| char labeltables\[6\]\[9\]={0}; | +| | +| int labeltablecount=0; | +| | +| char \*token1 = strtok(label, \",\"); | +| | +| while (token1 != NULL) { | +| | +| if(labeltablecount==0) | +| | +| { | +| | +| strcpy(labeltables\[labeltablecount++\],token1+6); | +| | +| } | +| | +| else | +| | +| { | +| | +| strcpy(labeltables\[labeltablecount++\],token1); | +| | +| } | +| | +| token1 = strtok(NULL, \",\"); | +| | +| } | +| | +| //step3 acl check | +| | +| int j=0; | +| | +| for(;j\ 2) | +| | +| arg = PG_GETARG_BYTEA_PP(2); | +| | +| res = encrypt_internal(0, 1, data, key, arg); | +| | +| PG_FREE_IF_COPY(data, 0); | +| | +| PG_FREE_IF_COPY(key, 1); | +| | +| if (PG_NARGS() \> 2) | +| | +| PG_FREE_IF_COPY(arg, 2); | +| | +| PG_RETURN_TEXT_P(res); | +| | +| } | ++-----------------------------------------------------------------------+ + +效果 + + ----------------------------------------------------------------------- + ![](./images/media/image8.png){width="5.28125in" + height="3.0208333333333335in"} + + ----------------------------------------------------------------------- + +## 系统表定义可空列 + ++-----------------------------------------------------------------------+ +| CATALOG(pg_sjx_mask,9790,PgSjxMaskRelationId) | +| | +| { | +| | +| NameData maskname; | +| | +| Oid toid; | +| | +| NameData tname; | +| | +| Oid coid; | +| | +| NameData cname; | +| | +| Oid foid; | +| | +| NameData fname; | +| | +| NameData input1 BKI_FORCE_NULL BKI_DEFAULT(\_null\_); | +| | +| } FormData_pg_sjx_mask; | ++-----------------------------------------------------------------------+ + +## 语法(表)from + ++-----------------------------------------------------------------------+ +| from_clause: | +| | +| FROM from_list { \$\$ = \$2; } | +| | +| \| /\*EMPTY\*/ { \$\$ = NIL; } | +| | +| ; | +| | +| from_list: | +| | +| table_ref { \$\$ = list_make1(\$1); } | +| | +| \| from_list \',\' table_ref { \$\$ = lappend(\$1, \$3); } | +| | +| ; | +| | +| table_ref: relation_expr opt_alias_clause | +| | +| { | +| | +| \$1-\>alias = \$2; | +| | +| \$\$ = (Node \*) \$1; | +| | +| } | +| | +| relation_expr: | +| | +| qualified_name | +| | +| { | +| | +| /\* inheritance query, implicitly \*/ | +| | +| \$\$ = \$1; | +| | +| \$\$-\>inh = true; | +| | +| \$\$-\>alias = NULL; | +| | +| } | +| | +| qualified_name: | +| | +| ColId | +| | +| { | +| | +| \$\$ = makeRangeVar(NULL, \$1, \@1); | +| | +| } | +| | +| \| ColId indirection | +| | +| { | +| | +| \$\$ = makeRangeVarFromQualifiedName(\$1, \$2, \@1, yyscanner); | +| | +| } | +| | +| ; | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| RangeVar \* | +| | +| makeRangeVar(char \*schemaname, char \*relname, int location) | +| | +| { | +| | +| LOG_FUNCTION_ENTRY(); | +| | +| RangeVar \*r = makeNode(RangeVar); | +| | +| r-\>catalogname = NULL; | +| | +| r-\>schemaname = schemaname; | +| | +| r-\>relname = relname; | +| | +| r-\>inh = true; | +| | +| r-\>relpersistence = RELPERSISTENCE_PERMANENT; | +| | +| r-\>alias = NULL; | +| | +| r-\>location = location; | +| | +| LOG_FUNCTION_EXIT(); | +| | +| return r; | +| | +| } | ++-----------------------------------------------------------------------+ + +**相关操作** + ++-----------------------------------------------------------------------+ +| foreach(fl, frmList) | +| | +| { | +| | +| Node \*n = lfirst(fl); | +| | +| ParseNamespaceItem \*nsitem = NULL; | +| | +| List \*namespace = NULL; | +| | +| n = transformFromClauseItem(pstate, n, | +| | +| &nsitem, | +| | +| &namespace); | +| | +| checkNameSpaceConflicts(pstate, pstate-\>p_namespace, namespace); | +| | +| /\* Mark the new namespace items as visible only to LATERAL \*/ | +| | +| setNamespaceLateralState(namespace, true, true); | +| | +| pstate-\>p_joinlist = lappend(pstate-\>p_joinlist, n); | +| | +| pstate-\>p_namespace = list_concat(pstate-\>p_namespace, namespace); | +| | +| } | +| | +| if (IsA(n, RangeVar)) | +| | +| { | +| | +| /\* Plain relation reference, or perhaps a CTE reference \*/ | +| | +| RangeVar \*rv = (RangeVar \*) n; | +| | +| RangeTblRef \*rtr = NULL; | +| | +| ParseNamespaceItem \*nsitem = NULL; | +| | +| /\* Check if it\'s a CTE or tuplestore reference \*/ | +| | +| nsitem = getNSItemForSpecialRelationTypes(pstate, rv); | +| | +| /\* if not found above, must be a table reference \*/ | +| | +| if (!nsitem) { | +| | +| nsitem = transformTableEntry(pstate, rv); | +| | +| } | +| | +| else { | +| | +| HE3_LOG_PRINT(\"he3db file %s func %s line %d if statement no else\", | +| | +| \_\_FILE\_\_, \_\_FUNCTION\_\_, \_\_LINE\_\_); | +| | +| } | +| | +| \*top_nsitem = nsitem; | +| | +| \*namespace = list_make1(nsitem); | +| | +| rtr = makeNode(RangeTblRef); | +| | +| rtr-\>rtindex = nsitem-\>p_rtindex; | +| | +| return (Node \*) rtr; | +| | +| } | ++-----------------------------------------------------------------------+ + +## 语法(函数表达式) + +**typedef struct FuncCall** + +字段名 类型 说明 + +type NodeTag 节点类型标识(固定为 T_FuncCall) + +funcname List\* ​​函数名​​(如 \[\"pg_catalog\", \"sum\"\] 表示 +pg_catalog.sum()) + +args List\* ​​参数列表​​(表达式节点,如 a + b 或列引用) + +agg_order List\* ORDER BY 子句(SortBy 节点列表),用于聚合或窗口函数 + +agg_filter Node\* FILTER (WHERE \...) 条件(仅聚合函数使用) + +over WindowDef\* OVER 子句定义(窗口函数的分区/排序规则) + +agg_within_group bool 是否为 WITHIN GROUP (ORDER BY \...) +语法(如百分位函数) + +agg_star bool 是否包含 \* 参数(如 COUNT(\*)) + +agg_distinct bool 是否使用 DISTINCT(如 COUNT(DISTINCT x)) + +func_variadic bool 是否标记 VARIADIC(用于可变参数函数,如 func(VARIADIC +args)) + +funcformat CoercionForm 显示格式(影响 SQL 生成,如隐式/显式类型转换) + +location int 词法位置(用于错误报告) + +**语法gram.y定义:getname(class,'zhang')** + ++-----------------------------------------------------------------------+ +| opt_target_list: target_list { \$\$ = \$1; } | +| | +| \| /\* EMPTY \*/ { \$\$ = NIL; } | +| | +| ; | +| | +| target_list: | +| | +| target_el { \$\$ = list_make1(\$1); } | +| | +| \| target_list \',\' target_el { \$\$ = lappend(\$1, \$3); } | +| | +| ; | +| | +| target_el: a_expr AS ColLabel | +| | +| { | +| | +| \$\$ = makeNode(ResTarget); | +| | +| \$\$-\>name = \$3; | +| | +| \$\$-\>indirection = NIL; | +| | +| \$\$-\>val = (Node \*) \$1; | +| | +| \$\$-\>location = \@1; | +| | +| }。。。 | +| | +| //\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| a_expr: c_expr { \$\$ = \$1; } | +| | +| //\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| c_expr: func_expr | +| | +| { \$\$ = \$1; } | +| | +| //\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| func_expr: func_application within_group_clause filter_clause | +| over_clause | +| | +| { | +| | +| FuncCall \*n = (FuncCall \*) \$1; | +| | +| //\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| func_application: func_name \'(\' \')\' | +| | +| { | +| | +| \$\$ = (Node \*) makeFuncCall(\$1, NIL, | +| | +| COERCE_EXPLICIT_CALL, | +| | +| \@1); | +| | +| } | +| | +| \| func_name \'(\' func_arg_list opt_sort_clause \')\' | +| | +| { | +| | +| FuncCall \*n = makeFuncCall(\$1, \$3, | +| | +| COERCE_EXPLICIT_CALL, | +| | +| \@1); | +| | +| n-\>agg_order = \$4; | +| | +| \$\$ = (Node \*) n; | +| | +| } | +| | +| //\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| func_name: type_function_name | +| | +| { \$\$ = list_make1(makeString(\$1)); } | +| | +| \| ColId indirection | +| | +| { | +| | +| \$\$ = check_func_name(lcons(makeString(\$1), \$2), | +| | +| yyscanner); | +| | +| } | +| | +| ; | +| | +| //\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| func_arg_list: func_arg_expr | +| | +| { | +| | +| \$\$ = list_make1(\$1); | +| | +| } | +| | +| \| func_arg_list \',\' func_arg_expr | +| | +| { | +| | +| \$\$ = lappend(\$1, \$3); | +| | +| } | +| | +| ; | +| | +| //\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| func_arg_expr: a_expr | +| | +| { | +| | +| \$\$ = \$1; | +| | +| } | +| | +| \| param_name COLON_EQUALS a_expr | +| | +| { | +| | +| NamedArgExpr \*na = makeNode(NamedArgExpr); | +| | +| na-\>name = \$1; | +| | +| na-\>arg = (Expr \*) \$3; | +| | +| na-\>argnumber = -1; /\* until determined \*/ | +| | +| na-\>location = \@1; | +| | +| \$\$ = (Node \*) na; | +| | +| } | +| | +| \| param_name EQUALS_GREATER a_expr | +| | +| { | +| | +| NamedArgExpr \*na = makeNode(NamedArgExpr); | +| | +| na-\>name = \$1; | +| | +| na-\>arg = (Expr \*) \$3; | +| | +| na-\>argnumber = -1; /\* until determined \*/ | +| | +| na-\>location = \@1; | +| | +| \$\$ = (Node \*) na; | +| | +| } | +| | +| ; | +| | +| //\*\*\*\*\*\*\*\*\*\*\*\* | ++-----------------------------------------------------------------------+ + +## 语法(列名)及相关操作(list类型) + +**语法:list结构s1.t1.c1** + ++-----------------------------------------------------------------------+ +| opt_target_list: target_list { \$\$ = \$1; } | +| | +| \| /\* EMPTY \*/ { \$\$ = NIL; } | +| | +| ; | +| | +| target_list: | +| | +| target_el { \$\$ = list_make1(\$1); } | +| | +| \| target_list \',\' target_el { \$\$ = lappend(\$1, \$3); } | +| | +| ; | +| | +| target_el: a_expr AS ColLabel | +| | +| { | +| | +| \$\$ = makeNode(ResTarget); | +| | +| \$\$-\>name = \$3; | +| | +| \$\$-\>indirection = NIL; | +| | +| \$\$-\>val = (Node \*) \$1; | +| | +| \$\$-\>location = \@1; | +| | +| }。。。 | +| | +| //\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| a_expr: c_expr { \$\$ = \$1; } | +| | +| //\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| c_expr: columnref { \$\$ = \$1; } | +| | +| \| AexprConst { \$\$ = \$1; } | +| | +| //\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| columnref: ColId | +| | +| { | +| | +| \$\$ = makeColumnRef(\$1, NIL, \@1, yyscanner); | +| | +| } | +| | +| \| ColId indirection | +| | +| { | +| | +| \$\$ = makeColumnRef(\$1, \$2, \@1, yyscanner); | +| | +| } | +| | +| ; | +| | +| indirection: | +| | +| indirection_el { \$\$ = list_make1(\$1); } | +| | +| \| indirection indirection_el { \$\$ = lappend(\$1, \$2); } | +| | +| ; | +| | +| indirection_el: | +| | +| \'.\' attr_name | +| | +| { | +| | +| \$\$ = (Node \*) makeString(\$2); | +| | +| } | ++-----------------------------------------------------------------------+ + +**相关操作** + ++-----------------------------------------------------------------------+ +| 示例 | +| | +| cover_label: Sconst { \$\$ = \$1; } | +| | +| \| NULL_P { \$\$ = NULL; } | +| | +| \| /\*EMPTY\*/ { \$\$ = NULL; } | +| | +| ; | +| | +| MaskStmt: CREATE MASKINGRULE name ON columnref SET name cover_label | +| | +| { | +| | +| MaskStmt \*n = makeNode(MaskStmt); | +| | +| n-\>maskname = \$3; | +| | +| n-\>targetname = \$5; | +| | +| n-\>functionname = \$7; | +| | +| n-\>input1 = \$8; | +| | +| \$\$ = (Node \*) n; | +| | +| } | +| | +| ; | +| | +| /\* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* Mask Statement peitingwei | +| | +| \* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| typedef struct MaskStmt | +| | +| { | +| | +| NodeTag type; | +| | +| char \*maskname; | +| | +| Node \*targetname; | +| | +| char \*functionname; | +| | +| char \*input1; | +| | +| } MaskStmt; | +| | +| //t1.c1 | +| | +| if (IsA(stmt-\>targetname, ColumnRef)) | +| | +| { | +| | +| ListCell \*l = NULL; | +| | +| foreach(l, ((ColumnRef \*)stmt-\>targetname)-\>fields) | +| | +| { | +| | +| Node \*i = lfirst(l); | +| | +| if (IsA(i, String)) | +| | +| { | +| | +| myname\[k++\]=strVal(i); | +| | +| } | +| | +| } | +| | +| } | +| | +| // | +| | +| 1 | ++-----------------------------------------------------------------------+ + +## 新增一种语法(一) + +### Checkpoint(简单) + ++-----------------------------------------------------------------------+ +| src/backend/parser/gram.y | +| | +| %type \ stmt toplevel_stmt schema_stmt routine_body_stmt | +| | +| AlterEventTrigStmt AlterCollationStmt | +| | +| AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt | +| | +| AlterFdwStmt AlterForeignServerStmt AlterGroupStmt | +| | +| AlterObjectDependsStmt AlterObjectSchemaStmt AlterOwnerStmt | +| | +| AlterOperatorStmt AlterTypeStmt AlterSeqStmt AlterSystemStmt | +| AlterTableStmt | +| | +| AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt | +| | +| AlterCompositeTypeStmt AlterUserMappingStmt | +| | +| AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt AlterStatsStmt | +| | +| AlterDefaultPrivilegesStmt DefACLAction | +| | +| AnalyzeStmt CallStmt ClosePortalStmt ClusterStmt CommentStmt | +| | +| ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt | +| | +| CreateDomainStmt CreateExtensionStmt CreateGroupStmt | +| CreateOpClassStmt | +| | +| CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt | +| | +| CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt | +| CreateTableSpaceStmt | +| | +| CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt | +| | +| CreateAssertionStmt CreateTransformStmt CreateTrigStmt | +| CreateEventTrigStmt | +| | +| CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt | +| | +| CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt | +| DoStmt | +| | +| DropOpClassStmt DropOpFamilyStmt DropStmt | +| | +| DropCastStmt DropRoleStmt | +| | +| DropdbStmt DropTableSpaceStmt | +| | +| DropTransformStmt | +| | +| DropUserMappingStmt ExplainStmt FetchStmt | +| | +| GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt | +| | +| ListenStmt LoadStmt LockStmt MergeStmt NotifyStmt ExplainableStmt | +| PreparableStmt | +| | +| CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt | +| | +| RemoveFuncStmt RemoveOperStmt RenameStmt ReturnStmt RevokeStmt | +| RevokeRoleStmt | +| | +| RuleActionStmt RuleActionStmtOrEmpty RuleStmt | +| | +| SecLabelStmt SelectStmt TransactionStmt TransactionStmtLegacy | +| TruncateStmt | +| | +| UnlistenStmt UpdateStmt VacuumStmt | +| | +| VariableResetStmt VariableSetStmt VariableShowStmt | +| | +| ViewStmt [CheckPointStmt]{.mark} CreateConversionStmt | +| | +| DeallocateStmt PrepareStmt ExecuteStmt | +| | +| DropOwnedStmt ReassignOwnedStmt | +| | +| AlterTSConfigurationStmt AlterTSDictionaryStmt | +| | +| CreateMatViewStmt RefreshMatViewStmt CreateAmStmt | +| | +| CreatePublicationStmt AlterPublicationStmt | +| | +| CreateSubscriptionStmt AlterSubscriptionStmt DropSubscriptionStmt | +| | +| %token \ ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER | +| | +| AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC | +| | +| ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC ATOMIC AT ATTACH ATTRIBUTE | +| AUTHORIZATION | +| | +| BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT | +| | +| BOOLEAN_P BOTH BREADTH BY | +| | +| CACHE CALL CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P | +| | +| CHARACTER CHARACTERISTICS CHECK [CHECKPOINT]{.mark} CLASS CLOSE | +| | +| /\* | +| | +| \* toplevel_stmt includes BEGIN and END. stmt does not include them, | +| because | +| | +| \* those words have different meanings in function bodys. | +| | +| \*/ | +| | +| toplevel_stmt: | +| | +| stmt | +| | +| \| TransactionStmtLegacy | +| | +| ; | +| | +| stmt: | +| | +| AlterEventTrigStmt | +| | +| \| AlterCollationStmt | +| | +| \| AlterDatabaseStmt | +| | +| \| AlterDatabaseSetStmt | +| | +| \| AlterDefaultPrivilegesStmt | +| | +| \| AlterDomainStmt | +| | +| \| AlterEnumStmt | +| | +| \| AlterExtensionStmt | +| | +| \| AlterExtensionContentsStmt | +| | +| \| AlterFdwStmt | +| | +| \| AlterForeignServerStmt | +| | +| \| AlterFunctionStmt | +| | +| \| AlterGroupStmt | +| | +| \| AlterObjectDependsStmt | +| | +| \| AlterObjectSchemaStmt | +| | +| \| AlterOwnerStmt | +| | +| \| AlterOperatorStmt | +| | +| \| AlterTypeStmt | +| | +| \| AlterPolicyStmt | +| | +| \| AlterSeqStmt | +| | +| \| AlterSystemStmt | +| | +| \| AlterTableStmt | +| | +| \| AlterTblSpcStmt | +| | +| \| AlterCompositeTypeStmt | +| | +| \| AlterPublicationStmt | +| | +| \| AlterRoleSetStmt | +| | +| \| AlterRoleStmt | +| | +| \| AlterSubscriptionStmt | +| | +| \| AlterStatsStmt | +| | +| \| AlterTSConfigurationStmt | +| | +| \| AlterTSDictionaryStmt | +| | +| \| AlterUserMappingStmt | +| | +| \| AnalyzeStmt | +| | +| \| CallStmt | +| | +| \| [CheckPointStmt]{.mark} | +| | +| \| ClosePortalStmt | +| | +| /\* | +| | +| \* Checkpoint statement | +| | +| \*/ | +| | +| CheckPointStmt: | +| | +| CHECKPOINT | +| | +| { | +| | +| CheckPointStmt \*n = makeNode(CheckPointStmt); | +| | +| \$\$ = (Node \*) n; | +| | +| } | +| | +| ; | +| | +| unreserved_keyword: | +| | +| ABORT_P | +| | +| \| ABSOLUTE_P | +| | +| \| ACCESS | +| | +| \| ACTION | +| | +| \| ADD_P | +| | +| \| ADMIN | +| | +| \| AFTER | +| | +| \| AGGREGATE | +| | +| \| ALSO | +| | +| \| ALTER | +| | +| \| ALWAYS | +| | +| \| ASENSITIVE | +| | +| \| ASSERTION | +| | +| \| ASSIGNMENT | +| | +| \| AT | +| | +| \| ATOMIC | +| | +| \| ATTACH | +| | +| \| ATTRIBUTE | +| | +| \| BACKWARD | +| | +| \| BEFORE | +| | +| \| BEGIN_P | +| | +| \| BREADTH | +| | +| \| BY | +| | +| \| CACHE | +| | +| \| CALL | +| | +| \| CALLED | +| | +| \| CASCADE | +| | +| \| CASCADED | +| | +| \| CATALOG_P | +| | +| \| CHAIN | +| | +| \| CHARACTERISTICS | +| | +| \| [CHECKPOINT]{.mark} | +| | +| \| CLASS | +| | +| bare_label_keyword: | +| | +| ABORT_P | +| | +| \| ABSOLUTE_P | +| | +| \| ACCESS | +| | +| \| ACTION | +| | +| \| ADD_P | +| | +| \| ADMIN | +| | +| \| AFTER | +| | +| \| AGGREGATE | +| | +| \| ALL | +| | +| \| ALSO | +| | +| \| ALTER | +| | +| \| ALWAYS | +| | +| \| ANALYSE | +| | +| \| ANALYZE | +| | +| \| AND | +| | +| \| ANY | +| | +| \| ASC | +| | +| \| ASENSITIVE | +| | +| \| ASSERTION | +| | +| \| ASSIGNMENT | +| | +| \| ASYMMETRIC | +| | +| \| AT | +| | +| \| ATOMIC | +| | +| \| ATTACH | +| | +| \| ATTRIBUTE | +| | +| \| AUTHORIZATION | +| | +| \| BACKWARD | +| | +| \| BEFORE | +| | +| \| BEGIN_P | +| | +| \| BETWEEN | +| | +| \| BIGINT | +| | +| \| BINARY | +| | +| \| BIT | +| | +| \| BOOLEAN_P | +| | +| \| BOTH | +| | +| \| BREADTH | +| | +| \| BY | +| | +| \| CACHE | +| | +| \| CALL | +| | +| \| CALLED | +| | +| \| CASCADE | +| | +| \| CASCADED | +| | +| \| CASE | +| | +| \| CAST | +| | +| \| CATALOG_P | +| | +| \| CHAIN | +| | +| \| CHARACTERISTICS | +| | +| \| CHECK | +| | +| \| [CHECKPOINT]{.mark} | +| | +| \| CLASS | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/include/parser/kwlist.h | +| | +| PG_KEYWORD(\"checkpoint\", CHECKPOINT, UNRESERVED_KEYWORD, | +| BARE_LABEL) | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/include/nodes/nodes.h | +| | +| T_CheckPointStmt, | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/include/nodes/parsenodes.h | +| | +| /\* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* Checkpoint Statement | +| | +| \* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| typedef struct CheckPointStmt | +| | +| { | +| | +| NodeTag type; | +| | +| } CheckPointStmt; | ++-----------------------------------------------------------------------+ +| src/include/tcop/cmdtaglist.h | +| | +| PG_CMDTAG(CMDTAG_CHECKPOINT, \"CHECKPOINT\", false, false, false) | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| 调用位置 | +| | +| src/backend/tcop/utility.c | +| | +| case T_CheckPointStmt: | +| | +| if (!has_privs_of_role(GetUserId(), ROLE_PG_CHECKPOINT)) | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | +| | +| errmsg(\"must be superuser or have privileges of pg_checkpoint to do | +| CHECKPOINT\"))); | +| | +| RequestCheckpoint(CHECKPOINT_IMMEDIATE \| CHECKPOINT_WAIT \| | +| | +| (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE)); | +| | +| break; | ++-----------------------------------------------------------------------+ + +### Drop database(中等) + ++-------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| src/backend/parser/gram.y | +| | +| /\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| \* | +| | +| \* DROP DATABASE \[ IF EXISTS \] dbname \[ \[ WITH \] ( options ) \] | +| | +| \* | +| | +| \* This is implicitly CASCADE, no need for drop behavior | +| | +| \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/ | +| | +| DropdbStmt: DROP DATABASE name | +| | +| { | +| | +| DropdbStmt \*n = makeNode(DropdbStmt); | +| | +| n-\>dbname = \$3; | +| | +| n-\>missing_ok = false; | +| | +| n-\>options = NULL; | +| | +| \$\$ = (Node \*) n; | +| | +| } | +| | +| \| DROP DATABASE IF_P EXISTS name | +| | +| { | +| | +| DropdbStmt \*n = makeNode(DropdbStmt); | +| | +| n-\>dbname = \$5; | +| | +| n-\>missing_ok = true; | +| | +| n-\>options = NULL; | +| | +| \$\$ = (Node \*) n; | +| | +| } | +| | +| \| DROP DATABASE name opt_with \'(\' drop_option_list \')\' | +| | +| { | +| | +| DropdbStmt \*n = makeNode(DropdbStmt); | +| | +| n-\>dbname = \$3; | +| | +| n-\>missing_ok = false; | +| | +| n-\>options = \$6; | +| | +| \$\$ = (Node \*) n; | +| | +| } | +| | +| \| DROP DATABASE IF_P EXISTS name opt_with \'(\' drop_option_list \')\' | +| | +| { | +| | +| DropdbStmt \*n = makeNode(DropdbStmt); | +| | +| n-\>dbname = \$5; | +| | +| n-\>missing_ok = true; | +| | +| n-\>options = \$8; | +| | +| \$\$ = (Node \*) n; | +| | +| } | +| | +| ; | +| | +| drop_option_list: | +| | +| drop_option | +| | +| { | +| | +| \$\$ = list_make1((Node \*) \$1); | +| | +| } | +| | +| \| drop_option_list \',\' drop_option | +| | +| { | +| | +| \$\$ = lappend(\$1, (Node \*) \$3); | +| | +| } | +| | +| ; | ++-------------------------------------------------------------------------------------------------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/include/nodes/parsenodes.h | +| | +| typedef struct DropdbStmt | +| | +| { | +| | +| NodeTag type; | +| | +| char \*dbname; /\* database to drop \*/ | +| | +| bool missing_ok; /\* skip error if db is missing? \*/ | +| | +| List \*options; /\* currently only FORCE is supported \*/ | +| | +| } DropdbStmt; | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/backend/tcop/utility.c | +| | +| case T_DropdbStmt: | +| | +| /\* no event triggers for global objects \*/ | +| | +| PreventInTransactionBlock(isTopLevel, \"DROP DATABASE\"); | +| | +| DropDatabase(pstate, (DropdbStmt \*) parsetree); | +| | +| break; | ++-----------------------------------------------------------------------+ + +## Security label操作 + +### 原理 + ++-----------------------------------------------------------------------+ +| #include \"commands/seclabel.h\" | +| | +| #include \"catalog/pg_seclabel.h\" | +| | +| #include \"catalog/pg_shseclabel.h\" | +| | +| /\* | +| | +| \* Internal APIs | +| | +| \*/ | +| | +| extern char \*[GetSecurityLabel]{.mark}(const ObjectAddress \*object, | +| | +| const char \*provider); | +| | +| extern void SetSecurityLabel(const ObjectAddress \*object, | +| | +| const char \*provider, const char \*label); | +| | +| extern void DeleteSecurityLabel(const ObjectAddress \*object); | +| | +| extern void DeleteSharedSecurityLabel(Oid objectId, Oid classId); | +| | +| /\* | +| | +| \* Statement and ESP hook support | +| | +| \*/ | +| | +| extern ObjectAddress ExecSecLabelStmt(SecLabelStmt \*stmt); | +| | +| typedef void (\*check_object_relabel_type) (const ObjectAddress | +| \*object, | +| | +| const char \*seclabel); | +| | +| extern void register_label_provider(const char \*provider, | +| | +| check_object_relabel_type hook); | +| | +| static void | +| | +| dummy_object_relabel(const ObjectAddress \*object, const char | +| \*seclabel) | +| | +| { | +| | +| } | +| | +| register_label_provider(\"dummy\", dummy_object_relabel); | ++-----------------------------------------------------------------------+ + +### 示例 + +#### 新增一个默认的provider + +src/backend/postmaster/postmaster.c + ++-----------------------------------------------------------------------+ +| #include \"commands/seclabel.h\" | +| | +| static void | +| | +| ptw_object_relabel(const ObjectAddress \*object, const char | +| \*seclabel) | +| | +| { | +| | +| return; | +| | +| } | +| | +| PostmasterMain(int argc, char \*argv\[\]) | +| | +| /\* | +| | +| \* Now that loadable modules have had their chance to request | +| additional | +| | +| \* shared memory, determine the value of any runtime-computed GUCs | +| that | +| | +| \* depend on the amount of shared memory required. | +| | +| \*/ | +| | +| InitializeShmemGUCs(); | +| | +| //haha | +| | +| register_label_provider(\"systemer\", ptw_object_relabel); | ++-----------------------------------------------------------------------+ + +#### 设置标签 + ++-----------------------------------------------------------------------+ +| postgres=# [security label for systemer on column t1.c1 is | +| \'user1:select,insert\|user2:update\';]{.mark} | +| | +| SECURITY LABEL | +| | +| postgres=# [security label for systemer on column t1.c2 is | +| \'user1:select\|user2:delete,select\';]{.mark} | +| | +| SECURITY LABEL | ++-----------------------------------------------------------------------+ + +#### 标签应用(user1如果对t1表c1列有读权限,那么不容许读c2列) + ++--------------------------------------------------------------------------------+ +| //haha special rule check | +| | +| if (strcmp(\"user1\",GetUserNameFromId(GetUserId(), true))==0 && | +| strcmp(\"table1\",get_rel_name(rte-\>relid))==0) | +| | +| { | +| | +| ObjectAddress object; | +| | +| char \*label1,\*label2; | +| | +| char \*targetAttName=NULL; | +| | +| //step0 | +| | +| int col=-1; | +| | +| while ((col = bms_next_member(rte-\>selectedCols, col)) \>= 0) | +| | +| { | +| | +| /\* bit #s are offset by FirstLowInvalidHeapAttributeNumber \*/ | +| | +| AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; | +| | +| if (attno == InvalidAttrNumber) | +| | +| { | +| | +| targetAttName = get_attname(rte-\>relid, 1, true); | +| | +| break; | +| | +| } | +| | +| else | +| | +| { | +| | +| if(strcmp(\"c1\",get_attname(rte-\>relid, attno, true))==0) | +| | +| targetAttName = get_attname(rte-\>relid, attno, true); | +| | +| } | +| | +| } | +| | +| if(targetAttName != NULL) | +| | +| { | +| | +| //step1 get sl | +| | +| object.classId = RelationRelationId; | +| | +| object.objectId = rte-\>relid; | +| | +| object.objectSubId = 1; | +| | +| label1 = GetSecurityLabel(&object, \"systemer\"); | +| | +| object.objectSubId = 2; | +| | +| label2 = GetSecurityLabel(&object, \"systemer\"); | +| | +| //最多16个规则,每个规则最长50个字符,每个规则2个标记位,每个标记为最长8个字符 | +| user1:select,insert\|user2:select | +| | +| //step2 parse label1 | +| | +| char label1aclstr1\[16\]\[51\]={0}; | +| | +| char label1aclstr2\[16\]\[2\]\[51\]={0}; | +| | +| int label1aclstr1count=0; | +| | +| int label1aclstr2count=0; | +| | +| // 第一层分割:按 \"\|\" 分割 | +| | +| char \*token1 = strtok(label1, \"\|\"); | +| | +| while (token1 != NULL) { | +| | +| strcpy(label1aclstr1\[label1aclstr1count++\],token1); | +| | +| token1 = strtok(NULL, \"\|\"); | +| | +| } | +| | +| // 第二层分割:按 \":\" 分割 | +| | +| for(int i=0;i\&1 | +| | +| else | +| | +| \# 参数化查询示例(PostgreSQL使用-v传递变量) | +| | +| psql -h \"\$DB_HOST\" -p \"\$DB_PORT\" -U \"\$DB_USER\" -d | +| \"\$DB_NAME\" \\ | +| | +| -v \"\$params\" -c \"\$sql\" 2\>&1 | +| | +| fi | +| | +| \# 检查执行状态 | +| | +| if \[ \$? -ne 0 \]; then | +| | +| echo \"\[ERROR\] SQL执行失败: \$sql\" \>&2 | +| | +| return 1 | +| | +| fi | +| | +| } | +| | +| \# 示例1:SELECT查询 | +| | +| echo \"执行SELECT查询\...\" | +| | +| result=\$(execute_sql \"SELECT \* FROM users WHERE age \> 25 LIMIT | +| 3;\") | +| | +| echo \"\$result\" | +| | +| \# 示例2:参数化INSERT | +| | +| echo \"执行参数化INSERT\...\" | +| | +| execute_sql \"INSERT INTO users(name, age) VALUES (:\'name\', | +| :age);\" \\ | +| | +| \"name=\'John Doe\' age=30\" | +| | +| if \[ \$? -eq 0 \]; then | +| | +| echo \"INSERT成功\" | +| | +| fi | +| | +| \# 示例3:UPDATE操作 | +| | +| echo \"执行UPDATE\...\" | +| | +| execute_sql \"UPDATE users SET age = age + 1 WHERE id = :id;\" | +| \"id=5\" | +| | +| \# 示例4:DELETE操作 | +| | +| echo \"执行DELETE\...\" | +| | +| execute_sql \"DELETE FROM users WHERE id = :id;\" \"id=10\" | +| | +| \# 示例5:执行SQL脚本文件 | +| | +| echo \"执行外部SQL脚本\...\" | +| | +| export PGPASSWORD=\"\$DB_PASS\" | +| | +| psql -h \"\$DB_HOST\" -p \"\$DB_PORT\" -U \"\$DB_USER\" -d | +| \"\$DB_NAME\" \\ | +| | +| -f \"/path/to/your_script.sql\" | +| | +| \# 事务处理示例(BEGIN/COMMIT) | +| | +| echo \"执行事务操作\...\" | +| | +| transaction_sql=\$(cat \<\name); | +| | +| values\[1\] = Int32GetDatum(record-\>age); | +| | +| values\[2\] = DirectFunctionCall1(date_in, | +| CStringGetDatum(record-\>birth_date)); | +| | +| values\[3\] = CStringGetTextDatum(record-\>email); | +| | +| values\[4\] = record-\>phone_number ? | +| CStringGetTextDatum(record-\>phone_number) : (Datum) 0; | +| | +| values\[5\] = DirectFunctionCall1(numeric_in, | +| CStringGetDatum(psprintf(\"%.2f\", record-\>salary))); | +| | +| values\[6\] = BoolGetDatum(record-\>is_active); | +| | +| values\[7\] = record-\>profile_json ? DirectFunctionCall1(jsonb_in, | +| CStringGetDatum(record-\>profile_json)) : (Datum) 0; | +| | +| values\[8\] = Float8GetDatum(record-\>location.x); | +| | +| values\[9\] = Float8GetDatum(record-\>location.y); | +| | +| values\[10\] = record-\>notes ? CStringGetTextDatum(record-\>notes) : | +| (Datum) 0; | +| | +| ret = SPI_execute_with_args(sql, 11, argTypes, values, NULL, false, | +| 1); | +| | +| if (ret != SPI_OK_INSERT) { | +| | +| SPI_finish(); | +| | +| elog(ERROR, \"Insert operation failed\"); | +| | +| } | +| | +| /\* 断开 SPI \*/ | +| | +| SPI_finish(); | +| | +| } | +| | +| /\* 查询操作:根据用户名查询记录 \*/ | +| | +| void select_user(const char \*name) { | +| | +| int ret; | +| | +| /\* 连接 SPI \*/ | +| | +| if ((ret = SPI_connect()) != SPI_OK_CONNECT) { | +| | +| elog(ERROR, \"SPI connection failed\"); | +| | +| } | +| | +| const char \*sql = \"SELECT \* FROM users WHERE name = \$1\"; | +| | +| Datum values\[1\]; | +| | +| Oid argTypes\[1\] = {TEXTOID}; | +| | +| values\[0\] = CStringGetTextDatum(name); | +| | +| ret = SPI_execute_with_args(sql, 1, argTypes, values, NULL, true, 0); | +| | +| if (ret != SPI_OK_SELECT) { | +| | +| SPI_finish(); | +| | +| elog(ERROR, \"Select operation failed\"); | +| | +| } | +| | +| /\* 处理返回的结果集 \*/ | +| | +| if (SPI_processed \> 0) { | +| | +| for (int i = 0; i \< SPI_processed; i++) { | +| | +| HeapTuple tuple = SPI_tuptable-\>vals\[i\]; | +| | +| TupleDesc tupdesc = SPI_tuptable-\>tupdesc; | +| | +| char \*name = SPI_getvalue(tuple, tupdesc, 2); | +| | +| char \*email = SPI_getvalue(tuple, tupdesc, 6); | +| | +| char \*phone = SPI_getvalue(tuple, tupdesc, 7); | +| | +| char \*profile = SPI_getvalue(tuple, tupdesc, 10); | +| | +| elog(INFO, \"User found - Name: %s, Email: %s, Phone: %s, Profile: | +| %s\", | +| | +| name ? name : \"\[NULL\]\", | +| | +| email ? email : \"\[NULL\]\", | +| | +| phone ? phone : \"\[NULL\]\", | +| | +| profile ? profile : \"\[NULL\]\"); | +| | +| } | +| | +| } | +| | +| /\* 断开 SPI \*/ | +| | +| SPI_finish(); | +| | +| } | +| | +| /\* 更新操作:更新用户信息 \*/ | +| | +| void update_user(const char \*name, const UserRecord \*new_data) { | +| | +| int ret; | +| | +| /\* 连接 SPI \*/ | +| | +| if ((ret = SPI_connect()) != SPI_OK_CONNECT) { | +| | +| elog(ERROR, \"SPI connection failed\"); | +| | +| } | +| | +| const char \*sql = \"UPDATE users SET age = \$1, email = \$2, salary | +| = \$3, \" | +| | +| \"is_active = \$4, notes = \$5 WHERE name = \$6\"; | +| | +| Datum values\[6\]; | +| | +| Oid argTypes\[6\] = {INT4OID, TEXTOID, NUMERICOID, BOOLOID, TEXTOID, | +| TEXTOID}; | +| | +| values\[0\] = Int32GetDatum(new_data-\>age); | +| | +| values\[1\] = CStringGetTextDatum(new_data-\>email); | +| | +| values\[2\] = DirectFunctionCall1(numeric_in, | +| CStringGetDatum(psprintf(\"%.2f\", new_data-\>salary))); | +| | +| values\[3\] = BoolGetDatum(new_data-\>is_active); | +| | +| values\[4\] = new_data-\>notes ? | +| CStringGetTextDatum(new_data-\>notes) : (Datum) 0; | +| | +| values\[5\] = CStringGetTextDatum(name); | +| | +| ret = SPI_execute_with_args(sql, 6, argTypes, values, NULL, false, | +| 0); | +| | +| if (ret != SPI_OK_UPDATE) { | +| | +| SPI_finish(); | +| | +| elog(ERROR, \"Update operation failed\"); | +| | +| } | +| | +| /\* 断开 SPI \*/ | +| | +| SPI_finish(); | +| | +| } | +| | +| /\* 删除操作:删除用户记录 \*/ | +| | +| void delete_user(const char \*name) { | +| | +| int ret; | +| | +| /\* 连接 SPI \*/ | +| | +| if ((ret = SPI_connect()) != SPI_OK_CONNECT) { | +| | +| elog(ERROR, \"SPI connection failed\"); | +| | +| } | +| | +| const char \*sql = \"DELETE FROM users WHERE name = \$1\"; | +| | +| Datum values\[1\]; | +| | +| Oid argTypes\[1\] = {TEXTOID}; | +| | +| values\[0\] = CStringGetTextDatum(name); | +| | +| ret = SPI_execute_with_args(sql, 1, argTypes, values, NULL, false, | +| 0); | +| | +| if (ret != SPI_OK_DELETE) { | +| | +| SPI_finish(); | +| | +| elog(ERROR, \"Delete operation failed\"); | +| | +| } | +| | +| /\* 断开 SPI \*/ | +| | +| SPI_finish(); | +| | +| } | ++-----------------------------------------------------------------------+ + +#### 示例二 + ++-----------------------------------------------------------------------+ +| timestamptz SPIGetT0() | +| | +| { | +| | +| int ret; | +| | +| /\* 连接 SPI \*/ | +| | +| if ((ret = SPI_connect()) != SPI_OK_CONNECT) { | +| | +| elog(ERROR, \"SPI connection failed\"); | +| | +| } | +| | +| StringInfoData command; | +| | +| initStringInfo(&command); | +| | +| appendStringInfoString(&command, \"select createtime from | +| ak_sys_key\"); | +| | +| ret = SPI_execute(command.data, true, 1); | +| | +| if (ret != SPI_OK_SELECT) { | +| | +| SPI_finish(); | +| | +| elog(ERROR, \"Select operation failed\"); | +| | +| } | +| | +| HeapTuple tuple = SPI_tuptable-\>vals\[0\]; | +| | +| TupleDesc tupdesc = SPI_tuptable-\>tupdesc; | +| | +| bool isNull; | +| | +| timestamptz t0; | +| | +| //char \*name = SPI_getvalue(tuple, tupdesc, 3); | +| | +| t0 = DatumGetTimestampTz(SPI_getbinval(tuple, tupdesc, 1, &isNull)); | +| | +| /\* 断开 SPI \*/ | +| | +| SPI_finish(); | +| | +| return t0; | +| | +| } | ++-----------------------------------------------------------------------+ + +## SQL引擎关键数据结构 + +### Port + ++-----------------------------------------------------------------------+ +| 字段名称​​ ​​类型​​ ​​说明​​ | +| | +| ​​sock​​ pgsocket 套接字描述符,用于与客户端通信的网络文件句柄。 | +| | +| ​​noblock​​ bool 标识套接字是否处于非阻塞模式(true 表示非阻塞)。 | +| | +| ​​proto​​ ProtocolVersion 前端/后端协议版本(如 FE/BE 协议版本号)。 | +| | +| ​​laddr​​ SockAddr 本地地址(Postmaster 监听的地址信息)。 | +| | +| ​​raddr​​ SockAddr 远程地址(客户端连接的地址信息)。 | +| | +| ​​remote_host​​ char\* 客户端的主机名或 IP 地址(字符串形式)。 | +| | +| ​​remote_hostname​​ char\* 客户端的主机名(非 IP 地址,若可用)。 | +| | +| ​​remote_hostname_resolv​​ int | +| 客户端主机名的解析状态(例如是否成功解析)。 | +| | +| ​​remote_hostname_errcode​​ int | +| 客户端主机名解析失败的错误码(若存在错误)。 | +| | +| ​​remote_port​​ char\* 客户端端口号的字符串表示形式。 | +| | +| ​​canAcceptConnections​​ CAC_state Postmaster 的当前连接接受状态(如 | +| CAC_OK 表示可接受连接)。 | +| | +| ​​database_name​​ char\* 客户端请求连接的数据库名称。 | +| | +| ​​user_name​​ char\* 客户端认证的用户名。 | +| | +| ​​cmdline_options​​ char\* 客户端启动参数(字符串形式,如未设置则为 | +| NULL)。 | +| | +| ​​guc_options​​ List\* 客户端传递的 GUC | +| 配置选项列表(交替存储选项名和值)。 | +| | +| ​​application_name​​ char\* | +| 客户端应用名称(仅用于连接授权日志记录,后续由 GUC 管理)。 | +| | +| ​​hba​​ HbaLine\* 指向当前连接的 pg_hba.conf 规则条目,用于认证流程。 | +| | +| ​​authn_id​​ const char\* 认证过程中用户的身份标识(依赖 | +| hba-\>auth_method,如未认证则为 NULL)。 | +| | +| ​​default_keepalives_idle​​ int 默认的 TCP Keepalive | +| 空闲时间(单位秒,若获取失败则为 -1)。 | +| | +| ​​default_keepalives_interval​​ int 默认的 TCP Keepalive | +| 探测间隔时间(单位秒,若获取失败则为 -1)。 | +| | +| ​​default_keepalives_count​​ int 默认的 TCP Keepalive | +| 探测失败次数阈值(若获取失败则为 -1)。 | +| | +| ​​default_tcp_user_timeout​​ int 默认的 TCP | +| 用户超时时间(单位毫秒,若获取失败则为 -1)。 | +| | +| ​​keepalives_idle​​ int 当前使用的 TCP Keepalive 空闲时间(若为默认值则为 | +| 0)。 | +| | +| ​​keepalives_interval​​ int 当前使用的 TCP Keepalive | +| 探测间隔时间(若为默认值则为 0)。 | +| | +| ​​keepalives_count​​ int 当前使用的 TCP Keepalive | +| 探测失败次数阈值(若为默认值则为 0)。 | +| | +| ​​tcp_user_timeout​​ int 当前使用的 TCP 用户超时时间(若为默认值则为 | +| 0)。 | +| | +| ​​gss​​ pg_gssinfo\*(条件编译) GSSAPI 相关信息的指针(仅在启用 | +| ENABLE_GSS 或 ENABLE_SSPI 时有效)。 | +| | +| ​​ssl_in_use​​ bool 标识当前连接是否使用 SSL/TLS 加密。 | +| | +| ​​peer_cn​​ char\* 对端证书的通用名称(Common Name,仅在 SSL | +| 启用时有效)。 | +| | +| ​​peer_dn​​ char\* 对端证书的可识别名称(Distinguished Name,仅在 SSL | +| 启用时有效)。 | +| | +| ​​peer_cert_valid​​ bool 标识对端证书是否有效(仅在 SSL 启用时有效)。 | +| | +| ​​ssl​​ SSL\*(条件编译) OpenSSL 的 SSL 对象指针(仅在启用 USE_OPENSSL | +| 时有效)。 | +| | +| ​​peer​​ X509\*(条件编译) OpenSSL 的对端证书对象指针(仅在启用 | +| USE_OPENSSL 时有效)。 | ++-----------------------------------------------------------------------+ + +### RawStmt(SelectStmt) + ++-----------------------------------------------------------------------+ +| \#### 语法树 | +| | +| typedef struct RawStmt | +| | +| { | +| | +| NodeTag type; | +| | +| Node \*stmt; /\*\*\* 指向具体语法树的指针 | +| | +| int stmt_location; | +| | +| int stmt_len; | +| | +| } RawStmt; | +| | +| typedef struct SelectStmt | +| | +| { | +| | +| NodeTag type; /\*\*\* 节点类型 | +| | +| List \*distinctClause; /\*\*\* distinct子句 | +| | +| IntoClause \*intoClause; /\*\*\* into子句 | +| | +| List \*targetList; /\*\*\* target list | +| | +| List \*fromClause; /\*\*\* from子句 | +| | +| Node \*whereClause; /\*\*\* where子句 | +| | +| List \*groupClause; /\*\*\* groupby子句 | +| | +| bool groupDistinct; /\*\*\* group by操作是否要去重 | +| | +| Node \*havingClause; /\*\*\* having子句 | +| | +| List \*windowClause; /\*\*\* 窗口函数 | +| | +| List \*valuesLists; /\*\*\* values list 对应 SELECT \* FROM (VALUES | +| \...)用法 | +| | +| List \*sortClause; /\*\*\* order by子句 | +| | +| Node \*limitOffset; /\*\*\* limit offset子句 | +| | +| Node \*limitCount; /\*\*\* limit count子句 | +| | +| LimitOption limitOption; /\*\*\* limit类型 | +| | +| List \*lockingClause; /\*\*\* locking子句,对应for update使用 | +| | +| WithClause \*withClause; /\*\*\* with子句,cte使用 | +| | +| HierarClause \*hierarClause; /\*\*\* connect by子句,层次查询使用 | +| | +| SetOperation op; | +| | +| bool all; | +| | +| struct SelectStmt \*larg; /\*\*\* 当前节点左孩子 | +| | +| struct SelectStmt \*rarg; /\*\*\* 当前节点右孩子 | +| | +| } SelectStmt; | +| | +| /\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| (gdb) p n | +| | +| \$211 = (SelectStmt \*) 0x2aa5998 | +| | +| (gdb) p \*n | +| | +| \$212 = {type = T_SelectStmt, distinctClause = 0x0, intoClause = 0x0, | +| targetList = 0x2aa5098, fromClause = 0x2aa54e8, | +| | +| whereClause = 0x2aa5948, groupClause = 0x0, groupDistinct = false, | +| havingClause = 0x0, windowClause = 0x0, valuesLists = 0x0, | +| | +| sortClause = 0x0, limitOffset = 0x0, limitCount = 0x0, limitOption = | +| LIMIT_OPTION_COUNT, lockingClause = 0x0, withClause = 0x0, | +| | +| hierarClause = 0x0, op = SETOP_NONE, all = false, larg = 0x0, rarg = | +| 0x0} | ++-----------------------------------------------------------------------+ + +### RawStmt(SelectStmt-相关操作样本代码) + ++-----------------------------------------------------------------------+ +| simple_select: | +| | +| SELECT opt_all_clause opt_target_list | +| | +| into_clause from_clause where_clause hierarchical_query_clause | +| | +| group_clause having_clause window_clause | +| | +| { | +| | +| SelectStmt \*n = makeNode(SelectStmt); | +| | +| n-\>targetList = \$3; | +| | +| n-\>intoClause = \$4; | +| | +| n-\>fromClause = \$5; | +| | +| n-\>whereClause = \$6; | +| | +| n-\>hierarClause = \$7; | +| | +| n-\>groupClause = (\$8)-\>list; | +| | +| n-\>groupDistinct = (\$8)-\>distinct; | +| | +| n-\>havingClause = \$9; | +| | +| n-\>windowClause = \$10; | +| | +| \$\$ = (Node \*) n; | +| | +| } | +| | +| if (IsA(parseTree, SelectStmt)) | +| | +| { | +| | +| SelectStmt \*stmt = (SelectStmt \*) parseTree; | +| | +| switch (nodeTag(parseTree)) | +| | +| { | +| | +| case T_SelectStmt: | +| | +| case T_InsertStmt: | +| | +| case T_UpdateStmt: | +| | +| foreach(o_target, targetlist) | +| | +| { | +| | +| ResTarget \*res = (ResTarget \*) lfirst(o_target); | +| | +| if (expand_star) | +| | +| { | +| | +| if (IsA(res-\>val, ColumnRef)) | +| | +| { | +| | +| ColumnRef \*cref = (ColumnRef \*) res-\>val; | +| | +| if (IsA(llast(cref-\>fields), A_Star)) | +| | +| { | +| | +| /\* It is something.\*, expand into multiple items \*/ | +| | +| p_target = list_concat(p_target, | +| | +| ExpandColumnRefStar(pstate, | +| | +| cref, | +| | +| true)); | +| | +| continue; | +| | +| } | +| | +| } | +| | +| else if (IsA(res-\>val, A_Indirection)) | +| | +| { | +| | +| A_Indirection \*ind = (A_Indirection \*) res-\>val; | +| | +| if (IsA(llast(ind-\>indirection), A_Star)) | +| | +| { | +| | +| /\* It is something.\*, expand into multiple items \*/ | +| | +| p_target = list_concat(p_target, | +| | +| ExpandIndirectionStar(pstate, | +| | +| ind, | +| | +| true, | +| | +| exprKind)); | +| | +| continue; | +| | +| } | +| | +| } | +| | +| } | +| | +| foreach(l, ((ColumnRef \*)stmt-\>targetname)-\>fields) | +| | +| { | +| | +| Node \*i = lfirst(l); | +| | +| if (IsA(i, String)) | +| | +| { | +| | +| myname\[k++\]=strVal(i); | +| | +| } | +| | +| } | ++-----------------------------------------------------------------------+ + +### Query + ++-----------------------------------------------------------------------+ +| \###### 查询树 | +| | +| typedef struct Query | +| | +| { | +| | +| NodeTag type; /\*\*\* T_Query | +| | +| CmdType commandType; /\*\*\* CMD_SELECT \| CMD_INSERT \... | +| | +| QuerySource querySource; | +| | +| uint64 queryId; | +| | +| bool canSetTag; | +| | +| Node \*utilityStmt; /\*\*\* utility语句使用 | +| | +| int resultRelation; /\*\*\* insert/delete/update语句使用 | +| | +| bool hasAggs; /\*\*\* 语句中是否使用了聚集函数 | +| | +| bool hasWindowFuncs; /\*\*\* 语句中是否使用了窗口函数 | +| | +| bool hasTargetSRFs; /\*\*\* 语句中是否使用了srf函数 | +| | +| bool hasSubLinks; /\*\*\* 语句中是否有子查询或者子链接 | +| | +| bool hasDistinctOn; /\*\*\* distinct子句 | +| | +| bool hasRecursive; /\*\*\* cte中是否有递归操作 | +| | +| bool hasModifyingCTE; /\*\*\* | +| INSERT/UPDATE/DELETE语句是否包含with子句 | +| | +| bool hasForUpdate; /\*\*\* 是否有for update操作 | +| | +| bool hasRowSecurity; /\*\*\* rewriter和optimizer阶段使用 | +| | +| bool isReturn; /\*\*\* 是否是一个return语句 | +| | +| List \*cteList; /\*\*\* with子句 | +| | +| List \*rtable; /\*\*\* 语句中使用到的范围表 | +| | +| FromExpr \*jointree; /\*\*\* from 和 where 子句 | +| | +| List \*targetList; /\*\*\* target list信息 | +| | +| List \*groupClause; /\*\*\* groupby 子句 | +| | +| bool groupDistinct; /\*\*\* groupby是否去重 | +| | +| List \*groupingSets; /\*\*\* groupby使用 | +| | +| Node \*havingQual; /\*\*\* having子句 | +| | +| List \*windowClause; /\*\*\* 窗口函数 | +| | +| List \*distinctClause; /\*\*\* distinct子句 | +| | +| List \*sortClause; /\*\*\* order by 子句 | +| | +| Node \*limitOffset; /\*\*\* limit offset子句 | +| | +| Node \*limitCount; /\*\*\* limit count子句 | +| | +| LimitOption limitOption; /\*\*\* limit类型 | +| | +| List \*mergeActionList; /\*\*\* merge语句使用 | +| | +| bool mergeUseOuterJoin; /\*\*\* merge语句使用 | +| | +| OverridingKind override; /\*\*\* override 子句 | +| | +| OnConflictExpr \*onConflict; | +| | +| List \*returningList; /\*\*\* return 语句使用 | +| | +| List \*rowMarks; /\*\*\* for update使用 | +| | +| Node \*setOperations; /\*\*\* union、except、intersect集合操作使用 | +| | +| List \*constraintDeps; /\*\*\* 约束相关的oid链表 | +| | +| List \*withCheckOptions; /\*\*\* check约束相关信息,rewrite使用 | +| | +| int stmt_location; | +| | +| int stmt_len; | +| | +| } Query; | +| | +| /\*\*\*\*\*\*\*\*\*\*\* | +| | +| (gdb) p result | +| | +| \$223 = (Query \*) 0x2aa5c38 | +| | +| (gdb) p \*result | +| | +| \$224 = {type = T_Query, commandType = CMD_SELECT, querySource = | +| QSRC_ORIGINAL, queryId = 0, canSetTag = false, utilityStmt = 0x0, | +| | +| resultRelation = 0, hasAggs = false, hasWindowFuncs = false, | +| hasTargetSRFs = false, hasSubLinks = false, hasDistinctOn = false, | +| | +| hasRecursive = false, hasModifyingCTE = false, hasForUpdate = false, | +| hasRowSecurity = false, isReturn = false, cteList = 0x0, | +| | +| rtable = 0x2aa5f60, jointree = 0x2ba4ba8, mergeActionList = 0x0, | +| mergeUseOuterJoin = false, targetList = 0x2ba4858, | +| | +| override = OVERRIDING_NOT_SET, onConflict = 0x0, returningList = 0x0, | +| groupClause = 0x0, groupDistinct = false, groupingSets = 0x0, | +| | +| havingQual = 0x0, windowClause = 0x0, distinctClause = 0x0, | +| sortClause = 0x0, limitOffset = 0x0, limitCount = 0x0, | +| | +| limitOption = LIMIT_OPTION_COUNT, rowMarks = 0x0, setOperations = | +| 0x0, constraintDeps = 0x0, withCheckOptions = 0x0, stmt_location = 0, | +| | +| stmt_len = 0} | +| | +| \*\*\*\*\*\*\*\*\*\*\*/ | ++-----------------------------------------------------------------------+ + +### PlannedStmt + + -------------------- --------------- ---------------------------------------------------------------------------------- ---------------------- + **​​字段名称​​** **​​类型/结构** **​​功能描述​​** **​​关联上下文​​** + + ​​commandType​​ CmdType 标识 SQL 操作类型(如 SELECT、INSERT、UPDATE 等),直接来源于查询树(Query)的 执行计划类型 + commandType + + ​​planTree​​ struct Plan\* 查询计划树根节点,由优化器生成,表示整个查询的执行逻辑(如连接顺序、扫描方式等) 计划树结构 + + ​​rtable​​ List\* 范围表(Range Table)条目列表,记录查询中涉及的所有表、子查询、CTE 等对象的元数据 表引用解析 + + ​​resultRelations​​ List\* 对 INSERT/UPDATE/DELETE 操作的目标表在 rtable 中的索引列表,用于定位需修改的表 DML 操作目标表 + + ​​subplans​​ List\* 存储所有子计划(SubPlan)的链表,子计划用于处理嵌套查询或 CTE,通过 PlannerGlobal 子计划管理与参数传递 + 的 subplans 转换而来 + + ​​paramExecTypes​​ List\* 记录执行期参数(PARAM_EXEC)的类型 OID 列表,由 PlannerGlobal 的 paramExecTypes 参数类型校验 + 转换而来,用于参数化执行 + + ​​hasModifyingCTE​​ bool 标识查询是否包含修改数据的 CTE(如 WITH 子句中的 CTE 优化与执行 + INSERT),影响执行顺序和可见性规则 + + ​​relationOids​​ List\* 查询计划依赖的所有关系(表、索引等)的 OID 依赖管理 + 列表,用于执行前的依赖关系检查(如缓存失效) + + ​​rowMarks​​ List\* 行锁标记列表(如 FOR UPDATE),记录需要加锁的行级锁信息 并发控制 + + ​​invalItems​​ List\* 缓存失效项列表,当计划依赖的对象(如表结构)变更时触发缓存失效 执行计划缓存管理 + + ​​parallelModeNeeded​​ bool 标识是否需要并行执行模式,由优化器根据代价模型决定 并行查询优化 + -------------------- --------------- ---------------------------------------------------------------------------------- ---------------------- + +### PlannedStmt(相关操作样本代码) + ++------------------------------------------------------------------------------------+ +| //peitingwei | +| | +| ListCell \*tlist; | +| | +| foreach(tlist, planstate-\>plan-\>targetlist) | +| | +| { | +| | +| TargetEntry \*tle = (TargetEntry \*) lfirst(tlist); | +| | +| if(tle==NULL \|\| | +| maskcheck(GetUserId(),get_rel_name(tle-\>resorigtbl),get_attname(tle-\>resorigtbl, | +| tle-\>resorigcol, true))==false) | +| | +| { | +| | +| continue; | +| | +| } | +| | +| if (strcmp(get_rel_name(tle-\>resorigtbl),\"he3table\")==0 && | +| strcmp(get_attname(tle-\>resorigtbl, tle-\>resorigcol, true),\"desc\")==0) | +| | +| { | +| | +| slot_getallattrs(slot); | +| | +| Jsonb \*in = DatumGetJsonbP(slot-\>tts_values\[tle-\>resno-1\]); | +| | +| char \*out = NULL; | +| | +| out = JsonbToCString(NULL, &in-\>root, VARSIZE(in)); | ++------------------------------------------------------------------------------------+ + +## oid和数据库对象名相互转换 + +### 现有收集到的转换函数 + + ---------- --------------------------------------- -------------------------------- + **对象** **Oid-\>name、oid-\>num、num-\>name** **Name-\>oid** + + 列 char \*get_attname(Oid relid, + AttrNumber attnum, bool missing_ok); + + 列 AttrNumber get_attnum(Oid relid, const Oid get_atttype(Oid relid, + char \*attname); AttrNumber attnum); + + 函数 char \*get_func_name(Oid funcid); Oid get_func_rettype(Oid + funcid); + + 表 char \*get_rel_name(Oid relid) + + schema Oid get_rel_namespace(Oid relid) + + user Oid GetUserId(void) GetUserNameFromId(GetUserId(), + true) + + + ---------- --------------------------------------- -------------------------------- + +### 通过系统表获取oid或者对象名 + ++------------------------------------------+----------------+-----------------------------------+------------------------------------------------+ +| **对象** | **系统表** | **oid字段** | **name字段** | ++------------------------------------------+----------------+-----------------------------------+------------------------------------------------+ +| 表、索引、序列、视图、物化视图、复合类型 | pg_class | oid - 对象标识符 | relname - 对象名称 | +| | | | | +| | | relnamespace - | relkind - | +| | | 所属模式的OID(关联pg_namespace) | 对象类型(r=普通表,i=索引,S=序列,v=视图等) | ++------------------------------------------+----------------+-----------------------------------+------------------------------------------------+ +| 数据类型 | pg_type | oid - 类型标识符 | typname - 类型名称 | +| | | | | +| | | typnamespace - 所属模式的OID | | ++------------------------------------------+----------------+-----------------------------------+------------------------------------------------+ +| 函数和存储过程 | pg_proc | oid - 函数标识符 | proname - 函数名称 | +| | | | | +| | | pronamespace - 所属模式的OID | | ++------------------------------------------+----------------+-----------------------------------+------------------------------------------------+ +| 模式(schema) | pg_namespace | oid - 模式标识符 | nspname - 模式名称 | ++------------------------------------------+----------------+-----------------------------------+------------------------------------------------+ +| 数据库 | pg_database | oid - 数据库标识符 | datname - 数据库名称 | ++------------------------------------------+----------------+-----------------------------------+------------------------------------------------+ +| 用户、角色 | pg_authid | oid - 角色标识符 | rolname - 角色名称 | ++------------------------------------------+----------------+-----------------------------------+------------------------------------------------+ +| 约束(主键、外键等) | pg_constraint | oid - 约束标识符 | conname - 约束名称 | ++------------------------------------------+----------------+-----------------------------------+------------------------------------------------+ +| 触发器 | pg_trigger | oid - 触发器标识符 | tgname - 触发器名称 | ++------------------------------------------+----------------+-----------------------------------+------------------------------------------------+ + +## 类型转换 + ++--------------------------------------------------------------------------------------------------------------------------------------------+ +| PostgreSQL在内部使用统一的Datum类型表示所有数据类型的值。本文档详细说明了PostgreSQL中常见数据类型在外部形式与内部Datum表示之间的转换过程。 | +| | +| 基本概念 | +| | +| Datum定义 | +| | +| typedef uintptr_t Datum; /\* 在src/include/postgres.h中定义 \*/ | +| | +| 存储方式 | +| | +| 直接存储:对于小于等于指针大小的值(如整数、布尔值) | +| | +| 引用存储:对于大于指针大小的值(如文本、数组) | +| | +| 数据类型转换机制 | +| | +| 基本类型转换流程 | +| | +| 输入转换:外部数据 → Datum | +| | +| 文本形式 → 内部二进制表示 | +| | +| 内部二进制表示 → Datum | +| | +| 输出转换:Datum → 外部数据 | +| | +| Datum → 内部二进制表示 | +| | +| 内部二进制表示 → 文本形式 | +| | +| 类型转换函数命名规则 | +| | +| 输入转换:XxxGetDatum(),如Int32GetDatum() | +| | +| 输出转换:DatumGetXxx(),如DatumGetInt32() | +| | +| 常见类型转换示例 | +| | +| 整数类型 (int4) | +| | +| // 输入:int4 → Datum | +| | +| int32 value = 42; | +| | +| Datum datum = Int32GetDatum(value); | +| | +| // 输出:Datum → int4 | +| | +| int32 result = DatumGetInt32(datum); | +| | +| 布尔类型 (bool) | +| | +| // 输入:bool → Datum | +| | +| bool value = true; | +| | +| Datum datum = BoolGetDatum(value); | +| | +| // 输出:Datum → bool | +| | +| bool result = DatumGetBool(datum); | +| | +| 变长字符串 (varchar, text) | +| | +| // 输入:字符串 → Datum | +| | +| text \*t = cstring_to_text(\"Hello\"); | +| | +| Datum datum = PointerGetDatum(t); | +| | +| // 输出:Datum → 字符串 | +| | +| text \*t = DatumGetTextPP(datum); | +| | +| char \*str = text_to_cstring(t); | +| | +| 时间戳类型 (timestamp) | +| | +| timestamp类型在PostgreSQL中有两种形式: | +| | +| timestamp without time zone(简写为timestamp) | +| | +| timestamp with time zone(简写为timestamptz) | +| | +| 内部表示 | +| | +| /\* 在 src/include/datatype/timestamp.h 中定义 \*/ | +| | +| typedef int64 Timestamp; // timestamp without time zone | +| | +| typedef int64 TimestampTz; // timestamp with time zone | +| | +| #define POSTGRESQL_EPOCH_JDATE 2451545 /\* == date2j(2000, 1, 1) \*/ | +| | +| timestamp在内部以64位整数存储,表示自PostgreSQL纪元(2000-01-01)以来的微秒数。 | +| | +| 转换函数和宏 | +| | +| /\* 基本转换宏 \*/ | +| | +| #define TimestampGetDatum(X) Int64GetDatum(X) | +| | +| #define DatumGetTimestamp(X) DatumGetInt64(X) | +| | +| #define DatumGetTimestampTz(X) DatumGetInt64(X) | +| | +| /\* timestamp与字符串之间的转换函数 \*/ | +| | +| Datum timestamp_in(PG_FUNCTION_ARGS) | +| | +| { | +| | +| char \*str = PG_GETARG_CSTRING(0); | +| | +| Timestamp timestamp; | +| | +| if (!parse_timestamp(str, ×tamp, NULL)) | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_INVALID_DATETIME_FORMAT), | +| | +| errmsg(\"invalid timestamp format\"))); | +| | +| PG_RETURN_TIMESTAMP(timestamp); | +| | +| } | +| | +| Datum timestamp_out(PG_FUNCTION_ARGS) | +| | +| { | +| | +| Timestamp timestamp = PG_GETARG_TIMESTAMP(0); | +| | +| char \*result = palloc(MAXDATELEN + 1); | +| | +| EncodeDateTime(result, timestamp, MAXDATELEN); | +| | +| PG_RETURN_CSTRING(result); | +| | +| } | +| | +| 完整转换示例 | +| | +| /\* | +| | +| \* 演示timestamp类型的转换和操作 | +| | +| \*/ | +| | +| static void | +| | +| timestamp_conversion_example(void) | +| | +| { | +| | +| /\* 1. 从字符串创建timestamp \*/ | +| | +| char \*time_str = \"2025-05-01 12:34:56\"; | +| | +| Datum ts_datum = DirectFunctionCall3(timestamp_in, | +| | +| CStringGetDatum(time_str), | +| | +| ObjectIdGetDatum(InvalidOid), | +| | +| Int32GetDatum(-1)); | +| | +| /\* 2. 获取当前时间戳 \*/ | +| | +| TimestampTz now = GetCurrentTimestamp(); | +| | +| Datum now_datum = TimestampGetDatum(now); | +| | +| /\* 3. Datum转换为timestamp值 \*/ | +| | +| Timestamp ts = DatumGetTimestamp(ts_datum); | +| | +| /\* 4. 时间戳计算示例 \*/ | +| | +| Timestamp yesterday = ts - USECS_PER_DAY; // 减去一天 | +| | +| Datum yesterday_datum = TimestampGetDatum(yesterday); | +| | +| /\* 5. 时区转换示例 \*/ | +| | +| TimestampTz local_ts = timestamp2timestamptz(ts); | +| | +| /\* 6. 格式化输出 \*/ | +| | +| char \*formatted = DatumGetCString( | +| | +| DirectFunctionCall1(timestamptz_out, | +| | +| TimestampGetDatum(local_ts)) | +| | +| ); | +| | +| /\* 7. 时间戳比较 \*/ | +| | +| bool is_past = (ts \< now); | +| | +| /\* 8. 提取时间戳部分 \*/ | +| | +| int year, month, day, hour, min, sec; | +| | +| timestamp2tm(ts, NULL, &year, &month, &day, &hour, &min, &sec, NULL); | +| | +| } | +| | +| 时间戳操作函数 | +| | +| /\* 常用的时间戳操作函数 \*/ | +| | +| // 比较两个时间戳 | +| | +| int timestamp_cmp(TimestampTz ts1, TimestampTz ts2) | +| | +| { | +| | +| if (ts1 \< ts2) | +| | +| return -1; | +| | +| if (ts1 \> ts2) | +| | +| return 1; | +| | +| return 0; | +| | +| } | +| | +| // 时间戳加法 | +| | +| TimestampTz | +| | +| timestamp_pl_interval(TimestampTz timestamp, Interval \*span) | +| | +| { | +| | +| // 实现时间戳加上一个时间间隔 | +| | +| // \...(简化的示例) | +| | +| return timestamp + span-\>time; | +| | +| } | +| | +| // 检查时间戳是否在范围内 | +| | +| bool | +| | +| timestamp_in_range(TimestampTz ts, TimestampTz start, TimestampTz end) | +| | +| { | +| | +| return (ts \>= start && ts \<= end); | +| | +| } | +| | +| 函数参数转换 | +| | +| 完整函数示例 | +| | +| /\* | +| | +| \* 演示多种参数类型转换的函数 | +| | +| \*/ | +| | +| PG_FUNCTION_INFO_V1(complex_type_function); | +| | +| Datum | +| | +| complex_type_function(PG_FUNCTION_ARGS) | +| | +| { | +| | +| /\* 1. 参数获取与类型转换 \*/ | +| | +| // 整数参数 | +| | +| if (PG_ARGISNULL(0)) | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), | +| | +| errmsg(\"int4 argument must not be null\"))); | +| | +| int32 int_arg = PG_GETARG_INT32(0); | +| | +| // 文本参数(处理NULL) | +| | +| text \*text_arg = PG_ARGISNULL(1) ? NULL : PG_GETARG_TEXT_PP(1); | +| | +| char \*str_arg = text_arg ? text_to_cstring(text_arg) : NULL; | +| | +| // VARCHAR参数(带长度限制) | +| | +| VarChar \*varchar_arg = PG_GETARG_VARCHAR_PP(2); | +| | +| int32 varchar_len = VARSIZE_ANY_EXHDR(varchar_arg); | +| | +| // TIMESTAMP参数 | +| | +| TimestampTz ts_arg = PG_GETARG_TIMESTAMPTZ(3); | +| | +| /\* 2. 参数验证 \*/ | +| | +| if (int_arg \< 0) | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_INVALID_PARAMETER_VALUE), | +| | +| errmsg(\"int4 argument must be non-negative\"))); | +| | +| /\* 3. 函数处理逻辑 \*/ | +| | +| // \... 处理逻辑 \... | +| | +| /\* 4. 返回值转换 \*/ | +| | +| text \*result_text = cstring_to_text(\"处理完成\"); | +| | +| PG_RETURN_TEXT_P(result_text); | +| | +| } | +| | +| /\* | +| | +| \* SQL定义: | +| | +| \* CREATE FUNCTION complex_type_function( | +| | +| \* arg1 int4, | +| | +| \* arg2 text, | +| | +| \* arg3 varchar(100), | +| | +| \* arg4 timestamp | +| | +| \* ) RETURNS text | +| | +| \* AS \'filename\', \'complex_type_function\' | +| | +| \* LANGUAGE C STRICT; | +| | +| \*/ | +| | +| 表字段转换 | +| | +| 复杂表定义示例 | +| | +| CREATE TABLE complex_table ( | +| | +| id int4 NOT NULL, | +| | +| name varchar(50), | +| | +| description text, | +| | +| status bool DEFAULT true, | +| | +| created_at timestamp, | +| | +| metadata jsonb | +| | +| ); | +| | +| 表字段处理示例 | +| | +| /\* | +| | +| \* 演示处理复杂表字段的函数 | +| | +| \*/ | +| | +| static void | +| | +| process_table_fields(TupleDesc tupdesc, HeapTuple tuple) | +| | +| { | +| | +| bool isnull; | +| | +| Datum value; | +| | +| int natts = tupdesc-\>natts; | +| | +| /\* 遍历所有字段 \*/ | +| | +| for (int i = 0; i \< natts; i++) | +| | +| { | +| | +| Form_pg_attribute attr = TupleDescAttr(tupdesc, i); | +| | +| value = heap_getattr(tuple, i + 1, tupdesc, &isnull); | +| | +| if (isnull) | +| | +| { | +| | +| elog(DEBUG1, \"Column %s is NULL\", NameStr(attr-\>attname)); | +| | +| continue; | +| | +| } | +| | +| /\* 根据字段类型进行相应处理 \*/ | +| | +| switch (attr-\>atttypid) | +| | +| { | +| | +| case INT4OID: | +| | +| { | +| | +| int32 int_val = DatumGetInt32(value); | +| | +| // 处理int4值 | +| | +| } | +| | +| break; | +| | +| case VARCHAROID: | +| | +| { | +| | +| VarChar \*varchar_val = DatumGetVarCharPP(value); | +| | +| char \*str = text_to_cstring((text \*) varchar_val); | +| | +| // 处理varchar值 | +| | +| pfree(str); | +| | +| } | +| | +| break; | +| | +| case TEXTOID: | +| | +| { | +| | +| text \*text_val = DatumGetTextPP(value); | +| | +| char \*str = text_to_cstring(text_val); | +| | +| // 处理text值 | +| | +| pfree(str); | +| | +| } | +| | +| break; | +| | +| case BOOLOID: | +| | +| { | +| | +| bool bool_val = DatumGetBool(value); | +| | +| // 处理bool值 | +| | +| } | +| | +| break; | +| | +| case TIMESTAMPTZOID: | +| | +| { | +| | +| TimestampTz ts_val = DatumGetTimestampTz(value); | +| | +| // 处理timestamp值 | +| | +| } | +| | +| break; | +| | +| case JSONBOID: | +| | +| { | +| | +| Jsonb \*jsonb_val = DatumGetJsonbP(value); | +| | +| // 处理jsonb值 | +| | +| } | +| | +| break; | +| | +| default: | +| | +| elog(WARNING, \"Unhandled column type: %u\", attr-\>atttypid); | +| | +| break; | +| | +| } | +| | +| } | +| | +| } | +| | +| 常见问题和注意事项 | +| | +| 内存管理 | +| | +| 使用palloc/pfree而不是malloc/free | +| | +| 注意处理短生命周期内存上下文 | +| | +| 及时释放临时分配的内存 | +| | +| NULL值处理 | +| | +| 使用PG_ARGISNULL检查函数参数是否为NULL | +| | +| 使用isnull标志检查表字段是否为NULL | +| | +| 正确处理NULL值的特殊情况 | +| | +| TOAST处理 | +| | +| 使用PP后缀的宏(如PG_GETARG_TEXT_PP)处理可能被TOAST的值 | +| | +| 注意处理大数据字段的解压和内存使用 | +| | +| 类型修饰符处理 | +| | +| PostgreSQL使用typmod来存储和处理类型修饰符(如varchar(n)中的长度限制)。下面是具体的实现示例: | +| | +| /\* | +| | +| \* 处理varchar(n)类型的长度限制 | +| | +| \*/ | +| | +| Datum | +| | +| varchar_in(PG_FUNCTION_ARGS) | +| | +| { | +| | +| char \*str = PG_GETARG_CSTRING(0); | +| | +| int32 typmod = PG_GETARG_INT32(1); | +| | +| VarChar \*result; | +| | +| result = (VarChar \*) cstring_to_text(str); | +| | +| /\* 检查并应用长度限制 \*/ | +| | +| if (typmod \>= 0) | +| | +| { | +| | +| size_t len = VARSIZE_ANY_EXHDR(result); | +| | +| /\* typmod包含头部4字节 \*/ | +| | +| size_t maxlen = typmod - VARHDRSZ; | +| | +| if (len \> maxlen) | +| | +| { | +| | +| /\* 超出长度限制时进行截断 \*/ | +| | +| size_t newlen = pg_mbcliplen(VARDATA_ANY(result), len, maxlen); | +| | +| result = (VarChar \*) cstring_to_text_with_len(str, newlen); | +| | +| /\* 可选:发出警告 \*/ | +| | +| ereport(WARNING, | +| | +| (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), | +| | +| errmsg(\"value too long for type varchar(%d)\", (int) maxlen))); | +| | +| } | +| | +| } | +| | +| PG_RETURN_VARCHAR_P(result); | +| | +| } | +| | +| /\* | +| | +| \* 类型修饰符的解析函数 | +| | +| \*/ | +| | +| static int32 | +| | +| varchartypmodout(ArrayType \*ta) | +| | +| { | +| | +| int32 typmod; | +| | +| /\* 从数组中获取长度限制 \*/ | +| | +| typmod = DatumGetInt32(((Datum \*) ARR_DATA_PTR(ta))\[0\]); | +| | +| if (typmod \< 0) | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_INVALID_PARAMETER_VALUE), | +| | +| errmsg(\"length for type varchar must be at least 1\"))); | +| | +| /\* 加上头部长度 \*/ | +| | +| typmod += VARHDRSZ; | +| | +| return typmod; | +| | +| } | +| | +| /\* | +| | +| \* 在表定义中的应用示例 | +| | +| \*/ | +| | +| void | +| | +| check_type_modifier(Form_pg_attribute attr) | +| | +| { | +| | +| /\* 对于varchar类型 \*/ | +| | +| if (attr-\>atttypid == VARCHAROID) | +| | +| { | +| | +| int32 declaredLength = attr-\>atttypmod - VARHDRSZ; | +| | +| if (declaredLength \< 0) | +| | +| declaredLength = 0; /\* 未指定长度时的默认处理 \*/ | +| | +| /\* | +| | +| \* 在这里可以进行长度检查和验证 | +| | +| \* typmod的值等于声明的长度加上VARHDRSZ(4) | +| | +| \*/ | +| | +| } | +| | +| } | +| | +| 实际应用场景 | +| | +| 创建表时指定长度: | +| | +| CREATE TABLE test_table ( | +| | +| id serial, | +| | +| name varchar(50), \-- typmod = 54 (50 + VARHDRSZ) | +| | +| code char(10) \-- typmod = 14 (10 + VARHDRSZ) | +| | +| ); | +| | +| 插入数据时的长度验证: | +| | +| /\* 在数据输入函数中验证长度 \*/ | +| | +| static void | +| | +| check_string_length(char \*str, int32 maxlen, const char \*type_name) | +| | +| { | +| | +| if (strlen(str) \> maxlen) | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), | +| | +| errmsg(\"value too long for type %s(%d)\", | +| | +| type_name, maxlen))); | +| | +| } | +| | +| /\* 处理输入数据 \*/ | +| | +| Datum | +| | +| process_varchar_input(PG_FUNCTION_ARGS) | +| | +| { | +| | +| char \*input = PG_GETARG_CSTRING(0); | +| | +| int32 typmod = PG_GETARG_INT32(1); | +| | +| if (typmod != -1) /\* -1表示未指定长度 \*/ | +| | +| { | +| | +| int32 maxlen = typmod - VARHDRSZ; | +| | +| check_string_length(input, maxlen, \"varchar\"); | +| | +| } | +| | +| /\* 继续处理\... \*/ | +| | +| } | +| | +| 类型修饰符的显示: | +| | +| Datum | +| | +| varchartypmodout(PG_FUNCTION_ARGS) | +| | +| { | +| | +| int32 typmod = PG_GETARG_INT32(0); | +| | +| char \*res = (char \*) palloc(64); | +| | +| if (typmod \>= VARHDRSZ) | +| | +| snprintf(res, 64, \"(%d)\", (int) (typmod - VARHDRSZ)); | +| | +| else | +| | +| \*res = \'\\0\'; | +| | +| PG_RETURN_CSTRING(res); | +| | +| } | +| | +| 通过这些机制,PostgreSQL确保: | +| | +| 在DDL操作时正确解析和存储类型修饰符 | +| | +| 在DML操作时正确验证数据长度 | +| | +| 在需要时正确处理数据截断 | +| | +| 提供清晰的错误信息和警告 | +| | +| 编码转换 | +| | +| 注意字符集编码的处理 | +| | +| 使用pg_do_encoding_conversion进行必要的编码转换 | ++--------------------------------------------------------------------------------------------------------------------------------------------+ + +## 新增语法(二) + +示例一 akenc table column,column; + + ----------------------------------------------------------------------- + + ----------------------------------------------------------------------- + +## 行级访问控制(含security label相关操作) + +### 读规则(原有policy) + + ----------------------------------------------------------------------- + + ----------------------------------------------------------------------- + +### 手动构造where过滤条件(修改语法树) + + ----------------------------------------------------------------------- + + ----------------------------------------------------------------------- + +### 读检查(读取slot中的字段,并检查,主要是做行数据访问控制,依赖sl) + +![](./images/media/image38.png){width="5.766666666666667in" +height="0.7923611111111111in"} + +src/backend/executor/execMain.c + ++-----------------------------------------------------------------------+ +| /\* | +| | +| \* Execute the plan and obtain a tuple | +| | +| \*/ | +| | +| slot = ExecProcNode(planstate); | ++-----------------------------------------------------------------------+ + +### 读访问控制实例:年级信息表,包括高一、高二、高三三个年级,高一的老师只能读取高一的年级信息,高二的老师只能读取高一、高二的年级信息,高三的老师可以读取所有年级的信息。 + ++----------------------------------------------------------------------------------------------------------+ +| //设置标签 | +| | +| security label for systemer on role postgres is \'level:2\'; | +| | +| //把设置标签动作,做到grant流程代码如下(这里只是一个示例,没有去demo) | +| | +| /\* | +| | +| \* Assign the default security label on the new database | +| | +| \*/ | +| | +| object.classId = DatabaseRelationId; | +| | +| object.objectId = databaseId; | +| | +| object.objectSubId = 0; | +| | +| SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext); | ++----------------------------------------------------------------------------------------------------------+ +| //创建表 | +| | +| CREATE TABLE Grade ( | +| | +| GradeID INT, | +| | +| GradeName VARCHAR(50), | +| | +| GradeLevel TEXT | +| | +| ); | +| | +| insert into grade values(1,\'class1\',\'1\'),(2,\'class2\',\'2\'),(3,\'class3\',\'3\'); | ++----------------------------------------------------------------------------------------------------------+ +| //过滤 | +| | +| src/backend/executor/execMain.c | +| | +| //haha | +| | +| #include \"commands/seclabel.h\" | +| | +| #include \"utils/lsyscache.h\" | +| | +| #include \"catalog/pg_class.h\" | +| | +| #include \"catalog/pg_authid.h\" | +| | +| #include \"utils/builtins.h\" | +| | +| /\* | +| | +| \* Loop until we\'ve processed the proper number of tuples from the plan. | +| | +| \*/ | +| | +| for (;;) | +| | +| { | +| | +| /\* Reset the per-output-tuple exprcontext \*/ | +| | +| ResetPerTupleExprContext(estate); | +| | +| /\* | +| | +| \* Execute the plan and obtain a tuple | +| | +| \*/ | +| | +| slot = ExecProcNode(planstate); | +| | +| /\* | +| | +| \* if the tuple is null, then we assume there is nothing more to | +| | +| \* process so we just end the loop\... | +| | +| \*/ | +| | +| if (TupIsNull(slot)) | +| | +| break; | +| | +| [//haha]{.mark} | +| | +| { | +| | +| //step1 get user level | +| | +| Oid myuseroid = GetUserId(); | +| | +| ObjectAddress object; | +| | +| char \*mylabel; | +| | +| object.classId = AuthIdRelationId; | +| | +| object.objectId = myuseroid; | +| | +| object.objectSubId = 0; | +| | +| //level:1 | +| | +| mylabel = GetSecurityLabel(&object, \"systemer\"); | +| | +| if(mylabel != NULL && strcmp(\"grade\",get_rel_name(slot-\>tts_tableOid))==0)) { | +| | +| char mylabellevel\[2\]\[10\]={0}; | +| | +| int label1aclstr1count=0; | +| | +| // 按 \":\" 分割,级别保存在mylabellevel\[1\] | +| | +| char \*token1 = strtok(mylabel, \":\"); | +| | +| while (token1 != NULL) { | +| | +| strcpy(mylabellevel\[label1aclstr1count++\],token1); | +| | +| token1 = strtok(NULL, \":\"); | +| | +| } | +| | +| //step2 get grandelevel | +| | +| slot_getallattrs(slot); | +| | +| char \*grandelevel; | +| | +| for(int i =0;i\tts_tupleDescriptor-\>natts;i++) | +| | +| { | +| | +| if(strcmp(slot-\>tts_tupleDescriptor-\>attrs\[i\].attname.data,\"gradelevel\")==0) | +| | +| { | +| | +| grandelevel = TextDatumGetCString(slot-\>tts_values\[i\]); | +| | +| break; | +| | +| } | +| | +| } | +| | +| //level check | +| | +| if(strcmp(mylabellevel\[1\],grandelevel) \< 0) | +| | +| continue; | +| | +| } | +| | +| } | ++----------------------------------------------------------------------------------------------------------+ +| //语法树重写(grandelevel字段隐藏列,因为要判断,所以如果sql中没有要追加到语法树中,然后在列过滤里去掉) | +| | +| src/backend/executor/execScan.c | +| | +| /\* | +| | +| \* check that the current tuple satisfies the qual-clause | +| | +| \* | +| | +| \* check for non-null qual here to avoid a function call to ExecQual() | +| | +| \* when the qual is null \... saves only a few cycles, but they add up | +| | +| \* \... | +| | +| \*/ | +| | +| if (qual == NULL \|\| ExecQual(qual, econtext)) | +| | +| { | +| | +| /\* | +| | +| \* Found a satisfactory scan tuple. | +| | +| \*/ | +| | +| if (projInfo) | +| | +| { | +| | +| /\* | +| | +| \* Form a projection tuple, store it in the result tuple slot | +| | +| \* and return it. | +| | +| \*/ | +| | +| [//haha]{.mark} | +| | +| TupleTableSlot \*slot1 = ExecProject(projInfo); | +| | +| slot1-\>tts_tableOid = slot-\>tts_tableOid; | +| | +| return slot1; | +| | +| } | +| | +| else | +| | +| { | +| | +| /\* | +| | +| \* Here, we aren\'t projecting, so just return scan tuple. | +| | +| \*/ | +| | +| return slot; | +| | +| } | +| | +| } | +| | +| src/backend/nodes/makefuncs.c | +| | +| /\* | +| | +| \* makeTargetEntry - | +| | +| \* creates a TargetEntry node | +| | +| \*/ | +| | +| TargetEntry \* | +| | +| makeTargetEntry(Expr \*expr, | +| | +| AttrNumber resno, | +| | +| char \*resname, | +| | +| bool resjunk) | +| | +| { | +| | +| TargetEntry \*tle = makeNode(TargetEntry); | +| | +| tle-\>expr = expr; | +| | +| tle-\>resno = resno; | +| | +| tle-\>resname = resname; | +| | +| /\* | +| | +| \* We always set these fields to 0. If the caller wants to change them he | +| | +| \* must do so explicitly. Few callers do that, so omitting these | +| | +| \* arguments reduces the chance of error. | +| | +| \*/ | +| | +| tle-\>ressortgroupref = 0; | +| | +| tle-\>resorigtbl = InvalidOid; | +| | +| tle-\>resorigcol = 0; | +| | +| [//haha]{.mark} | +| | +| bool levelresjunk=false; | +| | +| if(isAddLevel && resno==1) | +| | +| { | +| | +| levelresjunk=true; | +| | +| } | +| | +| tle-\>resjunk = levelresjunk; | +| | +| return tle; | +| | +| } | +| | +| src/backend/postmaster/postmaster.c | +| | +| #ifdef EXEC_BACKEND | +| | +| #include \"storage/spin.h\" | +| | +| #endif | +| | +| [//haha]{.mark} | +| | +| #include \"commands/seclabel.h\" | +| | +| /\* | +| | +| \* Now that loadable modules have had their chance to request additional | +| | +| \* shared memory, determine the value of any runtime-computed GUCs that | +| | +| \* depend on the amount of shared memory required. | +| | +| \*/ | +| | +| InitializeShmemGUCs(); | +| | +| [//haha]{.mark} | +| | +| register_label_provider(\"systemer\", ptw_object_relabel); | +| | +| src/backend/tcop/postgres.c | +| | +| #include \"utils/timeout.h\" | +| | +| #include \"utils/timestamp.h\" | +| | +| [//haha]{.mark} | +| | +| bool isAddLevel = false; | +| | +| /\* | +| | +| \* Do basic parsing of the query or queries (this should be safe even if | +| | +| \* we are in aborted transaction state!) | +| | +| \*/ | +| | +| [//haha]{.mark} | +| | +| char new_query_string\[256\]={0}; | +| | +| if(strncmp(query_string,\"select\",6)==0 && strstr(query_string,\"grade\")!=NULL&& | +| strstr(query_string,\"gradelevel\")==NULL) | +| | +| { | +| | +| strcpy(new_query_string,\"select gradelevel,\"); | +| | +| strcpy(new_query_string+strlen(\"select gradelevel,\"),query_string+6); | +| | +| isAddLevel = true; | +| | +| parsetree_list = pg_parse_query(new_query_string); | +| | +| } | +| | +| else | +| | +| { | +| | +| isAddLevel = false; | +| | +| parsetree_list = pg_parse_query(query_string); | +| | +| } | ++----------------------------------------------------------------------------------------------------------+ + +### 写规则(原有policy) + + ----------------------------------------------------------------------- + + ----------------------------------------------------------------------- + +### 写检查 + +src/backend/executor/execMain.c:ExecWithCheckOptions + +### insert(包含示例) + ++------------------------------------------------------------------------------------+ +| ![](./images/media/image39.png){width="5.761111111111111in" | +| height="1.4520833333333334in"} | ++------------------------------------------------------------------------------------+ +| 干预位置: | +| | +| ![](./images/media/image40.png){width="5.761111111111111in" | +| height="2.645138888888889in"} | ++------------------------------------------------------------------------------------+ +| 示例(部容许插入level3的记录)src/backend/executor/nodeModifyTable.c:ExecInsert | +| | +| /\* Since there was no insertion conflict, we\'re done \*/ | +| | +| } | +| | +| else | +| | +| { | +| | +| [//haha]{.mark} | +| | +| slot_getallattrs(slot); | +| | +| char \*grandelevel; | +| | +| for(int i =0;i\tts_tupleDescriptor-\>natts;i++) | +| | +| { | +| | +| if(strcmp(slot-\>tts_tupleDescriptor-\>attrs\[i\].attname.data,\"gradelevel\")==0) | +| | +| { | +| | +| grandelevel = TextDatumGetCString(slot-\>tts_values\[i\]); | +| | +| break; | +| | +| } | +| | +| } | +| | +| if(strcmp(grandelevel,\"3\")==0) | +| | +| { | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | +| | +| errmsg(\"can not insert\"))); | +| | +| } | +| | +| /\* insert the tuple normally \*/ | +| | +| table_tuple_insert(resultRelationDesc, slot, | +| | +| estate-\>es_output_cid, | +| | +| 0, NULL); | +| | +| /\* insert index entries for tuple \*/ | +| | +| if (resultRelInfo-\>ri_NumIndices \> 0) | +| | +| recheckIndexes = ExecInsertIndexTuples(resultRelInfo, | +| | +| slot, estate, false, | +| | +| false, NULL, NIL); | +| | +| } | ++------------------------------------------------------------------------------------+ + +### Update(包含示例) + +src/backend/executor/nodeModifyTable.c:ExecUpdateAct + ++------------------------------------------------------------------------------------+ +| ![](./images/media/image41.png){width="5.7555555555555555in" height="3.08125in"} | ++------------------------------------------------------------------------------------+ +| 干预位置 | +| | +| ![](./images/media/image42.png){width="5.761805555555555in" | +| height="3.404861111111111in"} | ++------------------------------------------------------------------------------------+ +| 示例(只容许更新level3的记录)src/backend/executor/nodeModifyTable.c:ExecUpdateAct | +| | +| /\* Check any RLS UPDATE WITH CHECK policies \*/ | +| | +| if (!partition_constraint_failed && | +| | +| resultRelInfo-\>ri_WithCheckOptions != NIL) | +| | +| { | +| | +| /\* | +| | +| \* ExecWithCheckOptions() will skip any WCOs which are not of the kind | +| | +| \* we are looking for at this point. | +| | +| \*/ | +| | +| ExecWithCheckOptions(WCO_RLS_UPDATE_CHECK, | +| | +| resultRelInfo, slot, estate); | +| | +| } | +| | +| //haha | +| | +| slot_getallattrs(slot); | +| | +| char \*grandelevel; | +| | +| for(int i =0;i\tts_tupleDescriptor-\>natts;i++) | +| | +| { | +| | +| if(strcmp(slot-\>tts_tupleDescriptor-\>attrs\[i\].attname.data,\"gradelevel\")==0) | +| | +| { | +| | +| grandelevel = TextDatumGetCString(slot-\>tts_values\[i\]); | +| | +| break; | +| | +| } | +| | +| } | +| | +| if(strcmp(grandelevel,\"3\")==0) | +| | +| { | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | +| | +| errmsg(\"can not update\"))); | +| | +| } | ++------------------------------------------------------------------------------------+ + +## 列级访问控制 + +### 列过滤(参照ExecFilterJunk重构返回元祖) + +src/backend/executor/execMain.c + ++-----------------------------------------------------------------------+ +| /\* | +| | +| \* Execute the plan and obtain a tuple | +| | +| \*/ | +| | +| slot = ExecProcNode(planstate); | ++-----------------------------------------------------------------------+ + +src/backend/executor/execMain.c + + ----------------------------------------------------------------------- + ![](./images/media/image43.png){width="5.766666666666667in" + height="5.190277777777778in"} + + ----------------------------------------------------------------------- + +### 示例:如果用户读取的列没有权限则不返回(只返回有权限的列) + ++-----------------------------------------------------------------------+ +| foreach(tlist, plan-\>targetlist) | +| | +| { | +| | +| //在此处添加 pg_attribute_aclcheck 逻辑,用以标记 tle->resjunk | +| 是否为真 | +| | +| TargetEntry \*tle = (TargetEntry \*) lfirst(tlist); | +| | +| Oid relOid = tle->resorigtbl; | +| | +| Oid userid = GetUserId(); | +| | +| AttrNumber attno = tle->resorigcol; | +| | +| AclMode requiredPerms = ACL_SELECT; | +| | +| AclMode relPerms; | +| | +| AclMode remainingPerms; | +| | +| if(get_rel_relkind(relOid) == RELKIND_RELATION) | +| | +| { | +| | +| relPerms = pg_class_aclmask(relOid, userid, requiredPerms, | +| ACLMASK_ALL); | +| | +| remainingPerms = requiredPerms & \~relPerms; | +| | +| if(remainingPerms !=0) | +| | +| { | +| | +| if(pg_attribute_aclcheck(relOid, attno, userid, ACL_SELECT) != | +| ACLCHECK_OK) | +| | +| { | +| | +| tle->resjunk = true; | +| | +| } | +| | +| } | +| | +| } | +| | +| if (tle->resjunk) | +| | +| { | +| | +| junk_filter_needed = true; | +| | +| //break; | +| | +| } | +| | +| } | ++-----------------------------------------------------------------------+ + +## Initdb + ++-----------------------------------------------------------------------+ +| initdb阶段执行SQL | +| | +| PG_CMD_PUTS(\"your sql \\n\\n\"); | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| 判断是否是在initdb流程 | +| | +| IsBootstrapProcessingMode()//initdb进程 | +| | +| InitdbSingle//postgres \--single进程 | ++-----------------------------------------------------------------------+ + +## Hook + ++-----------------------------------------------------------------------------------------------------------------------------------------------+ +| 1\. **ClientAuthentication_hook**:在用户认证过程完成后被调用 | +| | +| 用来获取用户安全标签 | +| | +| 2\. **object_access_hook(DDL)**:该 Hook 会在对 SQL 对象执行某些操作的 之前或之后 被调用 | +| | +| 1)OAT_POST_CREATE:DatabaseRelationId、NamespaceRelationId、RelationRelationId、ProcedureRelationId、 | +| | +| 2)OAT_DROP:DatabaseRelationId、NamespaceRelationId、RelationRelationId、ProcedureRelationId、 | +| | +| 3)OAT_TRUNCATE:RelationRelationId | +| | +| 4)OAT_POST_ALTER:DatabaseRelationId、NamespaceRelationId、RelationRelationId、ProcedureRelationId、 | +| | +| 5)OAT_NAMESPACE_SEARCH:NamespaceRelationId | +| | +| 6)OAT_FUNCTION_EXECUTE:ProcedureRelationId | +| | +| 3\. | +| **ExecutorCheckPerms_hook(DML)**:对数据库对象进行操作前进行调用RELKIND_RELATION、RELKIND_PARTITIONED_TABLE、RELKIND_SEQUENCE、RELKIND_VIEW | +| | +| 类型ACL_SELECT、ACL_INSERT、SEPG_DB_TABLE\_\_INSERT、ACL_DELETE | +| | +| 4.**needs_fmgr_hook**:要执行某个函数时,它会先调用 needs_fmgr_hook,判断该函数是否需要被 fmgr_hook 监听 | +| | +| 根据函数oid返回true或者false | +| | +| 5\. **fmgr_hook**:在函数执行前后 执行额外的权限检查或审计(fmgr_hook | +| 会被触发两次:FHET_START(函数执行前)、FHET_END(函数执行后,正常返回)、FHET_ABORT(函数异常终止)) | +| | +| 6\. **row_security_policy_hook_permissive、row_security_policy_hook_restrictive**:1)Hook | +| 用于添加额外的宽松(permissive)策略,与其他默认宽松策略结合使用。2)用于添加额外的 | +| 限制性(restrictive)策略,这些策略必须始终执行,不受其他策略影响。 | +| | +| Command: CREATE POLICY | +| | +| Description: define a new row level security policy for a table | +| | +| Syntax: | +| | +| CREATE POLICY name ON table_name | +| | +| \[ AS { PERMISSIVE \| RESTRICTIVE } \] | +| | +| \[ FOR { ALL \| SELECT \| INSERT \| UPDATE \| DELETE } \] | +| | +| \[ TO { role_name \| PUBLIC \| CURRENT_USER \| SESSION_USER } \[, \...\] \] | +| | +| \[ USING ( using_expression ) \] | +| | +| \[ WITH CHECK ( check_expression ) \] | ++-----------------------------------------------------------------------------------------------------------------------------------------------+ + +## 演练1 + +题目:用户ptw,对表he3table的desc1(json)字段进行脱敏,desc1.playload是数组如\[1,2,3,4\],desc1.name是字符串如"peitingwei",脱敏规则是,playload中的数组内容打乱顺序输出,name每次从{"tom","jerry","alice"}中随机取一个输出 + ++-----------------------------------------------------------------------+ +| src/backend/postmaster/postmaster.c | +| | +| #ifdef EXEC_BACKEND | +| | +| #include \"storage/spin.h\" | +| | +| #endif | +| | +| //peitingwei | +| | +| #include \"commands/seclabel.h\" | +| | +| static void ptw_object_relabel(const ObjectAddress \*object, const | +| char \*seclabel) | +| | +| { | +| | +| return; | +| | +| } | +| | +| //peitingwei end | +| | +| InitializeShmemGUCs(); | +| | +| //peitingwei | +| | +| register_label_provider(\"masker\", ptw_object_relabel); | +| | +| //peitingwei end | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/backend/tcop/postgres.c | +| | +| #include \"utils/timestamp.h\" | +| | +| //peitingwei | +| | +| #include \"catalog/pg_authid.h\" | +| | +| #include \"commands/seclabel.h\" | +| | +| //peitingwei end | +| | +| //peitingwei security label for masker on role ptw is \'he3table:de | +| | +| bool maskcheck(Oid userid, char \*tablename, char \*columnname) | +| | +| { | +| | +| //step1 get label | +| | +| ObjectAddress object; | +| | +| char \*label; | +| | +| object.objectId =userid; | +| | +| object.classId = AuthIdRelationId; | +| | +| object.objectSubId = 0; | +| | +| label = GetSecurityLabel(&object, \"masker\"); | +| | +| //step2 parse label | +| | +| char labeltables\[6\]\[20\]={0}; | +| | +| int labeltablecount=0; | +| | +| char \*token1 = strtok(label, \"\|\"); | +| | +| while (token1 != NULL) { | +| | +| strcpy(labeltables\[labeltablecount++\],token1); | +| | +| token1 = strtok(NULL, \"\|\"); | +| | +| } | +| | +| //step3 form required rule | +| | +| StringInfoData labelrule; | +| | +| initStringInfo(&labelrule); | +| | +| appendStringInfoString(&labelrule, tablename); | +| | +| appendStringInfoString(&labelrule, \":\"); | +| | +| appendStringInfoString(&labelrule, columnname); | +| | +| //step4 mask rule check | +| | +| int j=0; | +| | +| for(;j\plan-\>targetlist) | +| | +| { | +| | +| TargetEntry \*tle = (TargetEntry \*) lfirst(tlist); | +| | +| if(tle==NULL \|\| | +| maskcheck(GetUserId(),get_rel_name(tle-\>resorigtbl),get_attname(tle-\>resorigtbl, | +| tle-\>resorigcol, true))==false) | +| | +| { | +| | +| continue; | +| | +| } | +| | +| if (strcmp(get_rel_name(tle-\>resorigtbl),\"he3table\")==0 && | +| strcmp(get_attname(tle-\>resorigtbl, tle-\>resorigcol, true),\"desc\")==0) | +| | +| { | +| | +| slot_getallattrs(slot); | +| | +| Jsonb \*in = DatumGetJsonbP(slot-\>tts_values\[tle-\>resno-1\]); | +| | +| char \*out = NULL; | +| | +| out = JsonbToCString(NULL, &in-\>root, VARSIZE(in)); | +| | +| //step1 process playload | +| | +| //parse1 \"{\\\"playload\\\": \[1,2,3,4\], \\\"name\\\": \\\"peitingwei\\\"}\" | +| | +| //1,2,3,4\] | +| | +| char labeltables\[10\]\[50\]={0}; | +| | +| int labeltablecount=0; | +| | +| char \*token1 = strtok(out, \"\[\"); | +| | +| while (token1 != NULL) { | +| | +| strcpy(labeltables\[labeltablecount++\],token1); | +| | +| token1 = strtok(NULL, \"\[\"); | +| | +| } | +| | +| //parse2 1,2,3,4 | +| | +| char labeltables2\[10\]\[50\]={0}; | +| | +| int labeltablecount2=0; | +| | +| token1 = strtok(labeltables\[1\], \"\]\"); | +| | +| while (token1 != NULL) { | +| | +| strcpy(labeltables2\[labeltablecount2++\],token1); | +| | +| token1 = strtok(NULL, \"\]\"); | +| | +| } | +| | +| //parse3 1 2 3 4 | +| | +| char labeltables3\[100\]\[50\]={0}; | +| | +| int labeltablecount3=0; | +| | +| token1 = strtok(labeltables2\[0\], \",\"); | +| | +| while (token1 != NULL) { | +| | +| strcpy(labeltables3\[labeltablecount3++\],token1); | +| | +| token1 = strtok(NULL, \",\"); | +| | +| } | +| | +| //generate random series | +| | +| int randomarr\[100\]; | +| | +| int arr\[100\]; | +| | +| srand(time(NULL)); | +| | +| for (int i = 0; i \< labeltablecount3; i++) { | +| | +| arr\[i\] = i; | +| | +| } | +| | +| for (int i = labeltablecount3 - 1; i \> 0; i\--) { | +| | +| int j = rand() % (i + 1); | +| | +| int temp = arr\[i\]; | +| | +| arr\[i\] = arr\[j\]; | +| | +| arr\[j\] = temp; | +| | +| } | +| | +| //form new tuple | +| | +| StringInfoData newout; | +| | +| initStringInfo(&newout); | +| | +| appendStringInfoString(&newout, labeltables\[0\]); | +| | +| appendStringInfoString(&newout, \"\[\"); | +| | +| for(int k=labeltablecount3-1;k\>=0;k\--) | +| | +| { | +| | +| appendStringInfoString(&newout, labeltables3\[arr\[k\]\]); | +| | +| if(k!=0) | +| | +| { | +| | +| appendStringInfoString(&newout, \",\"); | +| | +| } | +| | +| else | +| | +| { | +| | +| appendStringInfoString(&newout, \"\]\"); | +| | +| } | +| | +| } | +| | +| appendStringInfoString(&newout, labeltables2\[1\]); | +| | +| //step2 process name | +| | +| //parse1 \"{\\\"playload\\\": \[1,2,3,4\], \\\"name\\\": \\\"peitingwei\\\"}\" | +| | +| labeltablecount=0; | +| | +| token1 = strtok(newout.data, \"\\\"\"); | +| | +| //parse1 peitingwei | +| | +| while (token1 != NULL) { | +| | +| strcpy(labeltables\[labeltablecount++\],token1); | +| | +| token1 = strtok(NULL, \"\\\"\"); | +| | +| } | +| | +| //reset newout | +| | +| resetStringInfo(&newout); | +| | +| for(int k=0;k\tts_values\[tle-\>resno-1\]=jsonb_from_cstring1(newout.data, | +| strlen(newout.data)); | +| | +| } | +| | +| } | +| | +| //peitingwei end | ++------------------------------------------------------------------------------------+ + +## 演练2 + +题目:将数据库角色划分为系统管理员、安全管理员、审计管理员: + +1\. 系统管理员负责授予和撤销用户权限 + +2\. 安全管理员负责创建和管理用户 + +3\. 审计管理员负责审计所有用户行为 + +![](./images/media/image44.png){width="5.760416666666667in" +height="4.2347222222222225in"}![](./images/media/image45.png){width="5.763194444444444in" +height="4.190277777777778in"} + +src/include/catalog/pg_authid.dat + ++-----------------------------------------------------------------------+ +| { oid =\> \'9500\', oid_symbol =\> \'ROLE_PG_DATABASE_OWNER\', | +| | +| rolname =\> \'sysadmin\', rolsuper =\> \'f\', rolinherit =\> \'t\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'t\', rolcanlogin =\> | +| \'t\', | +| | +| rolreplication =\> \'t\', rolbypassrls =\> \'t\', rolconnlimit =\> | +| \'-1\',rolaud =\> \'f\', rolsec =\> \'f\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'9501\', oid_symbol =\> \'ROLE_PG_DATABASE_OWNER\', | +| | +| rolname =\> \'secadmin\', rolsuper =\> \'f\', rolinherit =\> \'f\', | +| | +| rolcreaterole =\> \'t\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'t\', | +| | +| rolreplication =\> \'t\', rolbypassrls =\> \'t\', rolconnlimit =\> | +| \'-1\',rolaud =\> \'f\', rolsec =\> \'t\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | +| | +| { oid =\> \'9502\', oid_symbol =\> \'ROLE_PG_DATABASE_OWNER\', | +| | +| rolname =\> \'audadmin\', rolsuper =\> \'f\', rolinherit =\> \'f\', | +| | +| rolcreaterole =\> \'f\', rolcreatedb =\> \'f\', rolcanlogin =\> | +| \'t\', | +| | +| rolreplication =\> \'t\', rolbypassrls =\> \'t\', rolconnlimit =\> | +| \'-1\',rolaud =\> \'t\', rolsec =\> \'f\', | +| | +| rolpassword =\> \'\_null\_\', rolvaliduntil =\> \'\_null\_\' }, | ++-----------------------------------------------------------------------+ + +src/bin/initdb/initdb.c + ++-----------------------------------------------------------------------+ +| /\* | +| | +| \* Note that no objects created after setup_depend() will be | +| \"pinned\". | +| | +| \* They are all droppable at the whim of the DBA. | +| | +| \*/ | +| | +| setup_run_file(cmdfd, system_views_file); | +| | +| #ifdef ENABLE_EXTRA_CATALOG | +| | +| setup_run_file(cmdfd, he3_views_file); | +| | +| #endif | +| | +| setup_description(cmdfd); | +| | +| setup_collation(cmdfd); | +| | +| setup_run_file(cmdfd, dictionary_file); | +| | +| //peitingwei | +| | +| PG_CMD_PUTS(\"SET search_path = public;\"); | +| | +| PG_CMD_PUTS(\"create table audittable(id int);\"); | +| | +| PG_CMD_PUTS(\"grant select on audittable to audadmin;\"); | +| | +| //peitingwei end | ++-----------------------------------------------------------------------+ + +src/backend/utils/adt/acl.c + ++-----------------------------------------------------------------------+ +| case OBJECT_FUNCTION: | +| | +| /\* Grant EXECUTE by default, for now \*/ | +| | +| //peitingwei | +| | +| //world_default = ACL_EXECUTE; | +| | +| world_default = ACL_NO_RIGHTS; | +| | +| owner_default = ACL_ALL_RIGHTS_FUNCTION; | +| | +| break; | ++-----------------------------------------------------------------------+ + +src/backend/executor/execMain.c + ++-----------------------------------------------------------------------+ +| #include \"utils/snapmgr.h\" | +| | +| //peitingwei | +| | +| bool isaudadmin(Oid roleid); | +| | +| //peitingwei end | +| | +| static bool | +| | +| ExecCheckRTEPerms(RangeTblEntry \*rte) | +| | +| { | +| | +| LOG_FUNCTION_ENTRY(); | +| | +| AclMode requiredPerms; | +| | +| AclMode relPerms; | +| | +| AclMode remainingPerms; | +| | +| Oid relOid = 0; | +| | +| Oid userid = 0; | +| | +| /\* | +| | +| \* Only plain-relation RTEs need to be checked here. Function RTEs | +| are | +| | +| \* checked when the function is prepared for execution. Join, | +| subquery, | +| | +| \* and special RTEs need no checks. | +| | +| \*/ | +| | +| if (rte-\>rtekind != RTE_RELATION) { | +| | +| LOG_FUNCTION_EXIT(); | +| | +| return true; | +| | +| } | +| | +| //peitingwei | +| | +| //if((rte-\>requiredPerms & \~ACL_SELECT) != 0) | +| | +| //{ | +| | +| // elog(ERROR, \"audit log can not alter\"); | +| | +| //} | +| | +| if (!InitdbSingle && | +| strcmp(\"audittable\",get_rel_name(rte-\>relid))==0 && | +| !isaudadmin(GetUserId())) | +| | +| { | +| | +| elog(ERROR, \"not audadmin\"); | +| | +| } | +| | +| //peitingwei end | +| | +| //peitingwei | +| | +| bool isaudadmin(Oid roleid) | +| | +| { | +| | +| if ((roleid == 9502)) | +| | +| return true; | +| | +| return false; | +| | +| } | +| | +| //peitingwei end | ++-----------------------------------------------------------------------+ + +src/backend/commands/user.c + ++-----------------------------------------------------------------------+ +| #include \"utils/timestamp.h\" | +| | +| //peitingwei | +| | +| static bool issecadmin(Oid roleid); | +| | +| //peitingwei end | +| | +| /\* | +| | +| \* CREATE ROLE | +| | +| \*/ | +| | +| Oid | +| | +| CreateRole(ParseState \*pstate, CreateRoleStmt \*stmt) | +| | +| { | +| | +| LOG_FUNCTION_ENTRY(); | +| | +| Relation pg_authid_rel; | +| | +| TupleDesc pg_authid_dsc; | +| | +| HeapTuple tuple; | +| | +| Datum new_record\[Natts_pg_authid\] = { | +| | +| 0 | +| | +| }; | +| | +| bool new_record_nulls\[Natts_pg_authid\] = { | +| | +| 0 | +| | +| }; | +| | +| Oid roleid = 0; | +| | +| ListCell \*item = NULL; | +| | +| ListCell \*option = NULL; | +| | +| char \*password = NULL; /\* user password \*/ | +| | +| bool issuper = false; /\* Make the user a superuser? \*/ | +| | +| bool inherit = true; /\* Auto inherit privileges? \*/ | +| | +| char \*validUntil = NULL; /\* time the login is valid until \*/ | +| | +| Datum validUntil_datum; /\* same, as timestamptz Datum \*/ | +| | +| bool validUntil_null = false; | +| | +| //peitingwei | +| | +| if (!InitdbSingle && !issecadmin(GetUserId())) | +| | +| { | +| | +| elog(ERROR, \"not secadmin\"); | +| | +| } | +| | +| //peitingwei end | ++-----------------------------------------------------------------------+ + +src/backend/commands/tablespace.c + ++-----------------------------------------------------------------------+ +| /\* he3pg: disable tablespace ddl for now \*/ | +| | +| //peitingwei | +| | +| //elog(ERROR, \"he3pg is not support user define tablespace yet\"); | +| | +| /\* Must be superuser \*/ | +| | +| //peitingwei | +| | +| //if (!superuser()) { | +| | +| if (GetUserId()!=9500) { | +| | +| ereport(ERROR, | ++-----------------------------------------------------------------------+ + +src/backend/catalog/aclchk.c + ++-----------------------------------------------------------------------+ +| static void recordExtensionInitPrivWorker(Oid objoid, Oid classoid, | +| int objsubid, | +| | +| Acl \*new_acl); | +| | +| //peitingwei | +| | +| static bool issysadmin(Oid roleid); | +| | +| //peitingwei end | +| | +| static void | +| | +| ExecGrant_Relation(InternalGrant \*istmt) | +| | +| { | +| | +| Relation relation; | +| | +| Relation attRelation; | +| | +| ListCell \*cell = NULL; | +| | +| //peitingwei | +| | +| if (!InitdbSingle && !issysadmin(GetUserId())) | +| | +| { | +| | +| elog(ERROR, \"not sysadmin\"); | +| | +| } | +| | +| //peitingwei end | +| | +| /\* Determine ID to do the grant as, and available grant options \*/ | +| | +| //peitingwei | +| | +| grantorId=GetUserId(); | +| | +| /\* | +| | +| select_best_grantor(GetUserId(), this_privileges, | +| | +| old_acl, ownerId, | +| | +| &grantorId, &avail_goptions); | +| | +| \*/ | +| | +| switch (pg_class_tuple-\>relkind) | +| | +| { | +| | +| case RELKIND_SEQUENCE: | +| | +| objtype = OBJECT_SEQUENCE; | +| | +| break; | +| | +| default: | +| | +| objtype = OBJECT_TABLE; | +| | +| break; | +| | +| } | +| | +| /\* | +| | +| \* Restrict the privileges to what we can actually grant, and emit | +| | +| \* the standards-mandated warning and error messages. | +| | +| \*/ | +| | +| //peitingwei | +| | +| /\* | +| | +| this_privileges = | +| | +| restrict_and_check_grant(istmt-\>is_grant, avail_goptions, | +| | +| istmt-\>all_privs, this_privileges, | +| | +| relOid, grantorId, objtype, | +| | +| NameStr(pg_class_tuple-\>relname), | +| | +| 0, NULL); | +| | +| \*/ | +| | +| AclMode | +| | +| pg_class_aclmask_ext(Oid table_oid, Oid roleid, AclMode mask, | +| | +| AclMaskHow how, bool \*is_missing) | +| | +| { | +| | +| AclMode result; | +| | +| HeapTuple tuple; | +| | +| Form_pg_class classForm; | +| | +| Datum aclDatum; | +| | +| bool isNull = false; | +| | +| Acl \*acl = NULL; | +| | +| Oid ownerId = 0; | +| | +| /\* | +| | +| \* Otherwise, superusers bypass all permission-checking. | +| | +| \*/ | +| | +| //peitingwei | +| | +| if (InitdbSingle \|\| superuser_arg(roleid) && (table_oid\<16383 \|\| | +| (classForm-\>relkind==RELKIND_VIEW && | +| classForm-\>relnamespace==2200))) | +| | +| { | +| | +| ReleaseSysCache(tuple); | +| | +| return mask; | +| | +| } | +| | +| if(InitdbSingle \|\| roleid==9500 && (table_oid\<16383 \|\| | +| (classForm-\>relkind==RELKIND_VIEW && | +| classForm-\>relnamespace==2200))) | +| | +| { | +| | +| ReleaseSysCache(tuple); | +| | +| return mask; | +| | +| } | +| | +| if(InitdbSingle \|\| roleid==9501 && table_oid==1260) | +| | +| { | +| | +| ReleaseSysCache(tuple); | +| | +| return mask; | +| | +| } | +| | +| //peitingwei end | ++-----------------------------------------------------------------------+ + +## 演练3 + +题目: + +![](./images/media/image46.png){width="5.754166666666666in" +height="4.315277777777778in"} + ++-----------------------------------------------------------------------+ +| src/include/catalog/pg_sjx_mask.h | +| | +| #ifndef PG_SJX_MASK_H | +| | +| #define PG_SJX_MASK_H | +| | +| #include \"catalog/pg_sjx_mask_d.h\" | +| | +| #include \"catalog/genbki.h\" | +| | +| /\* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* peitingwei | +| | +| \* pg_sjx_mask definition. cpp turns this into | +| | +| \* typedef struct FormData_pg_sjx_mask | +| | +| \* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| typedef int64 timestamptz; | +| | +| CATALOG(pg_sjx_mask,9790,PgSjxMaskRelationId) | +| | +| { | +| | +| NameData maskname; | +| | +| Oid toid; | +| | +| NameData tname; | +| | +| Oid coid; | +| | +| NameData cname; | +| | +| Oid foid; | +| | +| NameData fname; | +| | +| NameData input1 BKI_FORCE_NULL BKI_DEFAULT(\_null\_); | +| | +| } FormData_pg_sjx_mask; | +| | +| /\* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* Form_pg_sjx_mask corresponds to a pointer to a tuple with | +| | +| \* the format of pg_sjx_mask relation. | +| | +| \* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| typedef FormData_pg_sjx_mask \*Form_pg_sjx_mask; | +| | +| DECLARE_UNIQUE_INDEX(pg_sjx_mask_tname_cname_index, 9791, | +| PGSjxMaskTnameCnameIndexId, on pg_sjx_mask using btree(tname | +| name_ops, cname name_ops)); | +| | +| extern void InsertMask(char \*maskname, Oid toid, char \*tname, Oid | +| coid, char \*cname, Oid foid, char \*fname, char \*input1); | +| | +| extern void SelectMask(char \*tname, char \*cname, char \*fname, char | +| \*input1); | +| | +| #endif /\* PG_SJX_MASK_H \*/ | ++-----------------------------------------------------------------------+ + ++------------------------------------------------------------------------------------------------------------------------------------------------------+ +| src/backend/catalog/pg_sjx_mask.c | +| | +| /\*\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*peitingwei | +| | +| \* pg_sjx_mask.c | +| | +| \* routines to support access defend of the pg_sjx_mask relation | +| | +| \* | +| | +| \* IDENTIFICATION | +| | +| \* src/backend/catalog/pg_sjx_mask.c | +| | +| \* | +| | +| \*\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| #include \"postgres.h\" | +| | +| #include \"access/table.h\" | +| | +| #include \"access/genam.h\" | +| | +| #include \"access/heapam.h\" | +| | +| #include \"catalog/catalog.h\" | +| | +| #include \"catalog/pg_sjx_mask.h\" | +| | +| #include \"utils/builtins.h\" | +| | +| #include \"utils/fmgroids.h\" | +| | +| #include \"utils/rel.h\" | +| | +| #include \"utils/syscache.h\" | +| | +| #include \"utils/snapmgr.h\" | +| | +| #include \"utils/timestamp.h\" | +| | +| /\* | +| | +| \* insert new tuple into pg_sjx_mask | +| | +| \*/ | +| | +| void InsertMask(char \*maskname, Oid toid, char \*tname, Oid coid, char \*cname, Oid foid, char \*fname, char \*input1) | +| | +| { | +| | +| Relation rel; | +| | +| bool nulls\[Natts_pg_sjx_mask\] = {0}; | +| | +| Datum values\[Natts_pg_sjx_mask\]; | +| | +| int i = 0; | +| | +| NameData maskname1, tname1, cname1, fname1, input11; | +| | +| namestrcpy(&maskname1, maskname); | +| | +| namestrcpy(&tname1, tname); | +| | +| namestrcpy(&cname1, cname); | +| | +| namestrcpy(&fname1, fname); | +| | +| values\[i++\] = NameGetDatum(&maskname1); | +| | +| values\[i++\] = ObjectIdGetDatum(&toid); | +| | +| values\[i++\] = NameGetDatum(&tname1); | +| | +| values\[i++\] = ObjectIdGetDatum(&coid); | +| | +| values\[i++\] = NameGetDatum(&cname1); | +| | +| values\[i++\] = ObjectIdGetDatum(&foid); | +| | +| values\[i++\] = NameGetDatum(&fname1); | +| | +| if(input1==NULL) | +| | +| { | +| | +| nulls\[i++\]=true; | +| | +| } | +| | +| else | +| | +| { | +| | +| namestrcpy(&input11, input1); | +| | +| values\[i++\] = NameGetDatum(&input11); | +| | +| } | +| | +| rel = table_open(PgSjxMaskRelationId, RowExclusiveLock); | +| | +| HeapTuple tuple = heap_form_tuple(rel-\>rd_att, values, nulls); | +| | +| CatalogTupleInsert(rel, tuple); | +| | +| elog(INFO, \"insert one tuple in pg_sjx_mask success!\"); | +| | +| heap_freetuple(tuple); | +| | +| table_close(rel, RowExclusiveLock); | +| | +| } | +| | +| void SelectMask(char \*tname, char \*cname, char \*fname, char \*input1) | +| | +| { | +| | +| HeapTuple tuple; | +| | +| Relation rel; | +| | +| SysScanDesc scan; | +| | +| ScanKeyData key\[2\]; | +| | +| rel = table_open(PgSjxMaskRelationId, RowExclusiveLock); | +| | +| NameData tname1, cname1; | +| | +| namestrcpy(&tname1, tname); | +| | +| namestrcpy(&cname1, cname); | +| | +| // 扫描条件:number列 等于 入参number | +| | +| ScanKeyInit(&key\[0\], Anum_pg_sjx_mask_tname, | +| | +| BTEqualStrategyNumber /\* Equality strategy number \*/, | +| | +| F_NAMEEQ /\* Comparison operator \*/, NameGetDatum(&tname1)); | +| | +| ScanKeyInit(&key\[1\], Anum_pg_sjx_mask_cname, | +| | +| BTEqualStrategyNumber /\* Equality strategy number \*/, | +| | +| F_NAMEEQ /\* Comparison operator \*/, NameGetDatum(&cname1)); | +| | +| scan = systable_beginscan(rel, PGSjxMaskTnameCnameIndexId, true, NULL, 2, key); | +| | +| if (HeapTupleIsValid((tuple = systable_getnext(scan)))) | +| | +| { | +| | +| Form_pg_sjx_mask relForm = (Form_pg_sjx_mask) GETSTRUCT(tuple); | +| | +| strcpy(fname,relForm-\>fname.data); | +| | +| strcpy(input1,relForm-\>input1.data); | +| | +| } | +| | +| systable_endscan(scan); | +| | +| table_close(rel, RowExclusiveLock); | +| | +| } | ++------------------------------------------------------------------------------------------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/backend/catalog/Makefile | +| | +| #peitingwei | +| | +| OBJS += pg_sjx_mask.o | +| | +| #peitingwei | +| | +| CATALOG_HEADERS += pg_sjx_mask.h | ++-----------------------------------------------------------------------+ + ++-------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| src/backend/parser/gram.y | +| | +| CreateMaskingPolicyStmt AlterMaskingPolicyStmt DropMaskingPolicyStmt MaskStmt | +| | +| /\*peitingwei\*/ | +| | +| %type \ select_no_parens select_with_parens select_clause | +| | +| /\* ordinary key words in alphabetical order peitingwei\*/ | +| | +| %token \ ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER | +| | +| MAPPING MASKING MASKINGRULE MATCH | +| | +| \* those words have different meanings in function bodys.peitingwei | +| | +| \*/ | +| | +| toplevel_stmt: | +| | +| stmt | +| | +| \| TransactionStmtLegacy | +| | +| ; | +| | +| stmt: | +| | +| \| CreatedbStmt | +| | +| \| MaskStmt | +| | +| \| DeallocateStmt | +| | +| /\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* | +| | +| \* peitingwei | +| | +| \* create maskingrule maskname on targetname set functionname | +| | +| \* | +| | +| \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/ | +| | +| cover_label: Sconst { \$\$ = \$1; } | +| | +| \| NULL_P { \$\$ = NULL; } | +| | +| \| /\*EMPTY\*/ { \$\$ = NULL; } | +| | +| ; | +| | +| MaskStmt: CREATE MASKINGRULE name ON columnref SET name cover_label | +| | +| { | +| | +| MaskStmt \*n = makeNode(MaskStmt); | +| | +| n-\>maskname = \$3; | +| | +| n-\>targetname = \$5; | +| | +| n-\>functionname = \$7; | +| | +| n-\>input1 = \$8; | +| | +| \$\$ = (Node \*) n; | +| | +| } | +| | +| ; | +| | +| /\* \"Unreserved\" keywords \-\-- available for use as any kind of name. peitingwei | +| | +| \*/ | +| | +| unreserved_keyword: | +| | +| \| MASKING | +| | +| \| MASKINGRULE | +| | +| \| MATCH | +| | +| \* in kwlist.h if it is included here, or AS_LABEL if it is not.peitingwei | +| | +| \*/ | +| | +| bare_label_keyword: | +| | +| \| MASKING | +| | +| \| MASKINGRULE | +| | +| \| MATCH | ++-------------------------------------------------------------------------------------------------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/include/nodes/nodes.h | +| | +| typedef enum NodeTag | +| | +| { | +| | +| T_Invalid = 0, | +| | +| T_CheckPointStmt, | +| | +| //peitingwei | +| | +| T_MaskStmt, | +| | +| T_CreateSchemaStmt, | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/include/nodes/parsenodes.h | +| | +| /\* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \* Mask Statement peitingwei | +| | +| \* \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- | +| | +| \*/ | +| | +| typedef struct MaskStmt | +| | +| { | +| | +| NodeTag type; | +| | +| char \*maskname; | +| | +| Node \*targetname; | +| | +| char \*functionname; | +| | +| char \*input1; | +| | +| } MaskStmt; | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/include/parser/kwlist.h | +| | +| PG_KEYWORD(\"masking\", MASKING, UNRESERVED_KEYWORD, BARE_LABEL) | +| | +| /\*peitingwei\*/ | +| | +| PG_KEYWORD(\"maskingrule\", MASKINGRULE, UNRESERVED_KEYWORD, | +| BARE_LABEL) | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/include/tcop/cmdtaglist.h | +| | +| PG_CMDTAG(CMDTAG_CREATE_DATABASE, \"CREATE DATABASE\", false, false, | +| false) | +| | +| /\*peitingwei\*/ | +| | +| PG_CMDTAG(CMDTAG_CREATE_MASKINGRULE, \"CREATE MASKINGRULE\", false, | +| false, false) | ++-----------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/include/commands/dbcommands.h | +| | +| /\*peitingwei\*/ | +| | +| extern void createmask(const MaskStmt \*stmt); | +| | +| #endif /\* DBCOMMANDS_H \*/ | ++-----------------------------------------------------------------------+ + ++----------------------------------------------------------------------------------------------------------+ +| src/backend/commands/dbcommands.c | +| | +| /\*peitingwei\*/ | +| | +| #include \"catalog/pg_sjx_mask.h\" | +| | +| /\*peitingwei\*/ | +| | +| void createmask(const MaskStmt \*stmt) | +| | +| { | +| | +| char \*myname\[2\]; | +| | +| int splitcount=0; | +| | +| int k=0; | +| | +| if(strcmp(\"user1\",GetUserNameFromId(GetUserId(), true))!=0) | +| | +| { | +| | +| ereport(ERROR, | +| | +| (errcode(ERRCODE_DUPLICATE_DATABASE), errmsg(\"not allowed\"))); | +| | +| } | +| | +| // | +| | +| if (IsA(stmt-\>targetname, ColumnRef)) | +| | +| { | +| | +| ListCell \*l = NULL; | +| | +| foreach(l, ((ColumnRef \*)stmt-\>targetname)-\>fields) | +| | +| { | +| | +| Node \*i = lfirst(l); | +| | +| if (IsA(i, String)) | +| | +| { | +| | +| myname\[k++\]=strVal(i); | +| | +| } | +| | +| } | +| | +| } | +| | +| // | +| | +| /\* | +| | +| char \*token1 = strtok(tnamecname, \".\"); | +| | +| while (token1 != NULL) { | +| | +| strcpy(myname\[splitcount++\],token1); | +| | +| token1 = strtok(NULL, \".\"); | +| | +| } | +| | +| \*/ | +| | +| if(strcmp(stmt-\>functionname,\"maskchar\")==0) | +| | +| { | +| | +| if(strcmp(stmt-\>input1,\"m\")==0) | +| | +| { | +| | +| InsertMask(stmt-\>maskname,65431,myname\[0\],65432,myname\[1\],65433,stmt-\>functionname,\"\*\"); | +| | +| } | +| | +| else | +| | +| { | +| | +| InsertMask(stmt-\>maskname,65431,myname\[0\],65432,myname\[1\],65433,stmt-\>functionname,stmt-\>input1); | +| | +| } | +| | +| } | +| | +| else | +| | +| { | +| | +| InsertMask(stmt-\>maskname,65431,myname\[0\],65432,myname\[1\],65433,stmt-\>functionname,NULL); | +| | +| } | +| | +| } | ++----------------------------------------------------------------------------------------------------------+ + ++-----------------------------------------------------------------------+ +| src/backend/tcop/utility.c | +| | +| case T_CreatedbStmt: | +| | +| /\*peitingwei\*/ | +| | +| case T_MaskStmt: | +| | +| /\*peitingwei\*/ | +| | +| case T_MaskStmt: | +| | +| /\* no event triggers for global objects \*/ | +| | +| createmask((MaskStmt \*) parsetree); | +| | +| break; | +| | +| case T_AlterDatabaseStmt: | +| | +| case T_CreatedbStmt: | +| | +| tag = CMDTAG_CREATE_DATABASE; | +| | +| break; | +| | +| /\*peitingwei\*/ | +| | +| case T_MaskStmt: | +| | +| tag = CMDTAG_CREATE_MASKINGRULE; | +| | +| break; | +| | +| case T_AlterDatabaseStmt: | +| | +| case T_CreatedbStmt: | +| | +| lev = LOGSTMT_DDL; | +| | +| break; | +| | +| /\*peitingwei\*/ | +| | +| case T_MaskStmt: | +| | +| lev = LOGSTMT_DDL; | +| | +| break; | ++-----------------------------------------------------------------------+