Skip to content

Commit eb08795

Browse files
author
Mura Li
committed
Add support for sqlite3_unlock_notify
1 parent c67b489 commit eb08795

5 files changed

+441
-5
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ env:
1212
matrix:
1313
- GOTAGS=
1414
- GOTAGS=libsqlite3
15-
- GOTAGS="sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_userauth sqlite_vacuum_incr sqlite_vtable"
15+
- GOTAGS="sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_userauth sqlite_vacuum_incr sqlite_vtable sqlite_unlock_notify"
1616
- GOTAGS=sqlite_vacuum_full
1717

1818
go:

sqlite3.go

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,38 @@ _sqlite3_exec(sqlite3* db, const char* pcmd, long long* rowid, long long* change
7878
return rv;
7979
}
8080
81+
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
82+
extern int sqlite3_step_blocking(sqlite3_stmt *stmt);
83+
extern int _sqlite3_step_blocking(sqlite3_stmt* stmt, long long* rowid, long long* changes);
84+
extern int sqlite3_prepare_v2_blocking(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail);
85+
86+
static int
87+
sqlite3_step_internal(sqlite3_stmt *stmt)
88+
{
89+
return sqlite3_step_blocking(stmt);
90+
}
91+
92+
static int
93+
_sqlite3_step_internal(sqlite3_stmt* stmt, long long* rowid, long long* changes)
94+
{
95+
return _sqlite3_step_blocking(stmt, rowid, changes);
96+
}
97+
8198
static int
82-
_sqlite3_step(sqlite3_stmt* stmt, long long* rowid, long long* changes)
99+
sqlite3_prepare_v2_internal(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail)
100+
{
101+
return sqlite3_prepare_v2_blocking(db, zSql, nBytes, ppStmt, pzTail);
102+
}
103+
104+
#else
105+
static int
106+
sqlite3_step_internal(sqlite3_stmt *stmt)
107+
{
108+
return sqlite3_step(stmt);
109+
}
110+
111+
static int
112+
_sqlite3_step_internal(sqlite3_stmt* stmt, long long* rowid, long long* changes)
83113
{
84114
int rv = sqlite3_step(stmt);
85115
sqlite3* db = sqlite3_db_handle(stmt);
@@ -88,6 +118,13 @@ _sqlite3_step(sqlite3_stmt* stmt, long long* rowid, long long* changes)
88118
return rv;
89119
}
90120
121+
static int
122+
sqlite3_prepare_v2_internal(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail)
123+
{
124+
return sqlite3_prepare_v2(db, zSql, nBytes, ppStmt, pzTail);
125+
}
126+
#endif
127+
91128
void _sqlite3_result_text(sqlite3_context* ctx, const char* s) {
92129
sqlite3_result_text(ctx, s, -1, &free);
93130
}
@@ -1637,7 +1674,7 @@ func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, er
16371674
defer C.free(unsafe.Pointer(pquery))
16381675
var s *C.sqlite3_stmt
16391676
var tail *C.char
1640-
rv := C.sqlite3_prepare_v2(c.db, pquery, -1, &s, &tail)
1677+
rv := C.sqlite3_prepare_v2_internal(c.db, pquery, -1, &s, &tail)
16411678
if rv != C.SQLITE_OK {
16421679
return nil, c.lastError()
16431680
}
@@ -1871,7 +1908,7 @@ func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result
18711908
}
18721909

