Commit c1df3127 authored by gwenn's avatar gwenn

Adds contextual details to errors when possible.

parent bedc25eb
......@@ -12,6 +12,7 @@ import "C"
import (
"errors"
"fmt"
"io"
"unsafe"
)
......@@ -68,7 +69,7 @@ func (c *Conn) blob_open(db, table, column string, row int64, write bool) (*C.sq
if bl != nil {
C.sqlite3_blob_close(bl)
}
return nil, c.error(rv)
return nil, c.error(rv, fmt.Sprintf("Conn.blob_open(db: %q, tbl: %q, col: %q, row: %d)", db, table, column, row))
}
if bl == nil {
return nil, errors.New("sqlite succeeded without returning a blob")
......@@ -80,11 +81,11 @@ func (c *Conn) blob_open(db, table, column string, row int64, write bool) (*C.sq
// (See http://sqlite.org/c3ref/blob_close.html)
func (r *BlobReader) Close() error {
if r == nil {
return errors.New("nil sqlite blob reader")
return errors.New("nil sqlite blob")
}
rv := C.sqlite3_blob_close(r.bl)
if rv != C.SQLITE_OK {
return r.c.error(rv)
return r.c.error(rv, "BlobReader.Close")
}
r.bl = nil
return nil
......@@ -110,7 +111,7 @@ func (r *BlobReader) Read(v []byte) (int, error) {
n := len(v)
rv := C.sqlite3_blob_read(r.bl, unsafe.Pointer(p), C.int(n), C.int(r.ReadOffset))
if rv != C.SQLITE_OK {
return 0, r.c.error(rv)
return 0, r.c.error(rv, "BlobReader.Read")
}
r.ReadOffset += n
return n, nil
......@@ -150,7 +151,7 @@ func (w *BlobReadWriter) Write(v []byte) (int, error) {
n := len(v)
rv := C.sqlite3_blob_write(w.bl, unsafe.Pointer(p), C.int(n), C.int(w.WriteOffset))
if rv != C.SQLITE_OK {
return 0, w.c.error(rv)
return 0, w.c.error(rv, "BlobReadWiter.Write")
}
w.WriteOffset += n
return n, err
......@@ -161,7 +162,7 @@ func (w *BlobReadWriter) Write(v []byte) (int, error) {
func (r *BlobReader) Reopen(rowid int64) error {
rv := C.sqlite3_blob_reopen(r.bl, C.sqlite3_int64(rowid))
if rv != C.SQLITE_OK {
return r.c.error(rv)
return r.c.error(rv, fmt.Sprintf("BlobReader.Reopen(%d)", rowid))
}
r.size = -1
r.ReadOffset = 0
......
......@@ -421,7 +421,8 @@ func (c *Conn) CreateScalarFunction(functionName string, nArg int, pApp interfac
if len(c.udfs) > 0 {
delete(c.udfs, functionName)
}
return c.error(C.sqlite3_create_function_v2(c.db, fname, C.int(nArg), C.SQLITE_UTF8, nil, nil, nil, nil, nil))
return c.error(C.sqlite3_create_function_v2(c.db, fname, C.int(nArg), C.SQLITE_UTF8, nil, nil, nil, nil, nil),
fmt.Sprintf("<Conn.CreateScalarFunction(%q)", functionName))
}
// To make sure it is not gced, keep a reference in the connection.
udf := &sqliteFunction{f, nil, nil, d, pApp, make(map[*ScalarContext]bool), nil}
......@@ -429,7 +430,8 @@ func (c *Conn) CreateScalarFunction(functionName string, nArg int, pApp interfac
c.udfs = make(map[string]*sqliteFunction)
}
c.udfs[functionName] = udf // FIXME same function name with different args is not supported
return c.error(C.goSqlite3CreateScalarFunction(c.db, fname, C.int(nArg), C.SQLITE_UTF8, unsafe.Pointer(udf)))
return c.error(C.goSqlite3CreateScalarFunction(c.db, fname, C.int(nArg), C.SQLITE_UTF8, unsafe.Pointer(udf)),
fmt.Sprintf("Conn.CreateScalarFunction(%q)", functionName))
}
// CreateAggregateFunction creates or redefines SQL aggregate functions.
......@@ -443,7 +445,8 @@ func (c *Conn) CreateAggregateFunction(functionName string, nArg int, pApp inter
if len(c.udfs) > 0 {
delete(c.udfs, functionName)
}
return c.error(C.sqlite3_create_function_v2(c.db, fname, C.int(nArg), C.SQLITE_UTF8, nil, nil, nil, nil, nil))
return c.error(C.sqlite3_create_function_v2(c.db, fname, C.int(nArg), C.SQLITE_UTF8, nil, nil, nil, nil, nil),
fmt.Sprintf("<Conn.CreateAggregateFunction(%q)", functionName))
}
// To make sure it is not gced, keep a reference in the connection.
udf := &sqliteFunction{nil, step, final, d, pApp, nil, make(map[*AggregateContext]bool)}
......@@ -451,5 +454,6 @@ func (c *Conn) CreateAggregateFunction(functionName string, nArg int, pApp inter
c.udfs = make(map[string]*sqliteFunction)
}
c.udfs[functionName] = udf // FIXME same function name with different args is not supported
return c.error(C.goSqlite3CreateAggregateFunction(c.db, fname, C.int(nArg), C.SQLITE_UTF8, unsafe.Pointer(udf)))
return c.error(C.goSqlite3CreateAggregateFunction(c.db, fname, C.int(nArg), C.SQLITE_UTF8, unsafe.Pointer(udf)),
fmt.Sprintf("Conn.CreateAggregateFunction(%q)", functionName))
}
......@@ -35,7 +35,10 @@ static int my_table_column_metadata(
*/
import "C"
import "unsafe"
import (
"fmt"
"unsafe"
)
// Databases returns one couple (name, file) for each database attached to the current database connection.
// (See http://www.sqlite.org/pragma.html#pragma_database_list)
......@@ -146,7 +149,7 @@ func (c *Conn) Column(dbName, tableName, columnName string) (*Column, error) {
rv := C.my_table_column_metadata(c.db, zDbName, zTableName, zColumnName, &zDataType, &zCollSeq,
&notNull, &primaryKey, &autoinc)
if rv != C.SQLITE_OK {
return nil, c.error(rv)
return nil, c.error(rv, fmt.Sprintf("Conn.Column(db: %q, tbl: %q, col: %q)", dbName, tableName, columnName))
}
return &Column{-1, columnName, C.GoString(zDataType), notNull == 1, "", primaryKey == 1,
autoinc == 1, C.GoString(zCollSeq)}, nil
......
......@@ -32,7 +32,7 @@ func (c *Conn) IntegrityCheck(dbName string, max int, quick bool) error {
return err
}
if msg != "ok" {
return c.specificError("Integrity check failed (%s)", msg)
return c.specificError("Integrity check failed on %q (%s)", dbName, msg)
}
return nil
}
......@@ -155,5 +155,5 @@ func (c *Conn) oneValue(query string, value interface{}) error { // no cache
} else if err == Done {
return io.EOF
}
return s.error(rv)
return s.error(rv, fmt.Sprintf("Conn.oneValue(%q)", query))
}
......@@ -243,7 +243,7 @@ func OpenVfs(filename string, vfsname string, flags ...OpenFlag) (*Conn, error)
// BusyTimeout sets a busy timeout.
// (See http://sqlite.org/c3ref/busy_timeout.html)
func (c *Conn) BusyTimeout(ms int) error { // TODO time.Duration ?
return c.error(C.sqlite3_busy_timeout(c.db, C.int(ms)))
return c.error(C.sqlite3_busy_timeout(c.db, C.int(ms)), "Conn.BusyTimeout")
}
// EnableFKey enables or disables the enforcement of foreign key constraints.
......@@ -292,7 +292,7 @@ func (c *Conn) queryOrSetEnableDbConfig(key, i C.int) (bool, error) {
// EnableExtendedResultCodes enables or disables the extended result codes feature of SQLite.
// (See http://sqlite.org/c3ref/extended_result_codes.html)
func (c *Conn) EnableExtendedResultCodes(b bool) error {
return c.error(C.sqlite3_extended_result_codes(c.db, btocint(b)))
return c.error(C.sqlite3_extended_result_codes(c.db, btocint(b)), "Conn.EnableExtendedResultCodes")
}
// Readonly determines if a database is read-only.
......@@ -335,7 +335,7 @@ func (c *Conn) Exec(cmd string, args ...interface{}) error {
if len(s.tail) > 0 {
if len(args) > 0 {
s.finalize()
return c.specificError("Cannot execute multiple statements when args are specified")
return c.specificError("cannot execute multiple statements when args are specified: %q", cmd)
}
}
cmd = s.tail
......@@ -478,7 +478,7 @@ func (c *Conn) exec(cmd string) error {
defer s.finalize()
rv := C.sqlite3_step(s.stmt)
if Errno(rv) != Done {
return s.error(rv)
return s.error(rv, "Conn.exec(%q)", cmd)
}
return nil
}
......@@ -511,7 +511,7 @@ func (c *Conn) Close() error {
c.db = nil
if rv != C.SQLITE_OK {
Log(int(rv), "error while closing Conn")
return c.error(rv)
return c.error(rv, "Conn.Close")
}
return nil
}
......
......@@ -34,7 +34,6 @@ import "C"
import (
"errors"
"fmt"
"reflect"
"time"
"unsafe"
)
......@@ -150,7 +149,7 @@ func (s *Stmt) exec() error {
rv := C.sqlite3_step(s.stmt)
C.sqlite3_reset(s.stmt)
if Errno(rv) != Done {
return s.error(rv)
return s.error(rv, "Stmt.exec")
}
return nil
}
......@@ -228,6 +227,7 @@ func (s *Stmt) SelectOneRow(args ...interface{}) (bool, error) {
}
// BindParameterCount returns the number of SQL parameters.
// FIXME If parameters of the ?NNN form are used, there may be gaps in the list.
// (See http://sqlite.org/c3ref/bind_parameter_count.html)
func (s *Stmt) BindParameterCount() int {
if s.bindParameterCount == -1 {
......@@ -252,7 +252,7 @@ func (s *Stmt) BindParameterIndex(name string) (int, error) {
defer C.free(unsafe.Pointer(cname))
index = int(C.sqlite3_bind_parameter_index(s.stmt, cname))
if index == 0 {
return index, s.specificError("invalid parameter name: %s", name)
return index, s.specificError("invalid parameter name: %q", name)
}
s.params[name] = index
return index, nil
......@@ -273,12 +273,12 @@ func (s *Stmt) BindParameterName(i int) (string, error) {
// NamedBind binds parameters by their name (name1, value1, ...)
func (s *Stmt) NamedBind(args ...interface{}) error {
if len(args)%2 != 0 {
return s.specificError("Expected an even number of arguments")
return s.specificError("expected an even number of arguments: %d", len(args))
}
for i := 0; i < len(args); i += 2 {
name, ok := args[i].(string)
if !ok {
return s.specificError("non-string param name")
return s.specificError("non-string param name at %d: %T", i, args[i])
}
index, err := s.BindParameterIndex(name) // How to look up only once for one statement ?
if err != nil {
......@@ -346,9 +346,9 @@ func (s *Stmt) BindByIndex(index int, value interface{}) error {
case ZeroBlobLength:
rv = C.sqlite3_bind_zeroblob(s.stmt, i, C.int(value))
default:
return s.specificError("unsupported type in Bind: %s", reflect.TypeOf(value))
return s.specificError("unsupported type in Bind: %T", value)
}
return s.error(rv)
return s.error(rv, "Stmt.Bind")
}
// Next evaluates an SQL statement
......@@ -376,7 +376,7 @@ func (s *Stmt) Next() (bool, error) {
}
C.sqlite3_reset(s.stmt)
if err != Done {
return false, s.error(rv)
return false, s.error(rv, "Stmt.Next")
}
// TODO Check column count > 0
return false, nil
......@@ -386,13 +386,13 @@ func (s *Stmt) Next() (bool, error) {
// and reset it back to its starting state so that it can be reused.
// (See http://sqlite.org/c3ref/reset.html)
func (s *Stmt) Reset() error {
return s.error(C.sqlite3_reset(s.stmt))
return s.error(C.sqlite3_reset(s.stmt), "Stmt.Reset")
}
// ClearBindings resets all bindings on a prepared statement.
// (See http://sqlite.org/c3ref/clear_bindings.html)
func (s *Stmt) ClearBindings() error {
return s.error(C.sqlite3_clear_bindings(s.stmt))
return s.error(C.sqlite3_clear_bindings(s.stmt), "Stmt.ClearBindings")
}
// ColumnCount returns the number of columns in the result set for the statement (with or without row).
......@@ -470,12 +470,12 @@ func (s *Stmt) ColumnType(index int) Type {
// (See http://sqlite.org/c3ref/column_blob.html)
func (s *Stmt) NamedScan(args ...interface{}) error {
if len(args)%2 != 0 {
return s.specificError("Expected an even number of arguments")
return s.specificError("expected an even number of arguments: %d", len(args))
}
for i := 0; i < len(args); i += 2 {
name, ok := args[i].(string)
if !ok {
return s.specificError("non-string field name")
return s.specificError("non-string field name at %d: %T", i, args[i])
}
index, err := s.ColumnIndex(name) // How to look up only once for one statement ?
if err != nil {
......@@ -660,7 +660,7 @@ func (s *Stmt) ScanByIndex(index int, value interface{}) (bool, error) {
case func(interface{}) (bool, error):
isNull, err = value(s.ScanValue(index))
default:
return false, s.specificError("unsupported type in Scan: %s", reflect.TypeOf(value))
return false, s.specificError("unsupported type in Scan: %T", value)
}
return isNull, err
}
......@@ -885,14 +885,14 @@ func (s *Stmt) checkTypeMismatch(source, target Type) error {
case Text:
fallthrough
case Blob:
return s.specificError("Type mismatch, source %s vs target %s", source, target)
return s.specificError("type mismatch, source %s vs target %s", source, target)
}
case Float:
switch source {
case Text:
fallthrough
case Blob:
return s.specificError("Type mismatch, source %s vs target %s", source, target)
return s.specificError("type mismatch, source %s vs target %s", source, target)
}
}
return nil
......@@ -923,7 +923,7 @@ func (s *Stmt) finalize() error {
s.stmt = nil
if rv != C.SQLITE_OK {
Log(int(rv), "error while finalizing Stmt")
return s.error(rv)
return s.error(rv, "Stmt.finalize")
}
return nil
}
......
......@@ -226,11 +226,11 @@ func goXAuth(udp unsafe.Pointer, action int, arg1, arg2, dbName, triggerName *C.
func (c *Conn) SetAuthorizer(f Authorizer, udp interface{}) error {
if f == nil {
c.authorizer = nil
return c.error(C.sqlite3_set_authorizer(c.db, nil, nil))
return c.error(C.sqlite3_set_authorizer(c.db, nil, nil), "<Conn.SetAuthorizer")
}
// To make sure it is not gced, keep a reference in the connection.
c.authorizer = &sqliteAuthorizer{f, udp}
return c.error(C.goSqlite3SetAuthorizer(c.db, unsafe.Pointer(c.authorizer)))
return c.error(C.goSqlite3SetAuthorizer(c.db, unsafe.Pointer(c.authorizer)), "Conn.SetAuthorizer")
}
// Returns true to try again.
......@@ -254,11 +254,11 @@ func goXBusy(udp unsafe.Pointer, count int) C.int {
func (c *Conn) BusyHandler(f BusyHandler, udp interface{}) error {
if f == nil {
c.busyHandler = nil
return c.error(C.sqlite3_busy_handler(c.db, nil, nil))
return c.error(C.sqlite3_busy_handler(c.db, nil, nil), "<Conn.BusyHandler")
}
// To make sure it is not gced, keep a reference in the connection.
c.busyHandler = &sqliteBusyHandler{f, udp}
return c.error(C.goSqlite3BusyHandler(c.db, unsafe.Pointer(c.busyHandler)))
return c.error(C.goSqlite3BusyHandler(c.db, unsafe.Pointer(c.busyHandler)), "Conn.BusyHandler")
}
// Returns true to interrupt.
......
......@@ -13,6 +13,7 @@ int goSqlite3CreateModule(sqlite3 *db, const char *zName, void *pClientData);
import "C"
import (
"fmt"
"reflect"
"unsafe"
)
......@@ -223,7 +224,7 @@ type VTabCursor interface {
func (c *Conn) DeclareVTab(sql string) error {
zSQL := C.CString(sql)
defer C.free(unsafe.Pointer(zSQL))
return c.error(C.sqlite3_declare_vtab(c.db, zSQL))
return c.error(C.sqlite3_declare_vtab(c.db, zSQL), fmt.Sprintf("Conn.DeclareVTab(%q)", sql))
}
// CreateModule registers a virtual table implementation.
......@@ -237,7 +238,8 @@ func (c *Conn) CreateModule(moduleName string, module Module) error {
c.modules = make(map[string]*sqliteModule)
}
c.modules[moduleName] = udm // FIXME What happens if different modules are registered with the same name?
return c.error(C.goSqlite3CreateModule(c.db, mname, unsafe.Pointer(udm)))
return c.error(C.goSqlite3CreateModule(c.db, mname, unsafe.Pointer(udm)),
fmt.Sprintf("Conn.CreateModule(%q)", moduleName))
}
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment