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  }