18731910
var rowid, changes C.longlong
1874-
rv := C._sqlite3_step(s.s, &rowid, &changes)
1911+
rv := C._sqlite3_step_internal(s.s, &rowid, &changes)
18751912
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
18761913
err := s.c.lastError()
18771914
C.sqlite3_reset(s.s)
@@ -1943,7 +1980,7 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error {
19431980
if rc.s.closed {
19441981
return io.EOF
19451982
}
1946-
rv := C.sqlite3_step(rc.s.s)
1983+
rv := C.sqlite3_step_internal(rc.s.s)
19471984
if rv == C.SQLITE_DONE {
19481985
return io.EOF
19491986
}

sqlite3_opt_unlock_notify.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright (C) 2018 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
2+
//
3+
// Use of this source code is governed by an MIT-style
4+
// license that can be found in the LICENSE file.
5+
6+
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
7+
#include <stdio.h>
8+
#include <sqlite3-binding.h>
9+
10+
extern int unlock_notify_wait(sqlite3 *db);
11+
12+
int
13+
sqlite3_step_blocking(sqlite3_stmt *stmt)
14+
{
15+
int rv;
16+
sqlite3* db;
17+
18+
db = sqlite3_db_handle(stmt);
19+
for (;;) {
20+
rv = sqlite3_step(stmt);
21+
if (rv != SQLITE_LOCKED) {
22+
break;
23+
}
24+
if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) {
25+
break;
26+
}
27+
rv = unlock_notify_wait(db);
28+
if (rv != SQLITE_OK) {
29+
break;
30+
}
31+
sqlite3_reset(stmt);
32+
}
33+
34+
return rv;
35+
}
36+
37+
int
38+
_sqlite3_step_blocking(sqlite3_stmt* stmt, long long* rowid, long long* changes)
39+
{
40+
int rv;
41+
sqlite3* db;
42+
43+
db = sqlite3_db_handle(stmt);
44+
for (;;) {
45+
rv = sqlite3_step(stmt);
46+
if (rv!=SQLITE_LOCKED) {
47+
break;
48+
}
49+
if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) {
50+
break;
51+
}
52+
rv = unlock_notify_wait(db);
53+
if (rv != SQLITE_OK) {
54+
break;
55+
}
56+
sqlite3_reset(stmt);
57+
}
58+
59+
*rowid = (long long) sqlite3_last_insert_rowid(db);
60+
*changes = (long long) sqlite3_changes(db);
61+
return rv;
62+
}
63+
64+
int
65+
sqlite3_prepare_v2_blocking(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail)
66+
{
67+
int rv;
68+
69+
for (;;) {
70+
rv = sqlite3_prepare_v2(db, zSql, nBytes, ppStmt, pzTail);
71+
if (rv!=SQLITE_LOCKED) {
72+
break;
73+
}
74+
if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) {
75+
break;
76+
}
77+
rv = unlock_notify_wait(db);
78+
if (rv != SQLITE_OK) {
79+
break;
80+
}
81+
}
82+
83+
return rv;
84+
}
85+
#endif

sqlite3_opt_unlock_notify.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright (C) 2018 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
2+
//
3+
// Use of this source code is governed by an MIT-style
4+
// license that can be found in the LICENSE file.
5+
6+
// +build cgo
7+
// +build sqlite_unlock_notify
8+
9+
package sqlite3
10+
11+
/*
12+
#cgo CFLAGS: -DSQLITE_ENABLE_UNLOCK_NOTIFY
13+
14+
#include <stdlib.h>
15+
#include <sqlite3-binding.h>
16+
17+
extern void unlock_notify_callback(void *arg, int argc);
18+
*/
19+
import "C"
20+
import (
21+
"fmt"
22+
"sync"
23+
"unsafe"
24+
)
25+
26+
type unlock_notify_table struct {
27+
sync.Mutex
28+
seqnum uint
29+
table map[uint]chan struct{}
30+
}
31+
32+
var unt unlock_notify_table = unlock_notify_table{table: make(map[uint]chan struct{})}
33+
34+
func (t *unlock_notify_table) add(c chan struct{}) uint {
35+
t.Lock()
36+
defer t.Unlock()
37+
h := t.seqnum
38+
t.table[h] = c
39+
t.seqnum++
40+
return h
41+
}
42+
43+
func (t *unlock_notify_table) remove(h uint) {
44+
t.Lock()
45+
defer t.Unlock()
46+
delete(t.table, h)
47+
}
48+
49+
func (t *unlock_notify_table) get(h uint) chan struct{} {
50+
t.Lock()
51+
defer t.Unlock()
52+
c, ok := t.table[h]
53+
if !ok {
54+
panic(fmt.Sprintf("Non-existent key for unlcok-notify channel: %d", h))
55+
}
56+
return c
57+
}
58+
59+
//export unlock_notify_callback
60+
func unlock_notify_callback(argv unsafe.Pointer, argc C.int) {
61+
for i := 0; i < int(argc); i++ {
62+
parg := ((*(*[1 << 30]*[1]uint)(argv))[i])
63+
arg := *parg
64+
h := arg[0]
65+
c := unt.get(h)
66+
c <- struct{}{}
67+
}
68+
}
69+
70+
//export unlock_notify_wait
71+
func unlock_notify_wait(db *C.sqlite3) C.int {
72+
// It has to be a bufferred channel to not block in sqlite_unlock_notify
73+
// as sqlite_unlock_notify could invoke the callback before it returns.
74+
c := make(chan struct{}, 1)
75+
defer close(c)
76+
77+
h := unt.add(c)
78+
defer unt.remove(h)
79+
80+
pargv := C.malloc(C.sizeof_uint)
81+
defer C.free(pargv)
82+
83+
argv := (*[1]uint)(pargv)
84+
argv[0] = h
85+
if rv := C.sqlite3_unlock_notify(db, (*[0]byte)(C.unlock_notify_callback), unsafe.Pointer(pargv)); rv != C.SQLITE_OK {
86+
return rv
87+
}
88+
89+
<-c
90+
91+
return C.SQLITE_OK
92+
}

0 commit comments

Comments
 (0)