mirror of
				https://github.com/superseriousbusiness/gotosocial
				synced 2025-06-05 21:59:39 +02:00 
			
		
		
		
	[experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863)
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
This commit is contained in:
		
							
								
								
									
										86
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| # Go SQLite VFS API | ||||
|  | ||||
| This package implements the SQLite [OS Interface](https://sqlite.org/vfs.html) (aka VFS). | ||||
|  | ||||
| It replaces the default SQLite VFS with a **pure Go** implementation, | ||||
| and exposes [interfaces](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs#VFS) | ||||
| that should allow you to implement your own custom VFSes. | ||||
|  | ||||
| Since it is a from scratch reimplementation, | ||||
| there are naturally some ways it deviates from the original. | ||||
|  | ||||
| The main differences are [file locking](#file-locking) and [WAL mode](#write-ahead-logging) support. | ||||
|  | ||||
| ### File Locking | ||||
|  | ||||
| POSIX advisory locks, which SQLite uses on Unix, are | ||||
| [broken by design](https://github.com/sqlite/sqlite/blob/b74eb0/src/os_unix.c#L1073-L1161). | ||||
|  | ||||
| On Linux and macOS, this module uses | ||||
| [OFD locks](https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html) | ||||
| to synchronize access to database files. | ||||
| OFD locks are fully compatible with POSIX advisory locks. | ||||
|  | ||||
| This module can also use | ||||
| [BSD locks](https://man.freebsd.org/cgi/man.cgi?query=flock&sektion=2), | ||||
| albeit with reduced concurrency (`BEGIN IMMEDIATE` behaves like `BEGIN EXCLUSIVE`). | ||||
| On BSD, macOS, and illumos, BSD locks are fully compatible with POSIX advisory locks; | ||||
| on Linux and z/OS, they are fully functional, but incompatible; | ||||
| elsewhere, they are very likely broken. | ||||
| BSD locks are the default on BSD and illumos, | ||||
| but you can opt into them with the `sqlite3_flock` build tag. | ||||
|  | ||||
| On Windows, this module uses `LockFileEx` and `UnlockFileEx`, | ||||
| like SQLite. | ||||
|  | ||||
| Otherwise, file locking is not supported, and you must use | ||||
| [`nolock=1`](https://sqlite.org/uri.html#urinolock) | ||||
| (or [`immutable=1`](https://sqlite.org/uri.html#uriimmutable)) | ||||
| to open database files. | ||||
| To use the [`database/sql`](https://pkg.go.dev/database/sql) driver | ||||
| with `nolock=1` you must disable connection pooling by calling | ||||
| [`db.SetMaxOpenConns(1)`](https://pkg.go.dev/database/sql#DB.SetMaxOpenConns). | ||||
|  | ||||
| You can use [`vfs.SupportsFileLocking`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs#SupportsFileLocking) | ||||
| to check if your build supports file locking. | ||||
|  | ||||
| ### Write-Ahead Logging | ||||
|  | ||||
| On 64-bit Linux and macOS, this module uses `mmap` to implement | ||||
| [shared-memory for the WAL-index](https://sqlite.org/wal.html#implementation_of_shared_memory_for_the_wal_index), | ||||
| like SQLite. | ||||
|  | ||||
| To allow `mmap` to work, each connection needs to reserve up to 4GB of address space. | ||||
| To limit the address space each connection reserves, | ||||
| use [`WithMemoryLimitPages`](../tests/testcfg/testcfg.go). | ||||
|  | ||||
| Otherwise, [WAL support is limited](https://sqlite.org/wal.html#noshm), | ||||
| and `EXCLUSIVE` locking mode must be set to create, read, and write WAL databases. | ||||
| To use `EXCLUSIVE` locking mode with the | ||||
| [`database/sql`](https://pkg.go.dev/database/sql) driver | ||||
| you must disable connection pooling by calling | ||||
| [`db.SetMaxOpenConns(1)`](https://pkg.go.dev/database/sql#DB.SetMaxOpenConns). | ||||
|  | ||||
| You can use [`vfs.SupportsSharedMemory`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs#SupportsSharedMemory) | ||||
| to check if your build supports shared memory. | ||||
|  | ||||
| ### Batch-Atomic Write | ||||
|  | ||||
| On 64-bit Linux, this module supports [batch-atomic writes](https://sqlite.org/cgi/src/technote/714) | ||||
| on the F2FS filesystem. | ||||
|  | ||||
| ### Build Tags | ||||
|  | ||||
| The VFS can be customized with a few build tags: | ||||
| - `sqlite3_flock` forces the use of BSD locks; it can be used on z/OS to enable locking, | ||||
|   and elsewhere to test BSD locks. | ||||
| - `sqlite3_nosys` prevents importing [`x/sys`](https://pkg.go.dev/golang.org/x/sys); | ||||
|   disables locking _and_ shared memory on all platforms. | ||||
| - `sqlite3_noshm` disables shared memory on all platforms. | ||||
|  | ||||
| > [!IMPORTANT] | ||||
| > The default configuration of this package is compatible with | ||||
| > the standard [Unix and Windows SQLite VFSes](https://sqlite.org/vfs.html#multiple_vfses); | ||||
| > `sqlite3_flock` is compatible with the [`unix-flock` VFS](https://sqlite.org/compile.html#enable_locking_style). | ||||
| > If incompatible file locking is used, accessing databases concurrently with _other_ SQLite libraries | ||||
| > will eventually corrupt data. | ||||
							
								
								
									
										175
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/api.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/api.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,175 @@ | ||||
| // Package vfs wraps the C SQLite VFS API. | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| ) | ||||
|  | ||||
| // 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. | ||||
| // | ||||
| // https://sqlite.org/c3ref/vfs.html | ||||
| type VFS interface { | ||||
| 	Open(name string, flags OpenFlag) (File, OpenFlag, error) | ||||
| 	Delete(name string, syncDir bool) error | ||||
| 	Access(name string, flags AccessFlag) (bool, error) | ||||
| 	FullPathname(name string) (string, error) | ||||
| } | ||||
|  | ||||
| // VFSFilename extends VFS with the ability to use Filename | ||||
| // objects for opening files. | ||||
| // | ||||
| // https://sqlite.org/c3ref/filename.html | ||||
| type VFSFilename interface { | ||||
| 	VFS | ||||
| 	OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error) | ||||
| } | ||||
|  | ||||
| // 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. | ||||
| // | ||||
| // https://sqlite.org/c3ref/io_methods.html | ||||
| type File interface { | ||||
| 	Close() error | ||||
| 	ReadAt(p []byte, off int64) (n int, err error) | ||||
| 	WriteAt(p []byte, off int64) (n int, err error) | ||||
| 	Truncate(size int64) error | ||||
| 	Sync(flags SyncFlag) error | ||||
| 	Size() (int64, error) | ||||
| 	Lock(lock LockLevel) error | ||||
| 	Unlock(lock LockLevel) error | ||||
| 	CheckReservedLock() (bool, error) | ||||
| 	SectorSize() int | ||||
| 	DeviceCharacteristics() DeviceCharacteristic | ||||
| } | ||||
|  | ||||
| // FileLockState extends File to implement the | ||||
| // SQLITE_FCNTL_LOCKSTATE file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntllockstate | ||||
| type FileLockState interface { | ||||
| 	File | ||||
| 	LockState() LockLevel | ||||
| } | ||||
|  | ||||
| // FileChunkSize extends File to implement the | ||||
| // SQLITE_FCNTL_CHUNK_SIZE file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlchunksize | ||||
| type FileChunkSize interface { | ||||
| 	File | ||||
| 	ChunkSize(size int) | ||||
| } | ||||
|  | ||||
| // FileSizeHint extends File to implement the | ||||
| // SQLITE_FCNTL_SIZE_HINT file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlsizehint | ||||
| type FileSizeHint interface { | ||||
| 	File | ||||
| 	SizeHint(size int64) error | ||||
| } | ||||
|  | ||||
| // FileHasMoved extends File to implement the | ||||
| // SQLITE_FCNTL_HAS_MOVED file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlhasmoved | ||||
| type FileHasMoved interface { | ||||
| 	File | ||||
| 	HasMoved() (bool, error) | ||||
| } | ||||
|  | ||||
| // FileOverwrite extends File to implement the | ||||
| // SQLITE_FCNTL_OVERWRITE file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntloverwrite | ||||
| type FileOverwrite interface { | ||||
| 	File | ||||
| 	Overwrite() error | ||||
| } | ||||
|  | ||||
| // FilePersistentWAL extends File to implement the | ||||
| // SQLITE_FCNTL_PERSIST_WAL file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpersistwal | ||||
| type FilePersistentWAL interface { | ||||
| 	File | ||||
| 	PersistentWAL() bool | ||||
| 	SetPersistentWAL(bool) | ||||
| } | ||||
|  | ||||
| // FilePowersafeOverwrite extends File to implement the | ||||
| // SQLITE_FCNTL_POWERSAFE_OVERWRITE file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpowersafeoverwrite | ||||
| type FilePowersafeOverwrite interface { | ||||
| 	File | ||||
| 	PowersafeOverwrite() bool | ||||
| 	SetPowersafeOverwrite(bool) | ||||
| } | ||||
|  | ||||
| // FileCommitPhaseTwo extends File to implement the | ||||
| // SQLITE_FCNTL_COMMIT_PHASETWO file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlcommitphasetwo | ||||
| type FileCommitPhaseTwo interface { | ||||
| 	File | ||||
| 	CommitPhaseTwo() error | ||||
| } | ||||
|  | ||||
| // FileBatchAtomicWrite extends File to implement the | ||||
| // SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE | ||||
| // and SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE file control opcodes. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlbeginatomicwrite | ||||
| type FileBatchAtomicWrite interface { | ||||
| 	File | ||||
| 	BeginAtomicWrite() error | ||||
| 	CommitAtomicWrite() error | ||||
| 	RollbackAtomicWrite() error | ||||
| } | ||||
|  | ||||
| // FilePragma extends File to implement the | ||||
| // SQLITE_FCNTL_PRAGMA file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpragma | ||||
| type FilePragma interface { | ||||
| 	File | ||||
| 	Pragma(name, value string) (string, error) | ||||
| } | ||||
|  | ||||
| // FileCheckpoint extends File to implement the | ||||
| // SQLITE_FCNTL_CKPT_START and SQLITE_FCNTL_CKPT_DONE | ||||
| // file control opcodes. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlckptstart | ||||
| type FileCheckpoint interface { | ||||
| 	File | ||||
| 	CheckpointDone() error | ||||
| 	CheckpointStart() error | ||||
| } | ||||
|  | ||||
| // FileSharedMemory extends File to possibly implement | ||||
| // shared-memory for the WAL-index. | ||||
| // The same shared-memory instance must be returned | ||||
| // for the entire life of the file. | ||||
| // It's OK for SharedMemory to return nil. | ||||
| type FileSharedMemory interface { | ||||
| 	File | ||||
| 	SharedMemory() SharedMemory | ||||
| } | ||||
|  | ||||
| // SharedMemory is a shared-memory WAL-index implementation. | ||||
| // Use [NewSharedMemory] to create a shared-memory. | ||||
| type SharedMemory interface { | ||||
| 	shmMap(context.Context, api.Module, int32, int32, bool) (uint32, error) | ||||
| 	shmLock(int32, int32, _ShmFlag) error | ||||
| 	shmUnmap(bool) | ||||
| 	io.Closer | ||||
| } | ||||
							
								
								
									
										234
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/const.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/const.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,234 @@ | ||||
| package vfs | ||||
|  | ||||
| import "github.com/ncruces/go-sqlite3/internal/util" | ||||
|  | ||||
| const ( | ||||
| 	_MAX_NAME            = 1e6 // Self-imposed limit for most NUL terminated strings. | ||||
| 	_MAX_SQL_LENGTH      = 1e9 | ||||
| 	_MAX_PATHNAME        = 1024 | ||||
| 	_DEFAULT_SECTOR_SIZE = 4096 | ||||
|  | ||||
| 	ptrlen = 4 | ||||
| ) | ||||
|  | ||||
| // https://sqlite.org/rescode.html | ||||
| type _ErrorCode uint32 | ||||
|  | ||||
| func (e _ErrorCode) Error() string { | ||||
| 	return util.ErrorCodeString(uint32(e)) | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	_OK                      _ErrorCode = util.OK | ||||
| 	_ERROR                   _ErrorCode = util.ERROR | ||||
| 	_PERM                    _ErrorCode = util.PERM | ||||
| 	_BUSY                    _ErrorCode = util.BUSY | ||||
| 	_READONLY                _ErrorCode = util.READONLY | ||||
| 	_IOERR                   _ErrorCode = util.IOERR | ||||
| 	_NOTFOUND                _ErrorCode = util.NOTFOUND | ||||
| 	_CANTOPEN                _ErrorCode = util.CANTOPEN | ||||
| 	_IOERR_READ              _ErrorCode = util.IOERR_READ | ||||
| 	_IOERR_SHORT_READ        _ErrorCode = util.IOERR_SHORT_READ | ||||
| 	_IOERR_WRITE             _ErrorCode = util.IOERR_WRITE | ||||
| 	_IOERR_FSYNC             _ErrorCode = util.IOERR_FSYNC | ||||
| 	_IOERR_DIR_FSYNC         _ErrorCode = util.IOERR_DIR_FSYNC | ||||
| 	_IOERR_TRUNCATE          _ErrorCode = util.IOERR_TRUNCATE | ||||
| 	_IOERR_FSTAT             _ErrorCode = util.IOERR_FSTAT | ||||
| 	_IOERR_UNLOCK            _ErrorCode = util.IOERR_UNLOCK | ||||
| 	_IOERR_RDLOCK            _ErrorCode = util.IOERR_RDLOCK | ||||
| 	_IOERR_DELETE            _ErrorCode = util.IOERR_DELETE | ||||
| 	_IOERR_ACCESS            _ErrorCode = util.IOERR_ACCESS | ||||
| 	_IOERR_CHECKRESERVEDLOCK _ErrorCode = util.IOERR_CHECKRESERVEDLOCK | ||||
| 	_IOERR_LOCK              _ErrorCode = util.IOERR_LOCK | ||||
| 	_IOERR_CLOSE             _ErrorCode = util.IOERR_CLOSE | ||||
| 	_IOERR_SHMOPEN           _ErrorCode = util.IOERR_SHMOPEN | ||||
| 	_IOERR_SHMSIZE           _ErrorCode = util.IOERR_SHMSIZE | ||||
| 	_IOERR_SHMLOCK           _ErrorCode = util.IOERR_SHMLOCK | ||||
| 	_IOERR_SHMMAP            _ErrorCode = util.IOERR_SHMMAP | ||||
| 	_IOERR_SEEK              _ErrorCode = util.IOERR_SEEK | ||||
| 	_IOERR_DELETE_NOENT      _ErrorCode = util.IOERR_DELETE_NOENT | ||||
| 	_IOERR_BEGIN_ATOMIC      _ErrorCode = util.IOERR_BEGIN_ATOMIC | ||||
| 	_IOERR_COMMIT_ATOMIC     _ErrorCode = util.IOERR_COMMIT_ATOMIC | ||||
| 	_IOERR_ROLLBACK_ATOMIC   _ErrorCode = util.IOERR_ROLLBACK_ATOMIC | ||||
| 	_CANTOPEN_FULLPATH       _ErrorCode = util.CANTOPEN_FULLPATH | ||||
| 	_CANTOPEN_ISDIR          _ErrorCode = util.CANTOPEN_ISDIR | ||||
| 	_READONLY_CANTINIT       _ErrorCode = util.READONLY_CANTINIT | ||||
| 	_OK_SYMLINK              _ErrorCode = util.OK_SYMLINK | ||||
| ) | ||||
|  | ||||
| // OpenFlag is a flag for the [VFS] Open method. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_open_autoproxy.html | ||||
| type OpenFlag uint32 | ||||
|  | ||||
| const ( | ||||
| 	OPEN_READONLY      OpenFlag = 0x00000001 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_READWRITE     OpenFlag = 0x00000002 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_CREATE        OpenFlag = 0x00000004 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_DELETEONCLOSE OpenFlag = 0x00000008 /* VFS only */ | ||||
| 	OPEN_EXCLUSIVE     OpenFlag = 0x00000010 /* VFS only */ | ||||
| 	OPEN_AUTOPROXY     OpenFlag = 0x00000020 /* VFS only */ | ||||
| 	OPEN_URI           OpenFlag = 0x00000040 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_MEMORY        OpenFlag = 0x00000080 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_MAIN_DB       OpenFlag = 0x00000100 /* VFS only */ | ||||
| 	OPEN_TEMP_DB       OpenFlag = 0x00000200 /* VFS only */ | ||||
| 	OPEN_TRANSIENT_DB  OpenFlag = 0x00000400 /* VFS only */ | ||||
| 	OPEN_MAIN_JOURNAL  OpenFlag = 0x00000800 /* VFS only */ | ||||
| 	OPEN_TEMP_JOURNAL  OpenFlag = 0x00001000 /* VFS only */ | ||||
| 	OPEN_SUBJOURNAL    OpenFlag = 0x00002000 /* VFS only */ | ||||
| 	OPEN_SUPER_JOURNAL OpenFlag = 0x00004000 /* VFS only */ | ||||
| 	OPEN_NOMUTEX       OpenFlag = 0x00008000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_FULLMUTEX     OpenFlag = 0x00010000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_SHAREDCACHE   OpenFlag = 0x00020000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_PRIVATECACHE  OpenFlag = 0x00040000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_WAL           OpenFlag = 0x00080000 /* VFS only */ | ||||
| 	OPEN_NOFOLLOW      OpenFlag = 0x01000000 /* Ok for sqlite3_open_v2() */ | ||||
| ) | ||||
|  | ||||
| // AccessFlag is a flag for the [VFS] Access method. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_access_exists.html | ||||
| type AccessFlag uint32 | ||||
|  | ||||
| const ( | ||||
| 	ACCESS_EXISTS    AccessFlag = 0 | ||||
| 	ACCESS_READWRITE AccessFlag = 1 /* Used by PRAGMA temp_store_directory */ | ||||
| 	ACCESS_READ      AccessFlag = 2 /* Unused */ | ||||
| ) | ||||
|  | ||||
| // SyncFlag is a flag for the [File] Sync method. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_sync_dataonly.html | ||||
| type SyncFlag uint32 | ||||
|  | ||||
| const ( | ||||
| 	SYNC_NORMAL   SyncFlag = 0x00002 | ||||
| 	SYNC_FULL     SyncFlag = 0x00003 | ||||
| 	SYNC_DATAONLY SyncFlag = 0x00010 | ||||
| ) | ||||
|  | ||||
| // LockLevel is a value used with [File] Lock and Unlock methods. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_lock_exclusive.html | ||||
| type LockLevel uint32 | ||||
|  | ||||
| const ( | ||||
| 	// No locks are held on the database. | ||||
| 	// The database may be neither read nor written. | ||||
| 	// Any internally cached data is considered suspect and subject to | ||||
| 	// verification against the database file before being used. | ||||
| 	// Other processes can read or write the database as their own locking | ||||
| 	// states permit. | ||||
| 	// This is the default state. | ||||
| 	LOCK_NONE LockLevel = 0 /* xUnlock() only */ | ||||
|  | ||||
| 	// The database may be read but not written. | ||||
| 	// Any number of processes can hold SHARED locks at the same time, | ||||
| 	// hence there can be many simultaneous readers. | ||||
| 	// But no other thread or process is allowed to write to the database file | ||||
| 	// while one or more SHARED locks are active. | ||||
| 	LOCK_SHARED LockLevel = 1 /* xLock() or xUnlock() */ | ||||
|  | ||||
| 	// A RESERVED lock means that the process is planning on writing to the | ||||
| 	// database file at some point in the future but that it is currently just | ||||
| 	// reading from the file. | ||||
| 	// Only a single RESERVED lock may be active at one time, | ||||
| 	// though multiple SHARED locks can coexist with a single RESERVED lock. | ||||
| 	// RESERVED differs from PENDING in that new SHARED locks can be acquired | ||||
| 	// while there is a RESERVED lock. | ||||
| 	LOCK_RESERVED LockLevel = 2 /* xLock() only */ | ||||
|  | ||||
| 	// A PENDING lock means that the process holding the lock wants to write to | ||||
| 	// the database as soon as possible and is just waiting on all current | ||||
| 	// SHARED locks to clear so that it can get an EXCLUSIVE lock. | ||||
| 	// No new SHARED locks are permitted against the database if a PENDING lock | ||||
| 	// is active, though existing SHARED locks are allowed to continue. | ||||
| 	LOCK_PENDING LockLevel = 3 /* internal use only */ | ||||
|  | ||||
| 	// An EXCLUSIVE lock is needed in order to write to the database file. | ||||
| 	// Only one EXCLUSIVE lock is allowed on the file and no other locks of any | ||||
| 	// kind are allowed to coexist with an EXCLUSIVE lock. | ||||
| 	// In order to maximize concurrency, SQLite works to minimize the amount of | ||||
| 	// time that EXCLUSIVE locks are held. | ||||
| 	LOCK_EXCLUSIVE LockLevel = 4 /* xLock() only */ | ||||
| ) | ||||
|  | ||||
| // DeviceCharacteristic is a flag retuned by the [File] DeviceCharacteristics method. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_iocap_atomic.html | ||||
| type DeviceCharacteristic uint32 | ||||
|  | ||||
| const ( | ||||
| 	IOCAP_ATOMIC                DeviceCharacteristic = 0x00000001 | ||||
| 	IOCAP_ATOMIC512             DeviceCharacteristic = 0x00000002 | ||||
| 	IOCAP_ATOMIC1K              DeviceCharacteristic = 0x00000004 | ||||
| 	IOCAP_ATOMIC2K              DeviceCharacteristic = 0x00000008 | ||||
| 	IOCAP_ATOMIC4K              DeviceCharacteristic = 0x00000010 | ||||
| 	IOCAP_ATOMIC8K              DeviceCharacteristic = 0x00000020 | ||||
| 	IOCAP_ATOMIC16K             DeviceCharacteristic = 0x00000040 | ||||
| 	IOCAP_ATOMIC32K             DeviceCharacteristic = 0x00000080 | ||||
| 	IOCAP_ATOMIC64K             DeviceCharacteristic = 0x00000100 | ||||
| 	IOCAP_SAFE_APPEND           DeviceCharacteristic = 0x00000200 | ||||
| 	IOCAP_SEQUENTIAL            DeviceCharacteristic = 0x00000400 | ||||
| 	IOCAP_UNDELETABLE_WHEN_OPEN DeviceCharacteristic = 0x00000800 | ||||
| 	IOCAP_POWERSAFE_OVERWRITE   DeviceCharacteristic = 0x00001000 | ||||
| 	IOCAP_IMMUTABLE             DeviceCharacteristic = 0x00002000 | ||||
| 	IOCAP_BATCH_ATOMIC          DeviceCharacteristic = 0x00004000 | ||||
| ) | ||||
|  | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html | ||||
| type _FcntlOpcode uint32 | ||||
|  | ||||
| const ( | ||||
| 	_FCNTL_LOCKSTATE             _FcntlOpcode = 1 | ||||
| 	_FCNTL_GET_LOCKPROXYFILE     _FcntlOpcode = 2 | ||||
| 	_FCNTL_SET_LOCKPROXYFILE     _FcntlOpcode = 3 | ||||
| 	_FCNTL_LAST_ERRNO            _FcntlOpcode = 4 | ||||
| 	_FCNTL_SIZE_HINT             _FcntlOpcode = 5 | ||||
| 	_FCNTL_CHUNK_SIZE            _FcntlOpcode = 6 | ||||
| 	_FCNTL_FILE_POINTER          _FcntlOpcode = 7 | ||||
| 	_FCNTL_SYNC_OMITTED          _FcntlOpcode = 8 | ||||
| 	_FCNTL_WIN32_AV_RETRY        _FcntlOpcode = 9 | ||||
| 	_FCNTL_PERSIST_WAL           _FcntlOpcode = 10 | ||||
| 	_FCNTL_OVERWRITE             _FcntlOpcode = 11 | ||||
| 	_FCNTL_VFSNAME               _FcntlOpcode = 12 | ||||
| 	_FCNTL_POWERSAFE_OVERWRITE   _FcntlOpcode = 13 | ||||
| 	_FCNTL_PRAGMA                _FcntlOpcode = 14 | ||||
| 	_FCNTL_BUSYHANDLER           _FcntlOpcode = 15 | ||||
| 	_FCNTL_TEMPFILENAME          _FcntlOpcode = 16 | ||||
| 	_FCNTL_MMAP_SIZE             _FcntlOpcode = 18 | ||||
| 	_FCNTL_TRACE                 _FcntlOpcode = 19 | ||||
| 	_FCNTL_HAS_MOVED             _FcntlOpcode = 20 | ||||
| 	_FCNTL_SYNC                  _FcntlOpcode = 21 | ||||
| 	_FCNTL_COMMIT_PHASETWO       _FcntlOpcode = 22 | ||||
| 	_FCNTL_WIN32_SET_HANDLE      _FcntlOpcode = 23 | ||||
| 	_FCNTL_WAL_BLOCK             _FcntlOpcode = 24 | ||||
| 	_FCNTL_ZIPVFS                _FcntlOpcode = 25 | ||||
| 	_FCNTL_RBU                   _FcntlOpcode = 26 | ||||
| 	_FCNTL_VFS_POINTER           _FcntlOpcode = 27 | ||||
| 	_FCNTL_JOURNAL_POINTER       _FcntlOpcode = 28 | ||||
| 	_FCNTL_WIN32_GET_HANDLE      _FcntlOpcode = 29 | ||||
| 	_FCNTL_PDB                   _FcntlOpcode = 30 | ||||
| 	_FCNTL_BEGIN_ATOMIC_WRITE    _FcntlOpcode = 31 | ||||
| 	_FCNTL_COMMIT_ATOMIC_WRITE   _FcntlOpcode = 32 | ||||
| 	_FCNTL_ROLLBACK_ATOMIC_WRITE _FcntlOpcode = 33 | ||||
| 	_FCNTL_LOCK_TIMEOUT          _FcntlOpcode = 34 | ||||
| 	_FCNTL_DATA_VERSION          _FcntlOpcode = 35 | ||||
| 	_FCNTL_SIZE_LIMIT            _FcntlOpcode = 36 | ||||
| 	_FCNTL_CKPT_DONE             _FcntlOpcode = 37 | ||||
| 	_FCNTL_RESERVE_BYTES         _FcntlOpcode = 38 | ||||
| 	_FCNTL_CKPT_START            _FcntlOpcode = 39 | ||||
| 	_FCNTL_EXTERNAL_READER       _FcntlOpcode = 40 | ||||
| 	_FCNTL_CKSM_FILE             _FcntlOpcode = 41 | ||||
| 	_FCNTL_RESET_CACHE           _FcntlOpcode = 42 | ||||
| ) | ||||
|  | ||||
| // https://sqlite.org/c3ref/c_shm_exclusive.html | ||||
| type _ShmFlag uint32 | ||||
|  | ||||
| const ( | ||||
| 	_SHM_UNLOCK    _ShmFlag = 1 | ||||
| 	_SHM_LOCK      _ShmFlag = 2 | ||||
| 	_SHM_SHARED    _ShmFlag = 4 | ||||
| 	_SHM_EXCLUSIVE _ShmFlag = 8 | ||||
| ) | ||||
							
								
								
									
										217
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/file.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/file.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,217 @@ | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"io/fs" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
| 	"syscall" | ||||
|  | ||||
| 	"github.com/ncruces/go-sqlite3/util/osutil" | ||||
| ) | ||||
|  | ||||
| type vfsOS struct{} | ||||
|  | ||||
| func (vfsOS) FullPathname(path string) (string, error) { | ||||
| 	path, err := filepath.Abs(path) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	fi, err := os.Lstat(path) | ||||
| 	if err != nil { | ||||
| 		if errors.Is(err, fs.ErrNotExist) { | ||||
| 			return path, nil | ||||
| 		} | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if fi.Mode()&fs.ModeSymlink != 0 { | ||||
| 		err = _OK_SYMLINK | ||||
| 	} | ||||
| 	return path, err | ||||
| } | ||||
|  | ||||
| func (vfsOS) Delete(path string, syncDir bool) error { | ||||
| 	err := os.Remove(path) | ||||
| 	if err != nil { | ||||
| 		if errors.Is(err, fs.ErrNotExist) { | ||||
| 			return _IOERR_DELETE_NOENT | ||||
| 		} | ||||
| 		return err | ||||
| 	} | ||||
| 	if runtime.GOOS != "windows" && syncDir { | ||||
| 		f, err := os.Open(filepath.Dir(path)) | ||||
| 		if err != nil { | ||||
| 			return _OK | ||||
| 		} | ||||
| 		defer f.Close() | ||||
| 		err = osSync(f, false, false) | ||||
| 		if err != nil { | ||||
| 			return _IOERR_DIR_FSYNC | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (vfsOS) Access(name string, flags AccessFlag) (bool, error) { | ||||
| 	err := osAccess(name, flags) | ||||
| 	if flags == ACCESS_EXISTS { | ||||
| 		if errors.Is(err, fs.ErrNotExist) { | ||||
| 			return false, nil | ||||
| 		} | ||||
| 	} else { | ||||
| 		if errors.Is(err, fs.ErrPermission) { | ||||
| 			return false, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return err == nil, err | ||||
| } | ||||
|  | ||||
| func (vfsOS) Open(name string, flags OpenFlag) (File, OpenFlag, error) { | ||||
| 	return nil, 0, _CANTOPEN | ||||
| } | ||||
|  | ||||
| func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error) { | ||||
| 	var oflags int | ||||
| 	if flags&OPEN_EXCLUSIVE != 0 { | ||||
| 		oflags |= os.O_EXCL | ||||
| 	} | ||||
| 	if flags&OPEN_CREATE != 0 { | ||||
| 		oflags |= os.O_CREATE | ||||
| 	} | ||||
| 	if flags&OPEN_READONLY != 0 { | ||||
| 		oflags |= os.O_RDONLY | ||||
| 	} | ||||
| 	if flags&OPEN_READWRITE != 0 { | ||||
| 		oflags |= os.O_RDWR | ||||
| 	} | ||||
|  | ||||
| 	var err error | ||||
| 	var f *os.File | ||||
| 	if name == nil { | ||||
| 		f, err = os.CreateTemp("", "*.db") | ||||
| 	} else { | ||||
| 		f, err = osutil.OpenFile(name.String(), oflags, 0666) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		if errors.Is(err, syscall.EISDIR) { | ||||
| 			return nil, flags, _CANTOPEN_ISDIR | ||||
| 		} | ||||
| 		return nil, flags, err | ||||
| 	} | ||||
|  | ||||
| 	if modeof := name.URIParameter("modeof"); modeof != "" { | ||||
| 		if err = osSetMode(f, modeof); err != nil { | ||||
| 			f.Close() | ||||
| 			return nil, flags, _IOERR_FSTAT | ||||
| 		} | ||||
| 	} | ||||
| 	if flags&OPEN_DELETEONCLOSE != 0 { | ||||
| 		os.Remove(f.Name()) | ||||
| 	} | ||||
|  | ||||
| 	file := vfsFile{ | ||||
| 		File:     f, | ||||
| 		psow:     true, | ||||
| 		readOnly: flags&OPEN_READONLY != 0, | ||||
| 		syncDir: runtime.GOOS != "windows" && | ||||
| 			flags&(OPEN_CREATE) != 0 && | ||||
| 			flags&(OPEN_MAIN_JOURNAL|OPEN_SUPER_JOURNAL|OPEN_WAL) != 0, | ||||
| 		shm: NewSharedMemory(name.String()+"-shm", flags), | ||||
| 	} | ||||
| 	return &file, flags, nil | ||||
| } | ||||
|  | ||||
| type vfsFile struct { | ||||
| 	*os.File | ||||
| 	shm      SharedMemory | ||||
| 	lock     LockLevel | ||||
| 	readOnly bool | ||||
| 	keepWAL  bool | ||||
| 	syncDir  bool | ||||
| 	psow     bool | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	// Ensure these interfaces are implemented: | ||||
| 	_ FileLockState          = &vfsFile{} | ||||
| 	_ FileHasMoved           = &vfsFile{} | ||||
| 	_ FileSizeHint           = &vfsFile{} | ||||
| 	_ FilePersistentWAL      = &vfsFile{} | ||||
| 	_ FilePowersafeOverwrite = &vfsFile{} | ||||
| ) | ||||
|  | ||||
| func (f *vfsFile) Close() error { | ||||
| 	if f.shm != nil { | ||||
| 		f.shm.Close() | ||||
| 	} | ||||
| 	return f.File.Close() | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) Sync(flags SyncFlag) error { | ||||
| 	dataonly := (flags & SYNC_DATAONLY) != 0 | ||||
| 	fullsync := (flags & 0x0f) == SYNC_FULL | ||||
|  | ||||
| 	err := osSync(f.File, fullsync, dataonly) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if runtime.GOOS != "windows" && f.syncDir { | ||||
| 		f.syncDir = false | ||||
| 		d, err := os.Open(filepath.Dir(f.File.Name())) | ||||
| 		if err != nil { | ||||
| 			return nil | ||||
| 		} | ||||
| 		defer d.Close() | ||||
| 		err = osSync(d, false, false) | ||||
| 		if err != nil { | ||||
| 			return _IOERR_DIR_FSYNC | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) Size() (int64, error) { | ||||
| 	return f.Seek(0, io.SeekEnd) | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) SectorSize() int { | ||||
| 	return _DEFAULT_SECTOR_SIZE | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) DeviceCharacteristics() DeviceCharacteristic { | ||||
| 	var res DeviceCharacteristic | ||||
| 	if osBatchAtomic(f.File) { | ||||
| 		res |= IOCAP_BATCH_ATOMIC | ||||
| 	} | ||||
| 	if f.psow { | ||||
| 		res |= IOCAP_POWERSAFE_OVERWRITE | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) SizeHint(size int64) error { | ||||
| 	return osAllocate(f.File, size) | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) HasMoved() (bool, error) { | ||||
| 	fi, err := f.Stat() | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	pi, err := os.Stat(f.Name()) | ||||
| 	if err != nil { | ||||
| 		if errors.Is(err, fs.ErrNotExist) { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		return false, err | ||||
| 	} | ||||
| 	return !os.SameFile(fi, pi), nil | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) LockState() LockLevel            { return f.lock } | ||||
| func (f *vfsFile) PowersafeOverwrite() bool        { return f.psow } | ||||
| func (f *vfsFile) PersistentWAL() bool             { return f.keepWAL } | ||||
| func (f *vfsFile) SetPowersafeOverwrite(psow bool) { f.psow = psow } | ||||
| func (f *vfsFile) SetPersistentWAL(keepWAL bool)   { f.keepWAL = keepWAL } | ||||
							
								
								
									
										174
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/filename.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/filename.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net/url" | ||||
|  | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| ) | ||||
|  | ||||
| // Filename is used by SQLite to pass filenames | ||||
| // to the Open method of a VFS. | ||||
| // | ||||
| // https://sqlite.org/c3ref/filename.html | ||||
| type Filename struct { | ||||
| 	ctx   context.Context | ||||
| 	mod   api.Module | ||||
| 	zPath uint32 | ||||
| 	flags OpenFlag | ||||
| 	stack [2]uint64 | ||||
| } | ||||
|  | ||||
| // OpenFilename is an internal API users should not call directly. | ||||
| func OpenFilename(ctx context.Context, mod api.Module, id uint32, flags OpenFlag) *Filename { | ||||
| 	if id == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &Filename{ | ||||
| 		ctx:   ctx, | ||||
| 		mod:   mod, | ||||
| 		zPath: id, | ||||
| 		flags: flags, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // String returns this filename as a string. | ||||
| func (n *Filename) String() string { | ||||
| 	if n == nil || n.zPath == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return util.ReadString(n.mod, n.zPath, _MAX_PATHNAME) | ||||
| } | ||||
|  | ||||
| // Database returns the name of the corresponding database file. | ||||
| // | ||||
| // https://sqlite.org/c3ref/filename_database.html | ||||
| func (n *Filename) Database() string { | ||||
| 	return n.path("sqlite3_filename_database") | ||||
| } | ||||
|  | ||||
| // Journal returns the name of the corresponding rollback journal file. | ||||
| // | ||||
| // https://sqlite.org/c3ref/filename_database.html | ||||
| func (n *Filename) Journal() string { | ||||
| 	return n.path("sqlite3_filename_journal") | ||||
| } | ||||
|  | ||||
| // Journal returns the name of the corresponding WAL file. | ||||
| // | ||||
| // https://sqlite.org/c3ref/filename_database.html | ||||
| func (n *Filename) WAL() string { | ||||
| 	return n.path("sqlite3_filename_wal") | ||||
| } | ||||
|  | ||||
| func (n *Filename) path(method string) string { | ||||
| 	if n == nil || n.zPath == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	n.stack[0] = uint64(n.zPath) | ||||
| 	fn := n.mod.ExportedFunction(method) | ||||
| 	if err := fn.CallWithStack(n.ctx, n.stack[:]); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return util.ReadString(n.mod, uint32(n.stack[0]), _MAX_PATHNAME) | ||||
| } | ||||
|  | ||||
| // DatabaseFile returns the main database [File] corresponding to a journal. | ||||
| // | ||||
| // https://sqlite.org/c3ref/database_file_object.html | ||||
| func (n *Filename) DatabaseFile() File { | ||||
| 	if n == nil || n.zPath == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if n.flags&(OPEN_MAIN_DB|OPEN_MAIN_JOURNAL|OPEN_WAL) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	n.stack[0] = uint64(n.zPath) | ||||
| 	fn := n.mod.ExportedFunction("sqlite3_database_file_object") | ||||
| 	if err := fn.CallWithStack(n.ctx, n.stack[:]); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	file, _ := vfsFileGet(n.ctx, n.mod, uint32(n.stack[0])).(File) | ||||
| 	return file | ||||
| } | ||||
|  | ||||
| // URIParameter returns the value of a URI parameter. | ||||
| // | ||||
| // https://sqlite.org/c3ref/uri_boolean.html | ||||
| func (n *Filename) URIParameter(key string) string { | ||||
| 	if n == nil || n.zPath == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	uriKey := n.mod.ExportedFunction("sqlite3_uri_key") | ||||
| 	n.stack[0] = uint64(n.zPath) | ||||
| 	n.stack[1] = uint64(0) | ||||
| 	if err := uriKey.CallWithStack(n.ctx, n.stack[:]); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	ptr := uint32(n.stack[0]) | ||||
| 	if ptr == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	// Parse the format from: | ||||
| 	// https://github.com/sqlite/sqlite/blob/b74eb0/src/pager.c#L4797-L4840 | ||||
| 	// This avoids having to alloc/free the key just to find a value. | ||||
| 	for { | ||||
| 		k := util.ReadString(n.mod, ptr, _MAX_NAME) | ||||
| 		if k == "" { | ||||
| 			return "" | ||||
| 		} | ||||
| 		ptr += uint32(len(k)) + 1 | ||||
|  | ||||
| 		v := util.ReadString(n.mod, ptr, _MAX_NAME) | ||||
| 		if k == key { | ||||
| 			return v | ||||
| 		} | ||||
| 		ptr += uint32(len(v)) + 1 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // URIParameters obtains values for URI parameters. | ||||
| // | ||||
| // https://sqlite.org/c3ref/uri_boolean.html | ||||
| func (n *Filename) URIParameters() url.Values { | ||||
| 	if n == nil || n.zPath == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	uriKey := n.mod.ExportedFunction("sqlite3_uri_key") | ||||
| 	n.stack[0] = uint64(n.zPath) | ||||
| 	n.stack[1] = uint64(0) | ||||
| 	if err := uriKey.CallWithStack(n.ctx, n.stack[:]); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	ptr := uint32(n.stack[0]) | ||||
| 	if ptr == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	var params url.Values | ||||
|  | ||||
| 	// Parse the format from: | ||||
| 	// https://github.com/sqlite/sqlite/blob/b74eb0/src/pager.c#L4797-L4840 | ||||
| 	// This is the only way to support multiple valued keys. | ||||
| 	for { | ||||
| 		k := util.ReadString(n.mod, ptr, _MAX_NAME) | ||||
| 		if k == "" { | ||||
| 			return params | ||||
| 		} | ||||
| 		ptr += uint32(len(k)) + 1 | ||||
|  | ||||
| 		v := util.ReadString(n.mod, ptr, _MAX_NAME) | ||||
| 		if params == nil { | ||||
| 			params = url.Values{} | ||||
| 		} | ||||
| 		params.Add(k, v) | ||||
| 		ptr += uint32(len(v)) + 1 | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										144
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/lock.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/lock.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| //go:build (linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import "github.com/ncruces/go-sqlite3/internal/util" | ||||
|  | ||||
| // SupportsFileLocking is false on platforms that do not support file locking. | ||||
| // To open a database file on those platforms, | ||||
| // you need to use the [nolock] or [immutable] URI parameters. | ||||
| // | ||||
| // [nolock]: https://sqlite.org/uri.html#urinolock | ||||
| // [immutable]: https://sqlite.org/uri.html#uriimmutable | ||||
| const SupportsFileLocking = true | ||||
|  | ||||
| const ( | ||||
| 	_PENDING_BYTE  = 0x40000000 | ||||
| 	_RESERVED_BYTE = (_PENDING_BYTE + 1) | ||||
| 	_SHARED_FIRST  = (_PENDING_BYTE + 2) | ||||
| 	_SHARED_SIZE   = 510 | ||||
| ) | ||||
|  | ||||
| func (f *vfsFile) Lock(lock LockLevel) error { | ||||
| 	// Argument check. SQLite never explicitly requests a pending lock. | ||||
| 	if lock != LOCK_SHARED && lock != LOCK_RESERVED && lock != LOCK_EXCLUSIVE { | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
|  | ||||
| 	switch { | ||||
| 	case f.lock < LOCK_NONE || f.lock > LOCK_EXCLUSIVE: | ||||
| 		// Connection state check. | ||||
| 		panic(util.AssertErr()) | ||||
| 	case f.lock == LOCK_NONE && lock > LOCK_SHARED: | ||||
| 		// We never move from unlocked to anything higher than a shared lock. | ||||
| 		panic(util.AssertErr()) | ||||
| 	case f.lock != LOCK_SHARED && lock == LOCK_RESERVED: | ||||
| 		// A shared lock is always held when a reserved lock is requested. | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
|  | ||||
| 	// If we already have an equal or more restrictive lock, do nothing. | ||||
| 	if f.lock >= lock { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// Do not allow any kind of write-lock on a read-only database. | ||||
| 	if f.readOnly && lock >= LOCK_RESERVED { | ||||
| 		return _IOERR_LOCK | ||||
| 	} | ||||
|  | ||||
| 	switch lock { | ||||
| 	case LOCK_SHARED: | ||||
| 		// Must be unlocked to get SHARED. | ||||
| 		if f.lock != LOCK_NONE { | ||||
| 			panic(util.AssertErr()) | ||||
| 		} | ||||
| 		if rc := osGetSharedLock(f.File); rc != _OK { | ||||
| 			return rc | ||||
| 		} | ||||
| 		f.lock = LOCK_SHARED | ||||
| 		return nil | ||||
|  | ||||
| 	case LOCK_RESERVED: | ||||
| 		// Must be SHARED to get RESERVED. | ||||
| 		if f.lock != LOCK_SHARED { | ||||
| 			panic(util.AssertErr()) | ||||
| 		} | ||||
| 		if rc := osGetReservedLock(f.File); rc != _OK { | ||||
| 			return rc | ||||
| 		} | ||||
| 		f.lock = LOCK_RESERVED | ||||
| 		return nil | ||||
|  | ||||
| 	case LOCK_EXCLUSIVE: | ||||
| 		// Must be SHARED, RESERVED or PENDING to get EXCLUSIVE. | ||||
| 		if f.lock <= LOCK_NONE || f.lock >= LOCK_EXCLUSIVE { | ||||
| 			panic(util.AssertErr()) | ||||
| 		} | ||||
| 		reserved := f.lock == LOCK_RESERVED | ||||
| 		// A PENDING lock is needed before acquiring an EXCLUSIVE lock. | ||||
| 		if f.lock < LOCK_PENDING { | ||||
| 			// If we're already RESERVED, we can block indefinitely, | ||||
| 			// since only new readers may briefly hold the PENDING lock. | ||||
| 			if rc := osGetPendingLock(f.File, reserved /* block */); rc != _OK { | ||||
| 				return rc | ||||
| 			} | ||||
| 			f.lock = LOCK_PENDING | ||||
| 		} | ||||
| 		// We already have PENDING, so we're just waiting for readers to leave. | ||||
| 		// If we were RESERVED, we can wait for a little while, before invoking | ||||
| 		// the busy handler; we will only do this once. | ||||
| 		if rc := osGetExclusiveLock(f.File, reserved /* wait */); rc != _OK { | ||||
| 			return rc | ||||
| 		} | ||||
| 		f.lock = LOCK_EXCLUSIVE | ||||
| 		return nil | ||||
|  | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) Unlock(lock LockLevel) error { | ||||
| 	// Argument check. | ||||
| 	if lock != LOCK_NONE && lock != LOCK_SHARED { | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
|  | ||||
| 	// Connection state check. | ||||
| 	if f.lock < LOCK_NONE || f.lock > LOCK_EXCLUSIVE { | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
|  | ||||
| 	// If we don't have a more restrictive lock, do nothing. | ||||
| 	if f.lock <= lock { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	switch lock { | ||||
| 	case LOCK_SHARED: | ||||
| 		rc := osDowngradeLock(f.File, f.lock) | ||||
| 		f.lock = LOCK_SHARED | ||||
| 		return rc | ||||
|  | ||||
| 	case LOCK_NONE: | ||||
| 		rc := osReleaseLock(f.File, f.lock) | ||||
| 		f.lock = LOCK_NONE | ||||
| 		return rc | ||||
|  | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) CheckReservedLock() (bool, error) { | ||||
| 	// Connection state check. | ||||
| 	if f.lock < LOCK_NONE || f.lock > LOCK_EXCLUSIVE { | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
|  | ||||
| 	if f.lock >= LOCK_RESERVED { | ||||
| 		return true, nil | ||||
| 	} | ||||
| 	return osCheckReservedLock(f.File) | ||||
| } | ||||
							
								
								
									
										23
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| //go:build !(linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) || sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| // SupportsFileLocking is false on platforms that do not support file locking. | ||||
| // To open a database file on those platforms, | ||||
| // you need to use the [nolock] or [immutable] URI parameters. | ||||
| // | ||||
| // [nolock]: https://sqlite.org/uri.html#urinolock | ||||
| // [immutable]: https://sqlite.org/uri.html#uriimmutable | ||||
| const SupportsFileLocking = false | ||||
|  | ||||
| func (f *vfsFile) Lock(LockLevel) error { | ||||
| 	return _IOERR_LOCK | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) Unlock(LockLevel) error { | ||||
| 	return _IOERR_UNLOCK | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) CheckReservedLock() (bool, error) { | ||||
| 	return false, _IOERR_CHECKRESERVEDLOCK | ||||
| } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| # Go `"memdb"` SQLite VFS | ||||
|  | ||||
| This package implements the [`"memdb"`](https://sqlite.org/src/doc/tip/src/memdb.c) | ||||
| SQLite VFS in pure Go. | ||||
|  | ||||
| It has some benefits over the C version: | ||||
| - the memory backing the database needs not be contiguous, | ||||
| - the database can grow/shrink incrementally without copying, | ||||
| - reader-writer concurrency is slightly improved. | ||||
							
								
								
									
										68
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| // Package memdb implements the "memdb" SQLite VFS. | ||||
| // | ||||
| // 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 memdb registers the VFS: | ||||
| // | ||||
| //	import _ "github.com/ncruces/go-sqlite3/vfs/memdb" | ||||
| package memdb | ||||
|  | ||||
| import ( | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/ncruces/go-sqlite3/vfs" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	vfs.Register("memdb", memVFS{}) | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	memoryMtx sync.Mutex | ||||
| 	// +checklocks:memoryMtx | ||||
| 	memoryDBs = map[string]*memDB{} | ||||
| ) | ||||
|  | ||||
| // 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) { | ||||
| 	memoryMtx.Lock() | ||||
| 	defer memoryMtx.Unlock() | ||||
|  | ||||
| 	db := &memDB{ | ||||
| 		refs: 1, | ||||
| 		name: name, | ||||
| 		size: int64(len(data)), | ||||
| 	} | ||||
|  | ||||
| 	// Convert data from WAL to rollback journal. | ||||
| 	if len(data) >= 20 && data[18] == 2 && data[19] == 2 { | ||||
| 		data[18] = 1 | ||||
| 		data[19] = 1 | ||||
| 	} | ||||
|  | ||||
| 	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) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	memoryDBs[name] = db | ||||
| } | ||||
|  | ||||
| // Delete deletes a shared memory database. | ||||
| func Delete(name string) { | ||||
| 	memoryMtx.Lock() | ||||
| 	defer memoryMtx.Unlock() | ||||
| 	delete(memoryDBs, name) | ||||
| } | ||||
							
								
								
									
										311
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										311
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,311 @@ | ||||
| package memdb | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"runtime" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/ncruces/go-sqlite3" | ||||
| 	"github.com/ncruces/go-sqlite3/vfs" | ||||
| ) | ||||
|  | ||||
| // Must be a multiple of 64K (the largest page size). | ||||
| const sectorSize = 65536 | ||||
|  | ||||
| type memVFS struct{} | ||||
|  | ||||
| func (memVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) { | ||||
| 	// For simplicity, we do not support reading or writing data | ||||
| 	// across "sector" boundaries. | ||||
| 	// | ||||
| 	// This is not a problem for most SQLite file types: | ||||
| 	// - databases, which only do page aligned reads/writes; | ||||
| 	// - temp journals, as used by the sorter, which does the same: | ||||
| 	//   https://github.com/sqlite/sqlite/blob/b74eb0/src/vdbesort.c#L409-L412 | ||||
| 	// | ||||
| 	// We refuse to open all other file types, | ||||
| 	// but returning OPEN_MEMORY means SQLite won't ask us to. | ||||
| 	const types = vfs.OPEN_MAIN_DB | | ||||
| 		vfs.OPEN_TEMP_DB | | ||||
| 		vfs.OPEN_TEMP_JOURNAL | ||||
| 	if flags&types == 0 { | ||||
| 		return nil, flags, sqlite3.CANTOPEN | ||||
| 	} | ||||
|  | ||||
| 	// A shared database has a name that begins with "/". | ||||
| 	shared := len(name) > 1 && name[0] == '/' | ||||
|  | ||||
| 	var db *memDB | ||||
| 	if shared { | ||||
| 		name = name[1:] | ||||
| 		memoryMtx.Lock() | ||||
| 		defer memoryMtx.Unlock() | ||||
| 		db = memoryDBs[name] | ||||
| 	} | ||||
| 	if db == nil { | ||||
| 		if flags&vfs.OPEN_CREATE == 0 { | ||||
| 			return nil, flags, sqlite3.CANTOPEN | ||||
| 		} | ||||
| 		db = &memDB{name: name} | ||||
| 	} | ||||
| 	if shared { | ||||
| 		db.refs++ // +checklocksforce: memoryMtx is held | ||||
| 		memoryDBs[name] = db | ||||
| 	} | ||||
|  | ||||
| 	return &memFile{ | ||||
| 		memDB:    db, | ||||
| 		readOnly: flags&vfs.OPEN_READONLY != 0, | ||||
| 	}, flags | vfs.OPEN_MEMORY, nil | ||||
| } | ||||
|  | ||||
| func (memVFS) Delete(name string, dirSync bool) error { | ||||
| 	return sqlite3.IOERR_DELETE | ||||
| } | ||||
|  | ||||
| func (memVFS) Access(name string, flag vfs.AccessFlag) (bool, error) { | ||||
| 	return false, nil | ||||
| } | ||||
|  | ||||
| func (memVFS) FullPathname(name string) (string, error) { | ||||
| 	return name, nil | ||||
| } | ||||
|  | ||||
| type memDB struct { | ||||
| 	name string | ||||
|  | ||||
| 	// +checklocks:lockMtx | ||||
| 	pending *memFile | ||||
| 	// +checklocks:lockMtx | ||||
| 	reserved *memFile | ||||
|  | ||||
| 	// +checklocks:dataMtx | ||||
| 	data []*[sectorSize]byte | ||||
|  | ||||
| 	// +checklocks:dataMtx | ||||
| 	size int64 | ||||
|  | ||||
| 	// +checklocks:lockMtx | ||||
| 	shared int | ||||
|  | ||||
| 	// +checklocks:memoryMtx | ||||
| 	refs int | ||||
|  | ||||
| 	lockMtx sync.Mutex | ||||
| 	dataMtx sync.RWMutex | ||||
| } | ||||
|  | ||||
| func (m *memDB) release() { | ||||
| 	memoryMtx.Lock() | ||||
| 	defer memoryMtx.Unlock() | ||||
| 	if m.refs--; m.refs == 0 && m == memoryDBs[m.name] { | ||||
| 		delete(memoryDBs, m.name) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type memFile struct { | ||||
| 	*memDB | ||||
| 	lock     vfs.LockLevel | ||||
| 	readOnly bool | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	// Ensure these interfaces are implemented: | ||||
| 	_ vfs.FileLockState = &memFile{} | ||||
| 	_ vfs.FileSizeHint  = &memFile{} | ||||
| ) | ||||
|  | ||||
| func (m *memFile) Close() error { | ||||
| 	m.release() | ||||
| 	return m.Unlock(vfs.LOCK_NONE) | ||||
| } | ||||
|  | ||||
| func (m *memFile) ReadAt(b []byte, off int64) (n int, err error) { | ||||
| 	m.dataMtx.RLock() | ||||
| 	defer m.dataMtx.RUnlock() | ||||
|  | ||||
| 	if off >= m.size { | ||||
| 		return 0, io.EOF | ||||
| 	} | ||||
|  | ||||
| 	base := off / sectorSize | ||||
| 	rest := off % sectorSize | ||||
| 	have := int64(sectorSize) | ||||
| 	if base == int64(len(m.data))-1 { | ||||
| 		have = modRoundUp(m.size, sectorSize) | ||||
| 	} | ||||
| 	n = copy(b, (*m.data[base])[rest:have]) | ||||
| 	if n < len(b) { | ||||
| 		// Assume reads are page aligned. | ||||
| 		return 0, io.ErrNoProgress | ||||
| 	} | ||||
| 	return n, nil | ||||
| } | ||||
|  | ||||
| func (m *memFile) WriteAt(b []byte, off int64) (n int, err error) { | ||||
| 	m.dataMtx.Lock() | ||||
| 	defer m.dataMtx.Unlock() | ||||
|  | ||||
| 	base := off / sectorSize | ||||
| 	rest := off % sectorSize | ||||
| 	for base >= int64(len(m.data)) { | ||||
| 		m.data = append(m.data, new([sectorSize]byte)) | ||||
| 	} | ||||
| 	n = copy((*m.data[base])[rest:], b) | ||||
| 	if n < len(b) { | ||||
| 		// Assume writes are page aligned. | ||||
| 		return n, io.ErrShortWrite | ||||
| 	} | ||||
| 	if size := off + int64(len(b)); size > m.size { | ||||
| 		m.size = size | ||||
| 	} | ||||
| 	return n, nil | ||||
| } | ||||
|  | ||||
| func (m *memFile) Truncate(size int64) error { | ||||
| 	m.dataMtx.Lock() | ||||
| 	defer m.dataMtx.Unlock() | ||||
| 	return m.truncate(size) | ||||
| } | ||||
|  | ||||
| // +checklocks:m.dataMtx | ||||
| func (m *memFile) truncate(size int64) error { | ||||
| 	if size < m.size { | ||||
| 		base := size / sectorSize | ||||
| 		rest := size % sectorSize | ||||
| 		if rest != 0 { | ||||
| 			clear((*m.data[base])[rest:]) | ||||
| 		} | ||||
| 	} | ||||
| 	sectors := divRoundUp(size, sectorSize) | ||||
| 	for sectors > int64(len(m.data)) { | ||||
| 		m.data = append(m.data, new([sectorSize]byte)) | ||||
| 	} | ||||
| 	clear(m.data[sectors:]) | ||||
| 	m.data = m.data[:sectors] | ||||
| 	m.size = size | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *memFile) Sync(flag vfs.SyncFlag) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *memFile) Size() (int64, error) { | ||||
| 	m.dataMtx.RLock() | ||||
| 	defer m.dataMtx.RUnlock() | ||||
| 	return m.size, nil | ||||
| } | ||||
|  | ||||
| const spinWait = 25 * time.Microsecond | ||||
|  | ||||
| func (m *memFile) Lock(lock vfs.LockLevel) error { | ||||
| 	if m.lock >= lock { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if m.readOnly && lock >= vfs.LOCK_RESERVED { | ||||
| 		return sqlite3.IOERR_LOCK | ||||
| 	} | ||||
|  | ||||
| 	m.lockMtx.Lock() | ||||
| 	defer m.lockMtx.Unlock() | ||||
|  | ||||
| 	switch lock { | ||||
| 	case vfs.LOCK_SHARED: | ||||
| 		if m.pending != nil { | ||||
| 			return sqlite3.BUSY | ||||
| 		} | ||||
| 		m.shared++ | ||||
|  | ||||
| 	case vfs.LOCK_RESERVED: | ||||
| 		if m.reserved != nil { | ||||
| 			return sqlite3.BUSY | ||||
| 		} | ||||
| 		m.reserved = m | ||||
|  | ||||
| 	case vfs.LOCK_EXCLUSIVE: | ||||
| 		if m.lock < vfs.LOCK_PENDING { | ||||
| 			if m.pending != nil { | ||||
| 				return sqlite3.BUSY | ||||
| 			} | ||||
| 			m.lock = vfs.LOCK_PENDING | ||||
| 			m.pending = m | ||||
| 		} | ||||
|  | ||||
| 		for before := time.Now(); m.shared > 1; { | ||||
| 			if time.Since(before) > spinWait { | ||||
| 				return sqlite3.BUSY | ||||
| 			} | ||||
| 			m.lockMtx.Unlock() | ||||
| 			runtime.Gosched() | ||||
| 			m.lockMtx.Lock() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	m.lock = lock | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *memFile) Unlock(lock vfs.LockLevel) error { | ||||
| 	if m.lock <= lock { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	m.lockMtx.Lock() | ||||
| 	defer m.lockMtx.Unlock() | ||||
|  | ||||
| 	if m.pending == m { | ||||
| 		m.pending = nil | ||||
| 	} | ||||
| 	if m.reserved == m { | ||||
| 		m.reserved = nil | ||||
| 	} | ||||
| 	if lock < vfs.LOCK_SHARED { | ||||
| 		m.shared-- | ||||
| 	} | ||||
| 	m.lock = lock | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *memFile) CheckReservedLock() (bool, error) { | ||||
| 	if m.lock >= vfs.LOCK_RESERVED { | ||||
| 		return true, nil | ||||
| 	} | ||||
| 	m.lockMtx.Lock() | ||||
| 	defer m.lockMtx.Unlock() | ||||
| 	return m.reserved != nil, nil | ||||
| } | ||||
|  | ||||
| func (m *memFile) SectorSize() int { | ||||
| 	return sectorSize | ||||
| } | ||||
|  | ||||
| func (m *memFile) DeviceCharacteristics() vfs.DeviceCharacteristic { | ||||
| 	return vfs.IOCAP_ATOMIC | | ||||
| 		vfs.IOCAP_SEQUENTIAL | | ||||
| 		vfs.IOCAP_SAFE_APPEND | | ||||
| 		vfs.IOCAP_POWERSAFE_OVERWRITE | ||||
| } | ||||
|  | ||||
| func (m *memFile) SizeHint(size int64) error { | ||||
| 	m.dataMtx.Lock() | ||||
| 	defer m.dataMtx.Unlock() | ||||
| 	if size > m.size { | ||||
| 		return m.truncate(size) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *memFile) LockState() vfs.LockLevel { | ||||
| 	return m.lock | ||||
| } | ||||
|  | ||||
| func divRoundUp(a, b int64) int64 { | ||||
| 	return (a + b - 1) / b | ||||
| } | ||||
|  | ||||
| func modRoundUp(a, b int64) int64 { | ||||
| 	return b - (b-a%b)%b | ||||
| } | ||||
							
								
								
									
										33
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| //go:build (freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| func osUnlock(file *os.File, start, len int64) _ErrorCode { | ||||
| 	if start == 0 && len == 0 { | ||||
| 		err := unix.Flock(int(file.Fd()), unix.LOCK_UN) | ||||
| 		if err != nil { | ||||
| 			return _IOERR_UNLOCK | ||||
| 		} | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func osLock(file *os.File, how int, def _ErrorCode) _ErrorCode { | ||||
| 	err := unix.Flock(int(file.Fd()), how) | ||||
| 	return osLockErrorCode(err, def) | ||||
| } | ||||
|  | ||||
| func osReadLock(file *os.File, _ /*start*/, _ /*len*/ int64, _ /*timeout*/ time.Duration) _ErrorCode { | ||||
| 	return osLock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK) | ||||
| } | ||||
|  | ||||
| func osWriteLock(file *os.File, _ /*start*/, _ /*len*/ int64, _ /*timeout*/ time.Duration) _ErrorCode { | ||||
| 	return osLock(file, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK) | ||||
| } | ||||
							
								
								
									
										95
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| //go:build !(sqlite3_flock || sqlite3_nosys) | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// https://github.com/apple/darwin-xnu/blob/main/bsd/sys/fcntl.h | ||||
| 	_F_OFD_SETLK         = 90 | ||||
| 	_F_OFD_SETLKW        = 91 | ||||
| 	_F_OFD_SETLKWTIMEOUT = 93 | ||||
| ) | ||||
|  | ||||
| type flocktimeout_t struct { | ||||
| 	fl      unix.Flock_t | ||||
| 	timeout unix.Timespec | ||||
| } | ||||
|  | ||||
| func osSync(file *os.File, fullsync, _ /*dataonly*/ bool) error { | ||||
| 	if fullsync { | ||||
| 		return file.Sync() | ||||
| 	} | ||||
| 	return unix.Fsync(int(file.Fd())) | ||||
| } | ||||
|  | ||||
| func osAllocate(file *os.File, size int64) error { | ||||
| 	off, err := file.Seek(0, io.SeekEnd) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if size <= off { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	store := unix.Fstore_t{ | ||||
| 		Flags:   unix.F_ALLOCATEALL | unix.F_ALLOCATECONTIG, | ||||
| 		Posmode: unix.F_PEOFPOSMODE, | ||||
| 		Offset:  0, | ||||
| 		Length:  size - off, | ||||
| 	} | ||||
|  | ||||
| 	// Try to get a continuous chunk of disk space. | ||||
| 	err = unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store) | ||||
| 	if err != nil { | ||||
| 		// OK, perhaps we are too fragmented, allocate non-continuous. | ||||
| 		store.Flags = unix.F_ALLOCATEALL | ||||
| 		unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store) | ||||
| 	} | ||||
| 	return file.Truncate(size) | ||||
| } | ||||
|  | ||||
| func osUnlock(file *os.File, start, len int64) _ErrorCode { | ||||
| 	err := unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &unix.Flock_t{ | ||||
| 		Type:  unix.F_UNLCK, | ||||
| 		Start: start, | ||||
| 		Len:   len, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return _IOERR_UNLOCK | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode { | ||||
| 	lock := flocktimeout_t{fl: unix.Flock_t{ | ||||
| 		Type:  typ, | ||||
| 		Start: start, | ||||
| 		Len:   len, | ||||
| 	}} | ||||
| 	var err error | ||||
| 	switch { | ||||
| 	case timeout == 0: | ||||
| 		err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &lock.fl) | ||||
| 	case timeout < 0: | ||||
| 		err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKW, &lock.fl) | ||||
| 	default: | ||||
| 		lock.timeout = unix.NsecToTimespec(int64(timeout / time.Nanosecond)) | ||||
| 		err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKWTIMEOUT, &lock.fl) | ||||
| 	} | ||||
| 	return osLockErrorCode(err, def) | ||||
| } | ||||
|  | ||||
| func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode { | ||||
| 	return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK) | ||||
| } | ||||
|  | ||||
| func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode { | ||||
| 	return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK) | ||||
| } | ||||
							
								
								
									
										34
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_f2fs_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_f2fs_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| //go:build (amd64 || arm64 || riscv64) && !sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	_F2FS_IOC_START_ATOMIC_WRITE  = 62721 | ||||
| 	_F2FS_IOC_COMMIT_ATOMIC_WRITE = 62722 | ||||
| 	_F2FS_IOC_ABORT_ATOMIC_WRITE  = 62725 | ||||
| 	_F2FS_IOC_GET_FEATURES        = 2147808524 | ||||
| 	_F2FS_FEATURE_ATOMIC_WRITE    = 4 | ||||
| ) | ||||
|  | ||||
| func osBatchAtomic(file *os.File) bool { | ||||
| 	flags, err := unix.IoctlGetInt(int(file.Fd()), _F2FS_IOC_GET_FEATURES) | ||||
| 	return err == nil && flags&_F2FS_FEATURE_ATOMIC_WRITE != 0 | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) BeginAtomicWrite() error { | ||||
| 	return unix.IoctlSetInt(int(f.Fd()), _F2FS_IOC_START_ATOMIC_WRITE, 0) | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) CommitAtomicWrite() error { | ||||
| 	return unix.IoctlSetInt(int(f.Fd()), _F2FS_IOC_COMMIT_ATOMIC_WRITE, 0) | ||||
| } | ||||
|  | ||||
| func (f *vfsFile) RollbackAtomicWrite() error { | ||||
| 	return unix.IoctlSetInt(int(f.Fd()), _F2FS_IOC_ABORT_ATOMIC_WRITE, 0) | ||||
| } | ||||
							
								
								
									
										71
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| //go:build !(sqlite3_flock || sqlite3_nosys) | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"math/rand" | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| func osSync(file *os.File, _ /*fullsync*/, _ /*dataonly*/ bool) error { | ||||
| 	// SQLite trusts Linux's fdatasync for all fsync's. | ||||
| 	return unix.Fdatasync(int(file.Fd())) | ||||
| } | ||||
|  | ||||
| func osAllocate(file *os.File, size int64) error { | ||||
| 	if size == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return unix.Fallocate(int(file.Fd()), 0, 0, size) | ||||
| } | ||||
|  | ||||
| func osUnlock(file *os.File, start, len int64) _ErrorCode { | ||||
| 	err := unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &unix.Flock_t{ | ||||
| 		Type:  unix.F_UNLCK, | ||||
| 		Start: start, | ||||
| 		Len:   len, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return _IOERR_UNLOCK | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode { | ||||
| 	lock := unix.Flock_t{ | ||||
| 		Type:  typ, | ||||
| 		Start: start, | ||||
| 		Len:   len, | ||||
| 	} | ||||
| 	var err error | ||||
| 	switch { | ||||
| 	case timeout == 0: | ||||
| 		err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock) | ||||
| 	case timeout < 0: | ||||
| 		err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLKW, &lock) | ||||
| 	default: | ||||
| 		before := time.Now() | ||||
| 		for { | ||||
| 			err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock) | ||||
| 			if errno, _ := err.(unix.Errno); errno != unix.EAGAIN { | ||||
| 				break | ||||
| 			} | ||||
| 			if timeout < time.Since(before) { | ||||
| 				break | ||||
| 			} | ||||
| 			osSleep(time.Duration(rand.Int63n(int64(time.Millisecond)))) | ||||
| 		} | ||||
| 	} | ||||
| 	return osLockErrorCode(err, def) | ||||
| } | ||||
|  | ||||
| func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode { | ||||
| 	return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK) | ||||
| } | ||||
|  | ||||
| func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode { | ||||
| 	return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK) | ||||
| } | ||||
							
								
								
									
										36
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_access.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_access.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| //go:build !unix || sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"io/fs" | ||||
| 	"os" | ||||
| ) | ||||
|  | ||||
| func osAccess(path string, flags AccessFlag) error { | ||||
| 	fi, err := os.Stat(path) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if flags == ACCESS_EXISTS { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	const ( | ||||
| 		S_IREAD  = 0400 | ||||
| 		S_IWRITE = 0200 | ||||
| 		S_IEXEC  = 0100 | ||||
| 	) | ||||
|  | ||||
| 	var want fs.FileMode = S_IREAD | ||||
| 	if flags == ACCESS_READWRITE { | ||||
| 		want |= S_IWRITE | ||||
| 	} | ||||
| 	if fi.IsDir() { | ||||
| 		want |= S_IEXEC | ||||
| 	} | ||||
| 	if fi.Mode()&want != want { | ||||
| 		return fs.ErrPermission | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										19
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_alloc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_alloc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| //go:build !(linux || darwin) || sqlite3_flock || sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"os" | ||||
| ) | ||||
|  | ||||
| func osAllocate(file *os.File, size int64) error { | ||||
| 	off, err := file.Seek(0, io.SeekEnd) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if size <= off { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return file.Truncate(size) | ||||
| } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_atomic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_atomic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| //go:build !linux || !(amd64 || arm64 || riscv64) || sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import "os" | ||||
|  | ||||
| func osBatchAtomic(*os.File) bool { | ||||
| 	return false | ||||
| } | ||||
							
								
								
									
										14
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_mode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_mode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| //go:build !unix || sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import "os" | ||||
|  | ||||
| func osSetMode(file *os.File, modeof string) error { | ||||
| 	fi, err := os.Stat(modeof) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	file.Chmod(fi.Mode()) | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sleep.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sleep.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| //go:build !windows || sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import "time" | ||||
|  | ||||
| func osSleep(d time.Duration) { | ||||
| 	time.Sleep(d) | ||||
| } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| //go:build !(linux || darwin) || sqlite3_flock || sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import "os" | ||||
|  | ||||
| func osSync(file *os.File, _ /*fullsync*/, _ /*dataonly*/ bool) error { | ||||
| 	return file.Sync() | ||||
| } | ||||
							
								
								
									
										33
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| //go:build unix && !sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"syscall" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| func osAccess(path string, flags AccessFlag) error { | ||||
| 	var access uint32 // unix.F_OK | ||||
| 	switch flags { | ||||
| 	case ACCESS_READWRITE: | ||||
| 		access = unix.R_OK | unix.W_OK | ||||
| 	case ACCESS_READ: | ||||
| 		access = unix.R_OK | ||||
| 	} | ||||
| 	return unix.Access(path, access) | ||||
| } | ||||
|  | ||||
| func osSetMode(file *os.File, modeof string) error { | ||||
| 	fi, err := os.Stat(modeof) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	file.Chmod(fi.Mode()) | ||||
| 	if sys, ok := fi.Sys().(*syscall.Stat_t); ok { | ||||
| 		file.Chown(int(sys.Uid), int(sys.Gid)) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										106
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| //go:build (linux || darwin || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| func osGetSharedLock(file *os.File) _ErrorCode { | ||||
| 	// Test the PENDING lock before acquiring a new SHARED lock. | ||||
| 	if lock, _ := osGetLock(file, _PENDING_BYTE, 1); lock == unix.F_WRLCK { | ||||
| 		return _BUSY | ||||
| 	} | ||||
| 	// Acquire the SHARED lock. | ||||
| 	return osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) | ||||
| } | ||||
|  | ||||
| func osGetReservedLock(file *os.File) _ErrorCode { | ||||
| 	// Acquire the RESERVED lock. | ||||
| 	return osWriteLock(file, _RESERVED_BYTE, 1, 0) | ||||
| } | ||||
|  | ||||
| func osGetPendingLock(file *os.File, block bool) _ErrorCode { | ||||
| 	var timeout time.Duration | ||||
| 	if block { | ||||
| 		timeout = -1 | ||||
| 	} | ||||
| 	// Acquire the PENDING lock. | ||||
| 	return osWriteLock(file, _PENDING_BYTE, 1, timeout) | ||||
| } | ||||
|  | ||||
| func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode { | ||||
| 	var timeout time.Duration | ||||
| 	if wait { | ||||
| 		timeout = time.Millisecond | ||||
| 	} | ||||
| 	// Acquire the EXCLUSIVE lock. | ||||
| 	return osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout) | ||||
| } | ||||
|  | ||||
| func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode { | ||||
| 	if state >= LOCK_EXCLUSIVE { | ||||
| 		// Downgrade to a SHARED lock. | ||||
| 		if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); 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 osUnlock(file, _PENDING_BYTE, 2) | ||||
| } | ||||
|  | ||||
| func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode { | ||||
| 	// Release all locks. | ||||
| 	return osUnlock(file, 0, 0) | ||||
| } | ||||
|  | ||||
| func osCheckReservedLock(file *os.File) (bool, _ErrorCode) { | ||||
| 	// Test the RESERVED lock. | ||||
| 	lock, rc := osGetLock(file, _RESERVED_BYTE, 1) | ||||
| 	return lock == unix.F_WRLCK, rc | ||||
| } | ||||
|  | ||||
| func osGetLock(file *os.File, start, len int64) (int16, _ErrorCode) { | ||||
| 	lock := unix.Flock_t{ | ||||
| 		Type:  unix.F_WRLCK, | ||||
| 		Start: start, | ||||
| 		Len:   len, | ||||
| 	} | ||||
| 	if unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock) != nil { | ||||
| 		return 0, _IOERR_CHECKRESERVEDLOCK | ||||
| 	} | ||||
| 	return lock.Type, _OK | ||||
| } | ||||
|  | ||||
| func osLockErrorCode(err error, def _ErrorCode) _ErrorCode { | ||||
| 	if err == nil { | ||||
| 		return _OK | ||||
| 	} | ||||
| 	if errno, ok := err.(unix.Errno); ok { | ||||
| 		switch errno { | ||||
| 		case | ||||
| 			unix.EACCES, | ||||
| 			unix.EAGAIN, | ||||
| 			unix.EBUSY, | ||||
| 			unix.EINTR, | ||||
| 			unix.ENOLCK, | ||||
| 			unix.EDEADLK, | ||||
| 			unix.ETIMEDOUT: | ||||
| 			return _BUSY | ||||
| 		case unix.EPERM: | ||||
| 			return _PERM | ||||
| 		} | ||||
| 		if errno == unix.EWOULDBLOCK && unix.EWOULDBLOCK != unix.EAGAIN { | ||||
| 			return _BUSY | ||||
| 		} | ||||
| 	} | ||||
| 	return def | ||||
| } | ||||
							
								
								
									
										186
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,186 @@ | ||||
| //go:build !sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"math/rand" | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
|  | ||||
| func osGetSharedLock(file *os.File) _ErrorCode { | ||||
| 	// Acquire the PENDING lock temporarily before acquiring a new SHARED lock. | ||||
| 	rc := osReadLock(file, _PENDING_BYTE, 1, 0) | ||||
| 	if rc == _OK { | ||||
| 		// Acquire the SHARED lock. | ||||
| 		rc = osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) | ||||
|  | ||||
| 		// Release the PENDING lock. | ||||
| 		osUnlock(file, _PENDING_BYTE, 1) | ||||
| 	} | ||||
| 	return rc | ||||
| } | ||||
|  | ||||
| func osGetReservedLock(file *os.File) _ErrorCode { | ||||
| 	// Acquire the RESERVED lock. | ||||
| 	return osWriteLock(file, _RESERVED_BYTE, 1, 0) | ||||
| } | ||||
|  | ||||
| func osGetPendingLock(file *os.File, block bool) _ErrorCode { | ||||
| 	var timeout time.Duration | ||||
| 	if block { | ||||
| 		timeout = -1 | ||||
| 	} | ||||
|  | ||||
| 	// Acquire the PENDING lock. | ||||
| 	return osWriteLock(file, _PENDING_BYTE, 1, timeout) | ||||
| } | ||||
|  | ||||
| func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode { | ||||
| 	var timeout time.Duration | ||||
| 	if wait { | ||||
| 		timeout = time.Millisecond | ||||
| 	} | ||||
|  | ||||
| 	// Release the SHARED lock. | ||||
| 	osUnlock(file, _SHARED_FIRST, _SHARED_SIZE) | ||||
|  | ||||
| 	// Acquire the EXCLUSIVE lock. | ||||
| 	rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout) | ||||
|  | ||||
| 	if rc != _OK { | ||||
| 		// Reacquire the SHARED lock. | ||||
| 		osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) | ||||
| 	} | ||||
| 	return rc | ||||
| } | ||||
|  | ||||
| func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode { | ||||
| 	if state >= LOCK_EXCLUSIVE { | ||||
| 		// Release the EXCLUSIVE lock. | ||||
| 		osUnlock(file, _SHARED_FIRST, _SHARED_SIZE) | ||||
|  | ||||
| 		// Reacquire the SHARED lock. | ||||
| 		if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); 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. | ||||
| 	if state >= LOCK_RESERVED { | ||||
| 		osUnlock(file, _RESERVED_BYTE, 1) | ||||
| 	} | ||||
| 	if state >= LOCK_PENDING { | ||||
| 		osUnlock(file, _PENDING_BYTE, 1) | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func osReleaseLock(file *os.File, state LockLevel) _ErrorCode { | ||||
| 	// Release all locks. | ||||
| 	if state >= LOCK_RESERVED { | ||||
| 		osUnlock(file, _RESERVED_BYTE, 1) | ||||
| 	} | ||||
| 	if state >= LOCK_SHARED { | ||||
| 		osUnlock(file, _SHARED_FIRST, _SHARED_SIZE) | ||||
| 	} | ||||
| 	if state >= LOCK_PENDING { | ||||
| 		osUnlock(file, _PENDING_BYTE, 1) | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func osCheckReservedLock(file *os.File) (bool, _ErrorCode) { | ||||
| 	// Test the RESERVED lock. | ||||
| 	rc := osLock(file, 0, _RESERVED_BYTE, 1, 0, _IOERR_CHECKRESERVEDLOCK) | ||||
| 	if rc == _BUSY { | ||||
| 		return true, _OK | ||||
| 	} | ||||
| 	if rc == _OK { | ||||
| 		// Release the RESERVED lock. | ||||
| 		osUnlock(file, _RESERVED_BYTE, 1) | ||||
| 	} | ||||
| 	return false, rc | ||||
| } | ||||
|  | ||||
| func osUnlock(file *os.File, start, len uint32) _ErrorCode { | ||||
| 	err := windows.UnlockFileEx(windows.Handle(file.Fd()), | ||||
| 		0, len, 0, &windows.Overlapped{Offset: start}) | ||||
| 	if err == windows.ERROR_NOT_LOCKED { | ||||
| 		return _OK | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return _IOERR_UNLOCK | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def _ErrorCode) _ErrorCode { | ||||
| 	var err error | ||||
| 	switch { | ||||
| 	case timeout == 0: | ||||
| 		err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len) | ||||
| 	case timeout < 0: | ||||
| 		err = osLockEx(file, flags, start, len) | ||||
| 	default: | ||||
| 		before := time.Now() | ||||
| 		for { | ||||
| 			err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len) | ||||
| 			if errno, _ := err.(windows.Errno); errno != windows.ERROR_LOCK_VIOLATION { | ||||
| 				break | ||||
| 			} | ||||
| 			if timeout < time.Since(before) { | ||||
| 				break | ||||
| 			} | ||||
| 			osSleep(time.Duration(rand.Int63n(int64(time.Millisecond)))) | ||||
| 		} | ||||
| 	} | ||||
| 	return osLockErrorCode(err, def) | ||||
| } | ||||
|  | ||||
| func osLockEx(file *os.File, flags, start, len uint32) error { | ||||
| 	return windows.LockFileEx(windows.Handle(file.Fd()), flags, | ||||
| 		0, len, 0, &windows.Overlapped{Offset: start}) | ||||
| } | ||||
|  | ||||
| func osReadLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode { | ||||
| 	return osLock(file, 0, start, len, timeout, _IOERR_RDLOCK) | ||||
| } | ||||
|  | ||||
| func osWriteLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode { | ||||
| 	return osLock(file, windows.LOCKFILE_EXCLUSIVE_LOCK, start, len, timeout, _IOERR_LOCK) | ||||
| } | ||||
|  | ||||
| func osLockErrorCode(err error, def _ErrorCode) _ErrorCode { | ||||
| 	if err == nil { | ||||
| 		return _OK | ||||
| 	} | ||||
| 	if errno, ok := err.(windows.Errno); ok { | ||||
| 		// https://devblogs.microsoft.com/oldnewthing/20140905-00/?p=63 | ||||
| 		switch errno { | ||||
| 		case | ||||
| 			windows.ERROR_LOCK_VIOLATION, | ||||
| 			windows.ERROR_IO_PENDING, | ||||
| 			windows.ERROR_OPERATION_ABORTED: | ||||
| 			return _BUSY | ||||
| 		} | ||||
| 	} | ||||
| 	return def | ||||
| } | ||||
|  | ||||
| func osSleep(d time.Duration) { | ||||
| 	if d > 0 { | ||||
| 		period := max(1, d/(5*time.Millisecond)) | ||||
| 		if period < 16 { | ||||
| 			windows.TimeBeginPeriod(uint32(period)) | ||||
| 		} | ||||
| 		time.Sleep(d) | ||||
| 		if period < 16 { | ||||
| 			windows.TimeEndPeriod(uint32(period)) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										48
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/registry.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/registry.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| package vfs | ||||
|  | ||||
| import "sync" | ||||
|  | ||||
| var ( | ||||
| 	// +checklocks:vfsRegistryMtx | ||||
| 	vfsRegistry    map[string]VFS | ||||
| 	vfsRegistryMtx sync.RWMutex | ||||
| ) | ||||
|  | ||||
| // Find returns a VFS given its name. | ||||
| // If there is no match, nil is returned. | ||||
| // If name is empty, the default VFS is returned. | ||||
| // | ||||
| // https://sqlite.org/c3ref/vfs_find.html | ||||
| func Find(name string) VFS { | ||||
| 	if name == "" || name == "os" { | ||||
| 		return vfsOS{} | ||||
| 	} | ||||
| 	vfsRegistryMtx.RLock() | ||||
| 	defer vfsRegistryMtx.RUnlock() | ||||
| 	return vfsRegistry[name] | ||||
| } | ||||
|  | ||||
| // Register registers a VFS. | ||||
| // Empty and "os" are reserved names. | ||||
| // | ||||
| // https://sqlite.org/c3ref/vfs_find.html | ||||
| func Register(name string, vfs VFS) { | ||||
| 	if name == "" || name == "os" { | ||||
| 		return | ||||
| 	} | ||||
| 	vfsRegistryMtx.Lock() | ||||
| 	defer vfsRegistryMtx.Unlock() | ||||
| 	if vfsRegistry == nil { | ||||
| 		vfsRegistry = map[string]VFS{} | ||||
| 	} | ||||
| 	vfsRegistry[name] = vfs | ||||
| } | ||||
|  | ||||
| // Unregister unregisters a VFS. | ||||
| // | ||||
| // https://sqlite.org/c3ref/vfs_find.html | ||||
| func Unregister(name string) { | ||||
| 	vfsRegistryMtx.Lock() | ||||
| 	defer vfsRegistryMtx.Unlock() | ||||
| 	delete(vfsRegistry, name) | ||||
| } | ||||
							
								
								
									
										173
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/shm.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/shm.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | ||||
| //go:build (darwin || linux) && (amd64 || arm64 || riscv64) && !(sqlite3_flock || sqlite3_noshm || sqlite3_nosys) | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"io" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| // SupportsSharedMemory is false on platforms that do not support shared memory. | ||||
| // To use [WAL without shared-memory], you need to set [EXCLUSIVE locking mode]. | ||||
| // | ||||
| // [WAL without shared-memory]: https://sqlite.org/wal.html#noshm | ||||
| // [EXCLUSIVE locking mode]: https://sqlite.org/pragma.html#pragma_locking_mode | ||||
| const SupportsSharedMemory = true | ||||
|  | ||||
| const ( | ||||
| 	_SHM_NLOCK = 8 | ||||
| 	_SHM_BASE  = 120 | ||||
| 	_SHM_DMS   = _SHM_BASE + _SHM_NLOCK | ||||
| ) | ||||
|  | ||||
| func (f *vfsFile) SharedMemory() SharedMemory { return f.shm } | ||||
|  | ||||
| // NewSharedMemory returns a shared-memory WAL-index | ||||
| // backed by a file with the given path. | ||||
| // It will return nil if shared-memory is not supported, | ||||
| // or not appropriate for the given flags. | ||||
| // Only [OPEN_MAIN_DB] databases may need a WAL-index. | ||||
| // You must ensure all concurrent accesses to a database | ||||
| // use shared-memory instances created with the same path. | ||||
| func NewSharedMemory(path string, flags OpenFlag) SharedMemory { | ||||
| 	if flags&OPEN_MAIN_DB == 0 || flags&(OPEN_DELETEONCLOSE|OPEN_MEMORY) != 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &vfsShm{ | ||||
| 		path:     path, | ||||
| 		readOnly: flags&OPEN_READONLY != 0, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type vfsShm struct { | ||||
| 	*os.File | ||||
| 	path     string | ||||
| 	regions  []*util.MappedRegion | ||||
| 	readOnly bool | ||||
| } | ||||
|  | ||||
| func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, error) { | ||||
| 	// Ensure size is a multiple of the OS page size. | ||||
| 	if int(size)&(unix.Getpagesize()-1) != 0 { | ||||
| 		return 0, _IOERR_SHMMAP | ||||
| 	} | ||||
|  | ||||
| 	if s.File == nil { | ||||
| 		var flag int | ||||
| 		if s.readOnly { | ||||
| 			flag = unix.O_RDONLY | ||||
| 		} else { | ||||
| 			flag = unix.O_RDWR | ||||
| 		} | ||||
| 		f, err := os.OpenFile(s.path, | ||||
| 			flag|unix.O_CREAT|unix.O_NOFOLLOW, 0666) | ||||
| 		if err != nil { | ||||
| 			return 0, _CANTOPEN | ||||
| 		} | ||||
| 		s.File = f | ||||
| 	} | ||||
|  | ||||
| 	// Dead man's switch. | ||||
| 	if lock, rc := osGetLock(s.File, _SHM_DMS, 1); rc != _OK { | ||||
| 		return 0, _IOERR_LOCK | ||||
| 	} else if lock == unix.F_WRLCK { | ||||
| 		return 0, _BUSY | ||||
| 	} else if lock == unix.F_UNLCK { | ||||
| 		if s.readOnly { | ||||
| 			return 0, _READONLY_CANTINIT | ||||
| 		} | ||||
| 		if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc != _OK { | ||||
| 			return 0, rc | ||||
| 		} | ||||
| 		if err := s.Truncate(0); err != nil { | ||||
| 			return 0, _IOERR_SHMOPEN | ||||
| 		} | ||||
| 	} | ||||
| 	if rc := osReadLock(s.File, _SHM_DMS, 1, 0); rc != _OK { | ||||
| 		return 0, rc | ||||
| 	} | ||||
|  | ||||
| 	// Check if file is big enough. | ||||
| 	o, err := s.Seek(0, io.SeekEnd) | ||||
| 	if err != nil { | ||||
| 		return 0, _IOERR_SHMSIZE | ||||
| 	} | ||||
| 	if n := (int64(id) + 1) * int64(size); n > o { | ||||
| 		if !extend { | ||||
| 			return 0, nil | ||||
| 		} | ||||
| 		err := osAllocate(s.File, n) | ||||
| 		if err != nil { | ||||
| 			return 0, _IOERR_SHMSIZE | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var prot int | ||||
| 	if s.readOnly { | ||||
| 		prot = unix.PROT_READ | ||||
| 	} else { | ||||
| 		prot = unix.PROT_READ | unix.PROT_WRITE | ||||
| 	} | ||||
| 	r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size, prot) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	s.regions = append(s.regions, r) | ||||
| 	return r.Ptr, nil | ||||
| } | ||||
|  | ||||
| func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) error { | ||||
| 	// Argument check. | ||||
| 	if n <= 0 || offset < 0 || offset+n > _SHM_NLOCK { | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 	switch flags { | ||||
| 	case | ||||
| 		_SHM_LOCK | _SHM_SHARED, | ||||
| 		_SHM_LOCK | _SHM_EXCLUSIVE, | ||||
| 		_SHM_UNLOCK | _SHM_SHARED, | ||||
| 		_SHM_UNLOCK | _SHM_EXCLUSIVE: | ||||
| 		// | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 	if n != 1 && flags&_SHM_EXCLUSIVE == 0 { | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
|  | ||||
| 	switch { | ||||
| 	case flags&_SHM_UNLOCK != 0: | ||||
| 		return osUnlock(s.File, _SHM_BASE+int64(offset), int64(n)) | ||||
| 	case flags&_SHM_SHARED != 0: | ||||
| 		return osReadLock(s.File, _SHM_BASE+int64(offset), int64(n), 0) | ||||
| 	case flags&_SHM_EXCLUSIVE != 0: | ||||
| 		return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), 0) | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (s *vfsShm) shmUnmap(delete bool) { | ||||
| 	if s.File == nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Unmap regions. | ||||
| 	for _, r := range s.regions { | ||||
| 		r.Unmap() | ||||
| 	} | ||||
| 	clear(s.regions) | ||||
| 	s.regions = s.regions[:0] | ||||
|  | ||||
| 	// Close the file. | ||||
| 	defer s.Close() | ||||
| 	if delete { | ||||
| 		os.Remove(s.Name()) | ||||
| 	} | ||||
| 	s.File = nil | ||||
| } | ||||
							
								
								
									
										21
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| //go:build !(darwin || linux) || !(amd64 || arm64 || riscv64) || sqlite3_flock || sqlite3_noshm || sqlite3_nosys | ||||
|  | ||||
| package vfs | ||||
|  | ||||
| // SupportsSharedMemory is false on platforms that do not support shared memory. | ||||
| // To use [WAL without shared-memory], you need to set [EXCLUSIVE locking mode]. | ||||
| // | ||||
| // [WAL without shared-memory]: https://sqlite.org/wal.html#noshm | ||||
| // [EXCLUSIVE locking mode]: https://sqlite.org/pragma.html#pragma_locking_mode | ||||
| const SupportsSharedMemory = false | ||||
|  | ||||
| // NewSharedMemory returns a shared-memory WAL-index | ||||
| // backed by a file with the given path. | ||||
| // It will return nil if shared-memory is not supported, | ||||
| // or not appropriate for the given flags. | ||||
| // Only [OPEN_MAIN_DB] databases may need a WAL-index. | ||||
| // You must ensure all concurrent accesses to a database | ||||
| // use shared-memory instances created with the same path. | ||||
| func NewSharedMemory(path string, flags OpenFlag) SharedMemory { | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										459
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										459
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,459 @@ | ||||
| package vfs | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/rand" | ||||
| 	"io" | ||||
| 	"reflect" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| 	"github.com/ncruces/julianday" | ||||
| 	"github.com/tetratelabs/wazero" | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| ) | ||||
|  | ||||
| // ExportHostFunctions is an internal API users need not call directly. | ||||
| // | ||||
| // ExportHostFunctions registers the required VFS host functions | ||||
| // with the provided env module. | ||||
| func ExportHostFunctions(env wazero.HostModuleBuilder) wazero.HostModuleBuilder { | ||||
| 	util.ExportFuncII(env, "go_vfs_find", vfsFind) | ||||
| 	util.ExportFuncIIJ(env, "go_localtime", vfsLocaltime) | ||||
| 	util.ExportFuncIIII(env, "go_randomness", vfsRandomness) | ||||
| 	util.ExportFuncIII(env, "go_sleep", vfsSleep) | ||||
| 	util.ExportFuncIII(env, "go_current_time_64", vfsCurrentTime64) | ||||
| 	util.ExportFuncIIIII(env, "go_full_pathname", vfsFullPathname) | ||||
| 	util.ExportFuncIIII(env, "go_delete", vfsDelete) | ||||
| 	util.ExportFuncIIIII(env, "go_access", vfsAccess) | ||||
| 	util.ExportFuncIIIIIII(env, "go_open", vfsOpen) | ||||
| 	util.ExportFuncII(env, "go_close", vfsClose) | ||||
| 	util.ExportFuncIIIIJ(env, "go_read", vfsRead) | ||||
| 	util.ExportFuncIIIIJ(env, "go_write", vfsWrite) | ||||
| 	util.ExportFuncIIJ(env, "go_truncate", vfsTruncate) | ||||
| 	util.ExportFuncIII(env, "go_sync", vfsSync) | ||||
| 	util.ExportFuncIII(env, "go_file_size", vfsFileSize) | ||||
| 	util.ExportFuncIIII(env, "go_file_control", vfsFileControl) | ||||
| 	util.ExportFuncII(env, "go_sector_size", vfsSectorSize) | ||||
| 	util.ExportFuncII(env, "go_device_characteristics", vfsDeviceCharacteristics) | ||||
| 	util.ExportFuncIII(env, "go_lock", vfsLock) | ||||
| 	util.ExportFuncIII(env, "go_unlock", vfsUnlock) | ||||
| 	util.ExportFuncIII(env, "go_check_reserved_lock", vfsCheckReservedLock) | ||||
| 	util.ExportFuncIIIIII(env, "go_shm_map", vfsShmMap) | ||||
| 	util.ExportFuncIIIII(env, "go_shm_lock", vfsShmLock) | ||||
| 	util.ExportFuncIII(env, "go_shm_unmap", vfsShmUnmap) | ||||
| 	util.ExportFuncVI(env, "go_shm_barrier", vfsShmBarrier) | ||||
| 	return env | ||||
| } | ||||
|  | ||||
| func vfsFind(ctx context.Context, mod api.Module, zVfsName uint32) uint32 { | ||||
| 	name := util.ReadString(mod, zVfsName, _MAX_NAME) | ||||
| 	if vfs := Find(name); vfs != nil && vfs != (vfsOS{}) { | ||||
| 		return 1 | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func vfsLocaltime(ctx context.Context, mod api.Module, pTm uint32, t int64) _ErrorCode { | ||||
| 	tm := time.Unix(t, 0) | ||||
| 	var isdst int | ||||
| 	if tm.IsDST() { | ||||
| 		isdst = 1 | ||||
| 	} | ||||
|  | ||||
| 	const size = 32 / 8 | ||||
| 	// https://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html | ||||
| 	util.WriteUint32(mod, pTm+0*size, uint32(tm.Second())) | ||||
| 	util.WriteUint32(mod, pTm+1*size, uint32(tm.Minute())) | ||||
| 	util.WriteUint32(mod, pTm+2*size, uint32(tm.Hour())) | ||||
| 	util.WriteUint32(mod, pTm+3*size, uint32(tm.Day())) | ||||
| 	util.WriteUint32(mod, pTm+4*size, uint32(tm.Month()-time.January)) | ||||
| 	util.WriteUint32(mod, pTm+5*size, uint32(tm.Year()-1900)) | ||||
| 	util.WriteUint32(mod, pTm+6*size, uint32(tm.Weekday()-time.Sunday)) | ||||
| 	util.WriteUint32(mod, pTm+7*size, uint32(tm.YearDay()-1)) | ||||
| 	util.WriteUint32(mod, pTm+8*size, uint32(isdst)) | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func vfsRandomness(ctx context.Context, mod api.Module, pVfs uint32, nByte int32, zByte uint32) uint32 { | ||||
| 	mem := util.View(mod, zByte, uint64(nByte)) | ||||
| 	n, _ := rand.Reader.Read(mem) | ||||
| 	return uint32(n) | ||||
| } | ||||
|  | ||||
| func vfsSleep(ctx context.Context, mod api.Module, pVfs uint32, nMicro int32) _ErrorCode { | ||||
| 	osSleep(time.Duration(nMicro) * time.Microsecond) | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func vfsCurrentTime64(ctx context.Context, mod api.Module, pVfs, piNow uint32) _ErrorCode { | ||||
| 	day, nsec := julianday.Date(time.Now()) | ||||
| 	msec := day*86_400_000 + nsec/1_000_000 | ||||
| 	util.WriteUint64(mod, piNow, uint64(msec)) | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func vfsFullPathname(ctx context.Context, mod api.Module, pVfs, zRelative uint32, nFull int32, zFull uint32) _ErrorCode { | ||||
| 	vfs := vfsGet(mod, pVfs) | ||||
| 	path := util.ReadString(mod, zRelative, _MAX_PATHNAME) | ||||
|  | ||||
| 	path, err := vfs.FullPathname(path) | ||||
|  | ||||
| 	if len(path) >= int(nFull) { | ||||
| 		return _CANTOPEN_FULLPATH | ||||
| 	} | ||||
| 	util.WriteString(mod, zFull, path) | ||||
|  | ||||
| 	return vfsErrorCode(err, _CANTOPEN_FULLPATH) | ||||
| } | ||||
|  | ||||
| func vfsDelete(ctx context.Context, mod api.Module, pVfs, zPath, syncDir uint32) _ErrorCode { | ||||
| 	vfs := vfsGet(mod, pVfs) | ||||
| 	path := util.ReadString(mod, zPath, _MAX_PATHNAME) | ||||
|  | ||||
| 	err := vfs.Delete(path, syncDir != 0) | ||||
| 	return vfsErrorCode(err, _IOERR_DELETE) | ||||
| } | ||||
|  | ||||
| func vfsAccess(ctx context.Context, mod api.Module, pVfs, zPath uint32, flags AccessFlag, pResOut uint32) _ErrorCode { | ||||
| 	vfs := vfsGet(mod, pVfs) | ||||
| 	path := util.ReadString(mod, zPath, _MAX_PATHNAME) | ||||
|  | ||||
| 	ok, err := vfs.Access(path, flags) | ||||
| 	var res uint32 | ||||
| 	if ok { | ||||
| 		res = 1 | ||||
| 	} | ||||
|  | ||||
| 	util.WriteUint32(mod, pResOut, res) | ||||
| 	return vfsErrorCode(err, _IOERR_ACCESS) | ||||
| } | ||||
|  | ||||
| func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, flags OpenFlag, pOutFlags, pOutVFS uint32) _ErrorCode { | ||||
| 	vfs := vfsGet(mod, pVfs) | ||||
|  | ||||
| 	var path string | ||||
| 	if zPath != 0 { | ||||
| 		path = util.ReadString(mod, zPath, _MAX_PATHNAME) | ||||
| 	} | ||||
|  | ||||
| 	var file File | ||||
| 	var err error | ||||
| 	if ffs, ok := vfs.(VFSFilename); ok { | ||||
| 		name := OpenFilename(ctx, mod, zPath, flags) | ||||
| 		file, flags, err = ffs.OpenFilename(name, flags) | ||||
| 	} else { | ||||
| 		file, flags, err = vfs.Open(path, flags) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return vfsErrorCode(err, _CANTOPEN) | ||||
| 	} | ||||
|  | ||||
| 	if file, ok := file.(FilePowersafeOverwrite); ok { | ||||
| 		name := OpenFilename(ctx, mod, zPath, flags) | ||||
| 		if b, ok := util.ParseBool(name.URIParameter("psow")); ok { | ||||
| 			file.SetPowersafeOverwrite(b) | ||||
| 		} | ||||
| 	} | ||||
| 	if file, ok := file.(FileSharedMemory); ok && | ||||
| 		pOutVFS != 0 && file.SharedMemory() != nil { | ||||
| 		util.WriteUint32(mod, pOutVFS, 1) | ||||
| 	} | ||||
| 	if pOutFlags != 0 { | ||||
| 		util.WriteUint32(mod, pOutFlags, uint32(flags)) | ||||
| 	} | ||||
| 	vfsFileRegister(ctx, mod, pFile, file) | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func vfsClose(ctx context.Context, mod api.Module, pFile uint32) _ErrorCode { | ||||
| 	err := vfsFileClose(ctx, mod, pFile) | ||||
| 	if err != nil { | ||||
| 		return vfsErrorCode(err, _IOERR_CLOSE) | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32, iOfst int64) _ErrorCode { | ||||
| 	file := vfsFileGet(ctx, mod, pFile).(File) | ||||
| 	buf := util.View(mod, zBuf, uint64(iAmt)) | ||||
|  | ||||
| 	n, err := file.ReadAt(buf, iOfst) | ||||
| 	if n == int(iAmt) { | ||||
| 		return _OK | ||||
| 	} | ||||
| 	if err != io.EOF { | ||||
| 		return vfsErrorCode(err, _IOERR_READ) | ||||
| 	} | ||||
| 	clear(buf[n:]) | ||||
| 	return _IOERR_SHORT_READ | ||||
| } | ||||
|  | ||||
| func vfsWrite(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32, iOfst int64) _ErrorCode { | ||||
| 	file := vfsFileGet(ctx, mod, pFile).(File) | ||||
| 	buf := util.View(mod, zBuf, uint64(iAmt)) | ||||
|  | ||||
| 	_, err := file.WriteAt(buf, iOfst) | ||||
| 	if err != nil { | ||||
| 		return vfsErrorCode(err, _IOERR_WRITE) | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func vfsTruncate(ctx context.Context, mod api.Module, pFile uint32, nByte int64) _ErrorCode { | ||||
| 	file := vfsFileGet(ctx, mod, pFile).(File) | ||||
| 	err := file.Truncate(nByte) | ||||
| 	return vfsErrorCode(err, _IOERR_TRUNCATE) | ||||
| } | ||||
|  | ||||
| func vfsSync(ctx context.Context, mod api.Module, pFile uint32, flags SyncFlag) _ErrorCode { | ||||
| 	file := vfsFileGet(ctx, mod, pFile).(File) | ||||
| 	err := file.Sync(flags) | ||||
| 	return vfsErrorCode(err, _IOERR_FSYNC) | ||||
| } | ||||
|  | ||||
| func vfsFileSize(ctx context.Context, mod api.Module, pFile, pSize uint32) _ErrorCode { | ||||
| 	file := vfsFileGet(ctx, mod, pFile).(File) | ||||
| 	size, err := file.Size() | ||||
| 	util.WriteUint64(mod, pSize, uint64(size)) | ||||
| 	return vfsErrorCode(err, _IOERR_SEEK) | ||||
| } | ||||
|  | ||||
| func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode { | ||||
| 	file := vfsFileGet(ctx, mod, pFile).(File) | ||||
| 	err := file.Lock(eLock) | ||||
| 	return vfsErrorCode(err, _IOERR_LOCK) | ||||
| } | ||||
|  | ||||
| func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode { | ||||
| 	file := vfsFileGet(ctx, mod, pFile).(File) | ||||
| 	err := file.Unlock(eLock) | ||||
| 	return vfsErrorCode(err, _IOERR_UNLOCK) | ||||
| } | ||||
|  | ||||
| func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut uint32) _ErrorCode { | ||||
| 	file := vfsFileGet(ctx, mod, pFile).(File) | ||||
| 	locked, err := file.CheckReservedLock() | ||||
|  | ||||
| 	var res uint32 | ||||
| 	if locked { | ||||
| 		res = 1 | ||||
| 	} | ||||
|  | ||||
| 	util.WriteUint32(mod, pResOut, res) | ||||
| 	return vfsErrorCode(err, _IOERR_CHECKRESERVEDLOCK) | ||||
| } | ||||
|  | ||||
| func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _FcntlOpcode, pArg uint32) _ErrorCode { | ||||
| 	file := vfsFileGet(ctx, mod, pFile).(File) | ||||
|  | ||||
| 	switch op { | ||||
| 	case _FCNTL_LOCKSTATE: | ||||
| 		if file, ok := file.(FileLockState); ok { | ||||
| 			util.WriteUint32(mod, pArg, uint32(file.LockState())) | ||||
| 			return _OK | ||||
| 		} | ||||
|  | ||||
| 	case _FCNTL_PERSIST_WAL: | ||||
| 		if file, ok := file.(FilePersistentWAL); ok { | ||||
| 			if i := util.ReadUint32(mod, pArg); int32(i) >= 0 { | ||||
| 				file.SetPersistentWAL(i != 0) | ||||
| 			} else if file.PersistentWAL() { | ||||
| 				util.WriteUint32(mod, pArg, 1) | ||||
| 			} else { | ||||
| 				util.WriteUint32(mod, pArg, 0) | ||||
| 			} | ||||
| 			return _OK | ||||
| 		} | ||||
|  | ||||
| 	case _FCNTL_POWERSAFE_OVERWRITE: | ||||
| 		if file, ok := file.(FilePowersafeOverwrite); ok { | ||||
| 			if i := util.ReadUint32(mod, pArg); int32(i) >= 0 { | ||||
| 				file.SetPowersafeOverwrite(i != 0) | ||||
| 			} else if file.PowersafeOverwrite() { | ||||
| 				util.WriteUint32(mod, pArg, 1) | ||||
| 			} else { | ||||
| 				util.WriteUint32(mod, pArg, 0) | ||||
| 			} | ||||
| 			return _OK | ||||
| 		} | ||||
|  | ||||
| 	case _FCNTL_CHUNK_SIZE: | ||||
| 		if file, ok := file.(FileChunkSize); ok { | ||||
| 			size := util.ReadUint32(mod, pArg) | ||||
| 			file.ChunkSize(int(size)) | ||||
| 			return _OK | ||||
| 		} | ||||
|  | ||||
| 	case _FCNTL_SIZE_HINT: | ||||
| 		if file, ok := file.(FileSizeHint); ok { | ||||
| 			size := util.ReadUint64(mod, pArg) | ||||
| 			err := file.SizeHint(int64(size)) | ||||
| 			return vfsErrorCode(err, _IOERR_TRUNCATE) | ||||
| 		} | ||||
|  | ||||
| 	case _FCNTL_HAS_MOVED: | ||||
| 		if file, ok := file.(FileHasMoved); ok { | ||||
| 			moved, err := file.HasMoved() | ||||
| 			var res uint32 | ||||
| 			if moved { | ||||
| 				res = 1 | ||||
| 			} | ||||
| 			util.WriteUint32(mod, pArg, res) | ||||
| 			return vfsErrorCode(err, _IOERR_FSTAT) | ||||
| 		} | ||||
|  | ||||
| 	case _FCNTL_OVERWRITE: | ||||
| 		if file, ok := file.(FileOverwrite); ok { | ||||
| 			err := file.Overwrite() | ||||
| 			return vfsErrorCode(err, _IOERR) | ||||
| 		} | ||||
|  | ||||
| 	case _FCNTL_COMMIT_PHASETWO: | ||||
| 		if file, ok := file.(FileCommitPhaseTwo); ok { | ||||
| 			err := file.CommitPhaseTwo() | ||||
| 			return vfsErrorCode(err, _IOERR) | ||||
| 		} | ||||
|  | ||||
| 	case _FCNTL_BEGIN_ATOMIC_WRITE: | ||||
| 		if file, ok := file.(FileBatchAtomicWrite); ok { | ||||
| 			err := file.BeginAtomicWrite() | ||||
| 			return vfsErrorCode(err, _IOERR_BEGIN_ATOMIC) | ||||
| 		} | ||||
| 	case _FCNTL_COMMIT_ATOMIC_WRITE: | ||||
| 		if file, ok := file.(FileBatchAtomicWrite); ok { | ||||
| 			err := file.CommitAtomicWrite() | ||||
| 			return vfsErrorCode(err, _IOERR_COMMIT_ATOMIC) | ||||
| 		} | ||||
| 	case _FCNTL_ROLLBACK_ATOMIC_WRITE: | ||||
| 		if file, ok := file.(FileBatchAtomicWrite); ok { | ||||
| 			err := file.RollbackAtomicWrite() | ||||
| 			return vfsErrorCode(err, _IOERR_ROLLBACK_ATOMIC) | ||||
| 		} | ||||
|  | ||||
| 	case _FCNTL_CKPT_DONE: | ||||
| 		if file, ok := file.(FileCheckpoint); ok { | ||||
| 			err := file.CheckpointDone() | ||||
| 			return vfsErrorCode(err, _IOERR) | ||||
| 		} | ||||
| 	case _FCNTL_CKPT_START: | ||||
| 		if file, ok := file.(FileCheckpoint); ok { | ||||
| 			err := file.CheckpointStart() | ||||
| 			return vfsErrorCode(err, _IOERR) | ||||
| 		} | ||||
|  | ||||
| 	case _FCNTL_PRAGMA: | ||||
| 		if file, ok := file.(FilePragma); ok { | ||||
| 			ptr := util.ReadUint32(mod, pArg+1*ptrlen) | ||||
| 			name := util.ReadString(mod, ptr, _MAX_SQL_LENGTH) | ||||
| 			var value string | ||||
| 			if ptr := util.ReadUint32(mod, pArg+2*ptrlen); ptr != 0 { | ||||
| 				value = util.ReadString(mod, ptr, _MAX_SQL_LENGTH) | ||||
| 			} | ||||
|  | ||||
| 			out, err := file.Pragma(name, value) | ||||
|  | ||||
| 			ret := vfsErrorCode(err, _ERROR) | ||||
| 			if ret == _ERROR { | ||||
| 				out = err.Error() | ||||
| 			} | ||||
| 			if out != "" { | ||||
| 				fn := mod.ExportedFunction("malloc") | ||||
| 				stack := [...]uint64{uint64(len(out) + 1)} | ||||
| 				if err := fn.CallWithStack(ctx, stack[:]); err != nil { | ||||
| 					panic(err) | ||||
| 				} | ||||
| 				util.WriteUint32(mod, pArg, uint32(stack[0])) | ||||
| 				util.WriteString(mod, uint32(stack[0]), out) | ||||
| 			} | ||||
| 			return ret | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Consider also implementing these opcodes (in use by SQLite): | ||||
| 	//  _FCNTL_BUSYHANDLER | ||||
| 	//  _FCNTL_LAST_ERRNO | ||||
| 	//  _FCNTL_SYNC | ||||
| 	return _NOTFOUND | ||||
| } | ||||
|  | ||||
| func vfsSectorSize(ctx context.Context, mod api.Module, pFile uint32) uint32 { | ||||
| 	file := vfsFileGet(ctx, mod, pFile).(File) | ||||
| 	return uint32(file.SectorSize()) | ||||
| } | ||||
|  | ||||
| func vfsDeviceCharacteristics(ctx context.Context, mod api.Module, pFile uint32) DeviceCharacteristic { | ||||
| 	file := vfsFileGet(ctx, mod, pFile).(File) | ||||
| 	return file.DeviceCharacteristics() | ||||
| } | ||||
|  | ||||
| var shmBarrier sync.Mutex | ||||
|  | ||||
| func vfsShmBarrier(ctx context.Context, mod api.Module, pFile uint32) { | ||||
| 	shmBarrier.Lock() | ||||
| 	defer shmBarrier.Unlock() | ||||
| } | ||||
|  | ||||
| func vfsShmMap(ctx context.Context, mod api.Module, pFile uint32, iRegion, szRegion int32, bExtend, pp uint32) _ErrorCode { | ||||
| 	shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory() | ||||
| 	p, err := shm.shmMap(ctx, mod, iRegion, szRegion, bExtend != 0) | ||||
| 	if err != nil { | ||||
| 		return vfsErrorCode(err, _IOERR_SHMMAP) | ||||
| 	} | ||||
| 	util.WriteUint32(mod, pp, p) | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func vfsShmLock(ctx context.Context, mod api.Module, pFile uint32, offset, n int32, flags _ShmFlag) _ErrorCode { | ||||
| 	shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory() | ||||
| 	err := shm.shmLock(offset, n, flags) | ||||
| 	return vfsErrorCode(err, _IOERR_SHMLOCK) | ||||
| } | ||||
|  | ||||
| func vfsShmUnmap(ctx context.Context, mod api.Module, pFile, bDelete uint32) _ErrorCode { | ||||
| 	shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory() | ||||
| 	shm.shmUnmap(bDelete != 0) | ||||
| 	return _OK | ||||
| } | ||||
|  | ||||
| func vfsGet(mod api.Module, pVfs uint32) VFS { | ||||
| 	var name string | ||||
| 	if pVfs != 0 { | ||||
| 		const zNameOffset = 16 | ||||
| 		name = util.ReadString(mod, util.ReadUint32(mod, pVfs+zNameOffset), _MAX_NAME) | ||||
| 	} | ||||
| 	if vfs := Find(name); vfs != nil { | ||||
| 		return vfs | ||||
| 	} | ||||
| 	panic(util.NoVFSErr + util.ErrorString(name)) | ||||
| } | ||||
|  | ||||
| func vfsFileRegister(ctx context.Context, mod api.Module, pFile uint32, file File) { | ||||
| 	const fileHandleOffset = 4 | ||||
| 	id := util.AddHandle(ctx, file) | ||||
| 	util.WriteUint32(mod, pFile+fileHandleOffset, id) | ||||
| } | ||||
|  | ||||
| func vfsFileGet(ctx context.Context, mod api.Module, pFile uint32) any { | ||||
| 	const fileHandleOffset = 4 | ||||
| 	id := util.ReadUint32(mod, pFile+fileHandleOffset) | ||||
| 	return util.GetHandle(ctx, id) | ||||
| } | ||||
|  | ||||
| func vfsFileClose(ctx context.Context, mod api.Module, pFile uint32) error { | ||||
| 	const fileHandleOffset = 4 | ||||
| 	id := util.ReadUint32(mod, pFile+fileHandleOffset) | ||||
| 	return util.DelHandle(ctx, id) | ||||
| } | ||||
|  | ||||
| func vfsErrorCode(err error, def _ErrorCode) _ErrorCode { | ||||
| 	if err == nil { | ||||
| 		return _OK | ||||
| 	} | ||||
| 	switch v := reflect.ValueOf(err); v.Kind() { | ||||
| 	case reflect.Uint8, reflect.Uint16, reflect.Uint32: | ||||
| 		return _ErrorCode(v.Uint()) | ||||
| 	} | ||||
| 	return def | ||||
| } | ||||
		Reference in New Issue
	
	Block a user