Files

117 lines
2.5 KiB
Go
Raw Permalink Normal View History

2023-06-01 18:11:37 +01:00
// Package memdb implements the "memdb" SQLite VFS.
2023-05-31 15:47:28 +01:00
//
2023-06-01 18:11:37 +01:00
// The "memdb" [vfs.VFS] allows the same in-memory database to be shared
2023-05-31 15:47:28 +01:00
// among multiple database connections in the same process,
// as long as the database name begins with "/".
//
2024-04-27 16:31:32 +01:00
// Importing package memdb registers the VFS:
2023-05-31 15:47:28 +01:00
//
2023-06-01 18:11:37 +01:00
// import _ "github.com/ncruces/go-sqlite3/vfs/memdb"
package memdb
2023-05-31 15:47:28 +01:00
import (
2025-09-26 16:25:15 +01:00
"crypto/rand"
2024-08-05 21:25:47 +01:00
"fmt"
"net/url"
2023-05-31 15:47:28 +01:00
"sync"
2024-08-05 21:25:47 +01:00
"testing"
2023-05-31 15:47:28 +01:00
2023-06-01 18:11:37 +01:00
"github.com/ncruces/go-sqlite3/vfs"
2023-05-31 15:47:28 +01:00
)
func init() {
2023-06-01 18:11:37 +01:00
vfs.Register("memdb", memVFS{})
2023-05-31 15:47:28 +01:00
}
var (
memoryMtx sync.Mutex
2023-06-02 03:32:13 +01:00
// +checklocks:memoryMtx
2023-06-01 18:11:37 +01:00
memoryDBs = map[string]*memDB{}
2023-05-31 15:47:28 +01:00
)
// Create creates a shared memory database,
// using data as its initial contents.
// The new database takes ownership of data,
// and the caller should not use data after this call.
func Create(name string, data []byte) {
2024-05-20 01:04:53 +01:00
db := &memDB{
refs: 1,
name: name,
size: int64(len(data)),
}
2023-05-31 15:47:28 +01:00
2024-08-09 00:48:25 +01:00
// Convert data from WAL/2 to rollback journal.
2024-10-01 15:16:06 +01:00
if len(data) >= 20 && (false ||
data[18] == 2 && data[19] == 2 ||
2024-08-09 00:48:25 +01:00
data[18] == 3 && data[19] == 3) {
2023-11-28 09:53:17 +00:00
data[18] = 1
data[19] = 1
}
2023-05-31 15:47:28 +01:00
sectors := divRoundUp(db.size, sectorSize)
db.data = make([]*[sectorSize]byte, sectors)
for i := range db.data {
sector := data[i*sectorSize:]
if len(sector) >= sectorSize {
db.data[i] = (*[sectorSize]byte)(sector)
} else {
db.data[i] = new([sectorSize]byte)
copy((*db.data[i])[:], sector)
}
}
2025-12-19 16:37:47 +00:00
memoryMtx.Lock()
2023-05-31 15:47:28 +01:00
memoryDBs[name] = db
2025-12-19 16:37:47 +00:00
memoryMtx.Unlock()
2023-05-31 15:47:28 +01:00
}
// Delete deletes a shared memory database.
func Delete(name string) {
memoryMtx.Lock()
delete(memoryDBs, name)
2025-12-19 16:37:47 +00:00
memoryMtx.Unlock()
2023-05-31 15:47:28 +01:00
}
2024-08-05 21:25:47 +01:00
// TestDB creates an empty shared memory database for the test to use.
// The database is automatically deleted when the test and all its subtests complete.
2025-09-26 16:25:15 +01:00
// Returns a URI filename appropriate to call Open with.
2024-08-05 21:25:47 +01:00
// Each subsequent call to TestDB returns a unique database.
2025-10-01 11:00:13 +01:00
//
// func Test_something(t *testing.T) {
// t.Parallel()
// dsn := memdb.TestDB(t, url.Values{
// "_pragma": {"busy_timeout(1000)"},
// })
//
// db, err := sql.Open("sqlite3", dsn)
// if err != nil {
// t.Fatal(err)
// }
// defer db.Close()
//
// // ...
// }
2024-08-05 21:25:47 +01:00
func TestDB(tb testing.TB, params ...url.Values) string {
tb.Helper()
2025-09-26 16:25:15 +01:00
name := fmt.Sprintf("%s_%s", tb.Name(), rand.Text())
2024-08-05 21:25:47 +01:00
tb.Cleanup(func() { Delete(name) })
Create(name, nil)
p := url.Values{"vfs": {"memdb"}}
for _, v := range params {
for k, v := range v {
for _, v := range v {
p.Add(k, v)
}
}
}
return (&url.URL{
Scheme: "file",
OmitHost: true,
Path: "/" + name,
RawQuery: p.Encode(),
}).String()
}