Merge pull request #1422 from mattn/fix-bind-unsupported-type

Return error instead of silently ignoring unsupported bind types
This commit is contained in:
mattn
2026-07-13 03:01:23 +00:00
committed by GitHub
2 changed files with 58 additions and 3 deletions

View File

@@ -2294,6 +2294,17 @@ func stmtArgs(args []driver.NamedValue, start, na int) []driver.NamedValue {
return stmtArgs return stmtArgs
} }
// bindError converts a non-OK return code from bindValue into an error.
// The synthetic SQLITE_MISUSE returned for unsupported Go types is never
// recorded in the database handle, so lastError may report no error; fall
// back to an explicit message instead of silently ignoring the failure.
func (s *SQLiteStmt) bindError(v driver.Value) error {
if err := s.c.lastError(); err != nil {
return err
}
return fmt.Errorf("sqlite3: unsupported bind type %T", v)
}
func (s *SQLiteStmt) bind(args []driver.NamedValue) error { func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
rv := C._sqlite3_reset_clear(s.s) rv := C._sqlite3_reset_clear(s.s)
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
@@ -2313,7 +2324,7 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
n := C.int(arg.Ordinal) n := C.int(arg.Ordinal)
rv = bindValue(s.s, n, arg.Value) rv = bindValue(s.s, n, arg.Value)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return s.c.lastError() return s.bindError(arg.Value)
} }
} }
return nil return nil
@@ -2323,7 +2334,7 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
if arg.Name == "" { if arg.Name == "" {
rv = bindValue(s.s, C.int(arg.Ordinal), arg.Value) rv = bindValue(s.s, C.int(arg.Ordinal), arg.Value)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return s.c.lastError() return s.bindError(arg.Value)
} }
continue continue
} }
@@ -2334,7 +2345,7 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
} }
rv = bindValue(s.s, C.int(idx), arg.Value) rv = bindValue(s.s, C.int(idx), arg.Value)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return s.c.lastError() return s.bindError(arg.Value)
} }
} }
} }

View File

@@ -1324,6 +1324,50 @@ func TestDateTimeNow(t *testing.T) {
} }
} }
func TestBindErrorPaths(t *testing.T) {
d := &SQLiteDriver{}
conn, err := d.Open(":memory:")
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer conn.Close()
c := conn.(*SQLiteConn)
if _, err := c.Exec("CREATE TABLE t (v)", nil); err != nil {
t.Fatal("Failed to create table:", err)
}
// An unsupported Go type must report an explicit error instead of
// silently binding NULL: positional parameter.
_, err = c.Exec("INSERT INTO t VALUES (?)", []driver.Value{int32(1)})
if err == nil || !strings.Contains(err.Error(), "unsupported bind type int32") {
t.Errorf("positional bind of unsupported type: got %v, want unsupported bind type error", err)
}
// The same for a named parameter.
stmt, err := c.Prepare("INSERT INTO t VALUES (:x)")
if err != nil {
t.Fatal("Failed to prepare:", err)
}
err = stmt.(*SQLiteStmt).bind([]driver.NamedValue{{Name: "x", Ordinal: 1, Value: int32(1)}})
if err == nil || !strings.Contains(err.Error(), "unsupported bind type int32") {
t.Errorf("named bind of unsupported type: got %v, want unsupported bind type error", err)
}
stmt.Close()
// A genuine SQLite bind failure must preserve the recorded error.
stmt, err = c.Prepare("INSERT INTO t VALUES (?)")
if err != nil {
t.Fatal("Failed to prepare:", err)
}
err = stmt.(*SQLiteStmt).bind([]driver.NamedValue{{Ordinal: 2, Value: int64(1)}})
var serr Error
if !errors.As(err, &serr) || serr.Code != ErrRange {
t.Errorf("out-of-range bind: got %v, want SQLITE_RANGE error", err)
}
stmt.Close()
}
func TestFunctionRegistration(t *testing.T) { func TestFunctionRegistration(t *testing.T) {
addi8_16_32 := func(a int8, b int16) int32 { return int32(a) + int32(b) } addi8_16_32 := func(a int8, b int16) int32 { return int32(a) + int32(b) }
addi64 := func(a, b int64) int64 { return a + b } addi64 := func(a, b int64) int64 { return a + b }