sqlite3_opt_preupdate_hook.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // Copyright (C) 2019 G.J.R. Timmer <gjr.timmer@gmail.com>.
  2. // Copyright (C) 2018 segment.com <friends@segment.com>
  3. //
  4. // Use of this source code is governed by an MIT-style
  5. // license that can be found in the LICENSE file.
  6. //go:build sqlite_preupdate_hook
  7. // +build sqlite_preupdate_hook
  8. package sqlite3
  9. /*
  10. #cgo CFLAGS: -DSQLITE_ENABLE_PREUPDATE_HOOK
  11. #cgo LDFLAGS: -lm
  12. #ifndef USE_LIBSQLITE3
  13. #include "sqlite3-binding.h"
  14. #else
  15. #include <sqlite3.h>
  16. #endif
  17. #include <stdlib.h>
  18. #include <string.h>
  19. void preUpdateHookTrampoline(void*, sqlite3 *, int, char *, char *, sqlite3_int64, sqlite3_int64);
  20. */
  21. import "C"
  22. import (
  23. "errors"
  24. "unsafe"
  25. )
  26. // RegisterPreUpdateHook sets the pre-update hook for a connection.
  27. //
  28. // The callback is passed a SQLitePreUpdateData struct with the data for
  29. // the update, as well as methods for fetching copies of impacted data.
  30. //
  31. // If there is an existing preupdate hook for this connection, it will be
  32. // removed. If callback is nil the existing hook (if any) will be removed
  33. // without creating a new one.
  34. func (c *SQLiteConn) RegisterPreUpdateHook(callback func(SQLitePreUpdateData)) {
  35. if callback == nil {
  36. C.sqlite3_preupdate_hook(c.db, nil, nil)
  37. } else {
  38. C.sqlite3_preupdate_hook(c.db, (*[0]byte)(unsafe.Pointer(C.preUpdateHookTrampoline)), unsafe.Pointer(newHandle(c, callback)))
  39. }
  40. }
  41. // Depth returns the source path of the write, see sqlite3_preupdate_depth()
  42. func (d *SQLitePreUpdateData) Depth() int {
  43. return int(C.sqlite3_preupdate_depth(d.Conn.db))
  44. }
  45. // Count returns the number of columns in the row
  46. func (d *SQLitePreUpdateData) Count() int {
  47. return int(C.sqlite3_preupdate_count(d.Conn.db))
  48. }
  49. func (d *SQLitePreUpdateData) row(dest []any, new bool) error {
  50. for i := 0; i < d.Count() && i < len(dest); i++ {
  51. var val *C.sqlite3_value
  52. var src any
  53. // Initially I tried making this just a function pointer argument, but
  54. // it's absurdly complicated to pass C function pointers.
  55. if new {
  56. C.sqlite3_preupdate_new(d.Conn.db, C.int(i), &val)
  57. } else {
  58. C.sqlite3_preupdate_old(d.Conn.db, C.int(i), &val)
  59. }
  60. switch C.sqlite3_value_type(val) {
  61. case C.SQLITE_INTEGER:
  62. src = int64(C.sqlite3_value_int64(val))
  63. case C.SQLITE_FLOAT:
  64. src = float64(C.sqlite3_value_double(val))
  65. case C.SQLITE_BLOB:
  66. len := C.sqlite3_value_bytes(val)
  67. blobptr := C.sqlite3_value_blob(val)
  68. src = C.GoBytes(blobptr, len)
  69. case C.SQLITE_TEXT:
  70. len := C.sqlite3_value_bytes(val)
  71. cstrptr := unsafe.Pointer(C.sqlite3_value_text(val))
  72. src = C.GoBytes(cstrptr, len)
  73. case C.SQLITE_NULL:
  74. src = nil
  75. }
  76. err := convertAssign(&dest[i], src)
  77. if err != nil {
  78. return err
  79. }
  80. }
  81. return nil
  82. }
  83. // Old populates dest with the row data to be replaced. This works similar to
  84. // database/sql's Rows.Scan()
  85. func (d *SQLitePreUpdateData) Old(dest ...any) error {
  86. if d.Op == SQLITE_INSERT {
  87. return errors.New("There is no old row for INSERT operations")
  88. }
  89. return d.row(dest, false)
  90. }
  91. // New populates dest with the replacement row data. This works similar to
  92. // database/sql's Rows.Scan()
  93. func (d *SQLitePreUpdateData) New(dest ...any) error {
  94. if d.Op == SQLITE_DELETE {
  95. return errors.New("There is no new row for DELETE operations")
  96. }
  97. return d.row(dest, true)
  98. }