千家信息网

PostgreSQL 源码解读(217)- A Faster, Lightweight Trigger Function in C

发表于:2025-01-21 作者:千家信息网编辑
千家信息网最后更新 2025年01月21日,本节介绍了如何使用C语言实现性能更高的轻量级触发器.主要内容翻译自 A Faster, Lightweight Trigger Function in C for PostgreSQL一、实现步骤已在
千家信息网最后更新 2025年01月21日PostgreSQL 源码解读(217)- A Faster, Lightweight Trigger Function in C

本节介绍了如何使用C语言实现性能更高的轻量级触发器.
主要内容翻译自 A Faster, Lightweight Trigger Function in C for PostgreSQL

一、实现步骤

已在CentOs7.x上使用源码安装了PG,当前用户为PG实例的owner(pg12),已配置好环境变量,可以运行pg_config命令

[pg12@localhost demo_plus]$ whoamipg12[pg12@localhost ~]$ cat .bashrc# .bashrc# Source global definitionsif [ -f /etc/bashrc ]; then    . /etc/bashrcfi# Uncomment the following line if you don't like systemctl's auto-paging feature:# export SYSTEMD_PAGER=# User specific aliases and functionsexport PATH=/appdb/xdb/pg12beta1/bin:$PATHexport PGDATA=/data/pgsql/pg12db1[pg12@localhost ~]$ pg_config --pgxs/appdb/xdb/pg12beta1/lib/postgresql/pgxs/src/makefiles/pgxs.mk

本例的场景是希望在update/insert的时候记录更新时间/插入时间,通常我们会使用plpgsql实现此需求,但这次改用C语言实现.
数据表脚本如下:

CREATE TABLE t_demo_trig(  id int,  insert_ts timestamp,  update_ts timestamp);

下面是C实现源文件

#include #include #include "postgres.h"#include "utils/rel.h"#include "executor/spi.h"#include "commands/trigger.h"#include "utils/fmgrprotos.h"#ifdef PG_MODULE_MAGICPG_MODULE_MAGIC;#endifextern Datum demo_trig(PG_FUNCTION_ARGS);PG_FUNCTION_INFO_V1(demo_trig);Datumdemo_trig(PG_FUNCTION_ARGS){    //从函数调用上下文中获取触发器(TriggerData结构体)    TriggerData *trigdata = (TriggerData *) fcinfo->context;    //TupleDesc   tupdesc;    HeapTuple   tuple;//更新前的元组    HeapTuple   rettuple;//更新后的元组    int         attnum = 0;//属性编号    Datum       datumVal;//数据值(Datum其实是一个普通指针)    //Get the structure of the tuple in the table.    //tupdesc = trigdata->tg_relation->rd_att;    //Make sure that the function is called from a trigger    if (!CALLED_AS_TRIGGER(fcinfo))        elog(ERROR, "are you sure you are calling from trigger manager?");    //If the trigger is part of an UPDATE event    if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))//UPDATE操作    {        //attnum = SPI_fnumber(tupdesc,"update_ts");        attnum = 3;        tuple = trigdata->tg_newtuple;    }    //If the trigger is part of INSERT event    else//插入操作    {        //attnum = SPI_fnumber(tupdesc,"insert_ts");        attnum = 2;        tuple = trigdata->tg_trigtuple;    }    //Get the current timestamp using "now"    //调用函数now(),获取当前时间    datumVal = DirectFunctionCall3(timestamp_in, CStringGetDatum("now"), ObjectIdGetDatum(InvalidOid), Int32GetDatum(-1));    //Connect to Server and modify the tuple    //使用SPI连接到数据库,并执行更新    SPI_connect();    rettuple = SPI_modifytuple(trigdata->tg_relation, tuple, 1, &attnum, &datumVal, NULL);    if (rettuple == NULL)    {        if (SPI_result == SPI_ERROR_ARGUMENT || SPI_result == SPI_ERROR_NOATTRIBUTE)                elog(ERROR, "SPI_result failed! SPI_ERROR_ARGUMENT or SPI_ERROR_NOATTRIBUTE");         elog(ERROR, "SPI_modifytuple failed!");    }    //收尾工作    SPI_finish();                           /* don't forget say Bye to SPI mgr */    //返回更新后的元组    return PointerGetDatum(rettuple);}

