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
|
|
|
"strconv"
|
|
|
|
|
"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 {
|
2023-02-09 16:40:48 +00:00
|
|
|
str string
|
|
|
|
|
msg string
|
2023-02-10 16:42:49 +00:00
|
|
|
sql string
|
2023-04-11 15:33:38 +01:00
|
|
|
code uint64
|
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 {
|
|
|
|
|
return ExtendedErrorCode(e.code)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
b.WriteString("sqlite3: ")
|
|
|
|
|
|
|
|
|
|
if e.str != "" {
|
|
|
|
|
b.WriteString(e.str)
|
|
|
|
|
} else {
|
2023-02-09 16:40:48 +00:00
|
|
|
b.WriteString(strconv.Itoa(int(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
|
|
|
|
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 {
|
|
|
|
|
return e.Code() == BUSY
|
|
|
|
|
}
|
|
|
|
|
|
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 {
|
2023-05-18 16:00:34 +01:00
|
|
|
return util.ErrorCodeString(uint32(e))
|
2023-02-25 15:11:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Temporary returns true for [BUSY] errors.
|
|
|
|
|
func (e ErrorCode) Temporary() bool {
|
|
|
|
|
return e == BUSY
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Error implements the error interface.
|
|
|
|
|
func (e ExtendedErrorCode) Error() string {
|
2023-05-18 16:00:34 +01:00
|
|
|
return util.ErrorCodeString(uint32(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 {
|
|
|
|
|
return ErrorCode(e) == BUSY
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Timeout returns true for [BUSY_TIMEOUT] errors.
|
|
|
|
|
func (e ExtendedErrorCode) Timeout() bool {
|
|
|
|
|
return e == BUSY_TIMEOUT
|
|
|
|
|
}
|
2023-11-16 01:16:38 +00:00
|
|
|
|
2023-11-21 13:40:55 +00:00
|
|
|
func errorCode(err error, def ErrorCode) (msg string, code uint32) {
|
|
|
|
|
switch code := err.(type) {
|
|
|
|
|
case ErrorCode:
|
|
|
|
|
return "", uint32(code)
|
|
|
|
|
case ExtendedErrorCode:
|
|
|
|
|
return "", uint32(code)
|
2023-12-05 16:11:56 +00:00
|
|
|
case *Error:
|
|
|
|
|
return code.msg, uint32(code.code)
|
2023-11-21 13:40:55 +00:00
|
|
|
case nil:
|
|
|
|
|
return "", _OK
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-16 01:16:38 +00:00
|
|
|
var ecode ErrorCode
|
|
|
|
|
var xcode xErrorCode
|
|
|
|
|
switch {
|
|
|
|
|
case errors.As(err, &xcode):
|
2023-11-21 13:40:55 +00:00
|
|
|
code = uint32(xcode)
|
2023-11-16 01:16:38 +00:00
|
|
|
case errors.As(err, &ecode):
|
2023-11-21 13:40:55 +00:00
|
|
|
code = uint32(ecode)
|
|
|
|
|
default:
|
|
|
|
|
code = uint32(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
|
|
|
}
|