第 11 课 PostgreSQL 增加一个内核C函数
时间:2019-05-23 13:25 来源:linux.it.net.cn 作者:IT
第一步 实现函数
增加的函数都放在src/backend/utils目录下,我要实现的函数是从服务器查询,并返回给调用者。
C函数原型:
char * sys_test(const uint32 id, const uint32 age)
开发者需要根据自己的需要,选择目录和文件:
我需要实现的功能在现有的文件中不存在相关性,我在adt目录下自己创建了一个文件:test.c,文件内容如下:
#include "postgres.h"
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include "access/heapam.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "postmaster/syslogger.h"
#include "storage/fd.h"
#include "utils/builtins.h"
/*
* Read a page, returning it as binary
*/
Datum
sys_test(PG_FUNCTION_ARGS)
{
uint32 id = PG_GETARG_INT32(0);
uint32 age = PG_GETARG_INT32(1);
printf("111111111111111111111\n");
strcpy(buf, "xxxxxxxxxxxxxxxxxx");
SET_VARSIZE(buf, nbytes + VARHDRSZ);
PG_RETURN_TEXT_P(buf);
}
实际功能还需要实现,我就打个样。
修改Makefile,使其可以被编译。打开文件adt/Makefile,增加文件test
如果你实现的函数,在现有的文件中实现,就不需要修改Makefile。
第二步 支持外部访问
打开文件:src/include/utils/builtins.h,在文件尾部增加自己的函数。
extern Datum sys_test(PG_FUNCTION_ARGS);
第三步 注册到名字空间
打开:src/include/catalog/pg_proc.h,找一个合适的位置增加自己的函数定义:
DATA(insert OID = 13624 ( sys_test PGNSP 0 PGUID 12 t f f f t f v 2 17 17 i f i f f "23 23" _null_ _null_ _null_ _null_ _null_ _null_ sys_read_page 2D _null_ ));
DESCR("sys_test");
其中的OID,不可重复,自己YY。
在模板库中插入记录,使任何库一创建就有。对于系统表来说,在它的头文件里有对这个系统表的详细定义说明,在这个头文件下面能按格式预置一些记录,这些记录在initdb时会插入到模板库的对应系统表中。具体来说,这些预置的记录,在编译过程中,会被perl脚本转换到postgres.bki中,这个bki文件在安装目录的share文件夹,当initdb时,会加载这个bki并解析成一条条sql运行,创建出一个个系统表,插入初始记录,把初始的信息准备好。有兴趣者可以看看initdb模块中 bootstrap_template1函数,初始化模块时第1件事就是加载postgres.bki文件翻译解析执行。
第四步 编译和验证函数可用
重新编译代码,使其修改重新编译。
make && make install
这种函数如果修改了pg_proc.h就必须要重新初始化数据库,不然不会生效:
./initdb ../data
在bin目录下启动服务进程:
./postgres
使用isql连接服务器:
./psql postgres
查询函数定义:
postgres-# \df+ SYS_TEST
功能列表
Schema | Name | Result data type | Argument data types | Owner | Language | Source code | Description
-------------+---------------+------------------+----------------------+--------+----------+---------------+-------------
SYS_CATALOG | SYS_TEST | BYTEA | TEXT, BIGINT, BIGINT | SYSTEM | INTERNAL | sys_test |
我们仔细看,函数参数“TEXT, BIGINT, BIGINT”,其实是错误的,与我们的函数定义:
char * sys_test(const uint32 id, const uint32 age)
是不一样的,参数列表错误,下面我们要找到原因,并修改。
关于参数设置可以参考:https://blog.csdn.net/postgres20/article/details/53446231
在pg_proc.h中插入的记录是什么含义?
以第一行为例详细说明如下:
DATA(insert OID = 13624 ( sys_read_page PGNSP 0 PGUID 12 t f f f t f v 2 17 17 i f i f f "25 20 20" null null null null null null sys_read_page 2D null ));
13624–OID使用内核中未使用的OID即可(src/include/catalog下unused_oids,可以显示未使用的oid) postgres内部预留了1W多个oid给系统用,选一个没有的就行,如果不知道哪些可用,在\src\include\catalog\ 下有个脚本文件unused_oids,运行一下就能找出哪些oid可用,但要这是一个linux脚本,需要在linux下运行。
PGNSP–函数所属的名字空间的OID,PGNSP即pg_catalog(oid=11),内置函数添加此值固定
PGUID–函数的拥有者OID,PGUID及initdb时指定用户(oid=10),内置函数添加此值固定
12–实现语言或该函数的调用接口,内置函数使用12(internal),SQL用14
t–函数是否为一个聚集函数
f–函数是否为一个窗口函数
f–函数是一个安全性定义者(即,一个”setuid”函数)
f–该函数没有副作用。除了通过返回值,没有关于参数的信息被传播。任何会抛出基于其参数值的错误信息的函数都不是泄露验证的。
t–当任意调用函数为空时,函数是否会返回空值。在那种情况下函数实际上根本不会被调用。非”strict”函数必须准备好处理空值输入。
f–函数是否返回一个集合(即,指定数据类型的多个值)
v–未知
2–输入参数的个数,对应后面的""25 20 20""两个参数,明显我们这里写错了,找到了原因,我们修改为我们对应的参数个数和类型:
"25 20 20" 改为 “23 23”,
23表示int4,继续往下看,有说明。
17–具有默认值的参数个数
17–返回值的数据类型
“25 20 20”–函数参数的数据类型的数组,这只包括输入参数(含INOUT和VARIADIC参数),因此也表现了函数的调用特征 重载函数也凭这区别,如果有多个,参数肯定不同,这个不同即可以是数量不同,也可以是类型不同,25 20 20 就是代表类型,如下:
TEST=# select oid,typname from pg_type where oid in (1082,23,1114,1184,17,25);
OID | TYPNAME
------+-------------
17 | BYTEA
23 | INT4
25 | TEXT
1082 | DATE
1114 | TIMESTAMP
1184 | TIMESTAMPTZ
null–函数参数的数据类型的数组,这包括所有参数(含OUT和INOUT参数)。但是,如果所有参数都是IN参数,这个域将为空。注意下标是从1开始 ,然而由于历史原因proargtypes的下标是从0开始
null–函数参数的模式的数组。编码为: i表示IN参数 , o表示OUT参数, b表示INOUT参数, v表示VARIADIC参数, t表示TABLE参数。 如果所有的参数都是IN参数,这个域为空。注意这里的下标对应着proallargtypes而不是proargtypes中的位置
null–函数参数的名字的数组。没有名字的参数在数组中设置为空字符串。如果没有一个参数有名字,这个域为空。注意这里的下标对应着proallargtypes而不是proargtypes中的位置
null–默认值的表达式树(按照nodeToString()的表现方式)。这是一个pronargdefaults元素的列表,对应于最后N个input参数(即最后N个proargtypes位置)。如果没有一个参数具有默认值,这个域为空
null–数据类型OID为了应用转换
我们再次来看看,是不是都对了
postgres=# \df sys_test
功能列表
Schema | Name | Result data type | Argument data types
-------------+---------------+------------------+---------------------
SYS_CATALOG | SYS_TEST | BYTEA | INTEGER, INTEGER
第五步 调用函数
postgres=# select SYS_TEST(1,2);
其他可用输出参数:
信息报告
\d [NAME] 描述表, 索引, 序列, 或者视图
\d{t|i|s|v|S} [PATTERN] (附加 "+" 获取更多信息)
列出表/索引/序列/视图/系统表
\da [PATTERN] 列出聚集函数
\db [PATTERN] 列出表空间(附加 "+" 获取更多信息)
\dc [PATTERN] 列出编码转换
\dC 列出定义的转换
\dd [PATTERN] 列出对象的注释
\dD [PATTERN] 列出域
\df [PATTERN] 列出函数(附加 "+" 获得更多信息)
\dF [PATTERN] 列出全文搜索配置(附加 "+" 获得更多信息)
\dFd [PATTERN] 列出全文搜索词典(附加 "+" 获得更多信息)
\dFt [PATTERN] 列出全文搜索模板
\dFp [PATTERN] 列出全文搜索分析器(附加 "+" 获得更多信息)
\dm[S+] [PATTERN] list materialized views
\dn [PATTERN] 列出模式(附加 "+" 获得更多信息)
\do [NAME] 列出操作符
\dp [PATTERN] 列出表, 视图, 及序列的访问权限
\dP [PATTERN] 列出包(附加 "+" 获得更多信息)
\dr [PATTERN] 列出角色
\dT [PATTERN] 列出数据类型(附加 "+" 获得更多信息)
\du [PATTERN] 列出用户
\l 列出所有数据库(附加 "+" 获得更多信息)
\ll [PATTERN] 列出锁
\llc 列出锁总数
\lx 列出xid最大值
\lredolog 列出当前的重做日志
\lc [PATTERN] 列出所有连接
\lac [PATTERN] 列出活动连接
\lfc [PATTERN] 列出空闲连接
\lwc [PATTERN] 列出等待连接
\lmc 列出最大连接数
\lstatdb [PATTERN] 列出数据库统计信息
\lstatidx [PATTERN] 列出索引统计信息
\lstatseq [PATTERN] 列出序列统计信息
\lstattable [PATTERN] 列出表格统计信息
(责任编辑:IT)
第一步 实现函数
增加的函数都放在src/backend/utils目录下,我要实现的函数是从服务器查询,并返回给调用者。 char * sys_test(const uint32 id, const uint32 age) 开发者需要根据自己的需要,选择目录和文件: 我需要实现的功能在现有的文件中不存在相关性,我在adt目录下自己创建了一个文件:test.c,文件内容如下: #include "postgres.h" #include <sys/file.h> #include <sys/stat.h> #include <unistd.h> #include <dirent.h> #include "access/heapam.h" #include "catalog/pg_type.h" #include "funcapi.h" #include "miscadmin.h" #include "postmaster/syslogger.h" #include "storage/fd.h" #include "utils/builtins.h" /* * Read a page, returning it as binary */ Datum sys_test(PG_FUNCTION_ARGS) { uint32 id = PG_GETARG_INT32(0); uint32 age = PG_GETARG_INT32(1); printf("111111111111111111111\n"); strcpy(buf, "xxxxxxxxxxxxxxxxxx"); SET_VARSIZE(buf, nbytes + VARHDRSZ); PG_RETURN_TEXT_P(buf); }
实际功能还需要实现,我就打个样。 如果你实现的函数,在现有的文件中实现,就不需要修改Makefile。 第二步 支持外部访问
打开文件:src/include/utils/builtins.h,在文件尾部增加自己的函数。 第三步 注册到名字空间打开:src/include/catalog/pg_proc.h,找一个合适的位置增加自己的函数定义: DATA(insert OID = 13624 ( sys_test PGNSP 0 PGUID 12 t f f f t f v 2 17 17 i f i f f "23 23" _null_ _null_ _null_ _null_ _null_ _null_ sys_read_page 2D _null_ )); DESCR("sys_test"); 其中的OID,不可重复,自己YY。
第四步 编译和验证函数可用重新编译代码,使其修改重新编译。 make && make install 这种函数如果修改了pg_proc.h就必须要重新初始化数据库,不然不会生效: ./initdb ../data 在bin目录下启动服务进程: ./postgres 使用isql连接服务器: ./psql postgres 查询函数定义: postgres-# \df+ SYS_TEST 功能列表 Schema | Name | Result data type | Argument data types | Owner | Language | Source code | Description -------------+---------------+------------------+----------------------+--------+----------+---------------+------------- SYS_CATALOG | SYS_TEST | BYTEA | TEXT, BIGINT, BIGINT | SYSTEM | INTERNAL | sys_test | 我们仔细看,函数参数“TEXT, BIGINT, BIGINT”,其实是错误的,与我们的函数定义: char * sys_test(const uint32 id, const uint32 age)
是不一样的,参数列表错误,下面我们要找到原因,并修改。 在pg_proc.h中插入的记录是什么含义?
"25 20 20" 改为 “23 23”, 23表示int4,继续往下看,有说明。
TEST=# select oid,typname from pg_type where oid in (1082,23,1114,1184,17,25); OID | TYPNAME ------+------------- 17 | BYTEA 23 | INT4 25 | TEXT 1082 | DATE 1114 | TIMESTAMP 1184 | TIMESTAMPTZ
我们再次来看看,是不是都对了 postgres=# \df sys_test 功能列表 Schema | Name | Result data type | Argument data types -------------+---------------+------------------+--------------------- SYS_CATALOG | SYS_TEST | BYTEA | INTEGER, INTEGER 第五步 调用函数postgres=# select SYS_TEST(1,2); 其他可用输出参数: 信息报告 \d [NAME] 描述表, 索引, 序列, 或者视图 \d{t|i|s|v|S} [PATTERN] (附加 "+" 获取更多信息) 列出表/索引/序列/视图/系统表 \da [PATTERN] 列出聚集函数 \db [PATTERN] 列出表空间(附加 "+" 获取更多信息) \dc [PATTERN] 列出编码转换 \dC 列出定义的转换 \dd [PATTERN] 列出对象的注释 \dD [PATTERN] 列出域 \df [PATTERN] 列出函数(附加 "+" 获得更多信息) \dF [PATTERN] 列出全文搜索配置(附加 "+" 获得更多信息) \dFd [PATTERN] 列出全文搜索词典(附加 "+" 获得更多信息) \dFt [PATTERN] 列出全文搜索模板 \dFp [PATTERN] 列出全文搜索分析器(附加 "+" 获得更多信息) \dm[S+] [PATTERN] list materialized views \dn [PATTERN] 列出模式(附加 "+" 获得更多信息) \do [NAME] 列出操作符 \dp [PATTERN] 列出表, 视图, 及序列的访问权限 \dP [PATTERN] 列出包(附加 "+" 获得更多信息) \dr [PATTERN] 列出角色 \dT [PATTERN] 列出数据类型(附加 "+" 获得更多信息) \du [PATTERN] 列出用户 \l 列出所有数据库(附加 "+" 获得更多信息) \ll [PATTERN] 列出锁 \llc 列出锁总数 \lx 列出xid最大值 \lredolog 列出当前的重做日志 \lc [PATTERN] 列出所有连接 \lac [PATTERN] 列出活动连接 \lfc [PATTERN] 列出空闲连接 \lwc [PATTERN] 列出等待连接 \lmc 列出最大连接数 \lstatdb [PATTERN] 列出数据库统计信息 \lstatidx [PATTERN] 列出索引统计信息 \lstatseq [PATTERN] 列出序列统计信息 \lstattable [PATTERN] 列出表格统计信息 (责任编辑:IT) |