Fix wrong results and cursor state sharing in series example

This commit is contained in:
Yasuhiro Matsumoto
2026-07-29 09:46:32 +09:00
parent 0cfec60306
commit 3b8cebf19b
2 changed files with 82 additions and 41 deletions

View File

@@ -20,14 +20,26 @@ func main() {
}
defer db.Close()
rows, err := db.Query("select * from series")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var value int
rows.Scan(&value)
fmt.Printf("value: %d\n", value)
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 {
log.Fatal(err)
}
for rows.Next() {
var value int
if err := rows.Scan(&value); err != nil {
log.Fatal(err)
}
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
import (
"errors"
"fmt"
"strings"
"github.com/mattn/go-sqlite3"
)
@@ -21,7 +23,7 @@ func (m *seriesModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTa
if err != nil {
return nil, err
}
return &seriesTable{0, 0, 1}, nil
return &seriesTable{}, nil
}
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() {}
type seriesTable struct {
start int64
stop int64
step int64
}
type seriesTable struct{}
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) {
// 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))
var params []byte
for c, cst := range csts {
if cst.Usable && cst.Op == sqlite3.OpEQ {
used[c] = true
if !cst.Usable || cst.Op != sqlite3.OpEQ {
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{
IdxNum: 0,
IdxStr: "default",
IdxStr: string(params),
Used: used,
}, nil
}
@@ -59,7 +80,9 @@ func (v *seriesTable) Disconnect() error { return nil }
func (v *seriesTable) Destroy() error { return nil }
type seriesCursor struct {
*seriesTable
start int64
stop int64
step int64
value int64
}
@@ -68,35 +91,38 @@ func (vc *seriesCursor) Column(c *sqlite3.SQLiteContext, col int) error {
case 0:
c.ResultInt64(vc.value)
case 1:
c.ResultInt64(vc.seriesTable.start)
c.ResultInt64(vc.start)
case 2:
c.ResultInt64(vc.seriesTable.stop)
c.ResultInt64(vc.stop)
case 3:
c.ResultInt64(vc.seriesTable.step)
c.ResultInt64(vc.step)
}
return nil
}
func (vc *seriesCursor) Filter(idxNum int, idxStr string, vals []any) error {
switch {
case len(vals) < 1:
vc.seriesTable.start = 0
vc.seriesTable.stop = 1000
vc.value = vc.seriesTable.start
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)
start, stop, step := int64(0), int64(1000), int64(1)
for i := 0; i < len(idxStr) && i < len(vals); i++ {
n, ok := vals[i].(int64)
if !ok {
return fmt.Errorf("series: argument %d must be an integer", i+1)
}
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
}
@@ -106,6 +132,9 @@ func (vc *seriesCursor) Next() error {
}
func (vc *seriesCursor) EOF() bool {
if vc.step < 0 {
return vc.value < vc.stop
}
return vc.value > vc.stop
}