使用 Go 编写 PostgreSQL 触发器

栏目: 数据库 · PostgreSQL · 发布时间: 6年前

内容简介:使用 Go 编写 PostgreSQL 触发器

怎样应用 PostgreSQL 函数和 Golang 中的触发器?

PostgreSQL 中的触发器是一种简单却功能强大的机制,它能反应表格(table)中所正在发生的变化。

下文描述了怎样在 Go 中编写 PostgreSQL 触发器。

POSTGRESQL函数和触发器

通过使用 CREATE FUNCTION SQL 语句,PostgreSQL 可以让你创建用户自定义函数。 函数本质上就是 PostgreSQL 怎样管理逻辑的用户自定义部分。

可以用多种语言编写函数 – 最常见的很可能就是 PL/pgSQL , 它即为编写“stored procedures”时所使用的函数。你也可以用其它语言编写,如  Python 和  Perl

你也可以用 C 代码来编写它们。因此必须将 C 代码编译进一个可动态加载的共享库 ( *.so )中。 PostgreSQL 则会被告知,在某个 *.so档中,某个函数会以某个符号名称存在。 这种方式有点类似于Apache 或 Nginx 中模块的工作方式。

函数可以用作触发器,而这点正是我们感兴趣的部分。

触发器

触发器是事件处理程序的一种形式——它们是当指定对象发生特定事件时可以执行的逻辑块。 通常,它们涉及的对象是表,但它们也可以是视图或外部表。

这些事件通常是:

  • insert (rows)

  • update (rows)

  • delete (rows)

  • truncate (table)

PostgreSQL 触发器用处广泛:

  • 他们可以每行调用一次或每个语句调用一次。例如,如果一条语句更新5行,则可以执行这条语句时调用一次,或者每5行调用,或者每行调用。

  • 可以在实际变化发生之前或之后调用它们。

  • “before”触发器可以更改修改的值或取消更改.

  • 触发器可用于对表施加任意约束。

最受欢迎的触发器的使用可能是创建审计日志(或更具体地说是修改日志)。 您可以在 这里这里 阅读更多有关触发器的信息。

使用 Go 语言动态加载模组

Go 语言从 1.5 版本开始支持创建 C 语言风格的共享库。这样一来,就可以将任意的 Go 函数导出给其他语言使用,就类似其他语言,例如 C 用的 dlopen/dlysm、 Python 的 ctypes,还有 Java 的 JNI 一样。

可以用下面的指令来创建一个 C 风格的共享库:

go build -o myso.so -buildmode=c-shared myso.go

这里的 myso.go 是一个用 Go 写的主包,代码如下:

package main

import "C"

//export MyName
func MyName(x int) int {
	return 42 + x
}

func main() {
	// 未实现
}

注意看导出函数 MyName 上面有一条修饰性注释。另外,如果要导出,代码里还得写 import "C"。

使用 Go 语言编写 PostgreSQL 函数

可以创建一个包含到处方法的 *.so 文件,然后在里面写要导出的 PostgreSQL 函数。

在写这类函数时,必须遵守一定的规则,详情请 点击这里

那么现在,我们先来定义我们的模块,然后写一个叫做 mytrigger 的导出函数。

// file module.go

package main

/*
#include "postgres.h"
#include "fmgr.h"

#cgo LDFLAGS: -Wl,--unresolved-symbols=ignore-all

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

PG_FUNCTION_INFO_V1(mytrigger);
*/
import "C"

func main() {
}

注意代码里的 LDFLAGS 声明。用了之后,链接器在生成 so 文件时,就不会提示遇到了无法解析的符号。因为,我们现在用的是 PostgreSQL,并没有需要链接的外部库;而那些符号,也只在 PostgreSQL 加载 so 文件时才来检查是否存在。

接着我们在文件 mytrigger.go 中细化触发器函数:

// file mytrigger.go

package main

/*
#include "postgres.h"
#include "commands/trigger.h"

//...

*/
import "C"
import (
	"fmt"
	"unsafe"
)

//export mytrigger
func mytrigger(fcInfo *C.FunctionCallInfoData) C.Datum {
	trigdata := (*C.TriggerData)(unsafe.Pointer(fcInfo.context))

	//...
}

