Skip to content

Commit e85adcf

Browse files
committed
use extended error codes and handle closed db connections in lastError
This commit changes the error handling logic so that it respects the offending result code (instead of only relying on sqlite3_errcode) and changes the db connection to always report the extended result code (which eliminates the need to call sqlite3_extended_errcode). These changes make it possible to correctly and safely handle errors when the underlying db connection has been closed.
1 parent a66fe30 commit e85adcf

8 files changed

+94
-58
lines changed

backup.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ func (destConn *SQLiteConn) Backup(dest string, srcConn *SQLiteConn, src string)
3636
runtime.SetFinalizer(bb, (*SQLiteBackup).Finish)
3737
return bb, nil
3838
}
39-
return nil, destConn.lastError()
39+
if destConn.db != nil {
40+
return nil, destConn.lastError(int(C.sqlite3_extended_errcode(destConn.db)))
41+
}
42+
return nil, Error{Code: 1, ExtendedCode: 1, err: "backup: destination connection is nil"}
4043
}
4144

4245
// Step to backs up for one step. Calls the underlying `sqlite3_backup_step`

error.go

+25-3
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ package sqlite3
1313
#endif
1414
*/
1515
import "C"
16-
import "syscall"
16+
import (
17+
"sync"
18+
"syscall"
19+
)
1720

1821
// ErrNo inherit errno.
1922
type ErrNo int
2023

2124
// ErrNoMask is mask code.
22-
const ErrNoMask C.int = 0xff
25+
const ErrNoMask = 0xff
2326

2427
// ErrNoExtended is extended errno.
2528
type ErrNoExtended int
@@ -85,7 +88,7 @@ func (err Error) Error() string {
8588
if err.err != "" {
8689
str = err.err
8790
} else {
88-
str = C.GoString(C.sqlite3_errstr(C.int(err.Code)))
91+
str = errorString(int(err.Code))
8992
}
9093
if err.SystemErrno != 0 {
9194
str += ": " + err.SystemErrno.Error()
@@ -148,3 +151,22 @@ var (
148151
ErrNoticeRecoverRollback = ErrNotice.Extend(2)
149152
ErrWarningAutoIndex = ErrWarning.Extend(1)
150153
)
154+
155+
var errStrCache sync.Map // int => string
156+
157+
// errorString returns the result of sqlite3_errstr for result code rv,
158+
// which may be cached.
159+
func errorString(rv int) string {
160+
if v, ok := errStrCache.Load(rv); ok {
161+
return v.(string)
162+
}
163+
s := C.GoString(C.sqlite3_errstr(C.int(rv)))
164+
// Prevent the cache from growing unbounded by ignoring invalid
165+
// error codes.
166+
if s != "unknown error" {
167+
if v, loaded := errStrCache.LoadOrStore(rv, s); loaded {
168+
s = v.(string)
169+
}
170+
}
171+
return s
172+
}

sqlite3.go

+36-25
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ func (c *SQLiteConn) RegisterCollation(name string, cmp func(string, string) int
540540
defer C.free(unsafe.Pointer(cname))
541541
rv := C.sqlite3_create_collation(c.db, cname, C.SQLITE_UTF8, handle, (*[0]byte)(unsafe.Pointer(C.compareTrampoline)))
542542
if rv != C.SQLITE_OK {
543-
return c.lastError()
543+
return c.lastError(int(rv))
544544
}
545545
return nil
546546
}
@@ -675,7 +675,7 @@ func (c *SQLiteConn) RegisterFunc(name string, impl any, pure bool) error {
675675
}
676676
rv := sqlite3CreateFunction(c.db, cname, C.int(numArgs), C.int(opts), newHandle(c, &fi), C.callbackTrampoline, nil, nil)
677677
if rv != C.SQLITE_OK {
678-
return c.lastError()
678+
return c.lastError(int(rv))
679679
}
680680
return nil
681681
}
@@ -804,7 +804,7 @@ func (c *SQLiteConn) RegisterAggregator(name string, impl any, pure bool) error
804804
}
805805
rv := sqlite3CreateFunction(c.db, cname, C.int(stepNArgs), C.int(opts), newHandle(c, &ai), nil, C.stepTrampoline, C.doneTrampoline)
806806
if rv != C.SQLITE_OK {
807-
return c.lastError()
807+
return c.lastError(int(rv))
808808
}
809809
return nil
810810
}
@@ -816,32 +816,38 @@ func (c *SQLiteConn) AutoCommit() bool {
816816
return int(C.sqlite3_get_autocommit(c.db)) != 0
817817
}
818818

