diff --git a/config.go b/config.go index 572fc94..79f8169 100644 --- a/config.go +++ b/config.go @@ -1,9 +1,14 @@ package sqlite3 -import "github.com/ncruces/go-sqlite3/internal/util" +import ( + "context" + + "github.com/ncruces/go-sqlite3/internal/util" + "github.com/tetratelabs/wazero/api" +) // Config makes configuration changes to a database connection. -// Only bool configuratiton options are supported. +// Only boolean configuration options are supported. // Called with no arg reads the current configuration value, // called with one arg sets and returns the new value. // @@ -27,3 +32,26 @@ func (c *Conn) Config(op DBConfig, arg ...bool) (bool, error) { uint64(op), uint64(argsPtr)) return util.ReadUint32(c.mod, argsPtr) != 0, c.error(r) } + +// ConfigLog sets up the error logging callback for the connection. +// +// https://www.sqlite.org/errlog.html +func (c *Conn) ConfigLog(cb func(code ExtendedErrorCode, msg string)) error { + var enable uint64 + if cb != nil { + enable = 1 + } + r := c.call("sqlite3_config_log_go", enable) + if err := c.error(r); err != nil { + return err + } + c.log = cb + return nil +} + +func logCallback(ctx context.Context, mod api.Module, _, iCode, zMsg uint32) { + if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.log != nil { + msg := util.ReadString(mod, zMsg, _MAX_LENGTH) + c.log(xErrorCode(iCode), msg) + } +} diff --git a/conn.go b/conn.go index b5eb52d..255e5b0 100644 --- a/conn.go +++ b/conn.go @@ -20,6 +20,7 @@ type Conn struct { interrupt context.Context pending *Stmt + log func(code xErrorCode, msg string) arena arena handle uint32 @@ -258,6 +259,12 @@ func (c *Conn) SetInterrupt(ctx context.Context) (old context.Context) { return old } +func (c *Conn) checkInterrupt() { + if c.interrupt != nil && c.interrupt.Err() != nil { + c.call("sqlite3_interrupt", uint64(c.handle)) + } +} + func progressCallback(ctx context.Context, mod api.Module, _ uint32) uint32 { if c, ok := ctx.Value(connKey{}).(*Conn); ok { if c.interrupt != nil && c.interrupt.Err() != nil { @@ -267,12 +274,6 @@ func progressCallback(ctx context.Context, mod api.Module, _ uint32) uint32 { return 0 } -func (c *Conn) checkInterrupt() { - if c.interrupt != nil && c.interrupt.Err() != nil { - c.call("sqlite3_interrupt", uint64(c.handle)) - } -} - // Pragma executes a PRAGMA statement and returns any results. // // https://sqlite.org/pragma.html diff --git a/embed/exports.txt b/embed/exports.txt index 8017168..2d84667 100644 --- a/embed/exports.txt +++ b/embed/exports.txt @@ -39,6 +39,7 @@ sqlite3_column_name sqlite3_column_text sqlite3_column_type sqlite3_column_value +sqlite3_config_log_go sqlite3_create_aggregate_function_go sqlite3_create_collation_go sqlite3_create_function_go diff --git a/embed/sqlite3.wasm b/embed/sqlite3.wasm index b5dca1f..8af604b 100755 Binary files a/embed/sqlite3.wasm and b/embed/sqlite3.wasm differ diff --git a/ext/fileio/fsdir.go b/ext/fileio/fsdir.go index bd78907..4228652 100644 --- a/ext/fileio/fsdir.go +++ b/ext/fileio/fsdir.go @@ -53,18 +53,18 @@ func (d fsdir) Open() (sqlite3.VTabCursor, error) { type cursor struct { fsys fs.FS - base string - rowID int64 - eof bool curr entry next chan entry done chan struct{} + base string + rowID int64 + eof bool } type entry struct { - path string fs.DirEntry - err error + err error + path string } func (c *cursor) Close() error { @@ -180,7 +180,7 @@ func (c *cursor) WalkDirFunc(path string, d fs.DirEntry, err error) error { select { case <-c.done: return fs.SkipAll - case c.next <- entry{path, d, err}: + case c.next <- entry{d, err, path}: return nil } } diff --git a/sqlite.go b/sqlite.go index e21881e..79b25c4 100644 --- a/sqlite.go +++ b/sqlite.go @@ -282,6 +282,7 @@ func (a *arena) string(s string) uint32 { func exportCallbacks(env wazero.HostModuleBuilder) wazero.HostModuleBuilder { util.ExportFuncII(env, "go_progress", progressCallback) + util.ExportFuncVIII(env, "go_log", logCallback) util.ExportFuncVI(env, "go_destroy", destroyCallback) util.ExportFuncVIII(env, "go_func", funcCallback) util.ExportFuncVIII(env, "go_step", stepCallback) diff --git a/sqlite3/log.c b/sqlite3/log.c new file mode 100644 index 0000000..1f66cab --- /dev/null +++ b/sqlite3/log.c @@ -0,0 +1,9 @@ +#include + +#include "sqlite3.h" + +void go_log(void*, int, const char*); + +int sqlite3_config_log_go(bool enable) { + return sqlite3_config(SQLITE_CONFIG_LOG, enable ? go_log : NULL, NULL); +} diff --git a/sqlite3/main.c b/sqlite3/main.c index bc63b35..65eae9f 100644 --- a/sqlite3/main.c +++ b/sqlite3/main.c @@ -10,6 +10,7 @@ #include "ext/uuid.c" // Bindings #include "func.c" +#include "log.c" #include "pointer.c" #include "progress.c" #include "time.c" diff --git a/tests/conn_test.go b/tests/conn_test.go index 8cf037b..303d476 100644 --- a/tests/conn_test.go +++ b/tests/conn_test.go @@ -339,3 +339,28 @@ func TestConn_Config(t *testing.T) { t.Error("want false") } } + +func TestConn_ConfigLog(t *testing.T) { + t.Parallel() + + db, err := sqlite3.Open(":memory:") + if err != nil { + t.Fatal(err) + } + defer db.Close() + + var code sqlite3.ExtendedErrorCode + err = db.ConfigLog(func(c sqlite3.ExtendedErrorCode, msg string) { + t.Log(msg) + code = c + }) + if err != nil { + t.Fatal(err) + } + + db.Prepare(`SELECT * FRM sqlite_schema`) + + if code != sqlite3.ExtendedErrorCode(sqlite3.ERROR) { + t.Error("want sqlite3.ERROR") + } +}