這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
怎樣應用 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 中模組的工作方式。
函數可以用作觸發器,而這點正是我們感興趣的部分。
JaneTrans 翻譯於 2個月前 0人頂 頂 翻譯得不錯哦!
觸發器
觸發器是事件處理常式的一種形式——它們是當指定對象發生特定事件時可以執行的邏輯塊。 通常,它們涉及的對象是表,但它們也可以是視圖或外部表格。
這些事件通常是:
insert (rows)
update (rows)
delete (rows)
truncate (table)
PostgreSQL 觸發器用處廣泛:
最受歡迎的觸發器的使用可能是建立審計日誌(或更具體地說是修改日誌)。 您可以在這裡和這裡閱讀更多有關觸發器的資訊。
總長 翻譯於 1個月前 0人頂 頂 翻譯得不錯哦!
使用 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 mainimport "C"//export MyNamefunc MyName(x int) int {return 42 + x}func main() {// 未實現}
注意看匯出函數 MyName 上面有一條修飾性注釋。另外,如果要匯出,代碼裡還得寫 import "C"。
AzureSora 翻譯於 1個月前 0人頂 頂 翻譯得不錯哦!
使用 Go 語言編寫 PostgreSQL 函數
可以建立一個包含到處方法的 *.so 檔案,然後在裡面寫要匯出的 PostgreSQL 函數。
在寫這類函數時,必須遵守一定的規則,詳情請點擊這裡。
那麼現在,我們先來定義我們的模組,然後寫一個叫做 mytrigger 的匯出函數。
// file module.gopackage main/*#include "postgres.h"#include "fmgr.h"#cgo LDFLAGS: -Wl,--unresolved-symbols=ignore-all#ifdef PG_MODULE_MAGICPG_MODULE_MAGIC;#endifPG_FUNCTION_INFO_V1(mytrigger);*/import "C"func main() {}
注意代碼裡的 LDFLAGS 聲明。用了之後,連結器在產生 so 檔案時,就不會提示遇到了無法解析的符號。因為,我們現在用的是 PostgreSQL,並沒有需要連結的外部庫;而那些符號,也只在 PostgreSQL 載入 so 檔案時才來檢查是否存在。
AzureSora 翻譯於 1個月前 0人頂 頂 翻譯得不錯哦!
接著我們在檔案 mytrigger.go 中細化觸發器函數:
// file mytrigger.gopackage main/*#include "postgres.h"#include "commands/trigger.h"//...*/import "C"import ("fmt""unsafe")//export mytriggerfunc 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.HeapTupleDataif 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 庫的連結和如何產生目標程式的過程指導。
s張利民z 翻譯於 1個月前 0人頂 頂 翻譯得不錯哦!
運行觸發器
在觸發器運行之前,先建立表:
$ sudo -u postgres psql -d testpsql (9.6.2)Type "help" for help.test=# CREATE TABLE urls ( url TEXT );CREATE TABLEtest=#
之後,是建立我們的函數(你需要有 C 語言的 USAGE 許可權,像這樣):
test=# CREATE FUNCTION mytrigger()test-# RETURNS TRIGGER AS '/home/alice/ptgo/ptgo.so'test-# LANGUAGE C;CREATE FUNCTIONtest=#
下一步,讓我們建立一個在表路徑上的 INSERT 和 UPDATE 觸發器,並讓它來調用我們的函數:
test=# CREATE TRIGGER trig_1test-# AFTER INSERT OR UPDATEtest-# ON urlstest-# FOR EACH ROWtest-# EXECUTE PROCEDURE mytrigger();CREATE TRIGGERtest=#
現在,讓我們插入兩行。“got url=” 通過我們的函數被列印出來:
test=# INSERT INTO urls VALUES ('http://example.com/');INFO: got url=http://example.com/INSERT 0 1test=#test=# INSERT INTO urls VALUES ('http://mydomain.com/');INFO: got url=http://mydomain.com/INSERT 0 1test=#
當行被更新的時候,函數接收更新後的值,因為它是一個 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 2test=#
就是這樣!我們有了我們自己的用 Go 編寫的 PostgreSQL 觸發器!
無若 翻譯於 1個月前 0人頂 頂 翻譯得不錯哦!
代碼
所有代碼可以在GitHub上訪問,連結如下:github.com/rapidloop/ptgo。你可隨意 fork 並修改以實現你自己的觸發器。它僅在 Linux 上測試過。請按照下面步驟擷取源碼:
git clone https://github.com/rapidloop/ptgocd ptgomake
你可能需要首先安裝 Postgres 開發包。對於基於 Debian 的系統,可以使用下面命令:
sudo apt-get install postgresql-server-dev-9.6
Tocy 翻譯於 2個月前 0人頂 頂 翻譯得不錯哦!