Postgresql Contrib模块插件总结
一、Hook函数总结
(一)初探hook
1.pg中的hook函数及其功能
Post_parse_analyze_hook //对查询树的分析 Post_rewriter_hook query_list_hook //此hook功能为重写查询树,对查询树Query的重写 Permission_processUtility_hook //主要对DDL语句做一些权限控制 Pgaudit_ExecutorEnd_hook //取得sql执行时间和影响行数信息 Pgaudit_ProcessUtility_hook //取得DDL相关信息 Pgaudit_object_hook //取得对象信息 executorStart_hook //处理查询开始 executorRun_hook //处理查询执行 executorFinish_hook //处理查询结束 executorEnd_hook //处理查询完成后 ......
2.设置hook的优点
不需要在原本内核代码很大的改动,只需要把hook函数挂到上面,对于内核的扩展非常的方便。Hook函数可移植性较高,在pg版本迭代中,内核或多或少都有变动,如果在内核中直接做的修改可能版本不同就会导致各种错误,更换版本后需要做大量的修改,而hook函数不需要做很多修改,甚至直接拿过去,把hook启动就可以直接用,所以说hook的可移植性高。
(二)、简单Hook的尝试
- 在pg源码中没有显示登录信息的hook,因此我仿照pg自带插件的hook对pg14进行尝试。
-
首先在passwordcheck下定义login_message_display_hook
-
在初始化_PG_init中将hook指向定义的hook函数
-
在postgres.c文件下的PostgresMain()函数下将此hook挂上
-
配置makefie 等文件,重新对passwordcheck进行编译安装,将hook启用,create exection passwordcheck(或者是vi data/postgresql.conf,编辑配置文件将passwordcheck启用)。重启数据库进入即可看到登录信息
(三)、hook原理
扩展控制文件解析执行调用过程
parse_extension_control_file():解析控制文件,取出调用具体版本,文件名等信息。 InsertExtensionTuple():把控制文件的信息保存到Tuple中。 execute_sql_string():解析sql文件。先调用pg_parse_query将sql转换成parsetree_list。 然后执行pg_analyze_and_rewrite(),pg_plan_queries(),ExecutorRun()对每一个parse tree重写执行。
1.扩展库文件解析执行调用过程
ProcedureCreate():在pg_proc.c中,主要是创建函数和存储过程。 OidFunctionCall():调用fmgr提供的接口。所有的函数调用都会调用此函数。 Internal_load_library():先判断是否load,如果没有则通过调用dlopen()函数进行load,然后dlsym()查找相关函数。 Dlsym():获取函数Magic_func()和_pg_init()函数的地址,如果没获取到Magic_func()则会报error。对于_PG_init()可有可无,也可用load直接加载。 Fetch_finfo_record():获取指定函数的地址,判断函数是否存在,不存在会报error。
2.hook可以通过create extension 起作用的原理
首先在文件中定义hook的全局指针变量,并且初始为null,在程序执行前就在系统中定义完成。 经过create extension后,通过Dlsym();函数获取magic_func和_PG_init的函数地址。 经过_PG_init后将hook指向定义好的相应函数,此时hook全局指针不为空,所以执行到判断是否为空代码块时,执行hook函数,所以hook就起作用了,完成我们预期功能的实现。
二、Makefile文件解析
在插件的makefile文件中通常包含几个必要变量分别是MODULE_big、EXTENTION、OBJS、PGFILEDESE、DATA、REGRESS、PG_CONFIG……
(一)、主要常用各参数的具体含义
1. MODULE_big 一个要从多个源文件中构建的共享库(在OBJS中列出对象文件)在pgxs.mk文件中定义,包含共享库参数。 2. EXTENTION 扩展的名称,这个是必要的变量,否则扩展安装时找不到,一个扩展一个名,并且要提供一个control文件,将会被安装到prefix/share/extension中。 3.DATA 指定.Sql扩展脚本文件,要安装到prefix/share/$MODULEDIR中的随机文件。其中•"1.0" :代表版本号,这个用于后续插件升级。例如我们引入了2.0版本,则可以创建一个 common_func--2.0.sql (用于直接安装2.0版本的该插件),创建一个 common_func--1.0--2.0.sql (用于将1.0版本的插件升级为2.0版本,遵循 extension--oldversion--newversion.sql 命名原则,具体在下述sql版本升级机制中详细陈述)。 4.REGRESS 测试脚本文件,不带.sql后缀(makefile中可以不添加此变量)。 5.USE_PGXS 此变量定义在src/makefiles/下的pgxs.mk文件中,是针对编译extension的解决方案。PostgreSQL安装为扩展提供了一个构建基础设施,称为PGXS。 因此,简单的扩展模块可以简单地建立在已经安装的服务器上。 PGXS主要用于包括C代码的扩展,尽管它也可以用于纯SQL扩展。 6.PG_CONFIG 要在其中构建的PostgreSQL安装的pg_config程序的路径(通常只用在你的PATH中的第一个pg_config) 把这个 makefile 作为Makefile放在保存你扩展的目录中。然后你可以执行make进行编译,并且接着make install来安装你的模块。默认情况下,该模块会为在你的PATH中找到的第一个pg_config程序所对应的PostgreSQL安装编译和安装。你可以通过在 makefile 中或者make命令行中设置PG_CONFIG指向另一个pg_config程序来使用一个不同的安装。 在PG_CONFIG中拥有多个变量参数,BINDIR = /usr/local/postgres/bin BINDIR 说明你的POSTGRESQL的 执行程序文件都安装在哪里 INCLUDEDIR = /usr/local/postgres/includes 客户端 C 程序的头文件的存放地 PKGINCLUDEDIR = /usr/local/postgres/includes 其他客户C程序头文件的存放地 INCLUDEDIR-SERVER = /usr/local/postgres/includes/server server 端C头程序的目录 LIBDIR = /usr/local/postgres/libs 系统动态加载库 PKGLIBDIR = /usr/local/postgres/libs 动态加载库位置 LOCALEDIR = /pgdata/root/locale 本地动态加载库 MANDIR = /pgdata/root/man SHAREDIR = /pgdata/postgresql 共享文件存放地 有的时候经常是安装某些 EXTENSION 无法工作的一个问题点 SYSCONFDIR = /etc/postgresql 系统配置文件存放地 PGXS = /usr/local/postgres/libs/pgxs/src/makefiles/pgxs.mk 本地扩展的文件的makefile CONFIGURE = '--prefix=/usr/local/postgres' '--bindir=/usr/local/postgres/bin' '--sysconfdir=/etc' '--lth-pam' '--with-systemd' '--with-libxml' '--with-segsize=4' POSTGRESQL 启动带有的参数
(二)、makefile总结
Makefile 文件最底部几行,作用在于帮助找到PG的PGHOME路径,通常只需要修改 subdir 一项,其他直接拷贝即可。这几行的含义是:
1.我们编译(make 或 make install)时,如果不加 USE_PGXS=1 ,则认为本文件夹放在了 subdir 路径下,这通常是我们把插件放在PG的源码 contrib 路径下编译,往上返回两层即为 PGHOME
2.否则,则通过环境变量中查找 pg_config 命令 (通常在 PGHOME/bin 路径下,如果是abase默认安装,本命令可直接使用),通过 pg_config 来查找 PGHOME
三、control文件解析
(一)、扩展控制参数含义
一个扩展控制文件的格式与postgresql.conf文件相同,即是一个parameter_name = value赋值的列表,每行一个。允许空行和#引入的注释。注意对任何不是单一词或数字的值需要加上引号。
控制文件可以设置下列参数:
1.directory(string):包含扩展的SQL脚本文件的目录。除非给出一个绝对路径,这个目录名是相对于安装的SHAREDIR目录。默认行为等效于指定directory = extension 2.default_version(string):扩展的默认版本,如果create extension中没有指定,将默认使用这个参数 3.comment(string):关于该扩展的注释。该注释会在初始创建扩展时应用,但是扩展更新时不会引用该注释(因为可能会覆盖用户增加的注释)。扩展的注释也可以通过在脚本文件中写上COMMENT命令来设置。 4.encoding(string):该脚本文件使用的字符集编码。当脚本文件包含任何非 ASCII 字符时,可以指定这个参数。否则文件都会被假定为数据库编码。 5.module_pathname(string):该参数告诉PG在执行到用户自定义函数的时候去这个路径下找库文件。 6.requires(string): 这个扩展依赖的其他扩展名的一个列表,例如requires = 'foo, bar'。被依赖的扩展必须先于这个扩展安装 7.superuser(boolean):默认情况下为true,只有超级用户能够创建该扩展或者将它更新到一个新版本 8.trusted(boolean):默认值为false,如果设置为true,则允许非超级管理员权限的用户安装superuser已设置为true的扩展,即对于在当前数据库上具有CREATE特权的任何人,都允许安装。 当执行CREATE EXTENSION的用户不是超级用户,但允许通过此参数安装时,则此安装或更新脚本作为引导超级用户运行,而不是作为调用用户 9.relocatable(boolean):扩展的可再定位性,如果支持扩展能被重定位到另一个模式则为true,默认为false。三种支持的可定位性级别: (1)一个完全可重定位的扩展能在任何时候被移动到另一个模式中,即使在它被载入到一个数据库中之后。这种移动通过ALTER EXTENSION SET SCHEMA命令完成,该命令会自动地把所有成员对象重命名到新的模式中,它的控制文件中需要设置relocatable = true (2)一个扩展可能在安装过程中是可重定位的,但是安装完后就不再可重定位。典型的情况是扩展的脚本文件需要显式地引用目标模式,例如为 SQL 函数设置search_path属性。对于这样一种扩展,在其控制文件中设置relocatable = false,并且使用@extschema@在脚本文件中引用目标模式。在脚本被执行前,所有这个字符串的出现都将被替换为实际的目标模式名。用户可以使用CREATE EXTENSION的SCHEMA选项设置目标模式名。 (3)如果扩展根本就不支持重定位,在它的控制文件中设置relocatable = false,并且还设置schema为想要的目标模式的名称。这将阻止使用CREATE EXTENSION的SCHEMA选项修改目标模式,除非它指定的是和控制文件中相同的模式。如果该扩展包括关于模式名的内部假设且模式名不能使用@extschema@的方法替换,这种选择通常是必须的。@extschema@替换机制在这种情况中也是可用的,不过由于模式名已经被控制文件所决定,它的使用受到了很大的限制。 10.schema(string) : 这个参数只能为非可重定位扩展设置。它强制扩展被载入到给定的模式中而非其他模式中。只有在初始创建一个扩展时才会参考schema参数,扩展更新时则不会参考这个参数
(三)、控制文件参数补充说明:
1. 关于relocatable,所有的情况下,脚本文件将被用search_path执行,它会被设置为指向目标模式,也就是说CREATE EXTENSION做的也是等效的工作:SET LOCAL search_path TO @extschema@, pg_temp;
2. 如果控制文件中给出了schema参数,目标模式就由该参数决定,否则目标模式由CREATE EXTENSION的SCHEMA选项给出,如果以上两者都没有给出则会用当前默认的对象创建模式(在调用者search_path中的第一个)。当使用扩展文件的schema参数时,如果目标模式还不存在将创建它,但是在另外两种情况下它必须已经存在。
3. 如果在控制文件中的requires中列举了任何先导扩展,它们的目标模式会被追加到search_path的初始设置中,遵循新扩展的目标模式。 这允许新扩展的脚本文件能够看到它们的对象。
四、sql脚本扩展版本更新机制
可以通过扩展的安装脚本的每一个发行版本关联一个版本名称或者版本号来实现动态的将数据库从一个版本更新到下一个版本,脚本的名称遵循extension--old_version--target_version.sql模式 (例如demo--1.0--1.1.sql包含着把扩展demo的版本1.0修改成版本1.1的命令)
命令ALTER EXTENSION UPDATE可以将把一个已安装的扩展更新到指定的新版本。此外,此命令能够根据更新脚本的版本序列实现版本更新,例如只有demo--1.0--1.1.sql和demo--1.1--2.0.sql可用,当前安装了1.0版本并且要求更新到版本2.0,ALTER EXTENSION 命令将将依次应用它们。
PostgreSQL不对更新脚本的版本名称的属性做任何假设,例如demo--1.0--1.1.sql,它不知道是否1.1遵循1.0,它只是匹配可用的版本名称并遵循需要应用最少更新脚本的路径。(版本名称实际上可以是不包含--或前缀或后缀的任何字符串-)
Question: 那么问题就来了,pgsql无法识别更新脚本的命名规范是否真实有效,是如何通过匹配可用的版本名称来搜索出可用的更新脚本的最短路径?
Answer: 通过pgsql的内置系统函数pg_extension_update_paths来检索扩展版本之间的可用更新路径。
pg_extension_update_paths 函数输出显示了扩展版本的每个组合,以及从source版本升级到目标版本时将执行的扩展脚本组合// 如果路径为空,则无法升级,如下图所示:
postgresql并不对更新脚本的版本名称的属性做任何假设,因此即可以实现版本升级,也可以实现“版本降级”,例如,将脚本命名为demo--1.1--1.0.sql。降级更新可能会导致的问题在于,降级脚本存在被意外使用的可能性,因为降级脚本会得到一个较短的路径,如果降级版本删除了任何不可替代的对象,这将会得到意想不到的结果。
一个已经存在一段时间的扩展可能存在多个版本。例如,如果你已经发布了扩展demo的1.0、1.1和1.2版本,就应该有更新脚本demo--1.0--1.1.sql和demo--1.1--1.2.sql。CREATE EXTENSION能够自动遵循更新链。例如,如果只有脚本文件demo--1.0.sql、demo--1.0--1.1.sql和demo--1.1--1.2.sql可用,那么安装版本1.2的请求会通过按顺序运行上述三个脚本来实现。这种处理和先安装1.0然后更新到1.2是一样的(和ALTER EXTENSION UPDATE一样,如果有多条路径可用则优先选择最短的)
在PostgreSQL10之前,还有必要创建新的脚本文件demo--1.1.sql和demo--1.2.sql,它们直接构建比较新的扩展版本,或者新的版本无法被直接安装,而是通过先安装1.0然后更新。
如果以这种风格维护的扩展中使用了二级(版本相关的)控制文件,记住每个版本都需要一个控制文件,即使它没有单独的安装脚本,因为该控制文件将决定如何执行到这个版本的隐式更新。例如,如果demo--1.0.control指定有requires = 'bar',但demo的其他控制文件没有这样做,在从1.0更新到另一个版本时,该扩展对bar的依赖将被删除。
五、简单自定义扩展开发
(一)、扩展开发基本步骤
在源码目录的contrib/目录下创建与扩展名同名的文件夹,在该文件夹内存放开发好的扩展源码文件,包含MakeFile文件、.sql文件、.control控制文件等
1.在该目录下,执行make和make install命令,进行编译将编译生成的so文件和sql文件迁移至安装目录的lib和extension下
2.将so文件和sql文件修改权限和用户组最后再在数据库中添加扩展:create extension 扩展名;
(二)、扩展开发使用的语言
PostgreSQL 支持使用PL/pgSQL语言或者原生的C语言开发扩展。PL/pgSQL开发相对简单,但性能上较原生的C语言要差。只需要通过指定sql文件的CREATE FUNCTION的LANGUAGE参数即可。
实例一:使用SQL语言实现自定义扩展
1、在源码目录的contrib/目录下创建与扩展名同名的文件夹,在该文件夹内,创建pgtest1--1.0.sql文件,定义test_add_fun1函数,实现数值相加,其中LANGUAGE参数设置为SQL
如果脚本是由 psql 而不是 CREATE EXTENSION 执行,则报错 \echo开始的行,会被扩展机制认为是注释行,如果脚本文件被送给psql而不是由CREATE EXTENSION载入,这种机制通常被用来抛出错误
2.创建pgtest1.control文件
3.创建Makefile文件
4.进入文件目录执行make和make install命令编译安装
5.重启数据库create extention
6.测试验证是否能正常使用
(注:可以正常使用结果不对是因为我在内核数值处理中进行了修改,添加了使得所有整形数据+1的操作,并非此扩展的问题。)
7.为pgtest1扩展新增multi函数(乘法算法),并将扩展版本更新为1.1
当前pgtest1扩展安装的版本为1.0,可通过pg_extension查看
在pgtest1扩展目录下新增pgtest1--1.0--1.1.sql升级脚本,并更改Makefile的DATA 参数:增加pgtest1--1.0--1.1.sql
8.重新编译执行
9.执行ALTER EXTENSION命令将demo扩展版本更新到1.1
10.测试验证升级版本
验证成功。(注:可以正常使用结果不对是因为我在内核数值处理中进行了修改,添加了使得所有整形数据+1的操作,并非此扩展的问题。)
还可以通过pg_extension_update_paths函数查看版本升级途径
实例二:使用C语言自定义开发扩展
1.在源码目录的contrib/目录下创建与扩展名同名的文件夹,并创建相关文件
2.pgtest--1.0.sql声明扩展的函数、该函数的字符串常量以及使用的语言
3.pgtest.c:编写c脚本,实现扩展的自定义函数逻辑
4.pgtest.control:编写控制文件,定义默认版本号以及设置是否可重定向
5.编写Makefile文件
6.进入源码文件夹下执行make与make install 命令编译安装
7.重启数据库并create extension
8.测试验证是否成功
验证成功。(注:可以正常使用结果不对是因为我在内核数值处理中进行了修改,添加了使得所有整形数据+1的操作,并非此扩展的问题。)
本文作者:supersimple
本文链接:https://www.cnblogs.com/supersimple/p/18499951
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步