mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
Unlock tweaks, tests.
This commit is contained in:
@@ -83,8 +83,12 @@ func (c conn) Close() error {
|
||||
|
||||
func (c conn) IsValid() bool {
|
||||
// Pool only normal locking mode connections.
|
||||
mode, _ := pragma(c.conn, "locking_mode")
|
||||
return mode == "normal"
|
||||
stmt, _, err := c.conn.Prepare(`PRAGMA locking_mode`)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer stmt.Close()
|
||||
return stmt.Step() && stmt.ColumnText(0) == "normal"
|
||||
}
|
||||
|
||||
func (c conn) ResetSession(ctx context.Context) error {
|
||||
@@ -175,18 +179,6 @@ func (c conn) ExecContext(ctx context.Context, query string, args []driver.Named
|
||||
}, nil
|
||||
}
|
||||
|
||||
func pragma(c *sqlite3.Conn, pragma string) (string, error) {
|
||||
stmt, _, err := c.Prepare(`PRAGMA ` + pragma)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer stmt.Close()
|
||||
if stmt.Step() {
|
||||
return stmt.ColumnText(0), nil
|
||||
}
|
||||
return "", stmt.Err()
|
||||
}
|
||||
|
||||
type stmt struct {
|
||||
stmt *sqlite3.Stmt
|
||||
conn *sqlite3.Conn
|
||||
|
||||
@@ -242,8 +242,9 @@ func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockS
|
||||
// Release the file lock only when all connections have released the lock.
|
||||
ptr.SetLock(_NO_LOCK)
|
||||
if fLock.shared--; fLock.shared == 0 {
|
||||
rc := fLock.Release()
|
||||
fLock.state = _NO_LOCK
|
||||
return uint32(fLock.Release())
|
||||
return uint32(rc)
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
23
vfs_test.go
23
vfs_test.go
@@ -7,6 +7,7 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -163,8 +164,19 @@ func Test_vfsDelete(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_vfsAccess(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
file := filepath.Join(t.TempDir(), "test.db")
|
||||
if f, err := os.Create(file); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
f.Close()
|
||||
}
|
||||
if err := os.Chmod(file, syscall.S_IRUSR); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mem := newMemory(128 + _MAX_PATHNAME)
|
||||
mem.writeString(8, t.TempDir())
|
||||
mem.writeString(8, dir)
|
||||
|
||||
rc := vfsAccess(context.TODO(), mem.mod, 0, 8, _ACCESS_EXISTS, 4)
|
||||
if rc != _OK {
|
||||
@@ -181,6 +193,15 @@ func Test_vfsAccess(t *testing.T) {
|
||||
if got := mem.readUint32(4); got != 1 {
|
||||
t.Error("can't access directory")
|
||||
}
|
||||
|
||||
mem.writeString(8, file)
|
||||
rc = vfsAccess(context.TODO(), mem.mod, 0, 8, _ACCESS_READWRITE, 4)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := mem.readUint32(4); got != 0 {
|
||||
t.Error("can access file")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_vfsFile(t *testing.T) {
|
||||
|
||||
19
vfs_unix.go
19
vfs_unix.go
@@ -33,16 +33,17 @@ func (l *vfsFileLocker) GetExclusive() xErrorCode {
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) Downgrade() xErrorCode {
|
||||
// Downgrade to a SHARED lock.
|
||||
if rc := l.readLock(_SHARED_FIRST, _SHARED_SIZE); rc != _OK {
|
||||
// In theory, the downgrade to a SHARED cannot fail because another
|
||||
// process is holding an incompatible lock. If it does, this
|
||||
// indicates that the other process is not following the locking
|
||||
// protocol. If this happens, return IOERR_RDLOCK. Returning
|
||||
// BUSY would confuse the upper layer.
|
||||
return IOERR_RDLOCK
|
||||
if l.state >= _EXCLUSIVE_LOCK {
|
||||
// Downgrade to a SHARED lock.
|
||||
if rc := l.readLock(_SHARED_FIRST, _SHARED_SIZE); rc != _OK {
|
||||
// In theory, the downgrade to a SHARED cannot fail because another
|
||||
// process is holding an incompatible lock. If it does, this
|
||||
// indicates that the other process is not following the locking
|
||||
// protocol. If this happens, return IOERR_RDLOCK. Returning
|
||||
// BUSY would confuse the upper layer.
|
||||
return IOERR_RDLOCK
|
||||
}
|
||||
}
|
||||
|
||||
// Release the PENDING and RESERVED locks.
|
||||
return l.unlock(_PENDING_BYTE, 2)
|
||||
}
|
||||
|
||||
@@ -39,27 +39,39 @@ func (l *vfsFileLocker) GetExclusive() xErrorCode {
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) Downgrade() xErrorCode {
|
||||
// Release the SHARED lock.
|
||||
l.unlock(_SHARED_FIRST, _SHARED_SIZE)
|
||||
if l.state >= _EXCLUSIVE_LOCK {
|
||||
// Release the SHARED lock.
|
||||
l.unlock(_SHARED_FIRST, _SHARED_SIZE)
|
||||
|
||||
// Reacquire the SHARED lock.
|
||||
if rc := l.readLock(_SHARED_FIRST, _SHARED_SIZE); rc != _OK {
|
||||
// This should never happen.
|
||||
// We should always be able to reacquire the read lock.
|
||||
return IOERR_RDLOCK
|
||||
// Reacquire the SHARED lock.
|
||||
if rc := l.readLock(_SHARED_FIRST, _SHARED_SIZE); rc != _OK {
|
||||
// This should never happen.
|
||||
// We should always be able to reacquire the read lock.
|
||||
return IOERR_RDLOCK
|
||||
}
|
||||
}
|
||||
|
||||
// Release the PENDING and RESERVED locks.
|
||||
l.unlock(_RESERVED_BYTE, 1)
|
||||
l.unlock(_PENDING_BYTE, 1)
|
||||
if l.state >= _RESERVED_LOCK {
|
||||
l.unlock(_RESERVED_BYTE, 1)
|
||||
}
|
||||
if l.state >= _PENDING_LOCK {
|
||||
l.unlock(_PENDING_BYTE, 1)
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) Release() xErrorCode {
|
||||
// Release all locks.
|
||||
l.unlock(_SHARED_FIRST, _SHARED_SIZE)
|
||||
l.unlock(_RESERVED_BYTE, 1)
|
||||
l.unlock(_PENDING_BYTE, 1)
|
||||
if l.state >= _RESERVED_LOCK {
|
||||
l.unlock(_RESERVED_BYTE, 1)
|
||||
}
|
||||
if l.state >= _SHARED_LOCK {
|
||||
l.unlock(_SHARED_FIRST, _SHARED_SIZE)
|
||||
}
|
||||
if l.state >= _PENDING_LOCK {
|
||||
l.unlock(_PENDING_BYTE, 1)
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user