diff --git a/README.md b/README.md index 429a33c..64d36ac 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,13 @@ provides a [`database/sql`](https://pkg.go.dev/database/sql) driver ([example usage](https://pkg.go.dev/github.com/ncruces/go-sqlite3/driver#example-package)). - Package [`github.com/ncruces/go-sqlite3/embed`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/embed) embeds a build of SQLite into your application. -- Package [`github.com/ncruces/go-sqlite3/sqlite3vfs`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/sqlite3vfs) +- Package [`github.com/ncruces/go-sqlite3/vfs`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs) wraps the [C SQLite VFS API](https://www.sqlite.org/vfs.html) and provides a pure Go implementation. ### Caveats This module replaces the SQLite [OS Interface](https://www.sqlite.org/vfs.html) -(aka VFS) with a [pure Go](sqlite3vfs/) implementation. +(aka VFS) with a [pure Go](vfs/) implementation. This has benefits, but also comes with some drawbacks. #### Write-Ahead Logging diff --git a/module.go b/module.go index 3d3221a..e279601 100644 --- a/module.go +++ b/module.go @@ -9,7 +9,7 @@ import ( "sync" "github.com/ncruces/go-sqlite3/internal/util" - "github.com/ncruces/go-sqlite3/sqlite3vfs" + "github.com/ncruces/go-sqlite3/vfs" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" ) @@ -53,7 +53,7 @@ func compileModule() { ctx := context.Background() sqlite3.runtime = wazero.NewRuntime(ctx) - env := sqlite3vfs.ExportHostFunctions(sqlite3.runtime.NewHostModuleBuilder("env")) + env := vfs.ExportHostFunctions(sqlite3.runtime.NewHostModuleBuilder("env")) _, sqlite3.err = env.Instantiate(ctx) if sqlite3.err != nil { return @@ -85,7 +85,7 @@ type module struct { func newModule(mod api.Module) (m *module, err error) { m = new(module) m.mod = mod - m.ctx, m.vfs = sqlite3vfs.NewContext(context.Background()) + m.ctx, m.vfs = vfs.NewContext(context.Background()) getFun := func(name string) api.Function { f := mod.ExportedFunction(name) diff --git a/sqlite3/download.sh b/sqlite3/download.sh index 23ea00c..ff8473a 100755 --- a/sqlite3/download.sh +++ b/sqlite3/download.sh @@ -20,7 +20,7 @@ curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.42.0/ext/misc/regexp.c curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.42.0/ext/misc/series.c" cd ~- -cd ../sqlite3vfs/tests/mptest/testdata/ +cd ../vfs/tests/mptest/testdata/ curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.42.0/mptest/mptest.c" curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.42.0/mptest/config01.test" curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.42.0/mptest/config02.test" @@ -29,6 +29,6 @@ curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.42.0/mptest/crash02.su curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.42.0/mptest/multiwrite01.test" cd ~- -cd ../sqlite3vfs/tests/speedtest1/testdata/ +cd ../vfs/tests/speedtest1/testdata/ curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.42.0/test/speedtest1.c" cd ~- \ No newline at end of file diff --git a/sqlite3reader/reader.go b/sqlite3reader/reader.go deleted file mode 100644 index 6688447..0000000 --- a/sqlite3reader/reader.go +++ /dev/null @@ -1,79 +0,0 @@ -package sqlite3reader - -import ( - "io" - - "github.com/ncruces/go-sqlite3" - "github.com/ncruces/go-sqlite3/sqlite3vfs" -) - -type vfs struct{} - -// Open implements the [sqlite3vfs.VFS] interface. -func (vfs) Open(name string, flags sqlite3vfs.OpenFlag) (sqlite3vfs.File, sqlite3vfs.OpenFlag, error) { - if flags&sqlite3vfs.OPEN_MAIN_DB == 0 { - return nil, flags, sqlite3.CANTOPEN - } - readerMtx.RLock() - defer readerMtx.RUnlock() - if ra, ok := readerDBs[name]; ok { - return readerFile{ra}, flags | sqlite3vfs.OPEN_READONLY, nil - } - return nil, flags, sqlite3.CANTOPEN -} - -// Delete implements the [sqlite3vfs.VFS] interface. -func (vfs) Delete(name string, dirSync bool) error { - return sqlite3.IOERR_DELETE -} - -// Access implements the [sqlite3vfs.VFS] interface. -func (vfs) Access(name string, flag sqlite3vfs.AccessFlag) (bool, error) { - return false, nil -} - -// FullPathname implements the [sqlite3vfs.VFS] interface. -func (vfs) FullPathname(name string) (string, error) { - return name, nil -} - -type readerFile struct{ SizeReaderAt } - -func (r readerFile) Close() error { - if c, ok := r.SizeReaderAt.(io.Closer); ok { - return c.Close() - } - return nil -} - -func (readerFile) WriteAt(b []byte, off int64) (n int, err error) { - return 0, sqlite3.READONLY -} - -func (readerFile) Truncate(size int64) error { - return sqlite3.READONLY -} - -func (readerFile) Sync(flag sqlite3vfs.SyncFlag) error { - return nil -} - -func (readerFile) Lock(lock sqlite3vfs.LockLevel) error { - return nil -} - -func (readerFile) Unlock(lock sqlite3vfs.LockLevel) error { - return nil -} - -func (readerFile) CheckReservedLock() (bool, error) { - return false, nil -} - -func (readerFile) SectorSize() int { - return 0 -} - -func (readerFile) DeviceCharacteristics() sqlite3vfs.DeviceCharacteristic { - return sqlite3vfs.IOCAP_IMMUTABLE -} diff --git a/tests/db_test.go b/tests/db_test.go index 6b4d185..551a467 100644 --- a/tests/db_test.go +++ b/tests/db_test.go @@ -6,7 +6,7 @@ import ( "github.com/ncruces/go-sqlite3" _ "github.com/ncruces/go-sqlite3/embed" - _ "github.com/ncruces/go-sqlite3/sqlite3memdb" + _ "github.com/ncruces/go-sqlite3/vfs/memdb" ) func TestDB_memory(t *testing.T) { diff --git a/tests/parallel/parallel_test.go b/tests/parallel/parallel_test.go index c9e682e..f30a433 100644 --- a/tests/parallel/parallel_test.go +++ b/tests/parallel/parallel_test.go @@ -11,7 +11,7 @@ import ( "github.com/ncruces/go-sqlite3" _ "github.com/ncruces/go-sqlite3/embed" - _ "github.com/ncruces/go-sqlite3/sqlite3memdb" + _ "github.com/ncruces/go-sqlite3/vfs/memdb" ) func TestParallel(t *testing.T) { diff --git a/tests/vfs_test.go b/tests/vfs_test.go index 478e717..9d164e8 100644 --- a/tests/vfs_test.go +++ b/tests/vfs_test.go @@ -7,12 +7,12 @@ import ( "github.com/ncruces/go-sqlite3" _ "github.com/ncruces/go-sqlite3/driver" _ "github.com/ncruces/go-sqlite3/embed" - "github.com/ncruces/go-sqlite3/sqlite3memdb" - "github.com/ncruces/go-sqlite3/sqlite3reader" + "github.com/ncruces/go-sqlite3/vfs/memdb" + "github.com/ncruces/go-sqlite3/vfs/readervfs" ) func TestMemoryVFS_Open_notfound(t *testing.T) { - sqlite3memdb.Delete("demo.db") + memdb.Delete("demo.db") _, err := sqlite3.Open("file:/demo.db?vfs=memdb&mode=ro") if err == nil { @@ -24,7 +24,7 @@ func TestMemoryVFS_Open_notfound(t *testing.T) { } func TestReaderVFS_Open_notfound(t *testing.T) { - sqlite3reader.Delete("demo.db") + readervfs.Delete("demo.db") _, err := sqlite3.Open("file:demo.db?vfs=reader") if err == nil { diff --git a/sqlite3vfs/README.md b/vfs/README.md similarity index 100% rename from sqlite3vfs/README.md rename to vfs/README.md diff --git a/sqlite3vfs/api.go b/vfs/api.go similarity index 97% rename from sqlite3vfs/api.go rename to vfs/api.go index 265384c..aa66c06 100644 --- a/sqlite3vfs/api.go +++ b/vfs/api.go @@ -1,5 +1,5 @@ -// Package sqlite3vfs wraps the C SQLite VFS API. -package sqlite3vfs +// Package vfs wraps the C SQLite VFS API. +package vfs import "net/url" diff --git a/sqlite3vfs/const.go b/vfs/const.go similarity index 99% rename from sqlite3vfs/const.go rename to vfs/const.go index 756be70..bc19e56 100644 --- a/sqlite3vfs/const.go +++ b/vfs/const.go @@ -1,4 +1,4 @@ -package sqlite3vfs +package vfs import "github.com/ncruces/go-sqlite3/internal/util" diff --git a/sqlite3vfs/file.go b/vfs/file.go similarity index 99% rename from sqlite3vfs/file.go rename to vfs/file.go index 1d1694e..3108296 100644 --- a/sqlite3vfs/file.go +++ b/vfs/file.go @@ -1,4 +1,4 @@ -package sqlite3vfs +package vfs import ( "errors" diff --git a/sqlite3vfs/lock.go b/vfs/lock.go similarity index 99% rename from sqlite3vfs/lock.go rename to vfs/lock.go index e05580a..e83530e 100644 --- a/sqlite3vfs/lock.go +++ b/vfs/lock.go @@ -1,4 +1,4 @@ -package sqlite3vfs +package vfs import ( "os" diff --git a/sqlite3vfs/lock_test.go b/vfs/lock_test.go similarity index 99% rename from sqlite3vfs/lock_test.go rename to vfs/lock_test.go index 9aa4ebb..00bf000 100644 --- a/sqlite3vfs/lock_test.go +++ b/vfs/lock_test.go @@ -1,4 +1,4 @@ -package sqlite3vfs +package vfs import ( "context" diff --git a/sqlite3memdb/README.md b/vfs/memdb/README.md similarity index 100% rename from sqlite3memdb/README.md rename to vfs/memdb/README.md diff --git a/sqlite3memdb/api.go b/vfs/memdb/api.go similarity index 70% rename from sqlite3memdb/api.go rename to vfs/memdb/api.go index c41978b..d2e4034 100644 --- a/sqlite3memdb/api.go +++ b/vfs/memdb/api.go @@ -1,27 +1,27 @@ -// Package sqlite3memdb implements the "memdb" SQLite VFS. +// Package memdb implements the "memdb" SQLite VFS. // -// The "memdb" [sqlite3vfs.VFS] allows the same in-memory database to be shared +// The "memdb" [vfs.VFS] allows the same in-memory database to be shared // among multiple database connections in the same process, // as long as the database name begins with "/". // -// Importing package sqlite3memdb registers the VFS. +// Importing package memdb registers the VFS. // -// import _ "github.com/ncruces/go-sqlite3/sqlite3memdb" -package sqlite3memdb +// import _ "github.com/ncruces/go-sqlite3/vfs/memdb" +package memdb import ( "sync" - "github.com/ncruces/go-sqlite3/sqlite3vfs" + "github.com/ncruces/go-sqlite3/vfs" ) func init() { - sqlite3vfs.Register("memdb", vfs{}) + vfs.Register("memdb", memVFS{}) } var ( memoryMtx sync.Mutex - memoryDBs = map[string]*dbase{} + memoryDBs = map[string]*memDB{} ) // Create creates a shared memory database, @@ -32,7 +32,7 @@ func Create(name string, data []byte) { memoryMtx.Lock() defer memoryMtx.Unlock() - db := new(dbase) + db := new(memDB) db.size = int64(len(data)) sectors := divRoundUp(db.size, sectorSize) diff --git a/sqlite3memdb/example_test.go b/vfs/memdb/example_test.go similarity index 86% rename from sqlite3memdb/example_test.go rename to vfs/memdb/example_test.go index 52ec70e..ca15fbb 100644 --- a/sqlite3memdb/example_test.go +++ b/vfs/memdb/example_test.go @@ -1,4 +1,4 @@ -package sqlite3memdb_test +package memdb_test import ( "database/sql" @@ -9,14 +9,14 @@ import ( _ "github.com/ncruces/go-sqlite3/driver" _ "github.com/ncruces/go-sqlite3/embed" - "github.com/ncruces/go-sqlite3/sqlite3memdb" + "github.com/ncruces/go-sqlite3/vfs/memdb" ) //go:embed testdata/test.db var testDB []byte func Example() { - sqlite3memdb.Create("test.db", testDB) + memdb.Create("test.db", testDB) db, err := sql.Open("sqlite3", "file:/test.db?vfs=memdb") if err != nil { diff --git a/sqlite3memdb/memdb.go b/vfs/memdb/memdb.go similarity index 63% rename from sqlite3memdb/memdb.go rename to vfs/memdb/memdb.go index 8829219..ade8229 100644 --- a/sqlite3memdb/memdb.go +++ b/vfs/memdb/memdb.go @@ -1,4 +1,4 @@ -package sqlite3memdb +package memdb import ( "io" @@ -8,24 +8,24 @@ import ( "time" "github.com/ncruces/go-sqlite3" - "github.com/ncruces/go-sqlite3/sqlite3vfs" + "github.com/ncruces/go-sqlite3/vfs" ) -type vfs struct{} +type memVFS struct{} -func (vfs) Open(name string, flags sqlite3vfs.OpenFlag) (sqlite3vfs.File, sqlite3vfs.OpenFlag, error) { +func (memVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) { // Allowed file types: // - databases, which only do page aligned reads/writes; // - temp journals, used by the sorter, which does the same. - const types = sqlite3vfs.OPEN_MAIN_DB | - sqlite3vfs.OPEN_TRANSIENT_DB | - sqlite3vfs.OPEN_TEMP_DB | - sqlite3vfs.OPEN_TEMP_JOURNAL + const types = vfs.OPEN_MAIN_DB | + vfs.OPEN_TRANSIENT_DB | + vfs.OPEN_TEMP_DB | + vfs.OPEN_TEMP_JOURNAL if flags&types == 0 { return nil, flags, sqlite3.CANTOPEN } - var db *dbase + var db *memDB shared := strings.HasPrefix(name, "/") if shared { @@ -34,41 +34,41 @@ func (vfs) Open(name string, flags sqlite3vfs.OpenFlag) (sqlite3vfs.File, sqlite db = memoryDBs[name[1:]] } if db == nil { - if flags&sqlite3vfs.OPEN_CREATE == 0 { + if flags&vfs.OPEN_CREATE == 0 { return nil, flags, sqlite3.CANTOPEN } - db = new(dbase) + db = new(memDB) } if shared { memoryDBs[name[1:]] = db } - return &file{ - dbase: db, - readOnly: flags&sqlite3vfs.OPEN_READONLY != 0, - }, flags | sqlite3vfs.OPEN_MEMORY, nil + return &memFile{ + memDB: db, + readOnly: flags&vfs.OPEN_READONLY != 0, + }, flags | vfs.OPEN_MEMORY, nil } -func (vfs) Delete(name string, dirSync bool) error { +func (memVFS) Delete(name string, dirSync bool) error { return sqlite3.IOERR_DELETE } -func (vfs) Access(name string, flag sqlite3vfs.AccessFlag) (bool, error) { +func (memVFS) Access(name string, flag vfs.AccessFlag) (bool, error) { return false, nil } -func (vfs) FullPathname(name string) (string, error) { +func (memVFS) FullPathname(name string) (string, error) { return name, nil } // Must be a multiple of 64K (the largest page size). const sectorSize = 65536 -type dbase struct { +type memDB struct { // +checklocks:lockMtx - pending *file + pending *memFile // +checklocks:lockMtx - reserved *file + reserved *memFile // +checklocks:dataMtx data []*[sectorSize]byte @@ -83,23 +83,23 @@ type dbase struct { dataMtx sync.RWMutex } -type file struct { - *dbase - lock sqlite3vfs.LockLevel +type memFile struct { + *memDB + lock vfs.LockLevel readOnly bool } var ( // Ensure these interfaces are implemented: - _ sqlite3vfs.FileLockState = &file{} - _ sqlite3vfs.FileSizeHint = &file{} + _ vfs.FileLockState = &memFile{} + _ vfs.FileSizeHint = &memFile{} ) -func (m *file) Close() error { - return m.Unlock(sqlite3vfs.LOCK_NONE) +func (m *memFile) Close() error { + return m.Unlock(vfs.LOCK_NONE) } -func (m *file) ReadAt(b []byte, off int64) (n int, err error) { +func (m *memFile) ReadAt(b []byte, off int64) (n int, err error) { m.dataMtx.RLock() defer m.dataMtx.RUnlock() @@ -121,7 +121,7 @@ func (m *file) ReadAt(b []byte, off int64) (n int, err error) { return n, nil } -func (m *file) WriteAt(b []byte, off int64) (n int, err error) { +func (m *memFile) WriteAt(b []byte, off int64) (n int, err error) { m.dataMtx.Lock() defer m.dataMtx.Unlock() @@ -141,14 +141,14 @@ func (m *file) WriteAt(b []byte, off int64) (n int, err error) { return n, nil } -func (m *file) Truncate(size int64) error { +func (m *memFile) Truncate(size int64) error { m.dataMtx.Lock() defer m.dataMtx.Unlock() return m.truncate(size) } // +checklocks:m.dataMtx -func (m *file) truncate(size int64) error { +func (m *memFile) truncate(size int64) error { if size < m.size { base := size / sectorSize rest := size % sectorSize @@ -166,22 +166,22 @@ func (m *file) truncate(size int64) error { return nil } -func (*file) Sync(flag sqlite3vfs.SyncFlag) error { +func (*memFile) Sync(flag vfs.SyncFlag) error { return nil } -func (m *file) Size() (int64, error) { +func (m *memFile) Size() (int64, error) { m.dataMtx.RLock() defer m.dataMtx.RUnlock() return m.size, nil } -func (m *file) Lock(lock sqlite3vfs.LockLevel) error { +func (m *memFile) Lock(lock vfs.LockLevel) error { if m.lock >= lock { return nil } - if m.readOnly && lock >= sqlite3vfs.LOCK_RESERVED { + if m.readOnly && lock >= vfs.LOCK_RESERVED { return sqlite3.IOERR_LOCK } @@ -190,7 +190,7 @@ func (m *file) Lock(lock sqlite3vfs.LockLevel) error { deadline := time.Now().Add(time.Millisecond) switch lock { - case sqlite3vfs.LOCK_SHARED: + case vfs.LOCK_SHARED: for m.pending != nil { if time.Now().After(deadline) { return sqlite3.BUSY @@ -201,18 +201,18 @@ func (m *file) Lock(lock sqlite3vfs.LockLevel) error { } m.shared++ - case sqlite3vfs.LOCK_RESERVED: + case vfs.LOCK_RESERVED: if m.reserved != nil { return sqlite3.BUSY } m.reserved = m - case sqlite3vfs.LOCK_EXCLUSIVE: - if m.lock < sqlite3vfs.LOCK_PENDING { + case vfs.LOCK_EXCLUSIVE: + if m.lock < vfs.LOCK_PENDING { if m.pending != nil { return sqlite3.BUSY } - m.lock = sqlite3vfs.LOCK_PENDING + m.lock = vfs.LOCK_PENDING m.pending = m } @@ -230,7 +230,7 @@ func (m *file) Lock(lock sqlite3vfs.LockLevel) error { return nil } -func (m *file) Unlock(lock sqlite3vfs.LockLevel) error { +func (m *memFile) Unlock(lock vfs.LockLevel) error { if m.lock <= lock { return nil } @@ -244,15 +244,15 @@ func (m *file) Unlock(lock sqlite3vfs.LockLevel) error { if m.reserved == m { m.reserved = nil } - if lock < sqlite3vfs.LOCK_SHARED { + if lock < vfs.LOCK_SHARED { m.shared-- } m.lock = lock return nil } -func (m *file) CheckReservedLock() (bool, error) { - if m.lock >= sqlite3vfs.LOCK_RESERVED { +func (m *memFile) CheckReservedLock() (bool, error) { + if m.lock >= vfs.LOCK_RESERVED { return true, nil } m.lockMtx.Lock() @@ -260,18 +260,18 @@ func (m *file) CheckReservedLock() (bool, error) { return m.reserved != nil, nil } -func (*file) SectorSize() int { +func (*memFile) SectorSize() int { return sectorSize } -func (*file) DeviceCharacteristics() sqlite3vfs.DeviceCharacteristic { - return sqlite3vfs.IOCAP_ATOMIC | - sqlite3vfs.IOCAP_SEQUENTIAL | - sqlite3vfs.IOCAP_SAFE_APPEND | - sqlite3vfs.IOCAP_POWERSAFE_OVERWRITE +func (*memFile) DeviceCharacteristics() vfs.DeviceCharacteristic { + return vfs.IOCAP_ATOMIC | + vfs.IOCAP_SEQUENTIAL | + vfs.IOCAP_SAFE_APPEND | + vfs.IOCAP_POWERSAFE_OVERWRITE } -func (m *file) SizeHint(size int64) error { +func (m *memFile) SizeHint(size int64) error { m.dataMtx.Lock() defer m.dataMtx.Unlock() if size > m.size { @@ -280,7 +280,7 @@ func (m *file) SizeHint(size int64) error { return nil } -func (m *file) LockState() sqlite3vfs.LockLevel { +func (m *memFile) LockState() vfs.LockLevel { return m.lock } diff --git a/sqlite3memdb/testdata/test.db b/vfs/memdb/testdata/test.db similarity index 100% rename from sqlite3memdb/testdata/test.db rename to vfs/memdb/testdata/test.db diff --git a/sqlite3vfs/os_bsd.go b/vfs/os_bsd.go similarity index 98% rename from sqlite3vfs/os_bsd.go rename to vfs/os_bsd.go index 88372c5..2f04d3b 100644 --- a/sqlite3vfs/os_bsd.go +++ b/vfs/os_bsd.go @@ -1,6 +1,6 @@ //go:build freebsd || openbsd || netbsd || dragonfly || (darwin && sqlite3_bsd) -package sqlite3vfs +package vfs import ( "os" diff --git a/sqlite3vfs/os_darwin.go b/vfs/os_darwin.go similarity index 99% rename from sqlite3vfs/os_darwin.go rename to vfs/os_darwin.go index 4f908dd..9043971 100644 --- a/sqlite3vfs/os_darwin.go +++ b/vfs/os_darwin.go @@ -1,6 +1,6 @@ //go:build !sqlite3_bsd -package sqlite3vfs +package vfs import ( "io" diff --git a/sqlite3vfs/os_linux.go b/vfs/os_linux.go similarity index 95% rename from sqlite3vfs/os_linux.go rename to vfs/os_linux.go index d8c3307..2161380 100644 --- a/sqlite3vfs/os_linux.go +++ b/vfs/os_linux.go @@ -1,4 +1,4 @@ -package sqlite3vfs +package vfs import ( "os" diff --git a/sqlite3vfs/os_ofd.go b/vfs/os_ofd.go similarity index 98% rename from sqlite3vfs/os_ofd.go rename to vfs/os_ofd.go index d8a3194..27179d4 100644 --- a/sqlite3vfs/os_ofd.go +++ b/vfs/os_ofd.go @@ -1,6 +1,6 @@ //go:build linux || illumos -package sqlite3vfs +package vfs import ( "os" diff --git a/sqlite3vfs/os_other.go b/vfs/os_other.go similarity index 94% rename from sqlite3vfs/os_other.go rename to vfs/os_other.go index 2b7d728..23fe3b4 100644 --- a/sqlite3vfs/os_other.go +++ b/vfs/os_other.go @@ -1,6 +1,6 @@ //go:build !linux && (!darwin || sqlite3_bsd) -package sqlite3vfs +package vfs import ( "io" diff --git a/sqlite3vfs/os_unix.go b/vfs/os_unix.go similarity index 99% rename from sqlite3vfs/os_unix.go rename to vfs/os_unix.go index 3c2194f..04ab39f 100644 --- a/sqlite3vfs/os_unix.go +++ b/vfs/os_unix.go @@ -1,6 +1,6 @@ //go:build unix -package sqlite3vfs +package vfs import ( "io/fs" diff --git a/sqlite3vfs/os_windows.go b/vfs/os_windows.go similarity index 99% rename from sqlite3vfs/os_windows.go rename to vfs/os_windows.go index 424a892..b004d4e 100644 --- a/sqlite3vfs/os_windows.go +++ b/vfs/os_windows.go @@ -1,4 +1,4 @@ -package sqlite3vfs +package vfs import ( "io/fs" diff --git a/sqlite3reader/README.md b/vfs/readervfs/README.md similarity index 100% rename from sqlite3reader/README.md rename to vfs/readervfs/README.md diff --git a/sqlite3reader/api.go b/vfs/readervfs/api.go similarity index 82% rename from sqlite3reader/api.go rename to vfs/readervfs/api.go index 5e9f8a2..ddd96e9 100644 --- a/sqlite3reader/api.go +++ b/vfs/readervfs/api.go @@ -1,12 +1,12 @@ -// Package sqlite3reader implements an SQLite VFS for immutable databases. +// Package readervfs implements an SQLite VFS for immutable databases. // -// The "reader" [sqlite3vfs.VFS] permits accessing any [io.ReaderAt] +// The "reader" [vfs.VFS] permits accessing any [io.ReaderAt] // as an immutable SQLite database. // -// Importing package sqlite3reader registers the VFS. +// Importing package readervfs registers the VFS. // -// import _ "github.com/ncruces/go-sqlite3/sqlite3reader" -package sqlite3reader +// import _ "github.com/ncruces/go-sqlite3/vfs/readervfs" +package readervfs import ( "io" @@ -14,11 +14,11 @@ import ( "sync" "github.com/ncruces/go-sqlite3" - "github.com/ncruces/go-sqlite3/sqlite3vfs" + "github.com/ncruces/go-sqlite3/vfs" ) func init() { - sqlite3vfs.Register("reader", vfs{}) + vfs.Register("reader", readerVFS{}) } var ( diff --git a/sqlite3reader/example_test.go b/vfs/readervfs/example_test.go similarity index 83% rename from sqlite3reader/example_test.go rename to vfs/readervfs/example_test.go index 93fc244..a6c6fb2 100644 --- a/sqlite3reader/example_test.go +++ b/vfs/readervfs/example_test.go @@ -1,4 +1,4 @@ -package sqlite3reader_test +package readervfs_test import ( "bytes" @@ -10,7 +10,7 @@ import ( _ "github.com/ncruces/go-sqlite3/driver" _ "github.com/ncruces/go-sqlite3/embed" - "github.com/ncruces/go-sqlite3/sqlite3reader" + "github.com/ncruces/go-sqlite3/vfs/readervfs" "github.com/psanford/httpreadat" ) @@ -18,8 +18,8 @@ import ( var testDB []byte func Example_http() { - sqlite3reader.Create("demo.db", httpreadat.New("https://www.sanford.io/demo.db")) - defer sqlite3reader.Delete("demo.db") + readervfs.Create("demo.db", httpreadat.New("https://www.sanford.io/demo.db")) + defer readervfs.Delete("demo.db") db, err := sql.Open("sqlite3", "file:demo.db?vfs=reader") if err != nil { @@ -65,8 +65,8 @@ func Example_http() { } func Example_embed() { - sqlite3reader.Create("test.db", sqlite3reader.NewSizeReaderAt(bytes.NewReader(testDB))) - defer sqlite3reader.Delete("test.db") + readervfs.Create("test.db", readervfs.NewSizeReaderAt(bytes.NewReader(testDB))) + defer readervfs.Delete("test.db") db, err := sql.Open("sqlite3", "file:test.db?vfs=reader") if err != nil { diff --git a/vfs/readervfs/reader.go b/vfs/readervfs/reader.go new file mode 100644 index 0000000..0ff3f2f --- /dev/null +++ b/vfs/readervfs/reader.go @@ -0,0 +1,79 @@ +package readervfs + +import ( + "io" + + "github.com/ncruces/go-sqlite3" + "github.com/ncruces/go-sqlite3/vfs" +) + +type readerVFS struct{} + +// Open implements the [vfs.VFS] interface. +func (readerVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) { + if flags&vfs.OPEN_MAIN_DB == 0 { + return nil, flags, sqlite3.CANTOPEN + } + readerMtx.RLock() + defer readerMtx.RUnlock() + if ra, ok := readerDBs[name]; ok { + return readerFile{ra}, flags | vfs.OPEN_READONLY, nil + } + return nil, flags, sqlite3.CANTOPEN +} + +// Delete implements the [vfs.VFS] interface. +func (readerVFS) Delete(name string, dirSync bool) error { + return sqlite3.IOERR_DELETE +} + +// Access implements the [vfs.VFS] interface. +func (readerVFS) Access(name string, flag vfs.AccessFlag) (bool, error) { + return false, nil +} + +// FullPathname implements the [vfs.VFS] interface. +func (readerVFS) FullPathname(name string) (string, error) { + return name, nil +} + +type readerFile struct{ SizeReaderAt } + +func (r readerFile) Close() error { + if c, ok := r.SizeReaderAt.(io.Closer); ok { + return c.Close() + } + return nil +} + +func (readerFile) WriteAt(b []byte, off int64) (n int, err error) { + return 0, sqlite3.READONLY +} + +func (readerFile) Truncate(size int64) error { + return sqlite3.READONLY +} + +func (readerFile) Sync(flag vfs.SyncFlag) error { + return nil +} + +func (readerFile) Lock(lock vfs.LockLevel) error { + return nil +} + +func (readerFile) Unlock(lock vfs.LockLevel) error { + return nil +} + +func (readerFile) CheckReservedLock() (bool, error) { + return false, nil +} + +func (readerFile) SectorSize() int { + return 0 +} + +func (readerFile) DeviceCharacteristics() vfs.DeviceCharacteristic { + return vfs.IOCAP_IMMUTABLE +} diff --git a/sqlite3reader/reader_test.go b/vfs/readervfs/reader_test.go similarity index 98% rename from sqlite3reader/reader_test.go rename to vfs/readervfs/reader_test.go index 2ce815e..4508a35 100644 --- a/sqlite3reader/reader_test.go +++ b/vfs/readervfs/reader_test.go @@ -1,4 +1,4 @@ -package sqlite3reader +package readervfs import ( "io" diff --git a/sqlite3reader/testdata/test.db b/vfs/readervfs/testdata/test.db similarity index 100% rename from sqlite3reader/testdata/test.db rename to vfs/readervfs/testdata/test.db diff --git a/sqlite3vfs/registry.go b/vfs/registry.go similarity index 97% rename from sqlite3vfs/registry.go rename to vfs/registry.go index ee1b7c6..a8a37a5 100644 --- a/sqlite3vfs/registry.go +++ b/vfs/registry.go @@ -1,4 +1,4 @@ -package sqlite3vfs +package vfs import "sync" diff --git a/sqlite3vfs/registry_test.go b/vfs/registry_test.go similarity index 64% rename from sqlite3vfs/registry_test.go rename to vfs/registry_test.go index 97bc8f8..3a17ea5 100644 --- a/sqlite3vfs/registry_test.go +++ b/vfs/registry_test.go @@ -1,18 +1,18 @@ -package sqlite3vfs_test +package vfs_test import ( "testing" "github.com/ncruces/go-sqlite3" _ "github.com/ncruces/go-sqlite3/embed" - "github.com/ncruces/go-sqlite3/sqlite3vfs" + "github.com/ncruces/go-sqlite3/vfs" ) type testVFS struct { *testing.T } -func (t testVFS) Open(name string, flags sqlite3vfs.OpenFlag) (sqlite3vfs.File, sqlite3vfs.OpenFlag, error) { +func (t testVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) { t.Log("Open", name, flags) t.SkipNow() return nil, flags, nil @@ -23,7 +23,7 @@ func (t testVFS) Delete(name string, syncDir bool) error { return nil } -func (t testVFS) Access(name string, flags sqlite3vfs.AccessFlag) (bool, error) { +func (t testVFS) Access(name string, flags vfs.AccessFlag) (bool, error) { t.Log("Access", name, flags) return true, nil } @@ -34,9 +34,8 @@ func (t testVFS) FullPathname(name string) (string, error) { } func TestRegister(t *testing.T) { - vfs := testVFS{t} - sqlite3vfs.Register("foo", vfs) - defer sqlite3vfs.Unregister("foo") + vfs.Register("foo", testVFS{t}) + defer vfs.Unregister("foo") conn, err := sqlite3.Open("file:file.db?vfs=foo") if err != nil { diff --git a/sqlite3vfs/tests/mptest/mptest_test.go b/vfs/tests/mptest/mptest_test.go similarity index 89% rename from sqlite3vfs/tests/mptest/mptest_test.go rename to vfs/tests/mptest/mptest_test.go index 73ed79f..e9670e2 100644 --- a/sqlite3vfs/tests/mptest/mptest_test.go +++ b/vfs/tests/mptest/mptest_test.go @@ -16,8 +16,8 @@ import ( "sync/atomic" "testing" - _ "github.com/ncruces/go-sqlite3/sqlite3memdb" - "github.com/ncruces/go-sqlite3/sqlite3vfs" + "github.com/ncruces/go-sqlite3/vfs" + _ "github.com/ncruces/go-sqlite3/vfs/memdb" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" @@ -41,7 +41,7 @@ func init() { rt = wazero.NewRuntime(ctx) wasi_snapshot_preview1.MustInstantiate(ctx, rt) - env := sqlite3vfs.ExportHostFunctions(rt.NewHostModuleBuilder("env")) + env := vfs.ExportHostFunctions(rt.NewHostModuleBuilder("env")) env.NewFunctionBuilder().WithFunc(system).Export("system") _, err := env.Instantiate(ctx) if err != nil { @@ -81,7 +81,7 @@ func system(ctx context.Context, mod api.Module, ptr uint32) uint32 { cfg := config(ctx).WithArgs(args...) go func() { - ctx, vfs := sqlite3vfs.NewContext(ctx) + ctx, vfs := vfs.NewContext(ctx) mod, _ := rt.InstantiateModule(ctx, module, cfg) mod.Close(ctx) vfs.Close() @@ -90,7 +90,7 @@ func system(ctx context.Context, mod api.Module, ptr uint32) uint32 { } func Test_config01(t *testing.T) { - ctx, vfs := sqlite3vfs.NewContext(newContext(t)) + ctx, vfs := vfs.NewContext(newContext(t)) name := filepath.Join(t.TempDir(), "test.db") cfg := config(ctx).WithArgs("mptest", name, "config01.test") mod, err := rt.InstantiateModule(ctx, module, cfg) @@ -109,7 +109,7 @@ func Test_config02(t *testing.T) { t.Skip("skipping in CI") } - ctx, vfs := sqlite3vfs.NewContext(newContext(t)) + ctx, vfs := vfs.NewContext(newContext(t)) name := filepath.Join(t.TempDir(), "test.db") cfg := config(ctx).WithArgs("mptest", name, "config02.test") mod, err := rt.InstantiateModule(ctx, module, cfg) @@ -125,7 +125,7 @@ func Test_crash01(t *testing.T) { t.Skip("skipping in short mode") } - ctx, vfs := sqlite3vfs.NewContext(newContext(t)) + ctx, vfs := vfs.NewContext(newContext(t)) name := filepath.Join(t.TempDir(), "test.db") cfg := config(ctx).WithArgs("mptest", name, "crash01.test") mod, err := rt.InstantiateModule(ctx, module, cfg) @@ -141,7 +141,7 @@ func Test_multiwrite01(t *testing.T) { t.Skip("skipping in short mode") } - ctx, vfs := sqlite3vfs.NewContext(newContext(t)) + ctx, vfs := vfs.NewContext(newContext(t)) name := filepath.Join(t.TempDir(), "test.db") cfg := config(ctx).WithArgs("mptest", name, "multiwrite01.test") mod, err := rt.InstantiateModule(ctx, module, cfg) @@ -153,7 +153,7 @@ func Test_multiwrite01(t *testing.T) { } func Test_config01_memory(t *testing.T) { - ctx, vfs := sqlite3vfs.NewContext(newContext(t)) + ctx, vfs := vfs.NewContext(newContext(t)) cfg := config(ctx).WithArgs("mptest", "test.db", "config01.test", "--vfs", "memdb", @@ -171,7 +171,7 @@ func Test_multiwrite01_memory(t *testing.T) { t.Skip("skipping in short mode") } - ctx, vfs := sqlite3vfs.NewContext(newContext(t)) + ctx, vfs := vfs.NewContext(newContext(t)) cfg := config(ctx).WithArgs("mptest", "/test.db", "multiwrite01.test", "--vfs", "memdb", diff --git a/sqlite3vfs/tests/mptest/testdata/.gitattributes b/vfs/tests/mptest/testdata/.gitattributes similarity index 100% rename from sqlite3vfs/tests/mptest/testdata/.gitattributes rename to vfs/tests/mptest/testdata/.gitattributes diff --git a/sqlite3vfs/tests/mptest/testdata/.gitignore b/vfs/tests/mptest/testdata/.gitignore similarity index 100% rename from sqlite3vfs/tests/mptest/testdata/.gitignore rename to vfs/tests/mptest/testdata/.gitignore diff --git a/sqlite3vfs/tests/mptest/testdata/build.sh b/vfs/tests/mptest/testdata/build.sh similarity index 100% rename from sqlite3vfs/tests/mptest/testdata/build.sh rename to vfs/tests/mptest/testdata/build.sh diff --git a/sqlite3vfs/tests/mptest/testdata/config01.test b/vfs/tests/mptest/testdata/config01.test similarity index 100% rename from sqlite3vfs/tests/mptest/testdata/config01.test rename to vfs/tests/mptest/testdata/config01.test diff --git a/sqlite3vfs/tests/mptest/testdata/config02.test b/vfs/tests/mptest/testdata/config02.test similarity index 100% rename from sqlite3vfs/tests/mptest/testdata/config02.test rename to vfs/tests/mptest/testdata/config02.test diff --git a/sqlite3vfs/tests/mptest/testdata/crash01.test b/vfs/tests/mptest/testdata/crash01.test similarity index 100% rename from sqlite3vfs/tests/mptest/testdata/crash01.test rename to vfs/tests/mptest/testdata/crash01.test diff --git a/sqlite3vfs/tests/mptest/testdata/crash02.subtest b/vfs/tests/mptest/testdata/crash02.subtest similarity index 100% rename from sqlite3vfs/tests/mptest/testdata/crash02.subtest rename to vfs/tests/mptest/testdata/crash02.subtest diff --git a/sqlite3vfs/tests/mptest/testdata/main.c b/vfs/tests/mptest/testdata/main.c similarity index 100% rename from sqlite3vfs/tests/mptest/testdata/main.c rename to vfs/tests/mptest/testdata/main.c diff --git a/sqlite3vfs/tests/mptest/testdata/mptest.wasm b/vfs/tests/mptest/testdata/mptest.wasm similarity index 100% rename from sqlite3vfs/tests/mptest/testdata/mptest.wasm rename to vfs/tests/mptest/testdata/mptest.wasm diff --git a/sqlite3vfs/tests/mptest/testdata/multiwrite01.test b/vfs/tests/mptest/testdata/multiwrite01.test similarity index 100% rename from sqlite3vfs/tests/mptest/testdata/multiwrite01.test rename to vfs/tests/mptest/testdata/multiwrite01.test diff --git a/sqlite3vfs/tests/speedtest1/speedtest1_test.go b/vfs/tests/speedtest1/speedtest1_test.go similarity index 93% rename from sqlite3vfs/tests/speedtest1/speedtest1_test.go rename to vfs/tests/speedtest1/speedtest1_test.go index c85ef0f..1166b12 100644 --- a/sqlite3vfs/tests/speedtest1/speedtest1_test.go +++ b/vfs/tests/speedtest1/speedtest1_test.go @@ -21,8 +21,8 @@ import ( "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" - _ "github.com/ncruces/go-sqlite3/sqlite3memdb" - "github.com/ncruces/go-sqlite3/sqlite3vfs" + "github.com/ncruces/go-sqlite3/vfs" + _ "github.com/ncruces/go-sqlite3/vfs/memdb" ) //go:embed testdata/speedtest1.wasm @@ -45,7 +45,7 @@ func TestMain(m *testing.M) { rt = wazero.NewRuntime(ctx) wasi_snapshot_preview1.MustInstantiate(ctx, rt) - env := sqlite3vfs.ExportHostFunctions(rt.NewHostModuleBuilder("env")) + env := vfs.ExportHostFunctions(rt.NewHostModuleBuilder("env")) _, err := env.Instantiate(ctx) if err != nil { panic(err) @@ -151,7 +151,7 @@ func saveProfiles(module wazero.CompiledModule, prof *wzprof.Profiling, cpu *wzp func Benchmark_speedtest1(b *testing.B) { output.Reset() - ctx, vfs := sqlite3vfs.NewContext(context.Background()) + ctx, vfs := vfs.NewContext(context.Background()) name := filepath.Join(b.TempDir(), "test.db") args := append(options, "--size", strconv.Itoa(b.N), name) cfg := wazero.NewModuleConfig(). diff --git a/sqlite3vfs/tests/speedtest1/testdata/.gitattributes b/vfs/tests/speedtest1/testdata/.gitattributes similarity index 100% rename from sqlite3vfs/tests/speedtest1/testdata/.gitattributes rename to vfs/tests/speedtest1/testdata/.gitattributes diff --git a/sqlite3vfs/tests/speedtest1/testdata/.gitignore b/vfs/tests/speedtest1/testdata/.gitignore similarity index 100% rename from sqlite3vfs/tests/speedtest1/testdata/.gitignore rename to vfs/tests/speedtest1/testdata/.gitignore diff --git a/sqlite3vfs/tests/speedtest1/testdata/build.sh b/vfs/tests/speedtest1/testdata/build.sh similarity index 100% rename from sqlite3vfs/tests/speedtest1/testdata/build.sh rename to vfs/tests/speedtest1/testdata/build.sh diff --git a/sqlite3vfs/tests/speedtest1/testdata/main.c b/vfs/tests/speedtest1/testdata/main.c similarity index 100% rename from sqlite3vfs/tests/speedtest1/testdata/main.c rename to vfs/tests/speedtest1/testdata/main.c diff --git a/sqlite3vfs/tests/speedtest1/testdata/speedtest1.wasm b/vfs/tests/speedtest1/testdata/speedtest1.wasm similarity index 100% rename from sqlite3vfs/tests/speedtest1/testdata/speedtest1.wasm rename to vfs/tests/speedtest1/testdata/speedtest1.wasm diff --git a/sqlite3vfs/vfs.go b/vfs/vfs.go similarity index 99% rename from sqlite3vfs/vfs.go rename to vfs/vfs.go index c17c95b..cd432e6 100644 --- a/sqlite3vfs/vfs.go +++ b/vfs/vfs.go @@ -1,4 +1,4 @@ -package sqlite3vfs +package vfs import ( "context" diff --git a/sqlite3vfs/vfs_test.go b/vfs/vfs_test.go similarity index 99% rename from sqlite3vfs/vfs_test.go rename to vfs/vfs_test.go index 22a60aa..8562afe 100644 --- a/sqlite3vfs/vfs_test.go +++ b/vfs/vfs_test.go @@ -1,4 +1,4 @@ -package sqlite3vfs +package vfs import ( "bytes"