Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/libc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
test:
strategy:
matrix:
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-13, macos-15]
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-15, macos-15-intel]
runs-on: ${{ matrix.os }}

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ jobs:
run: go test -v ./...

test-macintel:
runs-on: macos-13
runs-on: macos-15-intel
needs: test

steps:
Expand Down
4 changes: 4 additions & 0 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ func (e *Error) Error() string {
b.WriteString(": ")
b.WriteString(e.msg)
}
if e.sys != nil {
b.WriteString(": ")
b.WriteString(e.sys.Error())
}

return b.String()
}
Expand Down
2 changes: 1 addition & 1 deletion sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func (sqlt *sqlite) error(rc res_t, handle ptr_t, sql ...string) error {
msg = strings.TrimPrefix(msg, "sqlite3: ")
msg = strings.TrimPrefix(msg, util.ErrorCodeString(rc)[len("sqlite3: "):])
msg = strings.TrimPrefix(msg, ": ")
if msg == "not an error" {
if msg == "" || msg == "not an error" {
msg = ""
}
}
Expand Down
8 changes: 5 additions & 3 deletions vfs/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import (

// A VFS defines the interface between the SQLite core and the underlying operating system.
//
// Use sqlite3.ErrorCode or sqlite3.ExtendedErrorCode to return specific error codes to SQLite.
// Use [SystemError], sqlite3.ErrorCode, or sqlite3.ExtendedErrorCode
// to return specific error codes to SQLite.
//
// https://sqlite.org/c3ref/vfs.html
type VFS interface {
Expand All @@ -31,8 +32,9 @@ type VFSFilename interface {

// A File represents an open file in the OS interface layer.
//
// Use sqlite3.ErrorCode or sqlite3.ExtendedErrorCode to return specific error codes to SQLite.
// In particular, sqlite3.BUSY is necessary to correctly implement lock methods.
// Use [SystemError], sqlite3.ErrorCode, or sqlite3.ExtendedErrorCode
// to return specific error codes to SQLite.
// In particular, sqlite3.BUSY is needed to correctly implement lock methods.
//
// https://sqlite.org/c3ref/io_methods.html
type File interface {
Expand Down
2 changes: 1 addition & 1 deletion vfs/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (e _ErrorCode) Error() string {
}

const (
_OK _ErrorCode = util.OK
_OK = util.OK
_ERROR _ErrorCode = util.ERROR
_PERM _ErrorCode = util.PERM
_BUSY _ErrorCode = util.BUSY
Expand Down
2 changes: 1 addition & 1 deletion vfs/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (vfsOS) Delete(path string, syncDir bool) error {
if isUnix && syncDir {
f, err := os.Open(filepath.Dir(path))
if err != nil {
return _OK
return nil
}
defer f.Close()
err = osSync(f, 0, SYNC_FULL)
Expand Down
20 changes: 10 additions & 10 deletions vfs/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ func (f *vfsFile) Lock(lock LockLevel) error {
if f.lock != LOCK_NONE {
panic(util.AssertErr())
}
if rc := osGetSharedLock(f.File); rc != _OK {
return rc
if err := osGetSharedLock(f.File); err != nil {
return err
}
f.lock = LOCK_SHARED
return nil
Expand All @@ -62,8 +62,8 @@ func (f *vfsFile) Lock(lock LockLevel) error {
if f.lock != LOCK_SHARED {
panic(util.AssertErr())
}
if rc := osGetReservedLock(f.File); rc != _OK {
return rc
if err := osGetReservedLock(f.File); err != nil {
return err
}
f.lock = LOCK_RESERVED
return nil
Expand All @@ -73,8 +73,8 @@ func (f *vfsFile) Lock(lock LockLevel) error {
if f.lock <= LOCK_NONE || f.lock >= LOCK_EXCLUSIVE {
panic(util.AssertErr())
}
if rc := osGetExclusiveLock(f.File, &f.lock); rc != _OK {
return rc
if err := osGetExclusiveLock(f.File, &f.lock); err != nil {
return err
}
f.lock = LOCK_EXCLUSIVE
return nil
Expand All @@ -101,14 +101,14 @@ func (f *vfsFile) Unlock(lock LockLevel) error {

switch lock {
case LOCK_SHARED:
rc := osDowngradeLock(f.File, f.lock)
err := osDowngradeLock(f.File, f.lock)
f.lock = LOCK_SHARED
return rc
return err

case LOCK_NONE:
rc := osReleaseLock(f.File, f.lock)
err := osReleaseLock(f.File, f.lock)
f.lock = LOCK_NONE
return rc
return err

default:
panic(util.AssertErr())
Expand Down
48 changes: 24 additions & 24 deletions vfs/os_bsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import (
"golang.org/x/sys/unix"
)

func osGetSharedLock(file *os.File) _ErrorCode {
func osGetSharedLock(file *os.File) error {
return osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
}

func osGetReservedLock(file *os.File) _ErrorCode {
rc := osFlock(file, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK)
if rc == _BUSY {
func osGetReservedLock(file *os.File) error {
err := osFlock(file, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK)
if err == _BUSY {
// The documentation states that a lock is upgraded by
// releasing the previous lock, then acquiring the new lock.
// Going over the source code of various BSDs, though,
Expand All @@ -26,64 +26,64 @@ func osGetReservedLock(file *os.File) _ErrorCode {
// and invoke the busy handler if appropriate.
return _BUSY_SNAPSHOT
}
return rc
return err
}

func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
func osGetExclusiveLock(file *os.File, state *LockLevel) error {
if *state >= LOCK_RESERVED {
return _OK
return nil
}
return osGetReservedLock(file)
}

func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
rc := osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
if rc == _BUSY {
func osDowngradeLock(file *os.File, _ LockLevel) error {
err := osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
if err == _BUSY {
// The documentation states that a lock is downgraded by
// releasing the previous lock then acquiring the new lock.
// Going over the source code of various BSDs, though,
// with LOCK_SH|LOCK_NB this should never happen.
// Return IOERR_RDLOCK, as BUSY would cause an assert to fail.
return _IOERR_RDLOCK
}
return _OK
return err
}

func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode {
func osReleaseLock(file *os.File, _ LockLevel) error {
for {
err := unix.Flock(int(file.Fd()), unix.LOCK_UN)
if err == nil {
return _OK
return nil
}
if err != unix.EINTR {
return _IOERR_UNLOCK
return sysError{err, _IOERR_UNLOCK}
}
}
}

func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
func osCheckReservedLock(file *os.File) (bool, error) {
// Test the RESERVED lock with fcntl(F_GETLK).
// This only works on systems where fcntl and flock are compatible.
// However, SQLite only calls this while holding a shared lock,
// so the difference is immaterial.
lock, rc := osTestLock(file, _RESERVED_BYTE, 1)
return lock == unix.F_WRLCK, rc
lock, err := osTestLock(file, _RESERVED_BYTE, 1, _IOERR_CHECKRESERVEDLOCK)
return lock == unix.F_WRLCK, err
}

func osFlock(file *os.File, how int, def _ErrorCode) _ErrorCode {
func osFlock(file *os.File, how int, def _ErrorCode) error {
err := unix.Flock(int(file.Fd()), how)
return osLockErrorCode(err, def)
}

func osReadLock(file *os.File, start, len int64) _ErrorCode {
func osReadLock(file *os.File, start, len int64) error {
return osLock(file, unix.F_RDLCK, start, len, _IOERR_RDLOCK)
}

func osWriteLock(file *os.File, start, len int64) _ErrorCode {
func osWriteLock(file *os.File, start, len int64) error {
return osLock(file, unix.F_WRLCK, start, len, _IOERR_LOCK)
}

func osLock(file *os.File, typ int16, start, len int64, def _ErrorCode) _ErrorCode {
func osLock(file *os.File, typ int16, start, len int64, def _ErrorCode) error {
err := unix.FcntlFlock(file.Fd(), unix.F_SETLK, &unix.Flock_t{
Type: typ,
Start: start,
Expand All @@ -92,7 +92,7 @@ func osLock(file *os.File, typ int16, start, len int64, def _ErrorCode) _ErrorCo
return osLockErrorCode(err, def)
}

func osUnlock(file *os.File, start, len int64) _ErrorCode {
func osUnlock(file *os.File, start, len int64) error {
lock := unix.Flock_t{
Type: unix.F_UNLCK,
Start: start,
Expand All @@ -101,10 +101,10 @@ func osUnlock(file *os.File, start, len int64) _ErrorCode {
for {
err := unix.FcntlFlock(file.Fd(), unix.F_SETLK, &lock)
if err == nil {
return _OK
return nil
}
if err != unix.EINTR {
return _IOERR_UNLOCK
return sysError{err, _IOERR_UNLOCK}
}
}
}
12 changes: 6 additions & 6 deletions vfs/os_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ func osAllocate(file *os.File, size int64) error {
return file.Truncate(size)
}

func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
func osReadLock(file *os.File, start, len int64, timeout time.Duration) error {
return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK)
}

func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
func osWriteLock(file *os.File, start, len int64, timeout time.Duration) error {
return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK)
}

func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode {
func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) error {
lock := &flocktimeout_t{fl: unix.Flock_t{
Type: typ,
Start: start,
Expand All @@ -103,7 +103,7 @@ func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, d
return osLockErrorCode(err, def)
}

func osUnlock(file *os.File, start, len int64) _ErrorCode {
func osUnlock(file *os.File, start, len int64) error {
lock := unix.Flock_t{
Type: unix.F_UNLCK,
Start: start,
Expand All @@ -112,10 +112,10 @@ func osUnlock(file *os.File, start, len int64) _ErrorCode {
for {
err := unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &lock)
if err == nil {
return _OK
return nil
}
if err != unix.EINTR {
return _IOERR_UNLOCK
return sysError{err, _IOERR_UNLOCK}
}
}
}
31 changes: 14 additions & 17 deletions vfs/os_dotlk.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type vfsDotLocker struct {
reserved *os.File // +checklocks:vfsDotLocksMtx
}

func osGetSharedLock(file *os.File) _ErrorCode {
func osGetSharedLock(file *os.File) error {
vfsDotLocksMtx.Lock()
defer vfsDotLocksMtx.Unlock()

Expand All @@ -34,7 +34,7 @@ func osGetSharedLock(file *os.File) _ErrorCode {
if errors.Is(err, fs.ErrExist) {
return _BUSY // Another process has the lock.
}
return _IOERR_LOCK
return sysError{err, _IOERR_LOCK}
}
locker = &vfsDotLocker{}
vfsDotLocks[name] = locker
Expand All @@ -44,10 +44,10 @@ func osGetSharedLock(file *os.File) _ErrorCode {
return _BUSY
}
locker.shared++
return _OK
return nil
}

func osGetReservedLock(file *os.File) _ErrorCode {
func osGetReservedLock(file *os.File) error {
vfsDotLocksMtx.Lock()
defer vfsDotLocksMtx.Unlock()

Expand All @@ -61,10 +61,10 @@ func osGetReservedLock(file *os.File) _ErrorCode {
return _BUSY
}
locker.reserved = file
return _OK
return nil
}

func osGetExclusiveLock(file *os.File, _ *LockLevel) _ErrorCode {
func osGetExclusiveLock(file *os.File, _ *LockLevel) error {
vfsDotLocksMtx.Lock()
defer vfsDotLocksMtx.Unlock()

Expand All @@ -81,10 +81,10 @@ func osGetExclusiveLock(file *os.File, _ *LockLevel) _ErrorCode {
if locker.shared > 1 {
return _BUSY
}
return _OK
return nil
}

func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
func osDowngradeLock(file *os.File, _ LockLevel) error {
vfsDotLocksMtx.Lock()
defer vfsDotLocksMtx.Unlock()

Expand All @@ -100,10 +100,10 @@ func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
if locker.pending == file {
locker.pending = nil
}
return _OK
return nil
}

func osReleaseLock(file *os.File, state LockLevel) _ErrorCode {
func osReleaseLock(file *os.File, state LockLevel) error {
vfsDotLocksMtx.Lock()
defer vfsDotLocksMtx.Unlock()

Expand All @@ -115,7 +115,7 @@ func osReleaseLock(file *os.File, state LockLevel) _ErrorCode {

if locker.shared == 1 {
if err := dotlk.Unlock(name + ".lock"); err != nil {
return _IOERR_UNLOCK
return sysError{err, _IOERR_UNLOCK}
}
delete(vfsDotLocks, name)
}
Expand All @@ -127,17 +127,14 @@ func osReleaseLock(file *os.File, state LockLevel) _ErrorCode {
locker.pending = nil
}
locker.shared--
return _OK
return nil
}

func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
func osCheckReservedLock(file *os.File) (bool, error) {
vfsDotLocksMtx.Lock()
defer vfsDotLocksMtx.Unlock()

name := file.Name()
locker := vfsDotLocks[name]
if locker == nil {
return false, _OK
}
return locker.reserved != nil, _OK
return locker != nil && locker.reserved != nil, nil
}
Loading