diff --git a/context.go b/context.go index 45e1b32..f281db2 100644 --- a/context.go +++ b/context.go @@ -21,7 +21,7 @@ type Context struct { // Instead, boolean values are stored as integers 0 (false) and 1 (true). // // https://www.sqlite.org/c3ref/result_blob.html -func (c *Context) ResultBool(value bool) { +func (c Context) ResultBool(value bool) { var i int64 if value { i = 1 @@ -32,14 +32,14 @@ func (c *Context) ResultBool(value bool) { // ResultInt sets the result of the function to an int. // // https://www.sqlite.org/c3ref/result_blob.html -func (c *Context) ResultInt(value int) { +func (c Context) ResultInt(value int) { c.ResultInt64(int64(value)) } // ResultInt64 sets the result of the function to an int64. // // https://www.sqlite.org/c3ref/result_blob.html -func (c *Context) ResultInt64(value int64) { +func (c Context) ResultInt64(value int64) { c.call(c.api.resultInteger, uint64(c.handle), uint64(value)) } @@ -47,7 +47,7 @@ func (c *Context) ResultInt64(value int64) { // ResultFloat sets the result of the function to a float64. // // https://www.sqlite.org/c3ref/result_blob.html -func (c *Context) ResultFloat(value float64) { +func (c Context) ResultFloat(value float64) { c.call(c.api.resultFloat, uint64(c.handle), math.Float64bits(value)) } @@ -55,7 +55,7 @@ func (c *Context) ResultFloat(value float64) { // ResultText sets the result of the function to a string. // // https://www.sqlite.org/c3ref/result_blob.html -func (c *Context) ResultText(value string) { +func (c Context) ResultText(value string) { ptr := c.newString(value) c.call(c.api.resultText, uint64(c.handle), uint64(ptr), uint64(len(value)), @@ -66,7 +66,7 @@ func (c *Context) ResultText(value string) { // Returning a nil slice is the same as calling [Context.ResultNull]. // // https://www.sqlite.org/c3ref/result_blob.html -func (c *Context) ResultBlob(value []byte) { +func (c Context) ResultBlob(value []byte) { ptr := c.newBytes(value) c.call(c.api.resultBlob, uint64(c.handle), uint64(ptr), uint64(len(value)), @@ -76,7 +76,7 @@ func (c *Context) ResultBlob(value []byte) { // BindZeroBlob sets the result of the function to a zero-filled, length n BLOB. // // https://www.sqlite.org/c3ref/result_blob.html -func (c *Context) ResultZeroBlob(n int64) { +func (c Context) ResultZeroBlob(n int64) { c.call(c.api.resultZeroBlob, uint64(c.handle), uint64(n)) } @@ -84,7 +84,7 @@ func (c *Context) ResultZeroBlob(n int64) { // ResultNull sets the result of the function to NULL. // // https://www.sqlite.org/c3ref/result_blob.html -func (c *Context) ResultNull() { +func (c Context) ResultNull() { c.call(c.api.resultNull, uint64(c.handle)) } @@ -92,7 +92,7 @@ func (c *Context) ResultNull() { // ResultTime sets the result of the function to a [time.Time]. // // https://www.sqlite.org/c3ref/result_blob.html -func (c *Context) ResultTime(value time.Time, format TimeFormat) { +func (c Context) ResultTime(value time.Time, format TimeFormat) { if format == TimeFormatDefault { c.resultRFC3339Nano(value) return @@ -109,7 +109,7 @@ func (c *Context) ResultTime(value time.Time, format TimeFormat) { } } -func (c *Context) resultRFC3339Nano(value time.Time) { +func (c Context) resultRFC3339Nano(value time.Time) { const maxlen = uint64(len(time.RFC3339Nano)) ptr := c.new(maxlen) @@ -124,7 +124,7 @@ func (c *Context) resultRFC3339Nano(value time.Time) { // ResultError sets the result of the function an error. // // https://www.sqlite.org/c3ref/result_blob.html -func (c *Context) ResultError(err error) { +func (c Context) ResultError(err error) { if errors.Is(err, NOMEM) { c.call(c.api.resultErrorMem, uint64(c.handle)) return @@ -137,7 +137,7 @@ func (c *Context) ResultError(err error) { str := err.Error() ptr := c.newString(str) - c.call(c.api.resultBlob, + c.call(c.api.resultError, uint64(c.handle), uint64(ptr), uint64(len(str))) c.free(ptr) @@ -152,6 +152,6 @@ func (c *Context) ResultError(err error) { } if code != 0 { c.call(c.api.resultErrorCode, - uint64(c.handle), uint64(xcode)) + uint64(c.handle), uint64(code)) } } diff --git a/func.go b/func.go index 3bf73ae..92f9a1f 100644 --- a/func.go +++ b/func.go @@ -56,8 +56,12 @@ func (c *Conn) CreateWindowFunction(name string, nArg int, flag FunctionFlag, fn // // https://www.sqlite.org/appfunc.html type AggregateFunction interface { + // Step is invoked to add a row to the current window. + // The function arguments, if any, corresponding to the row being added are passed to Step. Step(ctx Context, arg ...Value) - Final(ctx Context) + + // Value is invoked to return the current value of the aggregate. + Value(ctx Context) } // WindowFunction is the interface an aggregate window function should implement. @@ -65,7 +69,9 @@ type AggregateFunction interface { // https://www.sqlite.org/windowfunctions.html type WindowFunction interface { AggregateFunction - Value(ctx Context) + + // Inverse is invoked to remove the oldest presently aggregated result of Step from the current window. + // The function arguments, if any, are those passed to Step for the row being removed. Inverse(ctx Context, arg ...Value) } @@ -92,48 +98,35 @@ func callbackCompare(ctx context.Context, mod api.Module, pApp, nKey1, pKey1, nK func callbackFunc(ctx context.Context, mod api.Module, pCtx, nArg, pArg uint32) { module := ctx.Value(moduleKey{}).(*module) fn := callbackHandle(module, pCtx).(func(ctx Context, arg ...Value)) - fn(Context{ - module: module, - handle: pCtx, - }, callbackArgs(module, nArg, pArg)...) + fn(Context{module, pCtx}, callbackArgs(module, nArg, pArg)...) } func callbackStep(ctx context.Context, mod api.Module, pCtx, nArg, pArg uint32) { module := ctx.Value(moduleKey{}).(*module) fn := callbackAggregate(module, pCtx, nil).(AggregateFunction) - fn.Step(Context{ - module: module, - handle: pCtx, - }, callbackArgs(module, nArg, pArg)...) + fn.Step(Context{module, pCtx}, callbackArgs(module, nArg, pArg)...) } func callbackFinal(ctx context.Context, mod api.Module, pCtx uint32) { var handle uint32 module := ctx.Value(moduleKey{}).(*module) fn := callbackAggregate(module, pCtx, &handle).(AggregateFunction) - fn.Final(Context{ - module: module, - handle: pCtx, - }) - util.DelHandle(ctx, handle) + fn.Value(Context{module, pCtx}) + if err := util.DelHandle(ctx, handle); err != nil { + Context{module, pCtx}.ResultError(err) + } } func callbackValue(ctx context.Context, mod api.Module, pCtx uint32) { module := ctx.Value(moduleKey{}).(*module) - fn := callbackAggregate(module, pCtx, nil).(WindowFunction) - fn.Value(Context{ - module: module, - handle: pCtx, - }) + fn := callbackAggregate(module, pCtx, nil).(AggregateFunction) + fn.Value(Context{module, pCtx}) } func callbackInverse(ctx context.Context, mod api.Module, pCtx, nArg, pArg uint32) { module := ctx.Value(moduleKey{}).(*module) fn := callbackAggregate(module, pCtx, nil).(WindowFunction) - fn.Inverse(Context{ - module: module, - handle: pCtx, - }, callbackArgs(module, nArg, pArg)...) + fn.Inverse(Context{module, pCtx}, callbackArgs(module, nArg, pArg)...) } func callbackHandle(module *module, pCtx uint32) any { @@ -141,18 +134,21 @@ func callbackHandle(module *module, pCtx uint32) any { return util.GetHandle(module.ctx, pApp) } -func callbackAggregate(module *module, pCtx uint32, delete *uint32) any { +func callbackAggregate(module *module, pCtx uint32, close *uint32) any { + // On close, we're getting rid of the handle. + // Don't allocate space to store it. var size uint64 - if delete == nil { + if close == nil { size = ptrlen } ptr := uint32(module.call(module.api.aggregateCtx, uint64(pCtx), size)) - if ptr != 0 { + // Try loading the handle, if we already have one, or want a new one. + if ptr != 0 || size != 0 { if handle := util.ReadUint32(module.mod, ptr); handle != 0 { fn := util.GetHandle(module.ctx, handle) - if delete != nil { - *delete = handle + if close != nil { + *close = handle } if fn != nil { return fn @@ -160,6 +156,7 @@ func callbackAggregate(module *module, pCtx uint32, delete *uint32) any { } } + // Create a new aggregate and store the handle. fn := callbackHandle(module, pCtx).(func() AggregateFunction)() if ptr != 0 { util.WriteUint32(module.mod, ptr, util.AddHandle(module.ctx, fn)) diff --git a/func_win_test.go b/func_win_test.go index 13ba5d2..87c28e2 100644 --- a/func_win_test.go +++ b/func_win_test.go @@ -61,18 +61,12 @@ func ExampleConn_CreateWindowFunction() { // 0 } -type countASCII struct { - result int -} +type countASCII struct{ result int } func newASCIICounter() sqlite3.AggregateFunction { return &countASCII{} } -func (f *countASCII) Final(ctx sqlite3.Context) { - f.Value(ctx) -} - func (f *countASCII) Value(ctx sqlite3.Context) { ctx.ResultInt(f.result) } diff --git a/value.go b/value.go index c98d3a0..a542412 100644 --- a/value.go +++ b/value.go @@ -18,7 +18,7 @@ type Value struct { // Type returns the initial [Datatype] of the value. // // https://www.sqlite.org/c3ref/value_blob.html -func (v *Value) Type() Datatype { +func (v Value) Type() Datatype { r := v.call(v.api.valueType, uint64(v.handle)) return Datatype(r) } @@ -29,7 +29,7 @@ func (v *Value) Type() Datatype { // with 0 converted to false and any other value to true. // // https://www.sqlite.org/c3ref/value_blob.html -func (v *Value) Bool() bool { +func (v Value) Bool() bool { if i := v.Int64(); i != 0 { return true } @@ -39,14 +39,14 @@ func (v *Value) Bool() bool { // Int returns the value as an int. // // https://www.sqlite.org/c3ref/value_blob.html -func (v *Value) Int() int { +func (v Value) Int() int { return int(v.Int64()) } // Int64 returns the value as an int64. // // https://www.sqlite.org/c3ref/value_blob.html -func (v *Value) Int64() int64 { +func (v Value) Int64() int64 { r := v.call(v.api.valueInteger, uint64(v.handle)) return int64(r) } @@ -54,7 +54,7 @@ func (v *Value) Int64() int64 { // Float returns the value as a float64. // // https://www.sqlite.org/c3ref/value_blob.html -func (v *Value) Float() float64 { +func (v Value) Float() float64 { r := v.call(v.api.valueFloat, uint64(v.handle)) return math.Float64frombits(r) } @@ -62,7 +62,7 @@ func (v *Value) Float() float64 { // Time returns the value as a [time.Time]. // // https://www.sqlite.org/c3ref/value_blob.html -func (v *Value) Time(format TimeFormat) time.Time { +func (v Value) Time(format TimeFormat) time.Time { var a any switch v.Type() { case INTEGER: @@ -83,7 +83,7 @@ func (v *Value) Time(format TimeFormat) time.Time { // Text returns the value as a string. // // https://www.sqlite.org/c3ref/value_blob.html -func (v *Value) Text() string { +func (v Value) Text() string { return string(v.RawText()) } @@ -91,7 +91,7 @@ func (v *Value) Text() string { // the value as a []byte. // // https://www.sqlite.org/c3ref/value_blob.html -func (v *Value) Blob(buf []byte) []byte { +func (v Value) Blob(buf []byte) []byte { return append(buf, v.RawBlob()...) } @@ -100,7 +100,7 @@ func (v *Value) Blob(buf []byte) []byte { // subsequent calls to [Value] methods. // // https://www.sqlite.org/c3ref/value_blob.html -func (v *Value) RawText() []byte { +func (v Value) RawText() []byte { r := v.call(v.api.valueText, uint64(v.handle)) return v.rawBytes(uint32(r)) } @@ -110,12 +110,12 @@ func (v *Value) RawText() []byte { // subsequent calls to [Value] methods. // // https://www.sqlite.org/c3ref/value_blob.html -func (v *Value) RawBlob() []byte { +func (v Value) RawBlob() []byte { r := v.call(v.api.valueBlob, uint64(v.handle)) return v.rawBytes(uint32(r)) } -func (v *Value) rawBytes(ptr uint32) []byte { +func (v Value) rawBytes(ptr uint32) []byte { if ptr == 0 { return nil } diff --git a/vfs/api.go b/vfs/api.go index 7425096..158f173 100644 --- a/vfs/api.go +++ b/vfs/api.go @@ -15,7 +15,7 @@ type VFS interface { FullPathname(name string) (string, error) } -// VFSParams extends VFS to with the ability to handle URI parameters +// VFSParams extends VFS with the ability to handle URI parameters // through the OpenParams method. // // https://www.sqlite.org/c3ref/uri_boolean.html