mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-11 21:49:13 +00:00
Enums (result codes, flags…)
This commit is contained in:
11
README.md
11
README.md
@@ -4,7 +4,7 @@
|
||||
|
||||
This is very much a WIP.
|
||||
|
||||
TODO:
|
||||
Roadmap:
|
||||
- [x] build SQLite using `zig cc --target=wasm32-wasi`
|
||||
- [x] `:memory:` databases
|
||||
- [x] use [`test_demovfs.c`](sqlite3/test_demovfs.c) for file databases
|
||||
@@ -13,6 +13,7 @@ TODO:
|
||||
- [`github.com/bvinc/go-sqlite-lite`](https://github.com/bvinc/go-sqlite-lite)
|
||||
- [`github.com/crawshaw/sqlite`](https://github.com/crawshaw/sqlite)
|
||||
- [`github.com/zombiezen/go-sqlite`](https://github.com/zombiezen/go-sqlite)
|
||||
- [`github.com/cvilsmeier/sqinn-go`](https://github.com/cvilsmeier/sqinn-go)
|
||||
- [`database/sql`](https://pkg.go.dev/database/sql) drivers can come later, if ever
|
||||
- [ ] implement own VFS to sidestep WASI syscalls:
|
||||
- locking will be a pain point:
|
||||
@@ -21,6 +22,14 @@ TODO:
|
||||
- file access needs to be synchronized, both in-process and out-of-process
|
||||
- out-of-process should be compatible with SQLite on Windows/Unix
|
||||
- [ ] benchmark to see if this is usefull at all:
|
||||
- [`github.com/cvilsmeier/sqinn-go`](https://github.com/cvilsmeier/sqinn-go-bench)
|
||||
- [`github.com/bvinc/go-sqlite-lite`](https://github.com/bvinc/go-sqlite-lite)
|
||||
- [`github.com/mattn/go-sqlite3`](https://github.com/mattn/go-sqlite3)
|
||||
- [`modernc.org/sqlite`](https://modernc.org/sqlite)
|
||||
|
||||
Random TODO list:
|
||||
- wrap more of the SQLite API, enough to run useful queries;
|
||||
- create a Go VFS that's enough to use `:memory:` databases without WASI;
|
||||
- expand that VFS to wrap an `io.ReaderAt`;
|
||||
- optimize small allocations that last a single function call;
|
||||
- …
|
||||
@@ -44,6 +44,5 @@ zig cc --target=wasm32-wasi -flto -g0 -O2 \
|
||||
-Wl,--export=sqlite3_errstr \
|
||||
-Wl,--export=sqlite3_errmsg \
|
||||
-Wl,--export=sqlite3_error_offset \
|
||||
-Wl,--export=sqlite3_extended_errcode \
|
||||
-Wl,--export=malloc \
|
||||
-Wl,--export=free
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
db, err := sqlite3.Open(":memory:", 0, "")
|
||||
db, err := sqlite3.Open(":memory:")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
stmt, _, err := db.Prepare(`SELECT id, name FROM users`, 0)
|
||||
stmt, _, err := db.Prepare(`SELECT id, name FROM users`)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
49
conn.go
49
conn.go
@@ -19,7 +19,11 @@ type Conn struct {
|
||||
api sqliteAPI
|
||||
}
|
||||
|
||||
func Open(name string, flags uint64, vfs string) (conn *Conn, err error) {
|
||||
func Open(name string) (conn *Conn, err error) {
|
||||
return OpenFlags(name, OPEN_READWRITE|OPEN_CREATE)
|
||||
}
|
||||
|
||||
func OpenFlags(name string, flags OpenFlag) (conn *Conn, err error) {
|
||||
once.Do(compile)
|
||||
|
||||
var fs fs.FS
|
||||
@@ -53,7 +57,6 @@ func Open(name string, flags uint64, vfs string) (conn *Conn, err error) {
|
||||
errstr: module.ExportedFunction("sqlite3_errstr"),
|
||||
errmsg: module.ExportedFunction("sqlite3_errmsg"),
|
||||
erroff: module.ExportedFunction("sqlite3_error_offset"),
|
||||
errext: module.ExportedFunction("sqlite3_extended_errcode"),
|
||||
open: module.ExportedFunction("sqlite3_open_v2"),
|
||||
close: module.ExportedFunction("sqlite3_close"),
|
||||
prepare: module.ExportedFunction("sqlite3_prepare_v3"),
|
||||
@@ -75,16 +78,7 @@ func Open(name string, flags uint64, vfs string) (conn *Conn, err error) {
|
||||
namePtr := c.newString(name)
|
||||
connPtr := c.newBytes(ptrSize)
|
||||
|
||||
if flags == 0 {
|
||||
flags = OPEN_READWRITE | OPEN_CREATE
|
||||
}
|
||||
|
||||
var vfsPtr uint32
|
||||
if vfs != "" {
|
||||
vfsPtr = c.newString(vfs)
|
||||
}
|
||||
|
||||
r, err := c.api.open.Call(ctx, uint64(namePtr), uint64(connPtr), flags, uint64(vfsPtr))
|
||||
r, err := c.api.open.Call(ctx, uint64(namePtr), uint64(connPtr), uint64(flags), 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -92,9 +86,8 @@ func Open(name string, flags uint64, vfs string) (conn *Conn, err error) {
|
||||
c.handle, _ = c.memory.ReadUint32Le(connPtr)
|
||||
c.free(connPtr)
|
||||
c.free(namePtr)
|
||||
c.free(vfsPtr)
|
||||
|
||||
if r[0] != OK {
|
||||
if r[0] != _OK {
|
||||
return nil, c.error(r[0])
|
||||
}
|
||||
return &c, nil
|
||||
@@ -106,7 +99,7 @@ func (c *Conn) Close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if r[0] != OK {
|
||||
if r[0] != _OK {
|
||||
return c.error(r[0])
|
||||
}
|
||||
return c.module.Close(context.TODO())
|
||||
@@ -122,19 +115,23 @@ func (c *Conn) Exec(sql string) error {
|
||||
|
||||
c.free(sqlPtr)
|
||||
|
||||
if r[0] != OK {
|
||||
if r[0] != _OK {
|
||||
return c.error(r[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) Prepare(sql string, flags uint64, args ...any) (stmt *Stmt, tail string, err error) {
|
||||
func (c *Conn) Prepare(sql string) (stmt *Stmt, tail string, err error) {
|
||||
return c.PrepareFlags(sql, 0)
|
||||
}
|
||||
|
||||
func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail string, err error) {
|
||||
sqlPtr := c.newString(sql)
|
||||
stmtPtr := c.newBytes(ptrSize)
|
||||
tailPtr := c.newBytes(ptrSize)
|
||||
|
||||
r, err := c.api.prepare.Call(context.TODO(), uint64(c.handle),
|
||||
uint64(sqlPtr), uint64(len(sql)+1), flags,
|
||||
uint64(sqlPtr), uint64(len(sql)+1), uint64(flags),
|
||||
uint64(stmtPtr), uint64(tailPtr))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
@@ -149,7 +146,7 @@ func (c *Conn) Prepare(sql string, flags uint64, args ...any) (stmt *Stmt, tail
|
||||
c.free(stmtPtr)
|
||||
c.free(sqlPtr)
|
||||
|
||||
if r[0] != OK {
|
||||
if r[0] != _OK {
|
||||
return nil, "", c.error(r[0])
|
||||
}
|
||||
if stmt.handle == 0 {
|
||||
@@ -159,12 +156,15 @@ func (c *Conn) Prepare(sql string, flags uint64, args ...any) (stmt *Stmt, tail
|
||||
}
|
||||
|
||||
func (c *Conn) error(rc uint64) *Error {
|
||||
serr := Error{Code: int(rc)}
|
||||
serr := Error{
|
||||
Code: ErrorCode(rc & 0xFF),
|
||||
ExtendedCode: ExtendedErrorCode(rc),
|
||||
}
|
||||
|
||||
var r []uint64
|
||||
|
||||
// string
|
||||
r, _ = c.api.errstr.Call(context.TODO(), uint64(rc))
|
||||
r, _ = c.api.errstr.Call(context.TODO(), rc)
|
||||
if r != nil {
|
||||
serr.str = c.getString(uint32(r[0]), 512)
|
||||
}
|
||||
@@ -175,12 +175,6 @@ func (c *Conn) error(rc uint64) *Error {
|
||||
serr.msg = c.getString(uint32(r[0]), 512)
|
||||
}
|
||||
|
||||
// extended code
|
||||
r, _ = c.api.errext.Call(context.TODO(), uint64(c.handle))
|
||||
if r != nil {
|
||||
serr.ExtendedCode = int(r[0])
|
||||
}
|
||||
|
||||
if serr.str == serr.msg {
|
||||
serr.msg = ""
|
||||
}
|
||||
@@ -247,7 +241,6 @@ type sqliteAPI struct {
|
||||
free api.Function
|
||||
errstr api.Function
|
||||
errmsg api.Function
|
||||
errext api.Function
|
||||
erroff api.Function
|
||||
open api.Function
|
||||
close api.Function
|
||||
|
||||
151
const.go
151
const.go
@@ -1,12 +1,153 @@
|
||||
package sqlite3
|
||||
|
||||
const (
|
||||
OK = 0
|
||||
ROW = 100
|
||||
DONE = 101
|
||||
_OK = 0 /* Successful result */
|
||||
_ROW = 100 /* sqlite3_step() has another row ready */
|
||||
_DONE = 101 /* sqlite3_step() has finished executing */
|
||||
)
|
||||
|
||||
type ErrorCode int
|
||||
|
||||
const (
|
||||
OPEN_READWRITE = 0x00000002
|
||||
OPEN_CREATE = 0x00000004
|
||||
ERROR ErrorCode = 1 /* Generic error */
|
||||
INTERNAL ErrorCode = 2 /* Internal logic error in SQLite */
|
||||
PERM ErrorCode = 3 /* Access permission denied */
|
||||
ABORT ErrorCode = 4 /* Callback routine requested an abort */
|
||||
BUSY ErrorCode = 5 /* The database file is locked */
|
||||
LOCKED ErrorCode = 6 /* A table in the database is locked */
|
||||
NOMEM ErrorCode = 7 /* A malloc() failed */
|
||||
READONLY ErrorCode = 8 /* Attempt to write a readonly database */
|
||||
INTERRUPT ErrorCode = 9 /* Operation terminated by sqlite3_interrupt()*/
|
||||
IOERR ErrorCode = 10 /* Some kind of disk I/O error occurred */
|
||||
CORRUPT ErrorCode = 11 /* The database disk image is malformed */
|
||||
NOTFOUND ErrorCode = 12 /* Unknown opcode in sqlite3_file_control() */
|
||||
FULL ErrorCode = 13 /* Insertion failed because database is full */
|
||||
CANTOPEN ErrorCode = 14 /* Unable to open the database file */
|
||||
PROTOCOL ErrorCode = 15 /* Database lock protocol error */
|
||||
EMPTY ErrorCode = 16 /* Internal use only */
|
||||
SCHEMA ErrorCode = 17 /* The database schema changed */
|
||||
TOOBIG ErrorCode = 18 /* String or BLOB exceeds size limit */
|
||||
CONSTRAINT ErrorCode = 19 /* Abort due to constraint violation */
|
||||
MISMATCH ErrorCode = 20 /* Data type mismatch */
|
||||
MISUSE ErrorCode = 21 /* Library used incorrectly */
|
||||
NOLFS ErrorCode = 22 /* Uses OS features not supported on host */
|
||||
AUTH ErrorCode = 23 /* Authorization denied */
|
||||
FORMAT ErrorCode = 24 /* Not used */
|
||||
RANGE ErrorCode = 25 /* 2nd parameter to sqlite3_bind out of range */
|
||||
NOTADB ErrorCode = 26 /* File opened that is not a database file */
|
||||
NOTICE ErrorCode = 27 /* Notifications from sqlite3_log() */
|
||||
WARNING ErrorCode = 28 /* Warnings from sqlite3_log() */
|
||||
)
|
||||
|
||||
type ExtendedErrorCode int
|
||||
|
||||
const (
|
||||
ERROR_MISSING_COLLSEQ = ExtendedErrorCode(ERROR | (1 << 8))
|
||||
ERROR_RETRY = ExtendedErrorCode(ERROR | (2 << 8))
|
||||
ERROR_SNAPSHOT = ExtendedErrorCode(ERROR | (3 << 8))
|
||||
IOERR_READ = ExtendedErrorCode(IOERR | (1 << 8))
|
||||
IOERR_SHORT_READ = ExtendedErrorCode(IOERR | (2 << 8))
|
||||
IOERR_WRITE = ExtendedErrorCode(IOERR | (3 << 8))
|
||||
IOERR_FSYNC = ExtendedErrorCode(IOERR | (4 << 8))
|
||||
IOERR_DIR_FSYNC = ExtendedErrorCode(IOERR | (5 << 8))
|
||||
IOERR_TRUNCATE = ExtendedErrorCode(IOERR | (6 << 8))
|
||||
IOERR_FSTAT = ExtendedErrorCode(IOERR | (7 << 8))
|
||||
IOERR_UNLOCK = ExtendedErrorCode(IOERR | (8 << 8))
|
||||
IOERR_RDLOCK = ExtendedErrorCode(IOERR | (9 << 8))
|
||||
IOERR_DELETE = ExtendedErrorCode(IOERR | (10 << 8))
|
||||
IOERR_BLOCKED = ExtendedErrorCode(IOERR | (11 << 8))
|
||||
IOERR_NOMEM = ExtendedErrorCode(IOERR | (12 << 8))
|
||||
IOERR_ACCESS = ExtendedErrorCode(IOERR | (13 << 8))
|
||||
IOERR_CHECKRESERVEDLOCK = ExtendedErrorCode(IOERR | (14 << 8))
|
||||
IOERR_LOCK = ExtendedErrorCode(IOERR | (15 << 8))
|
||||
IOERR_CLOSE = ExtendedErrorCode(IOERR | (16 << 8))
|
||||
IOERR_DIR_CLOSE = ExtendedErrorCode(IOERR | (17 << 8))
|
||||
IOERR_SHMOPEN = ExtendedErrorCode(IOERR | (18 << 8))
|
||||
IOERR_SHMSIZE = ExtendedErrorCode(IOERR | (19 << 8))
|
||||
IOERR_SHMLOCK = ExtendedErrorCode(IOERR | (20 << 8))
|
||||
IOERR_SHMMAP = ExtendedErrorCode(IOERR | (21 << 8))
|
||||
IOERR_SEEK = ExtendedErrorCode(IOERR | (22 << 8))
|
||||
IOERR_DELETE_NOENT = ExtendedErrorCode(IOERR | (23 << 8))
|
||||
IOERR_MMAP = ExtendedErrorCode(IOERR | (24 << 8))
|
||||
IOERR_GETTEMPPATH = ExtendedErrorCode(IOERR | (25 << 8))
|
||||
IOERR_CONVPATH = ExtendedErrorCode(IOERR | (26 << 8))
|
||||
IOERR_VNODE = ExtendedErrorCode(IOERR | (27 << 8))
|
||||
IOERR_AUTH = ExtendedErrorCode(IOERR | (28 << 8))
|
||||
IOERR_BEGIN_ATOMIC = ExtendedErrorCode(IOERR | (29 << 8))
|
||||
IOERR_COMMIT_ATOMIC = ExtendedErrorCode(IOERR | (30 << 8))
|
||||
IOERR_ROLLBACK_ATOMIC = ExtendedErrorCode(IOERR | (31 << 8))
|
||||
IOERR_DATA = ExtendedErrorCode(IOERR | (32 << 8))
|
||||
IOERR_CORRUPTFS = ExtendedErrorCode(IOERR | (33 << 8))
|
||||
LOCKED_SHAREDCACHE = ExtendedErrorCode(LOCKED | (1 << 8))
|
||||
LOCKED_VTAB = ExtendedErrorCode(LOCKED | (2 << 8))
|
||||
BUSY_RECOVERY = ExtendedErrorCode(BUSY | (1 << 8))
|
||||
BUSY_SNAPSHOT = ExtendedErrorCode(BUSY | (2 << 8))
|
||||
BUSY_TIMEOUT = ExtendedErrorCode(BUSY | (3 << 8))
|
||||
CANTOPEN_NOTEMPDIR = ExtendedErrorCode(CANTOPEN | (1 << 8))
|
||||
CANTOPEN_ISDIR = ExtendedErrorCode(CANTOPEN | (2 << 8))
|
||||
CANTOPEN_FULLPATH = ExtendedErrorCode(CANTOPEN | (3 << 8))
|
||||
CANTOPEN_CONVPATH = ExtendedErrorCode(CANTOPEN | (4 << 8))
|
||||
CANTOPEN_DIRTYWAL = ExtendedErrorCode(CANTOPEN | (5 << 8)) /* Not Used */
|
||||
CANTOPEN_SYMLINK = ExtendedErrorCode(CANTOPEN | (6 << 8))
|
||||
CORRUPT_VTAB = ExtendedErrorCode(CORRUPT | (1 << 8))
|
||||
CORRUPT_SEQUENCE = ExtendedErrorCode(CORRUPT | (2 << 8))
|
||||
CORRUPT_INDEX = ExtendedErrorCode(CORRUPT | (3 << 8))
|
||||
READONLY_RECOVERY = ExtendedErrorCode(READONLY | (1 << 8))
|
||||
READONLY_CANTLOCK = ExtendedErrorCode(READONLY | (2 << 8))
|
||||
READONLY_ROLLBACK = ExtendedErrorCode(READONLY | (3 << 8))
|
||||
READONLY_DBMOVED = ExtendedErrorCode(READONLY | (4 << 8))
|
||||
READONLY_CANTINIT = ExtendedErrorCode(READONLY | (5 << 8))
|
||||
READONLY_DIRECTORY = ExtendedErrorCode(READONLY | (6 << 8))
|
||||
ABORT_ROLLBACK = ExtendedErrorCode(ABORT | (2 << 8))
|
||||
CONSTRAINT_CHECK = ExtendedErrorCode(CONSTRAINT | (1 << 8))
|
||||
CONSTRAINT_COMMITHOOK = ExtendedErrorCode(CONSTRAINT | (2 << 8))
|
||||
CONSTRAINT_FOREIGNKEY = ExtendedErrorCode(CONSTRAINT | (3 << 8))
|
||||
CONSTRAINT_FUNCTION = ExtendedErrorCode(CONSTRAINT | (4 << 8))
|
||||
CONSTRAINT_NOTNULL = ExtendedErrorCode(CONSTRAINT | (5 << 8))
|
||||
CONSTRAINT_PRIMARYKEY = ExtendedErrorCode(CONSTRAINT | (6 << 8))
|
||||
CONSTRAINT_TRIGGER = ExtendedErrorCode(CONSTRAINT | (7 << 8))
|
||||
CONSTRAINT_UNIQUE = ExtendedErrorCode(CONSTRAINT | (8 << 8))
|
||||
CONSTRAINT_VTAB = ExtendedErrorCode(CONSTRAINT | (9 << 8))
|
||||
CONSTRAINT_ROWID = ExtendedErrorCode(CONSTRAINT | (10 << 8))
|
||||
CONSTRAINT_PINNED = ExtendedErrorCode(CONSTRAINT | (11 << 8))
|
||||
CONSTRAINT_DATATYPE = ExtendedErrorCode(CONSTRAINT | (12 << 8))
|
||||
NOTICE_RECOVER_WAL = ExtendedErrorCode(NOTICE | (1 << 8))
|
||||
NOTICE_RECOVER_ROLLBACK = ExtendedErrorCode(NOTICE | (2 << 8))
|
||||
WARNING_AUTOINDEX = ExtendedErrorCode(WARNING | (1 << 8))
|
||||
AUTH_USER = ExtendedErrorCode(AUTH | (1 << 8))
|
||||
)
|
||||
|
||||
type OpenFlag uint
|
||||
|
||||
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() */
|
||||
OPEN_EXRESCODE OpenFlag = 0x02000000 /* Extended result codes */
|
||||
)
|
||||
|
||||
type PrepareFlag uint
|
||||
|
||||
const (
|
||||
PREPARE_PERSISTENT PrepareFlag = 0x01
|
||||
PREPARE_NORMALIZE PrepareFlag = 0x02
|
||||
PREPARE_NO_VTAB PrepareFlag = 0x04
|
||||
)
|
||||
|
||||
6
error.go
6
error.go
@@ -6,8 +6,8 @@ import (
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
Code int
|
||||
ExtendedCode int
|
||||
Code ErrorCode
|
||||
ExtendedCode ExtendedErrorCode
|
||||
str string
|
||||
msg string
|
||||
}
|
||||
@@ -19,7 +19,7 @@ func (e Error) Error() string {
|
||||
if e.str != "" {
|
||||
b.WriteString(e.str)
|
||||
} else {
|
||||
b.WriteString(strconv.Itoa(e.Code))
|
||||
b.WriteString(strconv.Itoa(int(e.Code)))
|
||||
}
|
||||
|
||||
if e.msg != "" {
|
||||
|
||||
Reference in New Issue
Block a user