Skip to content

Commit 1c3ad12

Browse files
committed
WAL and vacuum hooks.
1 parent 7260962 commit 1c3ad12

File tree

17 files changed

+199
-37
lines changed

17 files changed

+199
-37
lines changed

config.go

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func (c *Conn) SetAuthorizer(cb func(action AuthorizerActionCode, name3rd, name4
8282

8383
}
8484

85-
func authorizerCallback(ctx context.Context, mod api.Module, pDB uint32, action AuthorizerActionCode, zName3rd, zName4th, zSchema, zNameInner uint32) AuthorizerReturnCode {
85+
func authorizerCallback(ctx context.Context, mod api.Module, pDB uint32, action AuthorizerActionCode, zName3rd, zName4th, zSchema, zNameInner uint32) (rc AuthorizerReturnCode) {
8686
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.authorizer != nil {
8787
var name3rd, name4th, schema, nameInner string
8888
if zName3rd != 0 {
@@ -97,7 +97,68 @@ func authorizerCallback(ctx context.Context, mod api.Module, pDB uint32, action
9797
if zNameInner != 0 {
9898
nameInner = util.ReadString(mod, zNameInner, _MAX_NAME)
9999
}
100-
return c.authorizer(action, name3rd, name4th, schema, nameInner)
100+
rc = c.authorizer(action, name3rd, name4th, schema, nameInner)
101101
}
102-
return AUTH_OK
102+
return rc
103+
}
104+
105+
// WalCheckpoint checkpoints a WAL database.
106+
//
107+
// https://sqlite.org/c3ref/wal_checkpoint_v2.html
108+
func (c *Conn) WalCheckpoint(schema string, mode CheckpointMode) (nLog, nCkpt int, err error) {
109+
defer c.arena.mark()()
110+
nLogPtr := c.arena.new(ptrlen)
111+
nCkptPtr := c.arena.new(ptrlen)
112+
schemaPtr := c.arena.string(schema)
113+
r := c.call("sqlite3_wal_checkpoint_v2",
114+
uint64(c.handle), uint64(schemaPtr), uint64(mode),
115+
uint64(nLogPtr), uint64(nCkptPtr))
116+
nLog = int(int32(util.ReadUint32(c.mod, nLogPtr)))
117+
nCkpt = int(int32(util.ReadUint32(c.mod, nCkptPtr)))
118+
return nLog, nCkpt, c.error(r)
119+
}
120+
121+
// WalAutoCheckpoint configures WAL auto-checkpoints.
122+
//
123+
// https://sqlite.org/c3ref/wal_autocheckpoint.html
124+
func (c *Conn) WalAutoCheckpoint(pages int) error {
125+
r := c.call("sqlite3_wal_autocheckpoint", uint64(c.handle), uint64(pages))
126+
return c.error(r)
127+
}
128+
129+
// WalHook registers a callback function to be invoked
130+
// each time data is committed to a database in WAL mode.
131+
//
132+
// https://sqlite.org/c3ref/wal_hook.html
133+
func (c *Conn) WalHook(cb func(db *Conn, schema string, pages int) error) {
134+
var enable uint64
135+
if cb != nil {
136+
enable = 1
137+
}
138+
c.call("sqlite3_wal_hook_go", uint64(c.handle), enable)
139+
c.wal = cb
140+
}
141+
142+
func walCallback(ctx context.Context, mod api.Module, _, pDB, zSchema uint32, pages int32) (rc uint32) {
143+
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.wal != nil {
144+
schema := util.ReadString(mod, zSchema, _MAX_NAME)
145+
err := c.wal(c, schema, int(pages))
146+
_, rc = errorCode(err, ERROR)
147+
}
148+
return rc
149+
}
150+
151+
// AutoVacuumPages registers a autovacuum compaction amount callback.
152+
//
153+
// https://sqlite.org/c3ref/autovacuum_pages.html
154+
func (c *Conn) AutoVacuumPages(cb func(schema string, dbPages, freePages, bytesPerPage uint) uint) error {
155+
funcPtr := util.AddHandle(c.ctx, cb)
156+
r := c.call("sqlite3_autovacuum_pages_go", uint64(c.handle), uint64(funcPtr))
157+
return c.error(r)
158+
}
159+
160+
func autoVacuumCallback(ctx context.Context, mod api.Module, pApp, zSchema, nDbPage, nFreePage, nBytePerPage uint32) uint32 {
161+
fn := util.GetHandle(ctx, pApp).(func(schema string, dbPages, freePages, bytesPerPage uint) uint)
162+
schema := util.ReadString(mod, zSchema, _MAX_NAME)
163+
return uint32(fn(schema, uint(nDbPage), uint(nFreePage), uint(nBytePerPage)))
103164
}

conn.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type Conn struct {
2929
update func(AuthorizerActionCode, string, string, int64)
3030
commit func() bool
3131
rollback func()
32+
wal func(*Conn, string, int) error
3233
arena arena
3334

3435
handle uint32
@@ -281,6 +282,12 @@ func (c *Conn) ReleaseMemory() error {
281282
return c.error(r)
282283
}
283284

285+
// GetInterrupt gets the context set with [Conn.SetInterrupt],
286+
// or nil if none was set.
287+
func (c *Conn) GetInterrupt() context.Context {
288+
return c.interrupt
289+
}
290+
284291
// SetInterrupt interrupts a long-running query when a context is done.
285292
//
286293
// Subsequent uses of the connection will return [INTERRUPT]
@@ -325,13 +332,13 @@ func (c *Conn) checkInterrupt() {
325332
}
326333
}
327334

328-
func progressCallback(ctx context.Context, mod api.Module, pDB uint32) uint32 {
335+
func progressCallback(ctx context.Context, mod api.Module, pDB uint32) (interrupt uint32) {
329336
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.commit != nil {
330337
if c.interrupt != nil && c.interrupt.Err() != nil {
331-
return 1
338+
interrupt = 1
332339
}
333340
}
334-
return 0
341+
return interrupt
335342
}
336343

337344
// BusyTimeout sets a busy timeout.
@@ -359,13 +366,13 @@ func (c *Conn) BusyHandler(cb func(count int) (retry bool)) error {
359366
return nil
360367
}
361368

362-
func busyCallback(ctx context.Context, mod api.Module, pDB, count uint32) uint32 {
369+
func busyCallback(ctx context.Context, mod api.Module, pDB uint32, count int32) (retry uint32) {
363370
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.busy != nil {
364-
if retry := c.busy(int(count)); retry {
365-
return 1
371+
if c.busy(int(count)) {
372+
retry = 1
366373
}
367374
}
368-
return 0
375+
return retry
369376
}
370377

371378
func (c *Conn) error(rc uint64, sql ...string) error {

const.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,18 @@ const (
305305
AUTH_IGNORE AuthorizerReturnCode = 2 /* Don't allow access, but don't generate an error */
306306
)
307307

308+
// CheckpointMode are all the checkpoint mode values.
309+
//
310+
// https://sqlite.org/c3ref/c_checkpoint_full.html
311+
type CheckpointMode uint32
312+
313+
const (
314+
CHECKPOINT_PASSIVE CheckpointMode = 0 /* Do as much as possible w/o blocking */
315+
CHECKPOINT_FULL CheckpointMode = 1 /* Wait for writers, then checkpoint */
316+
CHECKPOINT_RESTART CheckpointMode = 2 /* Like FULL but wait for readers */
317+
CHECKPOINT_TRUNCATE CheckpointMode = 3 /* Like RESTART but also truncate WAL */
318+
)
319+
308320
// TxnState are the allowed return values from [Conn.TxnState].
309321
//
310322
// https://sqlite.org/c3ref/c_txn_none.html

embed/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Embeddable Wasm build of SQLite
22

3-
This folder includes an embeddable Wasm build of SQLite 3.45.1 for use with
3+
This folder includes an embeddable Wasm build of SQLite 3.45.2 for use with
44
[`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3).
55

66
The following optional features are compiled in:

embed/exports.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
aligned_alloc
12
free
23
malloc
34
malloc_destructor
4-
aligned_alloc
55
sqlite3_anycollseq_init
6+
sqlite3_autovacuum_pages_go
67
sqlite3_backup_finish
78
sqlite3_backup_init
89
sqlite3_backup_pagecount
@@ -115,4 +116,7 @@ sqlite3_vtab_in_first
115116
sqlite3_vtab_in_next
116117
sqlite3_vtab_nochange
117118
sqlite3_vtab_on_conflict
118-
sqlite3_vtab_rhs_value
119+
sqlite3_vtab_rhs_value
120+
sqlite3_wal_autocheckpoint
121+
sqlite3_wal_checkpoint_v2
122+
sqlite3_wal_hook_go

embed/sqlite3.wasm

789 Bytes
Binary file not shown.

gormlite/migrator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ type _Index struct {
334334
// GetIndexes return Indexes []gorm.Index and execErr error,
335335
// See the [doc]
336336
//
337-
// [doc]: https://www.sqlite.org/pragma.html#pragma_index_list
337+
// [doc]: https://sqlite.org/pragma.html#pragma_index_list
338338
func (m _Migrator) GetIndexes(value interface{}) ([]gorm.Index, error) {
339339
indexes := make([]gorm.Index, 0)
340340
err := m.RunWithValue(value, func(stmt *gorm.Statement) error {

sqlite.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ func exportCallbacks(env wazero.HostModuleBuilder) wazero.HostModuleBuilder {
294294
util.ExportFuncII(env, "go_commit_hook", commitCallback)
295295
util.ExportFuncVI(env, "go_rollback_hook", rollbackCallback)
296296
util.ExportFuncVIIIIJ(env, "go_update_hook", updateCallback)
297+
util.ExportFuncIIIII(env, "go_wal_hook", walCallback)
298+
util.ExportFuncIIIIII(env, "go_autovacuum_pages", autoVacuumCallback)
297299
util.ExportFuncIIIIIII(env, "go_authorizer", authorizerCallback)
298300
util.ExportFuncVIII(env, "go_log", logCallback)
299301
util.ExportFuncVI(env, "go_destroy", destroyCallback)

sqlite3/hooks.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ int go_busy_handler(void *, int);
88
int go_commit_hook(void *);
99
void go_rollback_hook(void *);
1010
void go_update_hook(void *, int, char const *, char const *, sqlite3_int64);
11+
int go_wal_hook(void *, sqlite3 *, const char *, int);
1112

1213
int go_authorizer(void *, int, const char *, const char *, const char *,
1314
const char *);
1415

1516
void go_log(void *, int, const char *);
1617

18+
unsigned int go_autovacuum_pages(void *, const char *, unsigned int,
19+
unsigned int, unsigned int);
20+
1721
void sqlite3_progress_handler_go(sqlite3 *db, int n) {
1822
sqlite3_progress_handler(db, n, go_progress_handler, /*arg=*/db);
1923
}
@@ -34,11 +38,21 @@ void sqlite3_update_hook_go(sqlite3 *db, bool enable) {
3438
sqlite3_update_hook(db, enable ? go_update_hook : NULL, /*arg=*/db);
3539
}
3640

41+
void sqlite3_wal_hook_go(sqlite3 *db, bool enable) {
42+
sqlite3_wal_hook(db, enable ? go_wal_hook : NULL, /*arg=*/NULL);
43+
}
44+
3745
int sqlite3_set_authorizer_go(sqlite3 *db, bool enable) {
3846
return sqlite3_set_authorizer(db, enable ? go_authorizer : NULL, /*arg=*/db);
3947
}
4048

4149
int sqlite3_config_log_go(bool enable) {
4250
return sqlite3_config(SQLITE_CONFIG_LOG, enable ? go_log : NULL,
4351
/*arg=*/NULL);
52+
}
53+
54+
int sqlite3_autovacuum_pages_go(sqlite3 *db, go_handle app) {
55+
int rc = sqlite3_autovacuum_pages(db, go_autovacuum_pages, app, go_destroy);
56+
if (rc) go_destroy(app);
57+
return rc;
4458
}

stmt.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func (s *Stmt) Status(op StmtStatus, reset bool) int {
120120
}
121121
r := s.c.call("sqlite3_stmt_status", uint64(s.handle),
122122
uint64(op), i)
123-
return int(r)
123+
return int(int32(r))
124124
}
125125

126126
// ClearBindings resets all bindings on the prepared statement.
@@ -137,7 +137,7 @@ func (s *Stmt) ClearBindings() error {
137137
func (s *Stmt) BindCount() int {
138138
r := s.c.call("sqlite3_bind_parameter_count",
139139
uint64(s.handle))
140-
return int(r)
140+
return int(int32(r))
141141
}
142142

143143
// BindIndex returns the index of a parameter in the prepared statement
@@ -149,7 +149,7 @@ func (s *Stmt) BindIndex(name string) int {
149149
namePtr := s.c.arena.string(name)
150150
r := s.c.call("sqlite3_bind_parameter_index",
151151
uint64(s.handle), uint64(namePtr))
152-
return int(r)
152+
return int(int32(r))
153153
}
154154

155155
// BindName returns the name of a parameter in the prepared statement.
@@ -357,7 +357,7 @@ func (s *Stmt) BindValue(param int, value Value) error {
357357
func (s *Stmt) ColumnCount() int {
358358
r := s.c.call("sqlite3_column_count",
359359
uint64(s.handle))
360-
return int(r)
360+
return int(int32(r))
361361
}
362362

363363
// ColumnName returns the name of the result column.

0 commit comments

Comments
 (0)