1package sqlite3
2
3import (
4 "errors"
5 "strings"
6
7 "github.com/ncruces/go-sqlite3/internal/util"
8)
9
10// Error wraps an SQLite Error Code.
11//
12// https://sqlite.org/c3ref/errcode.html
13type Error struct {
14 msg string
15 sql string
16 code res_t
17}
18
19// Code returns the primary error code for this error.
20//
21// https://sqlite.org/rescode.html
22func (e *Error) Code() ErrorCode {
23 return ErrorCode(e.code)
24}
25
26// ExtendedCode returns the extended error code for this error.
27//
28// https://sqlite.org/rescode.html
29func (e *Error) ExtendedCode() ExtendedErrorCode {
30 return xErrorCode(e.code)
31}
32
33// Error implements the error interface.
34func (e *Error) Error() string {
35 var b strings.Builder
36 b.WriteString(util.ErrorCodeString(uint32(e.code)))
37
38 if e.msg != "" {
39 b.WriteString(": ")
40 b.WriteString(e.msg)
41 }
42
43 return b.String()
44}
45
46// Is tests whether this error matches a given [ErrorCode] or [ExtendedErrorCode].
47//
48// It makes it possible to do:
49//
50// if errors.Is(err, sqlite3.BUSY) {
51// // ... handle BUSY
52// }
53func (e *Error) Is(err error) bool {
54 switch c := err.(type) {
55 case ErrorCode:
56 return c == e.Code()
57 case ExtendedErrorCode:
58 return c == e.ExtendedCode()
59 }
60 return false
61}
62
63// As converts this error to an [ErrorCode] or [ExtendedErrorCode].
64func (e *Error) As(err any) bool {
65 switch c := err.(type) {
66 case *ErrorCode:
67 *c = e.Code()
68 return true
69 case *ExtendedErrorCode:
70 *c = e.ExtendedCode()
71 return true
72 }
73 return false
74}
75
76// Temporary returns true for [BUSY] errors.
77func (e *Error) Temporary() bool {
78 return e.Code() == BUSY
79}
80
81// Timeout returns true for [BUSY_TIMEOUT] errors.
82func (e *Error) Timeout() bool {
83 return e.ExtendedCode() == BUSY_TIMEOUT
84}
85
86// SQL returns the SQL starting at the token that triggered a syntax error.
87func (e *Error) SQL() string {
88 return e.sql
89}
90
91// Error implements the error interface.
92func (e ErrorCode) Error() string {
93 return util.ErrorCodeString(uint32(e))
94}
95
96// Temporary returns true for [BUSY] errors.
97func (e ErrorCode) Temporary() bool {
98 return e == BUSY || e == INTERRUPT
99}
100
101// ExtendedCode returns the extended error code for this error.
102func (e ErrorCode) ExtendedCode() ExtendedErrorCode {
103 return xErrorCode(e)
104}
105
106// Error implements the error interface.
107func (e ExtendedErrorCode) Error() string {
108 return util.ErrorCodeString(uint32(e))
109}
110
111// Is tests whether this error matches a given [ErrorCode].
112func (e ExtendedErrorCode) Is(err error) bool {
113 c, ok := err.(ErrorCode)
114 return ok && c == ErrorCode(e)
115}
116
117// As converts this error to an [ErrorCode].
118func (e ExtendedErrorCode) As(err any) bool {
119 c, ok := err.(*ErrorCode)
120 if ok {
121 *c = ErrorCode(e)
122 }
123 return ok
124}
125
126// Temporary returns true for [BUSY] errors.
127func (e ExtendedErrorCode) Temporary() bool {
128 return ErrorCode(e) == BUSY || ErrorCode(e) == INTERRUPT
129}
130
131// Timeout returns true for [BUSY_TIMEOUT] errors.
132func (e ExtendedErrorCode) Timeout() bool {
133 return e == BUSY_TIMEOUT
134}
135
136// Code returns the primary error code for this error.
137func (e ExtendedErrorCode) Code() ErrorCode {
138 return ErrorCode(e)
139}
140
141func errorCode(err error, def ErrorCode) (msg string, code res_t) {
142 switch code := err.(type) {
143 case nil:
144 return "", _OK
145 case ErrorCode:
146 return "", res_t(code)
147 case xErrorCode:
148 return "", res_t(code)
149 case *Error:
150 return code.msg, res_t(code.code)
151 }
152
153 var ecode ErrorCode
154 var xcode xErrorCode
155 switch {
156 case errors.As(err, &xcode):
157 code = res_t(xcode)
158 case errors.As(err, &ecode):
159 code = res_t(ecode)
160 default:
161 code = res_t(def)
162 }
163 return err.Error(), code
164}