819-
func (c *SQLiteConn) lastError() error {
820-
return lastError(c.db)
819+
func (c *SQLiteConn) lastError(rv int) error {
820+
return lastError(c.db, rv)
821821
}
822822

823-
// Note: may be called with db == nil
824-
func lastError(db *C.sqlite3) error {
825-
rv := C.sqlite3_errcode(db) // returns SQLITE_NOMEM if db == nil
826-
if rv == C.SQLITE_OK {
823+
func lastError(db *C.sqlite3, rv int) error {
824+
if rv == SQLITE_OK {
827825
return nil
828826
}
829-
extrv := C.sqlite3_extended_errcode(db) // returns SQLITE_NOMEM if db == nil
830-
errStr := C.GoString(C.sqlite3_errmsg(db)) // returns "out of memory" if db == nil
827+
extrv := rv
828+
// Convert the extended result code to a basic result code.
829+
rv &= ErrNoMask
831830

832831
// https://www.sqlite.org/c3ref/system_errno.html
833832
// sqlite3_system_errno is only meaningful if the error code was SQLITE_CANTOPEN,
834833
// or it was SQLITE_IOERR and the extended code was not SQLITE_IOERR_NOMEM
835834
var systemErrno syscall.Errno
836-
if rv == C.SQLITE_CANTOPEN || (rv == C.SQLITE_IOERR && extrv != C.SQLITE_IOERR_NOMEM) {
835+
if db != nil && (rv == C.SQLITE_CANTOPEN ||
836+
(rv == C.SQLITE_IOERR && extrv != C.SQLITE_IOERR_NOMEM)) {
837837
systemErrno = syscall.Errno(C.sqlite3_system_errno(db))
838838
}
839839

840+
var msg string
841+
if db != nil {
842+
msg = C.GoString(C.sqlite3_errmsg(db))
843+
} else {
844+
msg = errorString(extrv)
845+
}
840846
return Error{
841847
Code: ErrNo(rv),
842848
ExtendedCode: ErrNoExtended(extrv),
843849
SystemErrno: systemErrno,
844-
err: errStr,
850+
err: msg,
845851
}
846852
}
847853

@@ -1467,7 +1473,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
14671473
if rv != 0 {
14681474
// Save off the error _before_ closing the database.
14691475
// This is safe even if db is nil.
1470-
err := lastError(db)
1476+
err := lastError(db, int(rv))
14711477
if db != nil {
14721478
C.sqlite3_close_v2(db)
14731479
}
@@ -1476,13 +1482,18 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
14761482
if db == nil {
14771483
return nil, errors.New("sqlite succeeded without returning a database")
14781484
}
1485+
rv = C.sqlite3_extended_result_codes(db, 1)
1486+
if rv != SQLITE_OK {
1487+
C.sqlite3_close_v2(db)
1488+
return nil, lastError(db, int(rv))
1489+
}
14791490

14801491
exec := func(s string) error {
14811492
cs := C.CString(s)
14821493
rv := C.sqlite3_exec(db, cs, nil, nil, nil)
14831494
C.free(unsafe.Pointer(cs))
14841495
if rv != C.SQLITE_OK {
1485-
return lastError(db)
1496+
return lastError(db, int(rv))
14861497
}
14871498
return nil
14881499
}
@@ -1791,7 +1802,7 @@ func (c *SQLiteConn) Close() (err error) {
17911802
runtime.SetFinalizer(c, nil)
17921803
rv := C.sqlite3_close_v2(c.db)
17931804
if rv != C.SQLITE_OK {
1794-
err = c.lastError()
1805+
err = lastError(nil, int(rv))
17951806
}
17961807
deleteHandles(c)
17971808
c.db = nil
@@ -1810,7 +1821,7 @@ func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, er
18101821
var tail *C.char
18111822
rv := C._sqlite3_prepare_v2_internal(c.db, pquery, C.int(-1), &s, &tail)
18121823
if rv != C.SQLITE_OK {
1813-
return nil, c.lastError()
1824+
return nil, c.lastError(int(rv))
18141825
}
18151826
var t string
18161827
if tail != nil && *tail != '\000' {
@@ -1882,7 +1893,7 @@ func (c *SQLiteConn) SetFileControlInt(dbName string, op int, arg int) error {
18821893
cArg := C.int(arg)
18831894
rv := C.sqlite3_file_control(c.db, cDBName, C.int(op), unsafe.Pointer(&cArg))
18841895
if rv != C.SQLITE_OK {
1885-
return c.lastError()
1896+
return c.lastError(int(rv))
18861897
}
18871898
return nil
18881899
}
@@ -1902,7 +1913,7 @@ func (s *SQLiteStmt) Close() error {
19021913
runtime.SetFinalizer(s, nil)
19031914
rv := C.sqlite3_finalize(stmt)
19041915
if rv != C.SQLITE_OK {
1905-
return conn.lastError()
1916+
return conn.lastError(int(rv))
19061917
}
19071918
return nil
19081919
}
@@ -1917,7 +1928,7 @@ var placeHolder = []byte{0}
19171928
func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
19181929
rv := C.sqlite3_reset(s.s)
19191930
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
1920-
return s.c.lastError()
1931+
return s.c.lastError(int(rv))
19211932
}
19221933

19231934
bindIndices := make([][3]int, len(args))
@@ -1975,7 +1986,7 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
19751986
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b)))
19761987
}
19771988
if rv != C.SQLITE_OK {
1978-
return s.c.lastError()
1989+
return s.c.lastError(int(rv))
19791990
}
19801991
}
19811992
}
@@ -2085,7 +2096,7 @@ func (s *SQLiteStmt) execSync(args []driver.NamedValue) (driver.Result, error) {
20852096
var rowid, changes C.longlong
20862097
rv := C._sqlite3_step_row_internal(s.s, &rowid, &changes)
20872098
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
2088-
err := s.c.lastError()
2099+
err := s.c.lastError(int(rv))
20892100
C.sqlite3_reset(s.s)
20902101
C.sqlite3_clear_bindings(s.s)
20912102
return nil, err
@@ -2121,8 +2132,8 @@ func (rc *SQLiteRows) Close() error {
21212132
}
21222133
rv := C.sqlite3_reset(s.s)
21232134
if rv != C.SQLITE_OK {
2124-
s.mu.Unlock()
2125-
return s.c.lastError()
2135+
rc.s.mu.Unlock()
2136+
return rc.s.c.lastError(int(rv))
21262137
}
21272138
s.mu.Unlock()
21282139
return nil
@@ -2199,7 +2210,7 @@ func (rc *SQLiteRows) nextSyncLocked(dest []driver.Value) error {
21992210
if rv != C.SQLITE_ROW {
22002211
rv = C.sqlite3_reset(rc.s.s)
22012212
if rv != C.SQLITE_OK {
2202-
return rc.s.c.lastError()
2213+
return rc.s.c.lastError(int(rv))
22032214
}
22042215
return nil
22052216
}

sqlite3_opt_unlock_notify.c

+7-13
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
// license that can be found in the LICENSE file.
55

66
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
7-
#include <stdio.h>
87
#ifndef USE_LIBSQLITE3
98
#include "sqlite3-binding.h"
109
#else
@@ -13,6 +12,10 @@
1312

1413
extern int unlock_notify_wait(sqlite3 *db);
1514

15+
static inline int is_locked(int rv) {
16+
return rv == SQLITE_LOCKED || rv == SQLITE_LOCKED_SHAREDCACHE;
17+
}
18+
1619
int
1720
_sqlite3_step_blocking(sqlite3_stmt *stmt)
1821
{
@@ -22,10 +25,7 @@ _sqlite3_step_blocking(sqlite3_stmt *stmt)
2225
db = sqlite3_db_handle(stmt);
2326
for (;;) {
2427
rv = sqlite3_step(stmt);
25-
if (rv != SQLITE_LOCKED) {
26-
break;
27-
}
28-
if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) {
28+
if (!is_locked(rv)) {
2929
break;
3030
}
3131
rv = unlock_notify_wait(db);
@@ -47,10 +47,7 @@ _sqlite3_step_row_blocking(sqlite3_stmt* stmt, long long* rowid, long long* chan
4747
db = sqlite3_db_handle(stmt);
4848
for (;;) {
4949
rv = sqlite3_step(stmt);
50-
if (rv!=SQLITE_LOCKED) {
51-
break;
52-
}
53-
if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) {
50+
if (!is_locked(rv)) {
5451
break;
5552
}
5653
rv = unlock_notify_wait(db);
@@ -72,10 +69,7 @@ _sqlite3_prepare_v2_blocking(sqlite3 *db, const char *zSql, int nBytes, sqlite3_
7269

7370
for (;;) {
7471
rv = sqlite3_prepare_v2(db, zSql, nBytes, ppStmt, pzTail);
75-
if (rv!=SQLITE_LOCKED) {
76-
break;
77-
}
78-
if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) {
72+
if (!is_locked(rv)) {
7973
break;
8074
}
8175
rv = unlock_notify_wait(db);

sqlite3_opt_unlock_notify_test.go

+14-8
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ func TestUnlockNotify(t *testing.T) {
5151
wg.Add(1)
5252
timer := time.NewTimer(500 * time.Millisecond)
5353
go func() {
54+
defer wg.Done()
5455
<-timer.C
5556
err := tx.Commit()
5657
if err != nil {
5758
t.Fatal("Failed to commit transaction:", err)
5859
}
59-
wg.Done()
6060
}()
6161

6262
rows, err := db.Query("SELECT count(*) from foo")
@@ -111,33 +111,39 @@ func TestUnlockNotifyMany(t *testing.T) {
111111
wg.Add(1)
112112
timer := time.NewTimer(500 * time.Millisecond)
113113
go func() {
114+
defer wg.Done()
114115
<-timer.C
115116
err := tx.Commit()
116117
if err != nil {
117118
t.Fatal("Failed to commit transaction:", err)
118119
}
119-
wg.Done()
120120
}()
121121

122122
const concurrentQueries = 1000
123123
wg.Add(concurrentQueries)
124124
for i := 0; i < concurrentQueries; i++ {
125125
go func() {
126+
defer wg.Done()
126127
rows, err := db.Query("SELECT count(*) from foo")
127128
if err != nil {
128-
t.Fatal("Unable to query foo table:", err)
129+
t.Error("Unable to query foo table:", err)
130+
return
129131
}
130132

131133
if rows.Next() {
132134
var count int
133135
if err := rows.Scan(&count); err != nil {
134-
t.Fatal("Failed to Scan rows", err)
136+
t.Error("Failed to Scan rows", err)
137+
return
138+
}
139+
if count != 1 {
140+
t.Errorf("count=%d want=%d", count, 1)
135141
}
136142
}
137143
if err := rows.Err(); err != nil {
138-
t.Fatal("Failed at the call to Next:", err)
144+
t.Error("Failed at the call to Next:", err)
145+
return
139146
}
140-
wg.Done()
141147
}()
142148
}
143149
wg.Wait()
@@ -177,16 +183,17 @@ func TestUnlockNotifyDeadlock(t *testing.T) {
177183
wg.Add(1)
178184
timer := time.NewTimer(500 * time.Millisecond)
179185
go func() {
186+
defer wg.Done()
180187
<-timer.C
181188
err := tx.Commit()
182189
if err != nil {
183190
t.Fatal("Failed to commit transaction:", err)
184191
}
185-
wg.Done()
186192
}()
187193

188194
wg.Add(1)
189195
go func() {
196+
defer wg.Done()
190197
tx2, err := db.Begin()
191198
if err != nil {
192199
t.Fatal("Failed to begin transaction:", err)
@@ -201,7 +208,6 @@ func TestUnlockNotifyDeadlock(t *testing.T) {
201208
if err != nil {
202209
t.Fatal("Failed to commit transaction:", err)
203210
}
204-
wg.Done()
205211
}()
206212

207213
rows, err := tx.Query("SELECT count(*) from foo")

0 commit comments

Comments
 (0)