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