Makefile文件

[pg12@localhost demo_trgr]$ cat Makefile MODULES = trgrEXTENSION = trgrDATA = trgr--0.0.1.sql#PG_CONFIG = pg_config#PGXS := $(shell $(PG_CONFIG) --pgxs)#include $(PGXS)#prefix=/appdb/xdb/pg12beta1/ifdef USE_PGXSPG_CONFIG = pg_configPGXS := $(shell $(PG_CONFIG) --pgxs)include $(PGXS)elsesubdir = contrib/demo_trgrtop_builddir = ../..include $(top_builddir)/src/Makefile.globalinclude $(top_srcdir)/contrib/contrib-global.mkendif

control文件

[pg12@localhost demo_trgr]$ cat trgr.control comment = 'Simple number add function'default_version = '0.0.1'relocatable = truemodule_pathname = '/appdb/pg12/pg12beta3/lib/postgresql/trgr.so'

sql安装文件

[pg12@localhost demo_trgr]$ cat trgr--0.0.1.sql CREATE OR REPLACE FUNCTION demop_trig() RETURNS trigger     AS 'MODULE_PATHNAME','demo_trig'LANGUAGE C STRICT;

编译&安装

[pg12@localhost demo_trgr]$ makemake -C ../../src/backend generated-headersmake[1]: Entering directory `/home/pg12/source/postgresql-12beta3/src/backend'make -C catalog distprep generated-header-symlinksmake[2]: Entering directory `/home/pg12/source/postgresql-12beta3/src/backend/catalog'make[2]: Nothing to be done for `distprep'.make[2]: Nothing to be done for `generated-header-symlinks'.make[2]: Leaving directory `/home/pg12/source/postgresql-12beta3/src/backend/catalog'make -C utils distprep generated-header-symlinksmake[2]: Entering directory `/home/pg12/source/postgresql-12beta3/src/backend/utils'make[2]: Nothing to be done for `distprep'.make[2]: Nothing to be done for `generated-header-symlinks'.make[2]: Leaving directory `/home/pg12/source/postgresql-12beta3/src/backend/utils'make[1]: Leaving directory `/home/pg12/source/postgresql-12beta3/src/backend'gcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -O0 -DOPTIMIZER_DEBUG -g3 -gdwarf-2 -fPIC -I. -I. -I../../src/include  -D_GNU_SOURCE -I/usr/include/libxml2   -c -o trgr.o trgr.c -MMD -MP -MF .deps/trgr.Pogcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -O0 -DOPTIMIZER_DEBUG -g3 -gdwarf-2 -fPIC trgr.o -L../../src/port -L../../src/common   -Wl,--as-needed -Wl,-rpath,'/appdb/pg12/pg12beta3/lib',--enable-new-dtags  -shared -o trgr.so[pg12@localhost demo_trgr]$ make installmake -C ../../src/backend generated-headersmake[1]: Entering directory `/home/pg12/source/postgresql-12beta3/src/backend'make -C catalog distprep generated-header-symlinksmake[2]: Entering directory `/home/pg12/source/postgresql-12beta3/src/backend/catalog'make[2]: Nothing to be done for `distprep'.make[2]: Nothing to be done for `generated-header-symlinks'.make[2]: Leaving directory `/home/pg12/source/postgresql-12beta3/src/backend/catalog'make -C utils distprep generated-header-symlinksmake[2]: Entering directory `/home/pg12/source/postgresql-12beta3/src/backend/utils'make[2]: Nothing to be done for `distprep'.make[2]: Nothing to be done for `generated-header-symlinks'.make[2]: Leaving directory `/home/pg12/source/postgresql-12beta3/src/backend/utils'make[1]: Leaving directory `/home/pg12/source/postgresql-12beta3/src/backend'/bin/mkdir -p '/appdb/pg12/pg12beta3/share/postgresql/extension'/bin/mkdir -p '/appdb/pg12/pg12beta3/share/postgresql/extension'/bin/mkdir -p '/appdb/pg12/pg12beta3/lib/postgresql'/bin/install -c -m 644 ./trgr.control '/appdb/pg12/pg12beta3/share/postgresql/extension/'/bin/install -c -m 644 ./trgr--0.0.1.sql  '/appdb/pg12/pg12beta3/share/postgresql/extension/'/bin/install -c -m 755  trgr.so '/appdb/pg12/pg12beta3/lib/postgresql/'[pg12@localhost demo_trgr]$

