diff --git a/_example/vtable_eponymous_only/main.go b/_example/vtable_eponymous_only/main.go index 17b58af..134317e 100644 --- a/_example/vtable_eponymous_only/main.go +++ b/_example/vtable_eponymous_only/main.go @@ -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() } } diff --git a/_example/vtable_eponymous_only/vtable.go b/_example/vtable_eponymous_only/vtable.go index 9f22ebc..d87c4d6 100644 --- a/_example/vtable_eponymous_only/vtable.go +++ b/_example/vtable_eponymous_only/vtable.go @@ -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 }