Compare commits

...

9 Commits

Author SHA1 Message Date
Nuno Cruces
9719d4b0e3 Better tests. 2025-07-17 01:11:16 +01:00
Nuno Cruces
b21c69dc1f Fix mode. 2025-07-17 00:50:39 +01:00
Nuno Cruces
b0f8ff44a5 Support subtype. 2025-07-17 00:47:04 +01:00
Nuno Cruces
f37bca6a80 Speedup memdb. 2025-07-15 23:08:41 +01:00
Nuno Cruces
b4e8fcb752 Avoid UB. 2025-07-15 15:52:39 +01:00
dependabot[bot]
14b98a5d05 Bump golang.org/x/crypto from 0.39.0 to 0.40.0 (#295)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.39.0 to 0.40.0.
- [Commits](https://github.com/golang/crypto/compare/v0.39.0...v0.40.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.40.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-11 00:30:01 +01:00
Nuno Cruces
36a62264f9 Remove unneeded generics. 2025-07-10 13:05:55 +01:00
Nuno Cruces
33ea564f38 Deps. 2025-07-10 00:50:14 +01:00
Nuno Cruces
5c55d8692f Fix bitset. 2025-07-04 17:10:40 +01:00
42 changed files with 833 additions and 838 deletions

View File

@@ -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.

View File

@@ -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.
//

View File

@@ -546,8 +546,8 @@ func (s *stmt) setupBindings(args []driver.NamedValue) (err error) {
err = s.Stmt.BindTime(id, a, s.tmWrite)
case util.JSON:
err = s.Stmt.BindJSON(id, a.Value)
case util.PointerUnwrap:
err = s.Stmt.BindPointer(id, util.UnwrapPointer(a))
case util.Pointer:
err = s.Stmt.BindPointer(id, a.Value)
case nil:
err = s.Stmt.BindNull(id)
default:
@@ -565,7 +565,7 @@ func (s *stmt) CheckNamedValue(arg *driver.NamedValue) error {
switch arg.Value.(type) {
case bool, int, int64, float64, string, []byte,
time.Time, sqlite3.ZeroBlob,
util.JSON, util.PointerUnwrap,
util.JSON, util.Pointer,
nil:
return nil
default:

Binary file not shown.

View File

@@ -4,11 +4,11 @@ go 1.23.0
toolchain go1.24.0
require github.com/ncruces/go-sqlite3 v0.26.2
require github.com/ncruces/go-sqlite3 v0.26.3
require (
github.com/ncruces/julianday v1.0.0 // indirect
github.com/ncruces/sort v0.1.5 // indirect
github.com/tetratelabs/wazero v1.9.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/sys v0.34.0 // indirect
)

View File

@@ -1,12 +1,12 @@
github.com/ncruces/go-sqlite3 v0.26.2 h1:5UkIBwdfMN2irpVI1dgi9TjTUlxNI06Rti1C8O7ZKVg=
github.com/ncruces/go-sqlite3 v0.26.2/go.mod h1:XFTPtFIo1DmGCh+XVP8KGn9b/o2f+z0WZuT09x2N6eo=
github.com/ncruces/go-sqlite3 v0.26.3 h1:WFkQj4KNMhbqiBPGDrVpK74w1DzcxQu3wYpmdWAvfYM=
github.com/ncruces/go-sqlite3 v0.26.3/go.mod h1:XFTPtFIo1DmGCh+XVP8KGn9b/o2f+z0WZuT09x2N6eo=
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/ncruces/sort v0.1.5 h1:fiFWXXAqKI8QckPf/6hu/bGFwcEPrirIOFaJqWujs4k=
github.com/ncruces/sort v0.1.5/go.mod h1:obJToO4rYr6VWP0Uw5FYymgYGt3Br4RXcs/JdKaXAPk=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=

View File

@@ -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

Binary file not shown.

View File

@@ -144,18 +144,16 @@ func Test_readblob(t *testing.T) {
}
}
if stmt.Step() {
got := stmt.ColumnText(0)
if got != tt.want1 {
t.Errorf("got %q", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != tt.want1 {
t.Errorf("got %q", got)
}
if stmt.Step() {
got := stmt.ColumnText(0)
if got != tt.want2 {
t.Errorf("got %q", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != tt.want2 {
t.Errorf("got %q", got)
}
err = stmt.Err()

View File

@@ -147,20 +147,21 @@ func TestAffinity(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if got := stmt.ColumnText(0); got != "1" {
t.Errorf("got %q want 1", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != "1" {
t.Errorf("got %q want 1", got)
}
if stmt.Step() {
if got := stmt.ColumnText(0); got != "0.1" {
t.Errorf("got %q want 0.1", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != "0.1" {
t.Errorf("got %q want 0.1", got)
}
if stmt.Step() {
if got := stmt.ColumnText(0); got != "e" {
t.Errorf("got %q want e", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != "e" {
t.Errorf("got %q want e", got)
}
}

View File

@@ -141,10 +141,10 @@ func TestRegister(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if got := stmt.ColumnInt(0); got != 3 {
t.Errorf("got %d, want 3", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 3 {
t.Errorf("got %d, want 3", got)
}
err = db.Exec(`ALTER TABLE v_x RENAME TO v_y`)

View File

@@ -92,7 +92,9 @@ func TestRegister(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
x := stmt.ColumnInt(0)
y := stmt.ColumnInt(1)
hypot := stmt.ColumnInt(2)

View File

@@ -37,7 +37,9 @@ func TestRegister_boolean(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnBool(0); got != true {
t.Errorf("got %v, want true", got)
}

View File

@@ -19,8 +19,8 @@ type mode struct {
func (m mode) Value(ctx sqlite3.Context) {
var (
max = 0
typ = sqlite3.NULL
max uint
i64 int64
f64 float64
str string
@@ -32,7 +32,6 @@ func (m mode) Value(ctx sqlite3.Context) {
i64 = k
}
}
f64 = float64(i64)
for k, v := range m.reals {
if v > max || v == max && k < f64 {
typ = sqlite3.FLOAT
@@ -66,33 +65,45 @@ func (m mode) Value(ctx sqlite3.Context) {
}
}
func (b *mode) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
func (m *mode) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
switch arg[0].Type() {
case sqlite3.INTEGER:
b.ints.add(arg[0].Int64())
if m.reals == nil {
m.ints.add(arg[0].Int64())
break
}
fallthrough
case sqlite3.FLOAT:
b.reals.add(arg[0].Float())
m.reals.add(arg[0].Float())
for k, v := range m.ints {
m.reals[float64(k)] += v
}
m.ints = nil
case sqlite3.TEXT:
b.texts.add(arg[0].Text())
m.texts.add(arg[0].Text())
case sqlite3.BLOB:
b.blobs.add(string(arg[0].RawBlob()))
m.blobs.add(string(arg[0].RawBlob()))
}
}
func (b *mode) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
func (m *mode) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
switch arg[0].Type() {
case sqlite3.INTEGER:
b.ints.del(arg[0].Int64())
if m.reals == nil {
m.ints.del(arg[0].Int64())
break
}
fallthrough
case sqlite3.FLOAT:
b.reals.del(arg[0].Float())
m.reals.del(arg[0].Float())
case sqlite3.TEXT:
b.texts.del(arg[0].Text())
m.texts.del(arg[0].Text())
case sqlite3.BLOB:
b.blobs.del(string(arg[0].RawBlob()))
m.blobs.del(string(arg[0].RawBlob()))
}
}
type counter[T comparable] map[T]int
type counter[T comparable] map[T]uint
func (c *counter[T]) add(k T) {
if (*c) == nil {
@@ -102,11 +113,9 @@ func (c *counter[T]) add(k T) {
}
func (c counter[T]) del(k T) {
switch n := c[k]; n {
default:
c[k] = n - 1
case 1:
if n := c[k]; n == 1 {
delete(c, k)
case 0:
} else {
c[k] = n - 1
}
}

View File

@@ -21,10 +21,10 @@ func TestRegister_mode(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnInt(0); got != 3 {
t.Errorf("got %v, want 3", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 3 {
t.Errorf("got %v, want 3", got)
}
stmt.Close()
@@ -32,10 +32,10 @@ func TestRegister_mode(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %v, want 1", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %v, want 1", got)
}
stmt.Close()
@@ -43,10 +43,10 @@ func TestRegister_mode(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 2.5 {
t.Errorf("got %v, want 2.5", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnFloat(0); got != 2.5 {
t.Errorf("got %v, want 2.5", got)
}
stmt.Close()
@@ -54,21 +54,22 @@ func TestRegister_mode(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnText(0); got != "red" {
t.Errorf("got %q, want red", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != "red" {
t.Errorf("got %q, want red", got)
}
stmt.Close()
stmt, _, err = db.Prepare(`SELECT mode(column1) FROM (VALUES (X'cafebabe'), ('green'), ('blue'), (X'cafebabe'))`)
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnText(0); got != "\xca\xfe\xba\xbe" {
t.Errorf("got %q, want cafebabe", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != "\xca\xfe\xba\xbe" {
t.Errorf("got %q, want cafebabe", got)
}
stmt.Close()
@@ -82,4 +83,20 @@ func TestRegister_mode(t *testing.T) {
for stmt.Step() {
}
stmt.Close()
stmt, _, err = db.Prepare(`SELECT mode(column1) FROM (VALUES (?), (?), (?), (?), (?))`)
if err != nil {
t.Fatal(err)
}
stmt.BindInt(1, 1)
stmt.BindInt(2, 1)
stmt.BindInt(3, 2)
stmt.BindFloat(4, 2)
stmt.BindFloat(5, 2)
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 2 {
t.Errorf("got %v, want 2", got)
}
stmt.Close()
}

View File

@@ -38,7 +38,9 @@ func TestRegister_percentile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnFloat(0); got != 10 {
t.Errorf("got %v, want 10", got)
}
@@ -65,30 +67,30 @@ func TestRegister_percentile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 5.5 {
t.Errorf("got %v, want 5.5", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnFloat(0); got != 5.5 {
t.Errorf("got %v, want 5.5", got)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 7 {
t.Errorf("got %v, want 7", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnFloat(0); got != 7 {
t.Errorf("got %v, want 7", got)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 10 {
t.Errorf("got %v, want 10", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnFloat(0); got != 10 {
t.Errorf("got %v, want 10", got)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 14.5 {
t.Errorf("got %v, want 14.5", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnFloat(0); got != 14.5 {
t.Errorf("got %v, want 14.5", got)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 16 {
t.Errorf("got %v, want 16", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnFloat(0); got != 16 {
t.Errorf("got %v, want 16", got)
}
stmt.Close()
@@ -103,7 +105,9 @@ func TestRegister_percentile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnFloat(0); got != 4 {
t.Errorf("got %v, want 4", got)
}
@@ -134,7 +138,9 @@ func TestRegister_percentile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.NULL {
t.Error("want NULL")
}

View File

@@ -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)

View File

@@ -34,10 +34,10 @@ func TestRegister_variance(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnType(0); got != sqlite3.NULL {
t.Errorf("got %v, want NULL", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnType(0); got != sqlite3.NULL {
t.Errorf("got %v, want NULL", got)
}
stmt.Close()
@@ -57,7 +57,9 @@ func TestRegister_variance(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnFloat(0); got != 40 {
t.Errorf("got %v, want 40", got)
}
@@ -131,7 +133,9 @@ func TestRegister_covariance(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnInt(0); got != 0 {
t.Errorf("got %v, want 0", got)
}
@@ -156,49 +160,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()
@@ -248,7 +253,9 @@ func Benchmark_average(b *testing.B) {
b.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
b.Fatal(stmt.Err())
} else {
want := float64(b.N) / 2
if got := stmt.ColumnFloat(0); got != want {
b.Errorf("got %v, want %v", got, want)
@@ -282,7 +289,9 @@ func Benchmark_variance(b *testing.B) {
b.Fatal(err)
}
if stmt.Step() && b.N > 100 {
if !stmt.Step() {
b.Fatal(stmt.Err())
} else if b.N > 100 {
want := float64(b.N*b.N) / 12
if got := stmt.ColumnFloat(0); want > (got-want)*float64(b.N) {
b.Errorf("got %v, want %v", got, want)

View File

@@ -26,11 +26,10 @@ func TestRegister(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
return stmt.ColumnText(0)
if !stmt.Step() {
t.Fatal(stmt.Err())
}
t.Fatal(stmt.Err())
return ""
return stmt.ColumnText(0)
}
Register(db)

8
go.mod
View File

@@ -8,16 +8,16 @@ require (
github.com/ncruces/julianday v1.0.0
github.com/ncruces/sort v0.1.5
github.com/tetratelabs/wazero v1.9.0
golang.org/x/crypto v0.39.0
golang.org/x/sys v0.33.0
golang.org/x/crypto v0.40.0
golang.org/x/sys v0.34.0
)
require (
github.com/dchest/siphash v1.2.3 // ext/bloom
github.com/google/uuid v1.6.0 // ext/uuid
github.com/psanford/httpreadat v0.1.0 // example
golang.org/x/sync v0.15.0 // test
golang.org/x/text v0.26.0 // ext/unicode
golang.org/x/sync v0.16.0 // test
golang.org/x/text v0.27.0 // ext/unicode
lukechampine.com/adiantum v1.1.1 // vfs/adiantum
)

16
go.sum
View File

@@ -10,13 +10,13 @@ github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIw
github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
lukechampine.com/adiantum v1.1.1 h1:4fp6gTxWCqpEbLy40ExiYDDED3oUNWx5cTqBCtPdZqA=
lukechampine.com/adiantum v1.1.1/go.mod h1:LrAYVnTYLnUtE/yMp5bQr0HstAf060YUF8nM0B6+rUw=

View File

@@ -5,7 +5,7 @@ go 1.23.0
toolchain go1.24.0
require (
github.com/ncruces/go-sqlite3 v0.26.2
github.com/ncruces/go-sqlite3 v0.26.3
gorm.io/gorm v1.30.0
)
@@ -14,6 +14,6 @@ require (
github.com/jinzhu/now v1.1.5 // indirect
github.com/ncruces/julianday v1.0.0 // indirect
github.com/tetratelabs/wazero v1.9.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
)

View File

@@ -2,15 +2,15 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/ncruces/go-sqlite3 v0.26.2 h1:5UkIBwdfMN2irpVI1dgi9TjTUlxNI06Rti1C8O7ZKVg=
github.com/ncruces/go-sqlite3 v0.26.2/go.mod h1:XFTPtFIo1DmGCh+XVP8KGn9b/o2f+z0WZuT09x2N6eo=
github.com/ncruces/go-sqlite3 v0.26.3 h1:WFkQj4KNMhbqiBPGDrVpK74w1DzcxQu3wYpmdWAvfYM=
github.com/ncruces/go-sqlite3 v0.26.3/go.mod h1:XFTPtFIo1DmGCh+XVP8KGn9b/o2f+z0WZuT09x2N6eo=
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=

View File

@@ -1,11 +1,3 @@
package util
type Pointer[T any] struct{ Value T }
func (p Pointer[T]) unwrap() any { return p.Value }
type PointerUnwrap interface{ unwrap() any }
func UnwrapPointer(p PointerUnwrap) any {
return p.unwrap()
}
type Pointer struct{ Value any }

View File

@@ -1,13 +0,0 @@
package util
import (
"math"
"testing"
)
func TestUnwrapPointer(t *testing.T) {
p := Pointer[float64]{Value: math.Pi}
if got := UnwrapPointer(p); got != math.Pi {
t.Errorf("want π, got %v", got)
}
}

View File

@@ -8,6 +8,6 @@ import "github.com/ncruces/go-sqlite3/internal/util"
// [Value.Pointer], or [Context.ResultPointer].
//
// https://sqlite.org/bindptr.html
func Pointer[T any](value T) any {
return util.Pointer[T]{Value: value}
func Pointer(value any) any {
return util.Pointer{Value: value}
}

View File

@@ -13,7 +13,6 @@ trap 'rm -f libc.c libc.tmp' EXIT
cat << EOF > libc.c
#include <stdlib.h>
#include <string.h>
#include <strings.h>
EOF
"$WASI_SDK/clang" --target=wasm32-wasi -std=c23 -g0 -O2 \

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -408,7 +408,7 @@ func Test_strspn(t *testing.T) {
fill(memory[ptr:ptr+max(pos, length)], 5)
memory[ptr+pos] = 7
memory[ptr+length] = 0
memory[128] = 3
memory[128] = 7 | 128
memory[129] = 5
got := call(strspn, uint64(ptr), 129)
@@ -434,7 +434,7 @@ func Test_strspn(t *testing.T) {
clear(memory)
fill(memory[ptr:ptr+length], 5)
memory[len(memory)-1] = 7
memory[128] = 3
memory[128] = 7 | 128
memory[129] = 5
got := call(strspn, uint64(ptr), 129)
@@ -462,7 +462,7 @@ func Test_strcspn(t *testing.T) {
fill(memory[ptr:ptr+max(pos, length)], 5)
memory[ptr+pos] = 7
memory[ptr+length] = 0
memory[128] = 3
memory[128] = 5 | 128
memory[129] = 7
got := call(strcspn, uint64(ptr), 129)
@@ -488,7 +488,7 @@ func Test_strcspn(t *testing.T) {
clear(memory)
fill(memory[ptr:ptr+length], 5)
memory[len(memory)-1] = 7
memory[128] = 3
memory[128] = 5 | 128
memory[129] = 7
got := call(strcspn, uint64(ptr), 129)
@@ -761,8 +761,10 @@ func Fuzz_strspn(f *testing.F) {
}
if uint32(got) != uint32(want) {
t.Errorf("strspn(%q, %q) = %d, want %d",
s, chars, uint32(got), uint32(want))
t.Errorf("strspn(%v, %v) = %d, want %d",
[]byte(memory[ptr1:ptr1+len(s)]),
[]byte(memory[ptr2:ptr2+len(chars)]),
uint32(got), uint32(want))
}
})
}

View File

@@ -3,11 +3,8 @@
#ifndef _WASM_SIMD128_STRING_H
#define _WASM_SIMD128_STRING_H
#include <ctype.h>
#include <stdint.h>
#include <strings.h>
#include <wasm_simd128.h>
#include <__macro_PAGESIZE.h>
#ifdef __cplusplus
extern "C" {
@@ -90,15 +87,14 @@ void *memchr(const void *s, int c, size_t n) {
// memchr must behave as if it reads characters sequentially
// and stops as soon as a match is found.
// Aligning ensures loads beyond the first match are safe.
// Casting through uintptr_t makes this implementation-defined,
// rather than undefined behavior.
// Aligning ensures out of bounds loads are safe.
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
const v128_t *v = (v128_t *)((uintptr_t)s - align);
const v128_t vc = wasm_i8x16_splat(c);
uintptr_t addr = (uintptr_t)s - align;
v128_t vc = wasm_i8x16_splat(c);
for (;;) {
const v128_t cmp = wasm_i8x16_eq(*v, vc);
v128_t v = *(v128_t *)addr;
v128_t cmp = wasm_i8x16_eq(v, vc);
// Bitmask is slow on AArch64, any_true is much faster.
if (wasm_v128_any_true(cmp)) {
// Clear the bits corresponding to align (little-endian)
@@ -114,7 +110,7 @@ void *memchr(const void *s, int c, size_t n) {
// That's a match, unless it is beyond the end of the object.
// Recall that we decremented n, so less-than-or-equal-to is correct.
size_t ctz = __builtin_ctz(mask);
return ctz - align <= n ? (char *)v + ctz : NULL;
return ctz - align <= n ? (char *)s + (addr - (uintptr_t)s + ctz) : NULL;
}
}
// Decrement n; if it overflows we're done.
@@ -122,7 +118,7 @@ void *memchr(const void *s, int c, size_t n) {
return NULL;
}
align = 0;
v++;
addr += sizeof(v128_t);
}
}
@@ -155,16 +151,15 @@ void *memrchr(const void *s, int c, size_t n) {
__attribute__((weak))
size_t strlen(const char *s) {
// strlen must stop as soon as it finds the terminator.
// Aligning ensures loads beyond the terminator are safe.
// Casting through uintptr_t makes this implementation-defined,
// rather than undefined behavior.
// Aligning ensures out of bounds loads are safe.
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
const v128_t *v = (v128_t *)((uintptr_t)s - align);
uintptr_t addr = (uintptr_t)s - align;
for (;;) {
v128_t v = *(v128_t *)addr;
// Bitmask is slow on AArch64, all_true is much faster.
if (!wasm_i8x16_all_true(*v)) {
const v128_t cmp = wasm_i8x16_eq(*v, (v128_t){});
if (!wasm_i8x16_all_true(v)) {
const v128_t cmp = wasm_i8x16_eq(v, (v128_t){});
// Clear the bits corresponding to align (little-endian)
// so we can count trailing zeros.
int mask = wasm_i8x16_bitmask(cmp) >> align << align;
@@ -175,25 +170,24 @@ size_t strlen(const char *s) {
// it's as if we didn't find anything.
if (mask) {
// Find the offset of the first one bit (little-endian).
return (char *)v - s + __builtin_ctz(mask);
return addr - (uintptr_t)s + __builtin_ctz(mask);
}
}
align = 0;
v++;
addr += sizeof(v128_t);
}
}
static char *__strchrnul(const char *s, int c) {
// strchrnul must stop as soon as it finds the terminator.
// Aligning ensures loads beyond the terminator are safe.
// Casting through uintptr_t makes this implementation-defined,
// rather than undefined behavior.
// strchrnul must stop as soon as a match is found.
// Aligning ensures out of bounds loads are safe.
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
const v128_t *v = (v128_t *)((uintptr_t)s - align);
const v128_t vc = wasm_i8x16_splat(c);
uintptr_t addr = (uintptr_t)s - align;
v128_t vc = wasm_i8x16_splat(c);
for (;;) {
const v128_t cmp = wasm_i8x16_eq(*v, (v128_t){}) | wasm_i8x16_eq(*v, vc);
v128_t v = *(v128_t *)addr;
const v128_t cmp = wasm_i8x16_eq(v, (v128_t){}) | wasm_i8x16_eq(v, vc);
// Bitmask is slow on AArch64, any_true is much faster.
if (wasm_v128_any_true(cmp)) {
// Clear the bits corresponding to align (little-endian)
@@ -206,11 +200,11 @@ static char *__strchrnul(const char *s, int c) {
// it's as if we didn't find anything.
if (mask) {
// Find the offset of the first one bit (little-endian).
return (char *)v + __builtin_ctz(mask);
return (char *)s + (addr - (uintptr_t)s + __builtin_ctz(mask));
}
}
align = 0;
v++;
addr += sizeof(v128_t);
}
}
@@ -249,16 +243,16 @@ char *strrchr(const char *s, int c) {
// http://0x80.pl/notesen/2018-10-18-simd-byte-lookup.html
typedef struct {
__u8x16 l;
__u8x16 h;
__u8x16 lo;
__u8x16 hi;
} __wasm_v128_bitmap256_t;
__attribute__((always_inline))
static void __wasm_v128_setbit(__wasm_v128_bitmap256_t *bitmap, int i) {
uint8_t hi_nibble = (uint8_t)i >> 4;
uint8_t lo_nibble = (uint8_t)i & 0xf;
bitmap->l[lo_nibble] |= 1 << (hi_nibble - 0);
bitmap->h[lo_nibble] |= 1 << (hi_nibble - 8);
bitmap->lo[lo_nibble] |= (uint8_t)((uint32_t)1 << (hi_nibble - 0));
bitmap->hi[lo_nibble] |= (uint8_t)((uint32_t)1 << (hi_nibble - 8));
}
#ifndef __wasm_relaxed_simd__
@@ -269,19 +263,18 @@ static void __wasm_v128_setbit(__wasm_v128_bitmap256_t *bitmap, int i) {
__attribute__((always_inline))
static v128_t __wasm_v128_chkbits(__wasm_v128_bitmap256_t bitmap, v128_t v) {
v128_t indices_0_7 = v & wasm_u8x16_const_splat(0x8f);
v128_t indices_8_15 = (v & wasm_u8x16_const_splat(0x80)) ^ indices_0_7;
v128_t row_0_7 = wasm_i8x16_swizzle(bitmap.l, indices_0_7);
v128_t row_8_15 = wasm_i8x16_swizzle(bitmap.h, indices_8_15);
v128_t bitsets = row_0_7 | row_8_15;
v128_t hi_nibbles = wasm_u8x16_shr(v, 4);
v128_t bitmask_lookup = wasm_u8x16_const(1, 2, 4, 8, 16, 32, 64, 128, //
1, 2, 4, 8, 16, 32, 64, 128);
v128_t bitmask = wasm_i8x16_relaxed_swizzle(bitmask_lookup, hi_nibbles);
v128_t indices_0_7 = v & wasm_u8x16_const_splat(0x8f);
v128_t indices_8_15 = indices_0_7 ^ wasm_u8x16_const_splat(0x80);
v128_t row_0_7 = wasm_i8x16_swizzle(bitmap.lo, indices_0_7);
v128_t row_8_15 = wasm_i8x16_swizzle(bitmap.hi, indices_8_15);
v128_t bitsets = row_0_7 | row_8_15;
return wasm_i8x16_eq(bitsets & bitmask, bitmask);
}
@@ -290,17 +283,16 @@ static v128_t __wasm_v128_chkbits(__wasm_v128_bitmap256_t bitmap, v128_t v) {
__attribute__((weak))
size_t strspn(const char *s, const char *c) {
// strspn must stop as soon as it finds the terminator.
// Aligning ensures loads beyond the terminator are safe.
// Casting through uintptr_t makes this implementation-defined,
// rather than undefined behavior.
// Aligning ensures out of bounds loads are safe.
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
const v128_t *v = (v128_t *)((uintptr_t)s - align);
uintptr_t addr = (uintptr_t)s - align;
if (!c[0]) return 0;
if (!c[1]) {
const v128_t vc = wasm_i8x16_splat(*c);
v128_t vc = wasm_i8x16_splat(*c);
for (;;) {
const v128_t cmp = wasm_i8x16_eq(*v, vc);
v128_t v = *(v128_t *)addr;
v128_t cmp = wasm_i8x16_eq(v, vc);
// Bitmask is slow on AArch64, all_true is much faster.
if (!wasm_i8x16_all_true(cmp)) {
// Clear the bits corresponding to align (little-endian)
@@ -313,11 +305,11 @@ size_t strspn(const char *s, const char *c) {
// it's as if we didn't find anything.
if (mask) {
// Find the offset of the first one bit (little-endian).
return (char *)v - s + __builtin_ctz(mask);
return addr - (uintptr_t)s + __builtin_ctz(mask);
}
}
align = 0;
v++;
addr += sizeof(v128_t);
}
}
@@ -329,7 +321,8 @@ size_t strspn(const char *s, const char *c) {
}
for (;;) {
const v128_t cmp = __wasm_v128_chkbits(bitmap, *v);
v128_t v = *(v128_t *)addr;
v128_t cmp = __wasm_v128_chkbits(bitmap, v);
// Bitmask is slow on AArch64, all_true is much faster.
if (!wasm_i8x16_all_true(cmp)) {
// Clear the bits corresponding to align (little-endian)
@@ -342,11 +335,11 @@ size_t strspn(const char *s, const char *c) {
// it's as if we didn't find anything.
if (mask) {
// Find the offset of the first one bit (little-endian).
return (char *)v - s + __builtin_ctz(mask);
return addr - (uintptr_t)s + __builtin_ctz(mask);
}
}
align = 0;
v++;
addr += sizeof(v128_t);
}
}
@@ -355,11 +348,9 @@ size_t strcspn(const char *s, const char *c) {
if (!c[0] || !c[1]) return __strchrnul(s, *c) - s;
// strcspn must stop as soon as it finds the terminator.
// Aligning ensures loads beyond the terminator are safe.
// Casting through uintptr_t makes this implementation-defined,
// rather than undefined behavior.
// Aligning ensures out of bounds loads are safe.
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
const v128_t *v = (v128_t *)((uintptr_t)s - align);
uintptr_t addr = (uintptr_t)s - align;
__wasm_v128_bitmap256_t bitmap = {};
@@ -369,7 +360,8 @@ size_t strcspn(const char *s, const char *c) {
} while (*c++);
for (;;) {
const v128_t cmp = __wasm_v128_chkbits(bitmap, *v);
v128_t v = *(v128_t *)addr;
v128_t cmp = __wasm_v128_chkbits(bitmap, v);
// Bitmask is slow on AArch64, any_true is much faster.
if (wasm_v128_any_true(cmp)) {
// Clear the bits corresponding to align (little-endian)
@@ -382,11 +374,11 @@ size_t strcspn(const char *s, const char *c) {
// it's as if we didn't find anything.
if (mask) {
// Find the offset of the first one bit (little-endian).
return (char *)v - s + __builtin_ctz(mask);
return addr - (uintptr_t)s + __builtin_ctz(mask);
}
}
align = 0;
v++;
addr += sizeof(v128_t);
}
}

View File

@@ -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,
}
}

View File

@@ -59,7 +59,9 @@ func Test_endianness(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnInt64(0); got != value {
t.Errorf("got %d, want %d", got, value)
}
@@ -67,9 +69,5 @@ func Test_endianness(t *testing.T) {
t.Errorf("got %s, want %d", got, value)
}
}
if err != nil {
t.Fatal(err)
}
}
}

View File

@@ -64,7 +64,9 @@ func TestCreateFunction(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -73,7 +75,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -82,7 +86,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -91,7 +97,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.FLOAT {
t.Errorf("got %v, want FLOAT", got)
}
@@ -100,7 +108,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -109,7 +119,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.BLOB {
t.Errorf("got %v, want BLOB", got)
}
@@ -118,7 +130,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.BLOB {
t.Errorf("got %v, want BLOB", got)
}
@@ -127,7 +141,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -136,7 +152,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -148,7 +166,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -157,7 +177,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.NULL {
t.Errorf("got %v, want NULL", got)
}

View File

@@ -168,7 +168,9 @@ func TestStmt(t *testing.T) {
t.Errorf(`got %q, want "main"`, got)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -195,7 +197,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -222,7 +226,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -249,7 +255,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.FLOAT {
t.Errorf("got %v, want FLOAT", got)
}
@@ -276,7 +284,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.NULL {
t.Errorf("got %v, want NULL", got)
}
@@ -303,7 +313,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -328,7 +340,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -353,7 +367,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -378,7 +394,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.BLOB {
t.Errorf("got %v, want BLOB", got)
}
@@ -403,7 +421,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.BLOB {
t.Errorf("got %v, want BLOB", got)
}
@@ -428,7 +448,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.BLOB {
t.Errorf("got %v, want BLOB", got)
}
@@ -453,7 +475,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -480,7 +504,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.NULL {
t.Errorf("got %v, want NULL", got)
}
@@ -643,7 +669,9 @@ func TestStmt_ColumnValue(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
val := stmt.ColumnValue(0)
if _, err := val.InFirst(); err == nil {
t.Error("want error")
@@ -675,7 +703,9 @@ func TestStmt_Columns(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
var dest [5]any
if err := stmt.Columns(dest[:]...); err != nil {
t.Fatal(err)

View File

@@ -244,7 +244,9 @@ func TestDB_isoWeek(t *testing.T) {
tstart := time.Date(1500, 1, 1, 12, 0, 0, 0, time.UTC)
for tm := tstart; tm.Before(tend); tm = tm.AddDate(0, 0, 1) {
stmt.BindTime(1, tm, sqlite3.TimeFormatDefault)
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
y, w := tm.ISOWeek()
d := tm.Weekday()
if d == 0 {

View File

@@ -37,11 +37,10 @@ func TestConn_Transaction_exec(t *testing.T) {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
return stmt.ColumnInt(0)
if !stmt.Step() {
t.Fatal(stmt.Err())
}
t.Fatal(stmt.Err())
return 0
return stmt.ColumnInt(0)
}
insert := func(succeed bool) (err error) {
@@ -130,14 +129,12 @@ func TestConn_Transaction_panic(t *testing.T) {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
return
if !stmt.Step() {
t.Fatal(stmt.Err())
}
if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %d, want 1", got)
}
t.Fatal(stmt.Err())
}()
err = panics()
@@ -213,15 +210,10 @@ func TestConn_Transaction_interrupt(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
}
err = stmt.Err()
if err != nil {
t.Error(err)
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %d, want 1", got)
}
}
@@ -333,15 +325,10 @@ func TestConn_Transaction_rollback(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
}
err = stmt.Err()
if err != nil {
t.Error(err)
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %d, want 1", got)
}
}
@@ -382,11 +369,10 @@ func TestConn_Savepoint_exec(t *testing.T) {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
return stmt.ColumnInt(0)
if !stmt.Step() {
t.Fatal(stmt.Err())
}
t.Fatal(stmt.Err())
return 0
return stmt.ColumnInt(0)
}
insert := func(succeed bool) (err error) {
@@ -469,14 +455,12 @@ func TestConn_Savepoint_panic(t *testing.T) {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
return
if !stmt.Step() {
t.Fatal(stmt.Err())
}
if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %d, want 1", got)
}
t.Fatal(stmt.Err())
}()
err = panics()
@@ -553,15 +537,10 @@ func TestConn_Savepoint_interrupt(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
}
err = stmt.Err()
if err != nil {
t.Error(err)
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %d, want 1", got)
}
}
@@ -599,14 +578,9 @@ func TestConn_Savepoint_rollback(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
}
err = stmt.Err()
if err != nil {
t.Error(err)
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %d, want 1", got)
}
}

View File

@@ -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)))
}
// Type returns the numeric datatype of the value.
// 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
}

View File

@@ -2,7 +2,6 @@ package memdb
import (
"io"
"runtime"
"sync"
"time"
@@ -85,9 +84,10 @@ type memDB struct {
// +checklocks:memoryMtx
refs int32
shared int32 // +checklocks:lockMtx
pending bool // +checklocks:lockMtx
reserved bool // +checklocks:lockMtx
shared int32 // +checklocks:lockMtx
pending bool // +checklocks:lockMtx
reserved bool // +checklocks:lockMtx
waiter *sync.Cond // +checklocks:lockMtx
lockMtx sync.Mutex
dataMtx sync.RWMutex
@@ -195,8 +195,6 @@ func (m *memFile) Size() (int64, error) {
return m.size, nil
}
const spinWait = 25 * time.Microsecond
func (m *memFile) Lock(lock vfs.LockLevel) error {
if m.lock >= lock {
return nil
@@ -228,13 +226,18 @@ func (m *memFile) Lock(lock vfs.LockLevel) error {
m.pending = true
}
for before := time.Now(); m.shared > 1; {
if time.Since(before) > spinWait {
return sqlite3.BUSY
if m.shared > 1 {
before := time.Now()
if m.waiter == nil {
m.waiter = sync.NewCond(&m.lockMtx)
}
defer time.AfterFunc(time.Millisecond, m.waiter.Broadcast).Stop()
for m.shared > 1 {
if time.Since(before) > time.Millisecond {
return sqlite3.BUSY
}
m.waiter.Wait()
}
m.lockMtx.Unlock()
runtime.Gosched()
m.lockMtx.Lock()
}
}
@@ -257,7 +260,9 @@ func (m *memFile) Unlock(lock vfs.LockLevel) error {
m.pending = false
}
if lock < vfs.LOCK_SHARED {
m.shared--
if m.shared--; m.pending && m.shared <= 1 && m.waiter != nil {
m.waiter.Broadcast()
}
}
m.lock = lock
return nil

Binary file not shown.