148 lines
2.9 KiB
Go
148 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
type seriesModule struct{}
|
|
|
|
func (m *seriesModule) EponymousOnlyModule() {}
|
|
|
|
func (m *seriesModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
|
|
err := c.DeclareVTab(fmt.Sprintf(`
|
|
CREATE TABLE %s (
|
|
value INT,
|
|
start HIDDEN,
|
|
stop HIDDEN,
|
|
step HIDDEN
|
|
)`, args[0]))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &seriesTable{}, nil
|
|
}
|
|
|
|
func (m *seriesModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
|
|
return m.Create(c, args)
|
|
}
|
|
|
|
func (m *seriesModule) DestroyModule() {}
|
|
|
|
type seriesTable struct{}
|
|
|
|
func (v *seriesTable) Open() (sqlite3.VTabCursor, error) {
|
|
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 {
|
|
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: string(params),
|
|
Used: used,
|
|
}, nil
|
|
}
|
|
|
|
func (v *seriesTable) Disconnect() error { return nil }
|
|
func (v *seriesTable) Destroy() error { return nil }
|
|
|
|
type seriesCursor struct {
|
|
start int64
|
|
stop int64
|
|
step int64
|
|
value int64
|
|
}
|
|
|
|
func (vc *seriesCursor) Column(c *sqlite3.SQLiteContext, col int) error {
|
|
switch col {
|
|
case 0:
|
|
c.ResultInt64(vc.value)
|
|
case 1:
|
|
c.ResultInt64(vc.start)
|
|
case 2:
|
|
c.ResultInt64(vc.stop)
|
|
case 3:
|
|
c.ResultInt64(vc.step)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (vc *seriesCursor) Filter(idxNum int, idxStr string, vals []any) error {
|
|
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
|
|
}
|
|
|
|
func (vc *seriesCursor) Next() error {
|
|
vc.value += vc.step
|
|
return nil
|
|
}
|
|
|
|
func (vc *seriesCursor) EOF() bool {
|
|
if vc.step < 0 {
|
|
return vc.value < vc.stop
|
|
}
|
|
return vc.value > vc.stop
|
|
}
|
|
|
|
func (vc *seriesCursor) Rowid() (int64, error) {
|
|
return int64(vc.value), nil
|
|
}
|
|
|
|
func (vc *seriesCursor) Close() error {
|
|
return nil
|
|
}
|