Skip to content

Commit dac5877

Browse files
committed
modernc.org/sqlite bump + port #196 in-memory-conn fix +
ncruces attribution modernc.org/sqlite bumps; golang.org/x/{crypto,sys,text} ride along via `go mod tidy`. github.com/google/uuid and golang.org/x/text move from indirect to direct in go.mod since ext/uuid and ext/unicode import them at the leaf. modernc.org/libc stays pinned to the version the new sqlite release carries — no independent bump. conn.go ports the upstream #196 fix that regressed under #198's sqlite3_is_interrupted check. A new (*conn).inMemory bool is set once at open via Xsqlite3_db_filename(c.db, "main") — empty filename means the database lives only in this connection. (*conn).usable() short-circuits to true for in-memory conns so database/sql doesn't discard a cancelled-QueryContext'd handle and lose the entire database with it. File-backed conns keep the existing behaviour: an interrupted one is reported unusable so the pool drops it, since the data is safe on disk. conn_inmemory_test.go pins both branches (in-memory survives interrupt, file-backed is dropped) — a port of modernc's TestInMemoryDBSurvivesContextCancel.
1 parent 1588659 commit dac5877

5 files changed

Lines changed: 155 additions & 18 deletions

File tree

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,11 @@ preserved under its original license:
861861
- [github.com/glebarez/sqlite](https://github.com/glebarez/sqlite)
862862
MIT; see [LICENSE.glebarez](LICENSE.glebarez). The `gorm/` sub-package
863863
is ported from glebarez.
864+
- [github.com/ncruces/go-sqlite3](https://github.com/ncruces/go-sqlite3)
865+
MIT; see [LICENSE.ncruces](LICENSE.ncruces). Several `ext/` sub-packages
866+
port loadable extensions from ncruces — re-implemented against this
867+
driver's `(*Conn).RegisterFunc` / vtab helper APIs rather than copied
868+
verbatim, with a credit header in each ported package's doc.
864869

865870
## Acknowledgements
866871

@@ -870,6 +875,9 @@ preserved under its original license:
870875
driver whose API we mirror.
871876
- [glebarez/sqlite](https://github.com/glebarez/sqlite) — the gorm dialector
872877
this package's gorm sub-package is ported from.
878+
- [ncruces/go-sqlite3](https://github.com/ncruces/go-sqlite3) — Nuno Cruces's
879+
CGo-free (WASM/wazero) driver, whose loadable-extension lineup several
880+
`ext/` sub-packages are ported from.
873881
- [asg017/sqlite-vec](https://github.com/asg017/sqlite-vec) — the vector
874882
search extension bundled by `modernc.org/sqlite/vec` and re-exported here.
875883
- [zalgonoise/fts](https://github.com/zalgonoise/x/tree/master/fts) — the

conn.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ type conn struct {
3333
db uintptr // *sqlite3.Xsqlite3
3434
tls *libc.TLS
3535

36+
// inMemory records whether the underlying database lives only in memory
37+
// (DSN like ":memory:", "file::memory:", or a shared-cache memory URI).
38+
// For such databases the connection IS the database, so usable() must
39+
// not report it unusable just because an in-flight query was interrupted
40+
// — discarding the sole handle destroys the data. Ported from
41+
// modernc.org/sqlite #196 (regressed there by the #198 is_interrupted
42+
// check); set once at open via sqlite3_db_filename.
43+
inMemory bool
44+
3645
// Context handling can cause conn.Close and conn.interrupt to be invoked
3746
// concurrently.
3847
sync.Mutex
@@ -140,6 +149,17 @@ func newConn(dsn string) (*conn, error) {
140149
}
141150

142151
c.db = db
152+
// Cache whether this is a memory-only database (sqlite3_db_filename
153+
// returns "" for any non-file-backed DB) so usable() can keep an
154+
// interrupted in-memory conn in the pool instead of dropping the sole
155+
// handle to its data. See the inMemory field.
156+
zMain, mainErr := libc.CString("main")
157+
if mainErr != nil {
158+
c.Close()
159+
return nil, mainErr
160+
}
161+
c.inMemory = libc.GoString(sqlite3.Xsqlite3_db_filename(c.tls, c.db, zMain)) == ""
162+
libc.Xfree(c.tls, zMain)
143163
registerConn(c)
144164
if err = c.extendedResultCodes(true); err != nil {
145165
c.Close()
@@ -934,7 +954,17 @@ func (c *conn) IsValid() bool {
934954
}
935955

936956
func (c *conn) usable() bool {
937-
return c.db != 0 && sqlite3.Xsqlite3_is_interrupted(c.tls, c.db) == 0
957+
if c.db == 0 {
958+
return false
959+
}
960+
// In-memory databases live only in this connection; discarding it
961+
// because the previous query was interrupted (e.g. a cancelled
962+
// QueryContext) destroys the database. Keep it usable so database/sql
963+
// returns it to the pool rather than dropping it. See the inMemory field.
964+
if c.inMemory {
965+
return true
966+
}
967+
return sqlite3.Xsqlite3_is_interrupted(c.tls, c.db) == 0
938968
}
939969

940970
type userDefinedFunction struct {

conn_inmemory_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package sqlite
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"path/filepath"
7+
"testing"
8+
9+
sqlite3 "modernc.org/sqlite/lib"
10+
)
11+
12+
// TestInMemoryConnSurvivesInterrupt is a regression test for the ported
13+
// modernc.org/sqlite #196 fix. An in-memory database lives only in its
14+
// connection, so after an interrupt (e.g. a cancelled QueryContext) the
15+
// conn must stay usable() — otherwise database/sql discards the sole
16+
// handle and the whole store is lost. File-backed connections keep the
17+
// #198 behaviour: an interrupted one is reported unusable so the pool
18+
// drops it, since its data is safe on disk.
19+
//
20+
// Ported from modernc.org/sqlite's TestInMemoryDBSurvivesContextCancel.
21+
func TestInMemoryConnSurvivesInterrupt(t *testing.T) {
22+
t.Run("in-memory stays usable after interrupt", func(t *testing.T) {
23+
db, err := sql.Open(driverName, "file::memory:?cache=shared")
24+
if err != nil {
25+
t.Fatal(err)
26+
}
27+
defer db.Close()
28+
db.SetMaxOpenConns(1)
29+
30+
if _, err := db.Exec("CREATE TABLE t (v INT)"); err != nil {
31+
t.Fatal(err)
32+
}
33+
if _, err := db.Exec("INSERT INTO t VALUES (1), (2), (3)"); err != nil {
34+
t.Fatal(err)
35+
}
36+
37+
raw, err := db.Conn(context.Background())
38+
if err != nil {
39+
t.Fatal(err)
40+
}
41+
_ = raw.Raw(func(dc any) error {
42+
c, ok := dc.(*conn)
43+
if !ok {
44+
t.Fatalf("driver conn is %T, want *conn", dc)
45+
}
46+
if !c.inMemory {
47+
t.Fatalf("conn opened with file::memory: must be marked inMemory")
48+
}
49+
if !c.usable() {
50+
t.Fatalf("fresh in-memory conn must be usable")
51+
}
52+
sqlite3.Xsqlite3_interrupt(c.tls, c.db)
53+
if !c.usable() {
54+
t.Errorf("in-memory conn must remain usable after interrupt (#196)")
55+
}
56+
return nil
57+
})
58+
raw.Close()
59+
60+
// The store must survive: if the conn had been discarded, the
61+
// shared in-memory DB would be gone and the table with it.
62+
var n int
63+
if err := db.QueryRow("SELECT count(*) FROM t").Scan(&n); err != nil {
64+
t.Fatalf("table lost after interrupt: %v", err)
65+
}
66+
if n != 3 {
67+
t.Fatalf("expected 3 rows after interrupt, got %d", n)
68+
}
69+
})
70+
71+
t.Run("file-backed still discarded on interrupt", func(t *testing.T) {
72+
dir := t.TempDir()
73+
db, err := sql.Open(driverName, "file:"+filepath.Join(dir, "t.db"))
74+
if err != nil {
75+
t.Fatal(err)
76+
}
77+
defer db.Close()
78+
db.SetMaxOpenConns(1)
79+
80+
raw, err := db.Conn(context.Background())
81+
if err != nil {
82+
t.Fatal(err)
83+
}
84+
defer raw.Close()
85+
_ = raw.Raw(func(dc any) error {
86+
c, ok := dc.(*conn)
87+
if !ok {
88+
t.Fatalf("driver conn is %T, want *conn", dc)
89+
}
90+
if c.inMemory {
91+
t.Fatalf("file-backed conn unexpectedly marked inMemory")
92+
}
93+
if !c.usable() {
94+
t.Fatalf("fresh file-backed conn must be usable")
95+
}
96+
sqlite3.Xsqlite3_interrupt(c.tls, c.db)
97+
if c.usable() {
98+
t.Errorf("file-backed conn must be reported unusable after interrupt (#198)")
99+
}
100+
return nil
101+
})
102+
})
103+
}

go.mod

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@ module github.com/go-again/sqlite
33
go 1.25.0
44

55
require (
6-
golang.org/x/crypto v0.23.0
6+
github.com/google/uuid v1.6.0
7+
golang.org/x/crypto v0.52.0
8+
golang.org/x/text v0.37.0
79
gorm.io/gorm v1.31.1
810
lukechampine.com/adiantum v1.1.1
911
modernc.org/libc v1.72.3
10-
modernc.org/sqlite v1.50.1
12+
modernc.org/sqlite v1.51.0
1113
)
1214

1315
require (
1416
github.com/dustin/go-humanize v1.0.1 // indirect
15-
github.com/google/uuid v1.6.0 // indirect
1617
github.com/jinzhu/inflection v1.0.0 // indirect
1718
github.com/jinzhu/now v1.1.5 // indirect
1819
github.com/mattn/go-isatty v0.0.20 // indirect
1920
github.com/ncruces/go-strftime v1.0.0 // indirect
2021
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
21-
golang.org/x/sys v0.42.0 // indirect
22-
golang.org/x/text v0.37.0 // indirect
22+
golang.org/x/sys v0.45.0 // indirect
2323
modernc.org/mathutil v1.7.1 // indirect
2424
modernc.org/memory v1.11.0 // indirect
2525
)

go.sum

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,19 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF
1414
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
1515
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
1616
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
17-
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
18-
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
19-
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
20-
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
17+
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
18+
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
2119
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
20+
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
2221
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
2322
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
2423
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
25-
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
26-
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
27-
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
28-
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
24+
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
25+
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
2926
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
3027
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
31-
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
32-
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
3328
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
29+
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
3430
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
3531
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
3632
lukechampine.com/adiantum v1.1.1 h1:4fp6gTxWCqpEbLy40ExiYDDED3oUNWx5cTqBCtPdZqA=
@@ -57,8 +53,8 @@ modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
5753
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
5854
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
5955
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
60-
modernc.org/sqlite v1.50.1 h1:l+cQvn0sd0zJJtfygGHuQJ5AjlrwXmWPw4KP3ZMwr9w=
61-
modernc.org/sqlite v1.50.1/go.mod h1:tcNzv5p84E0skkmJn038y+hWJbLQXQqEnQfeh5r2JLM=
56+
modernc.org/sqlite v1.51.0 h1:aH/MMSoayAIhozZ7uJbVTT9QO/VhzBf0J9tymmmuC/U=
57+
modernc.org/sqlite v1.51.0/go.mod h1:tcNzv5p84E0skkmJn038y+hWJbLQXQqEnQfeh5r2JLM=
6258
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
6359
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
6460
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=

0 commit comments

Comments
 (0)