千家信息网

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

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,本节介绍了如何使用C语言实现性能更高的轻量级触发器.主要内容翻译自 A Faster, Lightweight Trigger Function in C for PostgreSQL一、实现步骤已在
千家信息网最后更新 2026年11月01日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安全错误 数据库的锁怎样保障安全 南京欣网互联网络科技 湖北九亚互联网络科技有限公司 广东猪兼强互联网科技有限公司 广州百单互联网科技有限公司 圆梦互联网科技 互联网与未来科技 新浪科技互联网 广州善班互联网科技 互联网科技的未来发展 互联网科技公司做什么 互联网科技大会案例 海南科技产业互联网平台 汉立互联网科技有限公司联系电话 富国互联网科技创新基金 随着互联网科技发展 青岛科技大学互联网大赛研究生 江阴互联网智能科技产品进口 艺电能源互联网科技有限公司招聘 青岛易易互联网科技 长沙易易互联网科技有限公司 保险科技 互联网保险 湖北九亚互联网络科技有限公司 上海睿民互联网科技有限公司 北京互联网科技 南京欣网互联网科技 汉立互联网科技有限公司联系电话 长沙茂晟互联网科技有限公司 互联网科技手抄报图片 科技互联网经济论坛 东风日产互联网科技公司叫什么
0