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