Merge pull request #1437 from mattn/fix-series-vtable-example

Fix wrong results and cursor state sharing in series example
This commit is contained in:
mattn
2026-07-29 05:38:11 +00:00
committed by GitHub
2 changed files with 82 additions and 41 deletions

View File

@@ -20,14 +20,26 @@ func main() {
} }
defer db.Close() defer db.Close()
rows, err := db.Query("select * from series") for _, query := range []string{
"select value from series where value between 3 and 5",
"select value from series where start = 10 and stop = 20 and step = 5",
"select value from series where start = 3 and stop = -3 and step = -2",
} {
fmt.Println(query)
rows, err := db.Query(query)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer rows.Close()
for rows.Next() { for rows.Next() {
var value int var value int
rows.Scan(&value) if err := rows.Scan(&value); err != nil {
log.Fatal(err)
}
fmt.Printf("value: %d\n", value) fmt.Printf("value: %d\n", value)
} }
if err := rows.Err(); err != nil {
log.Fatal(err)
}
rows.Close()
}
} }

View File

@@ -1,7 +1,9 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"strings"
"github.com/mattn/go-sqlite3" "github.com/mattn/go-sqlite3"
) )
@@ -21,7 +23,7 @@ func (m *seriesModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTa
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &seriesTable{0, 0, 1}, nil return &seriesTable{}, nil
} }
func (m *seriesModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) { func (m *seriesModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
@@ -30,27 +32,46 @@ func (m *seriesModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VT
func (m *seriesModule) DestroyModule() {} func (m *seriesModule) DestroyModule() {}
type seriesTable struct { type seriesTable struct{}
start int64
stop int64
step int64
}
func (v *seriesTable) Open() (sqlite3.VTabCursor, error) { func (v *seriesTable) Open() (sqlite3.VTabCursor, error) {
return &seriesCursor{v, 0}, nil return &seriesCursor{}, nil
} }
func (v *seriesTable) BestIndex(csts []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) { func (v *seriesTable) BestIndex(csts []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
// Consume equality constraints on the hidden parameter columns
// (start, stop, step) only. A constraint on the value column must be
// left to SQLite, otherwise it would be dropped from the WHERE clause
// and the query would return wrong results.
used := make([]bool, len(csts)) used := make([]bool, len(csts))
var params []byte
for c, cst := range csts { for c, cst := range csts {
if cst.Usable && cst.Op == sqlite3.OpEQ { if !cst.Usable || cst.Op != sqlite3.OpEQ {
used[c] = true continue
} }
var p byte
switch cst.Column {
case 1:
p = 's' // start
case 2:
p = 'e' // stop
case 3:
p = 't' // step
default:
continue
}
if strings.IndexByte(string(params), p) >= 0 {
continue
}
used[c] = true
params = append(params, p)
} }
// IdxStr records which parameter each Filter argument holds, in
// argument order.
return &sqlite3.IndexResult{ return &sqlite3.IndexResult{
IdxNum: 0, IdxNum: 0,
IdxStr: "default", IdxStr: string(params),
Used: used, Used: used,
}, nil }, nil
} }
@@ -59,7 +80,9 @@ func (v *seriesTable) Disconnect() error { return nil }
func (v *seriesTable) Destroy() error { return nil } func (v *seriesTable) Destroy() error { return nil }
type seriesCursor struct { type seriesCursor struct {
*seriesTable start int64
stop int64
step int64
value int64 value int64
} }
@@ -68,35 +91,38 @@ func (vc *seriesCursor) Column(c *sqlite3.SQLiteContext, col int) error {
case 0: case 0:
c.ResultInt64(vc.value) c.ResultInt64(vc.value)
case 1: case 1:
c.ResultInt64(vc.seriesTable.start) c.ResultInt64(vc.start)
case 2: case 2:
c.ResultInt64(vc.seriesTable.stop) c.ResultInt64(vc.stop)
case 3: case 3:
c.ResultInt64(vc.seriesTable.step) c.ResultInt64(vc.step)
} }
return nil return nil
} }
func (vc *seriesCursor) Filter(idxNum int, idxStr string, vals []any) error { func (vc *seriesCursor) Filter(idxNum int, idxStr string, vals []any) error {
switch { start, stop, step := int64(0), int64(1000), int64(1)
case len(vals) < 1: for i := 0; i < len(idxStr) && i < len(vals); i++ {
vc.seriesTable.start = 0 n, ok := vals[i].(int64)
vc.seriesTable.stop = 1000 if !ok {
vc.value = vc.seriesTable.start return fmt.Errorf("series: argument %d must be an integer", i+1)
case len(vals) < 2:
vc.seriesTable.start = vals[0].(int64)
vc.seriesTable.stop = 1000
vc.value = vc.seriesTable.start
case len(vals) < 3:
vc.seriesTable.start = vals[0].(int64)
vc.seriesTable.stop = vals[1].(int64)
vc.value = vc.seriesTable.start
case len(vals) < 4:
vc.seriesTable.start = vals[0].(int64)
vc.seriesTable.stop = vals[1].(int64)
vc.seriesTable.step = vals[2].(int64)
} }
switch idxStr[i] {
case 's':
start = n
case 'e':
stop = n
case 't':
step = n
}
}
if step == 0 {
return errors.New("series: step must not be zero")
}
vc.start = start
vc.stop = stop
vc.step = step
vc.value = start
return nil return nil
} }
@@ -106,6 +132,9 @@ func (vc *seriesCursor) Next() error {
} }
func (vc *seriesCursor) EOF() bool { func (vc *seriesCursor) EOF() bool {
if vc.step < 0 {
return vc.value < vc.stop
}
return vc.value > vc.stop return vc.value > vc.stop
} }