diff options
author | xengineering <me@xengineering.eu> | 2023-12-27 12:16:35 +0100 |
---|---|---|
committer | xengineering <me@xengineering.eu> | 2023-12-27 12:21:33 +0100 |
commit | aaf2bad8d3ab2a4f825c2d3db3f2d17de68e08b4 (patch) | |
tree | 31fb3197934eec3bbaca89871b5c095b59f96892 /vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go | |
parent | 318f00c5d496296e45311ea81e8b80d9bd03b1d5 (diff) | |
download | ceres-aaf2bad8d3ab2a4f825c2d3db3f2d17de68e08b4.tar ceres-aaf2bad8d3ab2a4f825c2d3db3f2d17de68e08b4.tar.zst ceres-aaf2bad8d3ab2a4f825c2d3db3f2d17de68e08b4.zip |
Add github.com/mattn/go-sqlite3
This can be used to implement the `database/sql` interface from the Go
standard library for sqlite databases. This is the currently preferred
method to store user data for Ceres.
Diffstat (limited to 'vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go')
-rw-r--r-- | vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go new file mode 100644 index 0000000..b43e482 --- /dev/null +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go @@ -0,0 +1,112 @@ +// Copyright (C) 2019 G.J.R. Timmer <gjr.timmer@gmail.com>. +// Copyright (C) 2018 segment.com <friends@segment.com> +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +// +build sqlite_preupdate_hook + +package sqlite3 + +/* +#cgo CFLAGS: -DSQLITE_ENABLE_PREUPDATE_HOOK +#cgo LDFLAGS: -lm + +#ifndef USE_LIBSQLITE3 +#include "sqlite3-binding.h" +#else +#include <sqlite3.h> +#endif +#include <stdlib.h> +#include <string.h> + +void preUpdateHookTrampoline(void*, sqlite3 *, int, char *, char *, sqlite3_int64, sqlite3_int64); +*/ +import "C" +import ( + "errors" + "unsafe" +) + +// RegisterPreUpdateHook sets the pre-update hook for a connection. +// +// The callback is passed a SQLitePreUpdateData struct with the data for +// the update, as well as methods for fetching copies of impacted data. +// +// If there is an existing preupdate hook for this connection, it will be +// removed. If callback is nil the existing hook (if any) will be removed +// without creating a new one. +func (c *SQLiteConn) RegisterPreUpdateHook(callback func(SQLitePreUpdateData)) { + if callback == nil { + C.sqlite3_preupdate_hook(c.db, nil, nil) + } else { + C.sqlite3_preupdate_hook(c.db, (*[0]byte)(unsafe.Pointer(C.preUpdateHookTrampoline)), unsafe.Pointer(newHandle(c, callback))) + } +} + +// Depth returns the source path of the write, see sqlite3_preupdate_depth() +func (d *SQLitePreUpdateData) Depth() int { + return int(C.sqlite3_preupdate_depth(d.Conn.db)) +} + +// Count returns the number of columns in the row +func (d *SQLitePreUpdateData) Count() int { + return int(C.sqlite3_preupdate_count(d.Conn.db)) +} + +func (d *SQLitePreUpdateData) row(dest []interface{}, new bool) error { + for i := 0; i < d.Count() && i < len(dest); i++ { + var val *C.sqlite3_value + var src interface{} + + // Initially I tried making this just a function pointer argument, but + // it's absurdly complicated to pass C function pointers. + if new { + C.sqlite3_preupdate_new(d.Conn.db, C.int(i), &val) + } else { + C.sqlite3_preupdate_old(d.Conn.db, C.int(i), &val) + } + + switch C.sqlite3_value_type(val) { + case C.SQLITE_INTEGER: + src = int64(C.sqlite3_value_int64(val)) + case C.SQLITE_FLOAT: + src = float64(C.sqlite3_value_double(val)) + case C.SQLITE_BLOB: + len := C.sqlite3_value_bytes(val) + blobptr := C.sqlite3_value_blob(val) + src = C.GoBytes(blobptr, len) + case C.SQLITE_TEXT: + len := C.sqlite3_value_bytes(val) + cstrptr := unsafe.Pointer(C.sqlite3_value_text(val)) + src = C.GoBytes(cstrptr, len) + case C.SQLITE_NULL: + src = nil + } + + err := convertAssign(&dest[i], src) + if err != nil { + return err + } + } + + return nil +} + +// Old populates dest with the row data to be replaced. This works similar to +// database/sql's Rows.Scan() +func (d *SQLitePreUpdateData) Old(dest ...interface{}) error { + if d.Op == SQLITE_INSERT { + return errors.New("There is no old row for INSERT operations") + } + return d.row(dest, false) +} + +// New populates dest with the replacement row data. This works similar to +// database/sql's Rows.Scan() +func (d *SQLitePreUpdateData) New(dest ...interface{}) error { + if d.Op == SQLITE_DELETE { + return errors.New("There is no new row for DELETE operations") + } + return d.row(dest, true) +} |