├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── module.go └── mytrigger.go /.gitignore: -------------------------------------------------------------------------------- 1 | ptgo.h 2 | ptgo.so 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | ptgo - PostgreSQL Triggers in Go 3 | 4 | Copyright (c) 2017 RapidLoop 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | SRC := $(wildcard *.go) 3 | 4 | export CGO_CFLAGS = -I$(shell pg_config --includedir-server) 5 | 6 | build: $(SRC) 7 | go build -o ptgo.so -buildmode=c-shared $(SRC) 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PostgreSQL Triggers in Go 2 | 3 | `ptgo` is an example of how to write PostgreSQL triggers in Go. Fork it and use 4 | it to implement your own triggers! 5 | 6 | Check out the [blog post](https://www.opsdash.com/blog/postgresql-triggers-golang.html) 7 | for a description of ptgo internals. 8 | 9 | Follow us on Twitter today! [@therapidloop](https://twitter.com/therapidloop) 10 | 11 | -------------------------------------------------------------------------------- /module.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | #include "postgres.h" 5 | #include "fmgr.h" 6 | 7 | #cgo LDFLAGS: -Wl,--unresolved-symbols=ignore-all 8 | 9 | #ifdef PG_MODULE_MAGIC 10 | PG_MODULE_MAGIC; 11 | #endif 12 | 13 | PG_FUNCTION_INFO_V1(mytrigger); 14 | */ 15 | import "C" 16 | 17 | func main() { 18 | } 19 | -------------------------------------------------------------------------------- /mytrigger.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | #include "postgres.h" 5 | #include "commands/trigger.h" 6 | #include "utils/elog.h" 7 | #include "utils/rel.h" 8 | #include "access/htup_details.h" 9 | 10 | static int trigger_fired_by_update(TriggerEvent tg_event) { 11 | return (TRIGGER_FIRED_BY_UPDATE(tg_event)) != 0; 12 | } 13 | 14 | static Datum pointer_get_datum(HeapTuple t) { 15 | return PointerGetDatum(t); 16 | } 17 | 18 | static char *getarg_text(TriggerData *trigdata, HeapTuple rettuple, int idx) { 19 | bool isnull; 20 | TupleDesc tupdesc = trigdata->tg_relation->rd_att; 21 | text * t = DatumGetTextP(heap_getattr(rettuple, idx, tupdesc, &isnull)); 22 | if (isnull || !t) { 23 | return ""; 24 | } 25 | return VARDATA(t); 26 | } 27 | 28 | static void elog_info(char *s) { 29 | elog(INFO, "%s", s); 30 | } 31 | 32 | */ 33 | import "C" 34 | import ( 35 | "fmt" 36 | "unsafe" 37 | ) 38 | 39 | //export mytrigger 40 | func mytrigger(fcInfo *C.FunctionCallInfoData) C.Datum { 41 | trigdata := (*C.TriggerData)(unsafe.Pointer(fcInfo.context)) 42 | 43 | var rettuple *C.HeapTupleData 44 | if C.trigger_fired_by_update(trigdata.tg_event) != 0 { 45 | rettuple = (*C.HeapTupleData)(trigdata.tg_newtuple) 46 | } else { 47 | rettuple = (*C.HeapTupleData)(trigdata.tg_trigtuple) 48 | } 49 | 50 | url := C.GoString(C.getarg_text(trigdata, rettuple, 1)) 51 | 52 | C.elog_info(C.CString(fmt.Sprintf("got url=%s", url))) 53 | fmt.Println(url) 54 | 55 | return C.pointer_get_datum(rettuple) 56 | } 57 | --------------------------------------------------------------------------------