Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion _example/vtable/vtable.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (v *ghRepoTable) Open() (sqlite3.VTabCursor, error) {
return &ghRepoCursor{0, repos}, nil
}

func (v *ghRepoTable) BestIndex(csts []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
func (v *ghRepoTable) BestIndex(csts []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy, colsUsed uint64) (*sqlite3.IndexResult, error) {
used := make([]bool, len(csts))
return &sqlite3.IndexResult{
IdxNum: 0,
Expand Down
2 changes: 1 addition & 1 deletion _example/vtable_eponymous_only/vtable.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (v *seriesTable) Open() (sqlite3.VTabCursor, error) {
return &seriesCursor{v, 0}, nil
}

func (v *seriesTable) BestIndex(csts []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
func (v *seriesTable) BestIndex(csts []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy, colsUsed uint64) (*sqlite3.IndexResult, error) {
used := make([]bool, len(csts))
for c, cst := range csts {
if cst.Usable && cst.Op == sqlite3.OpEQ {
Expand Down
4 changes: 2 additions & 2 deletions sqlite3_opt_vtable.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char {
vt := lookupHandle(pVTab).(*sqliteVTab)
info := (*C.sqlite3_index_info)(icp)
csts := constraints(info)
res, err := vt.vTab.BestIndex(csts, orderBys(info))
res, err := vt.vTab.BestIndex(csts, orderBys(info), uint64(info.colUsed))
if err != nil {
return mPrintf("%s", err.Error())
}
Expand Down Expand Up @@ -650,7 +650,7 @@ type EponymousOnlyModule interface {
// See: http://sqlite.org/c3ref/vtab.html
type VTab interface {
// http://sqlite.org/vtab.html#xbestindex
BestIndex([]InfoConstraint, []InfoOrderBy) (*IndexResult, error)
BestIndex([]InfoConstraint, []InfoOrderBy, uint64) (*IndexResult, error)
// http://sqlite.org/vtab.html#xdisconnect
Disconnect() error
// http://sqlite.org/vtab.html#sqlite3_module.xDestroy
Expand Down
36 changes: 26 additions & 10 deletions sqlite3_opt_vtable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,22 @@ import (
)

type testModule struct {
t *testing.T
intarray []int
t *testing.T
intarray []int
bestIndexLog []uint64
}

type testVTab struct {
intarray []int
log *[]uint64
}

type testVTabCursor struct {
vTab *testVTab
index int
}

func (m testModule) Create(c *SQLiteConn, args []string) (VTab, error) {
func (m *testModule) Create(c *SQLiteConn, args []string) (VTab, error) {
if len(args) != 6 {
m.t.Fatal("six arguments expected")
}
Expand All @@ -58,16 +60,19 @@ func (m testModule) Create(c *SQLiteConn, args []string) (VTab, error) {
if err != nil {
return nil, err
}
return &testVTab{m.intarray}, nil
return &testVTab{intarray: m.intarray, log: &m.bestIndexLog}, nil
}

func (m testModule) Connect(c *SQLiteConn, args []string) (VTab, error) {
func (m *testModule) Connect(c *SQLiteConn, args []string) (VTab, error) {
return m.Create(c, args)
}

func (m testModule) DestroyModule() {}
func (m *testModule) DestroyModule() {}

func (v *testVTab) BestIndex(cst []InfoConstraint, ob []InfoOrderBy) (*IndexResult, error) {
func (v *testVTab) BestIndex(cst []InfoConstraint, ob []InfoOrderBy, colsUsed uint64) (*IndexResult, error) {
if v.log != nil {
*v.log = append(*v.log, colsUsed)
}
used := make([]bool, 0, len(cst))
for range cst {
used = append(used, false)
Expand Down Expand Up @@ -128,9 +133,10 @@ func TestCreateModule(t *testing.T) {
tempFilename := TempFilename(t)
defer os.Remove(tempFilename)
intarray := []int{1, 2, 3}
module := &testModule{t: t, intarray: intarray}
sql.Register("sqlite3_TestCreateModule", &SQLiteDriver{
ConnectHook: func(conn *SQLiteConn) error {
return conn.CreateModule("test", testModule{t, intarray})
return conn.CreateModule("test", module)
},
})
db, err := sql.Open("sqlite3_TestCreateModule", tempFilename)
Expand All @@ -141,6 +147,7 @@ func TestCreateModule(t *testing.T) {
if err != nil {
t.Fatalf("could not create vtable: %v", err)
}
bestIndexCallsBeforeQuery := len(module.bestIndexLog)

var i, value int
rows, err := db.Query("SELECT rowid, * FROM vtab WHERE test = '3'")
Expand All @@ -153,6 +160,15 @@ func TestCreateModule(t *testing.T) {
t.Fatalf("want %v but %v", intarray[i], value)
}
}
bestIndexCalls := module.bestIndexLog[bestIndexCallsBeforeQuery:]
if len(bestIndexCalls) == 0 {
t.Fatal("expected BestIndex to be called during query planning")
}
for _, colsUsed := range bestIndexCalls {
if colsUsed != 1 {
t.Fatalf("expected colsUsed mask 1, got %d", colsUsed)
}
}

_, err = db.Exec("DROP TABLE vtab")
if err != nil {
Expand Down Expand Up @@ -377,7 +393,7 @@ func (t *vtabUpdateTable) Open() (VTabCursor, error) {
return &vtabUpdateCursor{t, 0}, nil
}

func (t *vtabUpdateTable) BestIndex(cst []InfoConstraint, ob []InfoOrderBy) (*IndexResult, error) {
func (t *vtabUpdateTable) BestIndex(cst []InfoConstraint, ob []InfoOrderBy, colsUsed uint64) (*IndexResult, error) {
return &IndexResult{Used: make([]bool, len(cst))}, nil
}

Expand Down Expand Up @@ -516,7 +532,7 @@ func (m testModuleEponymousOnly) Connect(c *SQLiteConn, args []string) (VTab, er

func (m testModuleEponymousOnly) DestroyModule() {}

func (v *testVTabEponymousOnly) BestIndex(cst []InfoConstraint, ob []InfoOrderBy) (*IndexResult, error) {
func (v *testVTabEponymousOnly) BestIndex(cst []InfoConstraint, ob []InfoOrderBy, colsUsed uint64) (*IndexResult, error) {
used := make([]bool, 0, len(cst))
for range cst {
used = append(used, false)
Expand Down