创建触发器

[local]:5432 pg12@testdb=# CREATE TABLE t_demo_trig(pg12@testdb(#   id int,pg12@testdb(#   insert_ts timestamp,pg12@testdb(#   update_ts timestamppg12@testdb(# );CREATE TABLETime: 93.441 ms[local]:5432 pg12@testdb=# create extension trgr;CREATE EXTENSIONTime: 1.403 ms[local]:5432 pg12@testdb=# CREATE FUNCTION demo_trig() RETURNS trigger     pg12@testdb-#   AS '/appdb/pg12/pg12beta3/lib/postgresql/trgr.so'pg12@testdb-# LANGUAGE C;CREATE FUNCTIONTime: 2.937 ms[local]:5432 pg12@testdb=# CREATE TRIGGER trigger_demotrgrpg12@testdb-#  BEFORE INSERT OR UPDATE ON public.t_demo_trig pg12@testdb-# FOR EACH ROW EXECUTE PROCEDURE public.demo_trig();CREATE TRIGGERTime: 31.037 ms

性能对比,C实现 VS plpgsql实现

[local]:5432 pg12@testdb=# drop trigger trigger_demotrgr on t_demo_trig;DROP TRIGGERTime: 58.935 ms[local]:5432 pg12@testdb=# insert into t_demo_trig(id) select x from generate_series(1,1000000) as x;INSERT 0 1000000Time: 5063.936 ms (00:05.064)[local]:5432 pg12@testdb=# drop trigger trigger_demotrgr on t_demo_trig;DROP TRIGGERTime: 58.935 ms[local]:5432 pg12@testdb=# CREATE OR REPLACE FUNCTION demo_trig_plpgsql()pg12@testdb-#   RETURNS TRIGGER AS $$pg12@testdb$#   BEGINpg12@testdb$#      if  (TG_OP = 'UPDATE') thenpg12@testdb$#         NEW.update_ts = now();pg12@testdb$#      else pg12@testdb$#         NEW.insert_ts = now();pg12@testdb$#      end if;pg12@testdb$#     RETURN NEW;pg12@testdb$#   END;pg12@testdb$#   $$ language 'plpgsql';CREATE FUNCTIONTime: 60.053 ms[local]:5432 pg12@testdb=# [local]:5432 pg12@testdb=# CREATE TRIGGER trigger_demotrgrpg12@testdb-#  BEFORE INSERT OR UPDATE ON public.t_demo_trig pg12@testdb-# FOR EACH ROW EXECUTE PROCEDURE public.demo_trig_plpgsql();CREATE TRIGGERTime: 0.938 ms[local]:5432 pg12@testdb=# insert into t_demo_trig(id) select x from generate_series(1,1000000) as x;INSERT 0 1000000Time: 8716.367 ms (00:08.716)

5063ms(C函数) vs 8716ms(plpgsql)

二、参考资料

A Faster, Lightweight Trigger Function in C for PostgreSQL

更新 函数 数据 文件 时间 触发器 性能 语言 源码 普通 上下 上下文 内容 参考资料 变量 命令 场景 实例 属性 指针 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 淘宝卖的服务器安全吗 唐山财务管理微服务架构数据库 修改数据库实例名 为什么服务器一般安装linux 网络安全整治情况 手机扑鱼达人软件开发 互联网软件开发运营是做什么 网络技术与应用内容 遂宁有软件开发公司吗 公安部网络安全攻防演练 大华用什么数据库 连不上网找不到服务器地址 世界最大服务器长什么样 张伯伦视频软件开发 崇明区网络安全审计系统咨询 网络安全设备维保费用 play商店显示连接不到服务器 技术软件开发合同 关于网络安全的小短文200字儿 网络安全专业怎么找工作 肥水不流外人田小说软件开发 使用现代网络技术用于课堂 上海网络安全教育 苹果商店极品飞车无法连接服务器 vb数据库编程实例下载 肉鸡服务器购买 我的世界好玩的32 k服务器 黄浦区提供软件开发质量保障 文化网络安全大赛是什么 方舟上服务器管理
0