github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/context.go (about)

     1  package sqlite3
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"math"
     7  	"time"
     8  
     9  	"github.com/ncruces/go-sqlite3/internal/util"
    10  )
    11  
    12  // Context is the context in which an SQL function executes.
    13  // An SQLite [Context] is in no way related to a Go [context.Context].
    14  //
    15  // https://sqlite.org/c3ref/context.html
    16  type Context struct {
    17  	c      *Conn
    18  	handle uint32
    19  }
    20  
    21  // Conn returns the database connection of the
    22  // [Conn.CreateFunction] or [Conn.CreateWindowFunction]
    23  // routines that originally registered the application defined function.
    24  //
    25  // https://sqlite.org/c3ref/context_db_handle.html
    26  func (ctx Context) Conn() *Conn {
    27  	return ctx.c
    28  }
    29  
    30  // SetAuxData saves metadata for argument n of the function.
    31  //
    32  // https://sqlite.org/c3ref/get_auxdata.html
    33  func (ctx Context) SetAuxData(n int, data any) {
    34  	ptr := util.AddHandle(ctx.c.ctx, data)
    35  	ctx.c.call("sqlite3_set_auxdata_go", uint64(ctx.handle), uint64(n), uint64(ptr))
    36  }
    37  
    38  // GetAuxData returns metadata for argument n of the function.
    39  //
    40  // https://sqlite.org/c3ref/get_auxdata.html
    41  func (ctx Context) GetAuxData(n int) any {
    42  	ptr := uint32(ctx.c.call("sqlite3_get_auxdata", uint64(ctx.handle), uint64(n)))
    43  	return util.GetHandle(ctx.c.ctx, ptr)
    44  }
    45  
    46  // ResultBool sets the result of the function to a bool.
    47  // SQLite does not have a separate boolean storage class.
    48  // Instead, boolean values are stored as integers 0 (false) and 1 (true).
    49  //
    50  // https://sqlite.org/c3ref/result_blob.html
    51  func (ctx Context) ResultBool(value bool) {
    52  	var i int64
    53  	if value {
    54  		i = 1
    55  	}
    56  	ctx.ResultInt64(i)
    57  }
    58  
    59  // ResultInt sets the result of the function to an int.
    60  //
    61  // https://sqlite.org/c3ref/result_blob.html
    62  func (ctx Context) ResultInt(value int) {
    63  	ctx.ResultInt64(int64(value))
    64  }
    65  
    66  // ResultInt64 sets the result of the function to an int64.
    67  //
    68  // https://sqlite.org/c3ref/result_blob.html
    69  func (ctx Context) ResultInt64(value int64) {
    70  	ctx.c.call("sqlite3_result_int64",
    71  		uint64(ctx.handle), uint64(value))
    72  }
    73  
    74  // ResultFloat sets the result of the function to a float64.
    75  //
    76  // https://sqlite.org/c3ref/result_blob.html
    77  func (ctx Context) ResultFloat(value float64) {
    78  	ctx.c.call("sqlite3_result_double",
    79  		uint64(ctx.handle), math.Float64bits(value))
    80  }
    81  
    82  // ResultText sets the result of the function to a string.
    83  //
    84  // https://sqlite.org/c3ref/result_blob.html
    85  func (ctx Context) ResultText(value string) {
    86  	ptr := ctx.c.newString(value)
    87  	ctx.c.call("sqlite3_result_text64",
    88  		uint64(ctx.handle), uint64(ptr), uint64(len(value)),
    89  		uint64(ctx.c.freer), _UTF8)
    90  }
    91  
    92  // ResultRawText sets the text result of the function to a []byte.
    93  //
    94  // https://sqlite.org/c3ref/result_blob.html
    95  func (ctx Context) ResultRawText(value []byte) {
    96  	ptr := ctx.c.newBytes(value)
    97  	ctx.c.call("sqlite3_result_text64",
    98  		uint64(ctx.handle), uint64(ptr), uint64(len(value)),
    99  		uint64(ctx.c.freer), _UTF8)
   100  }
   101  
   102  // ResultBlob sets the result of the function to a []byte.
   103  // Returning a nil slice is the same as calling [Context.ResultNull].
   104  //
   105  // https://sqlite.org/c3ref/result_blob.html
   106  func (ctx Context) ResultBlob(value []byte) {
   107  	ptr := ctx.c.newBytes(value)
   108  	ctx.c.call("sqlite3_result_blob64",
   109  		uint64(ctx.handle), uint64(ptr), uint64(len(value)),
   110  		uint64(ctx.c.freer))
   111  }
   112  
   113  // ResultZeroBlob sets the result of the function to a zero-filled, length n BLOB.
   114  //
   115  // https://sqlite.org/c3ref/result_blob.html
   116  func (ctx Context) ResultZeroBlob(n int64) {
   117  	ctx.c.call("sqlite3_result_zeroblob64",
   118  		uint64(ctx.handle), uint64(n))
   119  }
   120  
   121  // ResultNull sets the result of the function to NULL.
   122  //
   123  // https://sqlite.org/c3ref/result_blob.html
   124  func (ctx Context) ResultNull() {
   125  	ctx.c.call("sqlite3_result_null",
   126  		uint64(ctx.handle))
   127  }
   128  
   129  // ResultTime sets the result of the function to a [time.Time].
   130  //
   131  // https://sqlite.org/c3ref/result_blob.html
   132  func (ctx Context) ResultTime(value time.Time, format TimeFormat) {
   133  	if format == TimeFormatDefault {
   134  		ctx.resultRFC3339Nano(value)
   135  		return
   136  	}
   137  	switch v := format.Encode(value).(type) {
   138  	case string:
   139  		ctx.ResultText(v)
   140  	case int64:
   141  		ctx.ResultInt64(v)
   142  	case float64:
   143  		ctx.ResultFloat(v)
   144  	default:
   145  		panic(util.AssertErr())
   146  	}
   147  }
   148  
   149  func (ctx Context) resultRFC3339Nano(value time.Time) {
   150  	const maxlen = uint64(len(time.RFC3339Nano)) + 5
   151  
   152  	ptr := ctx.c.new(maxlen)
   153  	buf := util.View(ctx.c.mod, ptr, maxlen)
   154  	buf = value.AppendFormat(buf[:0], time.RFC3339Nano)
   155  
   156  	ctx.c.call("sqlite3_result_text64",
   157  		uint64(ctx.handle), uint64(ptr), uint64(len(buf)),
   158  		uint64(ctx.c.freer), _UTF8)
   159  }
   160  
   161  // ResultPointer sets the result of the function to NULL, just like [Context.ResultNull],
   162  // except that it also associates ptr with that NULL value such that it can be retrieved
   163  // within an application-defined SQL function using [Value.Pointer].
   164  //
   165  // https://sqlite.org/c3ref/result_blob.html
   166  func (ctx Context) ResultPointer(ptr any) {
   167  	valPtr := util.AddHandle(ctx.c.ctx, ptr)
   168  	ctx.c.call("sqlite3_result_pointer_go", uint64(valPtr))
   169  }
   170  
   171  // ResultJSON sets the result of the function to the JSON encoding of value.
   172  //
   173  // https://sqlite.org/c3ref/result_blob.html
   174  func (ctx Context) ResultJSON(value any) {
   175  	data, err := json.Marshal(value)
   176  	if err != nil {
   177  		ctx.ResultError(err)
   178  		return
   179  	}
   180  	ctx.ResultRawText(data)
   181  }
   182  
   183  // ResultValue sets the result of the function to a copy of [Value].
   184  //
   185  // https://sqlite.org/c3ref/result_blob.html
   186  func (ctx Context) ResultValue(value Value) {
   187  	if value.c != ctx.c {
   188  		ctx.ResultError(MISUSE)
   189  		return
   190  	}
   191  	ctx.c.call("sqlite3_result_value",
   192  		uint64(ctx.handle), uint64(value.handle))
   193  }
   194  
   195  // ResultError sets the result of the function an error.
   196  //
   197  // https://sqlite.org/c3ref/result_blob.html
   198  func (ctx Context) ResultError(err error) {
   199  	if errors.Is(err, NOMEM) {
   200  		ctx.c.call("sqlite3_result_error_nomem", uint64(ctx.handle))
   201  		return
   202  	}
   203  
   204  	if errors.Is(err, TOOBIG) {
   205  		ctx.c.call("sqlite3_result_error_toobig", uint64(ctx.handle))
   206  		return
   207  	}
   208  
   209  	msg, code := errorCode(err, _OK)
   210  	if msg != "" {
   211  		defer ctx.c.arena.mark()()
   212  		ptr := ctx.c.arena.string(msg)
   213  		ctx.c.call("sqlite3_result_error",
   214  			uint64(ctx.handle), uint64(ptr), uint64(len(msg)))
   215  	}
   216  	if code != _OK {
   217  		ctx.c.call("sqlite3_result_error_code",
   218  			uint64(ctx.handle), uint64(code))
   219  	}
   220  }
   221  
   222  // VTabNoChange may return true if a column is being fetched as part
   223  // of an update during which the column value will not change.
   224  //
   225  // https://sqlite.org/c3ref/vtab_nochange.html
   226  func (ctx Context) VTabNoChange() bool {
   227  	r := ctx.c.call("sqlite3_vtab_nochange", uint64(ctx.handle))
   228  	return r != 0
   229  }