导出的 Go 函数名 mytrigger 由 PostgreSQL 函数管理机制保证唯一。在触发器中,行数据被传入,它可能修改行数据(例如在“before”触发器中),然后将修改后的数据透传下去。

这里我们写一个简单的函数体,在执行 INSERT 和 UPDATE 操作时触发,该函数将不修改数据,只读取和打印它,假设行数据的第一列是“text”类型的。

现在正好看一下触发器函数的代码在 C 中是怎样的,请看 PostgreSQL 文档网址中的 这个实例

函数体中,我们先取行数据,因为函数被 INSERT 和 UPDATE 调用:

var rettuple *C.HeapTupleData
	if C.trigger_fired_by_update(trigdata.tg_event) != 0 {
		rettuple = (*C.HeapTupleData)(trigdata.tg_newtuple)
	} else {
		rettuple = (*C.HeapTupleData)(trigdata.tg_trigtuple)
	}

然后我们取第一列数据(顺序从1开始),假设数据是 “text” 类型的(没有 NULL 列):

url := C.GoString(C.getarg_text(trigdata, rettuple, 1))

我们只将它打印出来,并不进行修改处理:

C.elog_info(C.CString(fmt.Sprintf("got url=%s", url)))
	fmt.Println(url)

最后返回原始的未修改过的数据:

return C.pointer_get_datum(rettuple)

完整的代码可以看 这里 。下面部分是 github 库的链接和如何生成目标程序的过程指导。

运行触发器

在触发器运行之前,先创建表:

$ sudo -u postgres psql -d test
psql (9.6.2)
Type "help" for help.

test=# CREATE TABLE urls ( url TEXT );
CREATE TABLE
test=#

之后,是创建我们的函数(你需要有 C 语言的 USAGE 权限,像这样):

test=# CREATE FUNCTION mytrigger()
test-# RETURNS TRIGGER AS '/home/alice/ptgo/ptgo.so'
test-# LANGUAGE C;
CREATE FUNCTION
test=#

下一步,让我们创建一个在表路径上的 INSERT 和 UPDATE 触发器,并让它来调用我们的函数:

test=# CREATE TRIGGER trig_1
test-# AFTER INSERT OR UPDATE
test-# ON urls
test-# FOR EACH ROW
test-# EXECUTE PROCEDURE mytrigger();
CREATE TRIGGER
test=#

现在,让我们插入两行。“got url=” 通过我们的函数被打印出来:

test=# INSERT INTO urls VALUES ('http://example.com/');
INFO:  got url=http://example.com/
INSERT 0 1
test=#
test=# INSERT INTO urls VALUES ('http://mydomain.com/');
INFO:  got url=http://mydomain.com/
INSERT 0 1
test=#

当行被更新的时候,函数接收更新后的值,因为它是一个 AFTER 触发器:

test=# UPDATE urls SET url='http://www.test.com/';
INFO:  got url=http://www.test.com/
INFO:  got url=http://www.test.com/
UPDATE 2
test=#

就是这样!我们有了我们自己的用 Go 编写的 PostgreSQL 触发器!

代码

所有代码可以在GitHub上访问,链接如下: github.com/rapidloop/ptgo 。你可随意 fork 并修改以实现你自己的触发器。它仅在 Linux 上测试过。请按照下面步骤获取源码:

git clone https://github.com/rapidloop/ptgo
cd ptgo
make

你可能需要首先安装 Postgres 开发包。对于基于 Debian 的系统,可以使用下面命令:

sudo apt-get install postgresql-server-dev-9.6

以上所述就是小编给大家介绍的《使用 Go 编写 PostgreSQL 触发器》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

基业长青

基业长青

[美] 詹姆斯·柯林斯、[美] 杰里·波勒斯 / 真如 / 中信出版社 / 2006-9 / 39.00元

如何建立一个伟大并长盛不衰的公司?有思想的人们早已经厌倦了“年度流行语”般稍纵即逝的管理概念,他们渴求获得能经受时间考验的管理思想。 柯林斯和波勒斯在斯坦福大学为期6年的研究项目中,选取了18个卓越非凡、长盛不衰的公司作了深入的研究,这些公司包括通用电气、3M、默克、沃尔玛、惠普、迪士尼等,它们平均拥有近百年的历史。是什么使这些公司不同于它们的竞争对手呢?他们拥有什么别的公司所不具有的法宝呢......一起来看看 《基业长青》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具