diff --git a/const.go b/const.go index e4fc829..4a4dfeb 100644 --- a/const.go +++ b/const.go @@ -185,12 +185,12 @@ const ( type FunctionFlag uint32 const ( - DETERMINISTIC FunctionFlag = 0x000000800 - DIRECTONLY FunctionFlag = 0x000080000 - INNOCUOUS FunctionFlag = 0x000200000 - SELFORDER1 FunctionFlag = 0x002000000 - // SUBTYPE FunctionFlag = 0x000100000 - // RESULT_SUBTYPE FunctionFlag = 0x001000000 + DETERMINISTIC FunctionFlag = 0x000000800 + DIRECTONLY FunctionFlag = 0x000080000 + SUBTYPE FunctionFlag = 0x000100000 + INNOCUOUS FunctionFlag = 0x000200000 + RESULT_SUBTYPE FunctionFlag = 0x001000000 + SELFORDER1 FunctionFlag = 0x002000000 ) // StmtStatus name counter values associated with the [Stmt.Status] method. diff --git a/context.go b/context.go index 7caf586..154c228 100644 --- a/context.go +++ b/context.go @@ -227,6 +227,14 @@ func (ctx Context) ResultError(err error) { } } +// ResultSubtype sets the subtype of the result of the function. +// +// https://sqlite.org/c3ref/result_subtype.html +func (ctx Context) ResultSubtype(t uint) { + ctx.c.call("sqlite3_result_subtype", + stk_t(ctx.handle), stk_t(uint32(t))) +} + // VTabNoChange may return true if a column is being fetched as part // of an update during which the column value will not change. // diff --git a/embed/bcw2/bcw2.wasm b/embed/bcw2/bcw2.wasm index 2af374b..febb808 100755 Binary files a/embed/bcw2/bcw2.wasm and b/embed/bcw2/bcw2.wasm differ diff --git a/embed/exports.txt b/embed/exports.txt index 1de4b33..bb049c2 100644 --- a/embed/exports.txt +++ b/embed/exports.txt @@ -98,6 +98,7 @@ sqlite3_result_error_toobig sqlite3_result_int64 sqlite3_result_null sqlite3_result_pointer_go +sqlite3_result_subtype sqlite3_result_text_go sqlite3_result_value sqlite3_result_zeroblob64 @@ -126,6 +127,7 @@ sqlite3_value_int64 sqlite3_value_nochange sqlite3_value_numeric_type sqlite3_value_pointer_go +sqlite3_value_subtype sqlite3_value_text sqlite3_value_type sqlite3_vtab_collation diff --git a/embed/sqlite3.wasm b/embed/sqlite3.wasm index 7ad7f74..320416e 100755 Binary files a/embed/sqlite3.wasm and b/embed/sqlite3.wasm differ diff --git a/ext/stats/stats.go b/ext/stats/stats.go index 9a8ebe8..9d7ae27 100644 --- a/ext/stats/stats.go +++ b/ext/stats/stats.go @@ -58,8 +58,11 @@ import ( // Register registers statistics functions. func Register(db *sqlite3.Conn) error { - const flags = sqlite3.DETERMINISTIC | sqlite3.INNOCUOUS - const order = sqlite3.SELFORDER1 | flags + const ( + flags = sqlite3.DETERMINISTIC | sqlite3.INNOCUOUS + json = sqlite3.RESULT_SUBTYPE | flags + order = sqlite3.SELFORDER1 | flags + ) return errors.Join( db.CreateWindowFunction("var_pop", 1, flags, newVariance(var_pop)), db.CreateWindowFunction("var_samp", 1, flags, newVariance(var_samp)), @@ -81,7 +84,7 @@ func Register(db *sqlite3.Conn) error { db.CreateWindowFunction("regr_slope", 2, flags, newCovariance(regr_slope)), db.CreateWindowFunction("regr_intercept", 2, flags, newCovariance(regr_intercept)), db.CreateWindowFunction("regr_count", 2, flags, newCovariance(regr_count)), - db.CreateWindowFunction("regr_json", 2, flags, newCovariance(regr_json)), + db.CreateWindowFunction("regr_json", 2, json, newCovariance(regr_json)), db.CreateWindowFunction("median", 1, order, newPercentile(median)), db.CreateWindowFunction("percentile", 2, order, newPercentile(percentile_100)), db.CreateWindowFunction("percentile_cont", 2, order, newPercentile(percentile_cont)), @@ -227,6 +230,7 @@ func (fn *covariance) Value(ctx sqlite3.Context) { case regr_json: var buf [128]byte ctx.ResultRawText(fn.regr_json(buf[:0])) + ctx.ResultSubtype('J') return } ctx.ResultFloat(r) diff --git a/ext/stats/stats_test.go b/ext/stats/stats_test.go index 9862728..7c14638 100644 --- a/ext/stats/stats_test.go +++ b/ext/stats/stats_test.go @@ -156,49 +156,50 @@ func TestRegister_covariance(t *testing.T) { if err != nil { t.Fatal(err) } - if stmt.Step() { - if got := stmt.ColumnFloat(0); got != 0.9881049293224639 { - t.Errorf("got %v, want 0.9881049293224639", got) - } - if got := stmt.ColumnFloat(1); got != 21.25 { - t.Errorf("got %v, want 21.25", got) - } - if got := stmt.ColumnFloat(2); got != 17 { - t.Errorf("got %v, want 17", got) - } - if got := stmt.ColumnFloat(3); got != 4.2 { - t.Errorf("got %v, want 4.2", got) - } - if got := stmt.ColumnFloat(4); got != 75 { - t.Errorf("got %v, want 75", got) - } - if got := stmt.ColumnFloat(5); got != 14.8 { - t.Errorf("got %v, want 14.8", got) - } - if got := stmt.ColumnFloat(6); got != 500 { - t.Errorf("got %v, want 500", got) - } - if got := stmt.ColumnFloat(7); got != 85 { - t.Errorf("got %v, want 85", got) - } - if got := stmt.ColumnFloat(8); got != 0.17 { - t.Errorf("got %v, want 0.17", got) - } - if got := stmt.ColumnFloat(9); got != -8.55 { - t.Errorf("got %v, want -8.55", got) - } - if got := stmt.ColumnFloat(10); got != 0.9763513513513513 { - t.Errorf("got %v, want 0.9763513513513513", got) - } - if got := stmt.ColumnInt(11); got != 5 { - t.Errorf("got %v, want 5", got) - } - var a map[string]float64 - if err := stmt.ColumnJSON(12, &a); err != nil { - t.Error(err) - } else if got := a["count"]; got != 5 { - t.Errorf("got %v, want 5", got) - } + if !stmt.Step() { + t.Fatal(stmt.Err()) + } + if got := stmt.ColumnFloat(0); got != 0.9881049293224639 { + t.Errorf("got %v, want 0.9881049293224639", got) + } + if got := stmt.ColumnFloat(1); got != 21.25 { + t.Errorf("got %v, want 21.25", got) + } + if got := stmt.ColumnFloat(2); got != 17 { + t.Errorf("got %v, want 17", got) + } + if got := stmt.ColumnFloat(3); got != 4.2 { + t.Errorf("got %v, want 4.2", got) + } + if got := stmt.ColumnFloat(4); got != 75 { + t.Errorf("got %v, want 75", got) + } + if got := stmt.ColumnFloat(5); got != 14.8 { + t.Errorf("got %v, want 14.8", got) + } + if got := stmt.ColumnFloat(6); got != 500 { + t.Errorf("got %v, want 500", got) + } + if got := stmt.ColumnFloat(7); got != 85 { + t.Errorf("got %v, want 85", got) + } + if got := stmt.ColumnFloat(8); got != 0.17 { + t.Errorf("got %v, want 0.17", got) + } + if got := stmt.ColumnFloat(9); got != -8.55 { + t.Errorf("got %v, want -8.55", got) + } + if got := stmt.ColumnFloat(10); got != 0.9763513513513513 { + t.Errorf("got %v, want 0.9763513513513513", got) + } + if got := stmt.ColumnInt(11); got != 5 { + t.Errorf("got %v, want 5", got) + } + var a map[string]float64 + if err := stmt.ColumnJSON(12, &a); err != nil { + t.Error(err) + } else if got := a["count"]; got != 5 { + t.Errorf("got %v, want 5", got) } stmt.Close() diff --git a/stmt.go b/stmt.go index 363b45a..706182f 100644 --- a/stmt.go +++ b/stmt.go @@ -631,7 +631,6 @@ func (s *Stmt) ColumnValue(col int) Value { stk_t(s.handle), stk_t(col))) return Value{ c: s.c, - unprot: true, handle: ptr, } } diff --git a/util/sql3util/wasm/sql3parse_table.wasm b/util/sql3util/wasm/sql3parse_table.wasm index fa8af14..f7dc0cd 100755 Binary files a/util/sql3util/wasm/sql3parse_table.wasm and b/util/sql3util/wasm/sql3parse_table.wasm differ diff --git a/value.go b/value.go index c89d536..54d8f21 100644 --- a/value.go +++ b/value.go @@ -15,15 +15,6 @@ import ( type Value struct { c *Conn handle ptr_t - unprot bool - copied bool -} - -func (v Value) protected() stk_t { - if v.unprot { - panic(util.ValueErr) - } - return stk_t(v.handle) } // Dup makes a copy of the SQL value and returns a pointer to that copy. @@ -33,7 +24,6 @@ func (v Value) Dup() *Value { ptr := ptr_t(v.c.call("sqlite3_value_dup", stk_t(v.handle))) return &Value{ c: v.c, - copied: true, handle: ptr, } } @@ -42,9 +32,6 @@ func (v Value) Dup() *Value { // // https://sqlite.org/c3ref/value_dup.html func (dup *Value) Close() error { - if !dup.copied { - panic(util.ValueErr) - } dup.c.call("sqlite3_value_free", stk_t(dup.handle)) dup.handle = 0 return nil @@ -54,14 +41,21 @@ func (dup *Value) Close() error { // // https://sqlite.org/c3ref/value_blob.html func (v Value) Type() Datatype { - return Datatype(v.c.call("sqlite3_value_type", v.protected())) + return Datatype(v.c.call("sqlite3_value_type", stk_t(v.handle))) +} + +// Subtype returns the subtype of the value. +// +// https://sqlite.org/c3ref/value_subtype.html +func (v Value) Subtype() uint { + return uint(uint32(v.c.call("sqlite3_value_subtype", stk_t(v.handle)))) } // NumericType returns the numeric datatype of the value. // // https://sqlite.org/c3ref/value_blob.html func (v Value) NumericType() Datatype { - return Datatype(v.c.call("sqlite3_value_numeric_type", v.protected())) + return Datatype(v.c.call("sqlite3_value_numeric_type", stk_t(v.handle))) } // Bool returns the value as a bool. @@ -85,14 +79,14 @@ func (v Value) Int() int { // // https://sqlite.org/c3ref/value_blob.html func (v Value) Int64() int64 { - return int64(v.c.call("sqlite3_value_int64", v.protected())) + return int64(v.c.call("sqlite3_value_int64", stk_t(v.handle))) } // Float returns the value as a float64. // // https://sqlite.org/c3ref/value_blob.html func (v Value) Float() float64 { - f := uint64(v.c.call("sqlite3_value_double", v.protected())) + f := uint64(v.c.call("sqlite3_value_double", stk_t(v.handle))) return math.Float64frombits(f) } @@ -138,7 +132,7 @@ func (v Value) Blob(buf []byte) []byte { // // https://sqlite.org/c3ref/value_blob.html func (v Value) RawText() []byte { - ptr := ptr_t(v.c.call("sqlite3_value_text", v.protected())) + ptr := ptr_t(v.c.call("sqlite3_value_text", stk_t(v.handle))) return v.rawBytes(ptr, 1) } @@ -148,7 +142,7 @@ func (v Value) RawText() []byte { // // https://sqlite.org/c3ref/value_blob.html func (v Value) RawBlob() []byte { - ptr := ptr_t(v.c.call("sqlite3_value_blob", v.protected())) + ptr := ptr_t(v.c.call("sqlite3_value_blob", stk_t(v.handle))) return v.rawBytes(ptr, 0) } @@ -157,14 +151,14 @@ func (v Value) rawBytes(ptr ptr_t, nul int32) []byte { return nil } - n := int32(v.c.call("sqlite3_value_bytes", v.protected())) + n := int32(v.c.call("sqlite3_value_bytes", stk_t(v.handle))) return util.View(v.c.mod, ptr, int64(n+nul))[:n] } // Pointer gets the pointer associated with this value, // or nil if it has no associated pointer. func (v Value) Pointer() any { - ptr := ptr_t(v.c.call("sqlite3_value_pointer_go", v.protected())) + ptr := ptr_t(v.c.call("sqlite3_value_pointer_go", stk_t(v.handle))) return util.GetHandle(v.c.ctx, ptr) } @@ -194,7 +188,7 @@ func (v Value) JSON(ptr any) error { // // https://sqlite.org/c3ref/value_blob.html func (v Value) NoChange() bool { - b := int32(v.c.call("sqlite3_value_nochange", v.protected())) + b := int32(v.c.call("sqlite3_value_nochange", stk_t(v.handle))) return b != 0 } @@ -202,7 +196,7 @@ func (v Value) NoChange() bool { // // https://sqlite.org/c3ref/value_blob.html func (v Value) FromBind() bool { - b := int32(v.c.call("sqlite3_value_frombind", v.protected())) + b := int32(v.c.call("sqlite3_value_frombind", stk_t(v.handle))) return b != 0 } diff --git a/vfs/tests/mptest/wasm/mptest.wasm b/vfs/tests/mptest/wasm/mptest.wasm index 431d0a6..3b792c3 100644 Binary files a/vfs/tests/mptest/wasm/mptest.wasm and b/vfs/tests/mptest/wasm/mptest.wasm differ diff --git a/vfs/tests/speedtest1/wasm/speedtest1.wasm b/vfs/tests/speedtest1/wasm/speedtest1.wasm index 5856b10..a372fc4 100644 Binary files a/vfs/tests/speedtest1/wasm/speedtest1.wasm and b/vfs/tests/speedtest1/wasm/speedtest1.wasm differ