2023-01-12 13:43:35 +00:00
|
|
|
package sqlite3
|
|
|
|
|
|
|
|
|
|
import (
|
2023-11-16 01:16:38 +00:00
|
|
|
"errors"
|
2023-01-12 13:43:35 +00:00
|
|
|
"strings"
|
2023-05-18 16:00:34 +01:00
|
|
|
|
|
|
|
|
"github.com/ncruces/go-sqlite3/internal/util"
|
2023-01-12 13:43:35 +00:00
|
|
|
)
|
|
|
|
|
|
2023-02-09 16:40:48 +00:00
|
|
|
// Error wraps an SQLite Error Code.
|
|
|
|
|
//
|
2023-11-09 16:35:45 +00:00
|
|
|
// https://sqlite.org/c3ref/errcode.html
|
2023-01-12 13:43:35 +00:00
|
|
|
type Error struct {
|
2025-10-15 16:22:36 +01:00
|
|
|
sys error
|
2023-02-09 16:40:48 +00:00
|
|
|
msg string
|
2023-02-10 16:42:49 +00:00
|
|
|
sql string
|
2025-01-21 01:42:57 +00:00
|
|
|
code res_t
|
2023-01-12 13:43:35 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-09 16:40:48 +00:00
|
|
|
// Code returns the primary error code for this error.
|
|
|
|
|
//
|
2023-11-09 16:35:45 +00:00
|
|
|
// https://sqlite.org/rescode.html
|
2023-02-09 16:40:48 +00:00
|
|
|
func (e *Error) Code() ErrorCode {
|
|
|
|
|
return ErrorCode(e.code)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExtendedCode returns the extended error code for this error.
|
|
|
|
|
//
|
2023-11-09 16:35:45 +00:00
|
|
|
// https://sqlite.org/rescode.html
|
2023-02-09 16:40:48 +00:00
|
|
|
func (e *Error) ExtendedCode() ExtendedErrorCode {
|
2025-03-28 11:51:45 +00:00
|
|
|
return xErrorCode(e.code)
|
2023-02-09 16:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Error implements the error interface.
|
2023-01-21 00:33:46 +00:00
|
|
|
func (e *Error) Error() string {
|
2023-01-12 13:43:35 +00:00
|
|
|
var b strings.Builder
|
2025-10-15 16:22:36 +01:00
|
|
|
b.WriteString(util.ErrorCodeString(e.code))
|
2023-01-12 13:43:35 +00:00
|
|
|
|
|
|
|
|
if e.msg != "" {
|
2023-12-06 15:39:26 +00:00
|
|
|
b.WriteString(": ")
|
2023-01-12 13:43:35 +00:00
|
|
|
b.WriteString(e.msg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return b.String()
|
|
|
|
|
}
|
2023-01-19 14:31:32 +00:00
|
|
|
|
2025-10-15 16:22:36 +01:00
|
|
|
// Unwrap returns the underlying operating system error
|
|
|
|
|
// that caused the I/O error or failure to open a file.
|
|
|
|
|
//
|
|
|
|
|
// https://sqlite.org/c3ref/system_errno.html
|
|
|
|
|
func (e *Error) Unwrap() error {
|
|
|
|
|
return e.sys
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-25 15:11:07 +00:00
|
|
|
// Is tests whether this error matches a given [ErrorCode] or [ExtendedErrorCode].
|
|
|
|
|
//
|
2023-03-01 10:34:08 +00:00
|
|
|
// It makes it possible to do:
|
2023-02-25 15:11:07 +00:00
|
|
|
//
|
|
|
|
|
// if errors.Is(err, sqlite3.BUSY) {
|
|
|
|
|
// // ... handle BUSY
|
|
|
|
|
// }
|
|
|
|
|
func (e *Error) Is(err error) bool {
|
|
|
|
|
switch c := err.(type) {
|
|
|
|
|
case ErrorCode:
|
|
|
|
|
return c == e.Code()
|
|
|
|
|
case ExtendedErrorCode:
|
|
|
|
|
return c == e.ExtendedCode()
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-30 12:25:07 +01:00
|
|
|
// As converts this error to an [ErrorCode] or [ExtendedErrorCode].
|
|
|
|
|
func (e *Error) As(err any) bool {
|
|
|
|
|
switch c := err.(type) {
|
|
|
|
|
case *ErrorCode:
|
|
|
|
|
*c = e.Code()
|
|
|
|
|
return true
|
|
|
|
|
case *ExtendedErrorCode:
|
|
|
|
|
*c = e.ExtendedCode()
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-20 13:38:03 +00:00
|
|
|
// Temporary returns true for [BUSY] errors.
|
|
|
|
|
func (e *Error) Temporary() bool {
|
2025-04-08 12:41:46 +01:00
|
|
|
return e.Code() == BUSY || e.Code() == INTERRUPT
|
2023-02-20 13:38:03 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-25 15:11:07 +00:00
|
|
|
// Timeout returns true for [BUSY_TIMEOUT] errors.
|
|
|
|
|
func (e *Error) Timeout() bool {
|
|
|
|
|
return e.ExtendedCode() == BUSY_TIMEOUT
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-10 16:42:49 +00:00
|
|
|
// SQL returns the SQL starting at the token that triggered a syntax error.
|
|
|
|
|
func (e *Error) SQL() string {
|
|
|
|
|
return e.sql
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-25 15:11:07 +00:00
|
|
|
// Error implements the error interface.
|
|
|
|
|
func (e ErrorCode) Error() string {
|
2025-10-15 16:22:36 +01:00
|
|
|
return util.ErrorCodeString(e)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// As converts this error to an [ExtendedErrorCode].
|
|
|
|
|
func (e ErrorCode) As(err any) bool {
|
|
|
|
|
c, ok := err.(*xErrorCode)
|
|
|
|
|
if ok {
|
|
|
|
|
*c = xErrorCode(e)
|
|
|
|
|
}
|
|
|
|
|
return ok
|
2023-02-25 15:11:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Temporary returns true for [BUSY] errors.
|
|
|
|
|
func (e ErrorCode) Temporary() bool {
|
2025-03-28 11:51:45 +00:00
|
|
|
return e == BUSY || e == INTERRUPT
|
2023-02-25 15:11:07 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-06 11:45:20 +00:00
|
|
|
// ExtendedCode returns the extended error code for this error.
|
|
|
|
|
func (e ErrorCode) ExtendedCode() ExtendedErrorCode {
|
2025-03-28 11:51:45 +00:00
|
|
|
return xErrorCode(e)
|
2024-11-06 11:45:20 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-25 15:11:07 +00:00
|
|
|
// Error implements the error interface.
|
|
|
|
|
func (e ExtendedErrorCode) Error() string {
|
2025-10-15 16:22:36 +01:00
|
|
|
return util.ErrorCodeString(e)
|
2023-02-25 15:11:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Is tests whether this error matches a given [ErrorCode].
|
|
|
|
|
func (e ExtendedErrorCode) Is(err error) bool {
|
|
|
|
|
c, ok := err.(ErrorCode)
|
|
|
|
|
return ok && c == ErrorCode(e)
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-30 12:25:07 +01:00
|
|
|
// As converts this error to an [ErrorCode].
|
|
|
|
|
func (e ExtendedErrorCode) As(err any) bool {
|
|
|
|
|
c, ok := err.(*ErrorCode)
|
|
|
|
|
if ok {
|
|
|
|
|
*c = ErrorCode(e)
|
|
|
|
|
}
|
|
|
|
|
return ok
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-25 15:11:07 +00:00
|
|
|
// Temporary returns true for [BUSY] errors.
|
|
|
|
|
func (e ExtendedErrorCode) Temporary() bool {
|
2025-03-28 11:51:45 +00:00
|
|
|
return ErrorCode(e) == BUSY || ErrorCode(e) == INTERRUPT
|
2023-02-25 15:11:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Timeout returns true for [BUSY_TIMEOUT] errors.
|
|
|
|
|
func (e ExtendedErrorCode) Timeout() bool {
|
|
|
|
|
return e == BUSY_TIMEOUT
|
|
|
|
|
}
|
2023-11-16 01:16:38 +00:00
|
|
|
|
2024-11-06 11:45:20 +00:00
|
|
|
// Code returns the primary error code for this error.
|
|
|
|
|
func (e ExtendedErrorCode) Code() ErrorCode {
|
|
|
|
|
return ErrorCode(e)
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-21 01:42:57 +00:00
|
|
|
func errorCode(err error, def ErrorCode) (msg string, code res_t) {
|
2023-11-21 13:40:55 +00:00
|
|
|
switch code := err.(type) {
|
2024-01-11 02:18:12 +00:00
|
|
|
case nil:
|
|
|
|
|
return "", _OK
|
2023-11-21 13:40:55 +00:00
|
|
|
case ErrorCode:
|
2025-01-21 01:42:57 +00:00
|
|
|
return "", res_t(code)
|
2024-01-11 02:18:12 +00:00
|
|
|
case xErrorCode:
|
2025-01-21 01:42:57 +00:00
|
|
|
return "", res_t(code)
|
2023-12-05 16:11:56 +00:00
|
|
|
case *Error:
|
2025-01-21 01:42:57 +00:00
|
|
|
return code.msg, res_t(code.code)
|
2023-11-21 13:40:55 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-16 01:16:38 +00:00
|
|
|
var xcode xErrorCode
|
2025-10-15 16:22:36 +01:00
|
|
|
if errors.As(err, &xcode) {
|
2025-01-21 01:42:57 +00:00
|
|
|
code = res_t(xcode)
|
2025-10-15 16:22:36 +01:00
|
|
|
} else {
|
2025-01-21 01:42:57 +00:00
|
|
|
code = res_t(def)
|
2023-11-16 01:16:38 +00:00
|
|
|
}
|
2023-11-21 13:40:55 +00:00
|
|
|
return err.Error(), code
|
2023-11-16 01:16:38 +00:00
|
|
|
}
|