//go:build sqlite_vtable || vtable package vtab import ( "fmt" gosqlite3 "github.com/mattn/go-sqlite3" ) // ─── 桥接层:Module → gosqlite3.Module ────────────────────── type moduleAdapter struct { mod Module } func (a *moduleAdapter) Create(c *gosqlite3.SQLiteConn, args []string) (gosqlite3.VTab, error) { return a.build(c, args, true) } func (a *moduleAdapter) Connect(c *gosqlite3.SQLiteConn, args []string) (gosqlite3.VTab, error) { return a.build(c, args, false) } func (a *moduleAdapter) DestroyModule() {} func (a *moduleAdapter) build(c *gosqlite3.SQLiteConn, args []string, isCreate bool) (gosqlite3.VTab, error) { declare := func(schema string) error { return c.DeclareVTab(schema) } var ( table Table err error ) if isCreate { table, err = a.mod.Create(args, declare) } else { table, err = a.mod.Connect(args, declare) } if err != nil { return nil, err } base := &baseVtabAdapter{table: table} // if wt, ok := table.(WritableTable); ok { // return &writableVtabAdapter{baseVtabAdapter: base, wt: wt}, nil // } return base, nil } func NewModuleAdapter(mod Module) gosqlite3.Module { return &moduleAdapter{mod} } // ─── 桥接层:Table(只读)──────────────────────────────────── type planKey struct { idxNum int idxStr string } type baseVtabAdapter struct { table Table // plans 保存每个查询计划中 Used=true 的约束(按值传入顺序), // BestIndex 写入,Filter 通过 (idxNum, idxStr) 查询后绑定值。 plans map[planKey][]ConstraintInfo } func (v *baseVtabAdapter) BestIndex(csts []gosqlite3.InfoConstraint, obs []gosqlite3.InfoOrderBy) (*gosqlite3.IndexResult, error) { ci := make([]ConstraintInfo, len(csts)) for i, c := range csts { ci[i] = ConstraintInfo{Column: c.Column, Op: c.Op, Usable: c.Usable} } ob := make([]OrderByInfo, len(obs)) for i, o := range obs { ob[i] = OrderByInfo{Column: o.Column, Desc: o.Desc} } out, err := v.table.BestIndex(ci, ob) if err != nil { return nil, err } // 按值传入顺序收集 Used=true 的约束,供 Filter 阶段绑定值。 var usedCi []ConstraintInfo for i, used := range out.Used { if used { usedCi = append(usedCi, ci[i]) } } if v.plans == nil { v.plans = make(map[planKey][]ConstraintInfo) } v.plans[planKey{out.IdxNum, out.IdxStr}] = usedCi return &gosqlite3.IndexResult{ Used: out.Used, IdxNum: out.IdxNum, IdxStr: out.IdxStr, AlreadyOrdered: out.AlreadyOrdered, EstimatedCost: out.EstimatedCost, EstimatedRows: out.EstimatedRows, }, nil } func (v *baseVtabAdapter) Disconnect() error { return v.table.Disconnect() } func (v *baseVtabAdapter) Destroy() error { return v.table.Destroy() } func (v *baseVtabAdapter) Open() (gosqlite3.VTabCursor, error) { c, err := v.table.Open() if err != nil { return nil, err } return &cursorAdapter{cursor: c, adapter: v}, nil } // ─── 桥接层:WritableTable ──────────────────────────────────── // writableVtabAdapter 内嵌只读适配器并实现 gosqlite3.VTabUpdater, // go-sqlite3 通过类型断言检测到该接口后会启用 INSERT/UPDATE/DELETE。 type writableVtabAdapter struct { *baseVtabAdapter wt WritableTable } func (v *writableVtabAdapter) Delete(rowid any) error { return v.wt.Delete(rowid) } func (v *writableVtabAdapter) Insert(rowid any, values []any) (int64, error) { // rowid 是 SQLite 建议的值(通常为 nil,由表自行决定) return v.wt.Insert(values) } func (v *writableVtabAdapter) Update(rowid any, values []any) error { return v.wt.Update(rowid, values) } // ─── 桥接层:Cursor ─────────────────────────────────────────── type cursorAdapter struct { cursor Cursor adapter *baseVtabAdapter } func (c *cursorAdapter) Close() error { return c.cursor.Close() } func (c *cursorAdapter) Next() error { return c.cursor.Next() } func (c *cursorAdapter) EOF() bool { return c.cursor.EOF() } func (c *cursorAdapter) Filter(idxNum int, idxStr string, vals []any) error { ci := c.adapter.plans[planKey{idxNum, idxStr}] constraints := make([]ConstraintInfo, len(vals)) for i, val := range vals { constraints[i].Value = val if i < len(ci) { constraints[i].Column = ci[i].Column constraints[i].Op = ci[i].Op constraints[i].Usable = true } } return c.cursor.Filter(idxNum, constraints) } func (c *cursorAdapter) Rowid() (int64, error) { return c.cursor.Rowid() } func (c *cursorAdapter) Column(ctx *gosqlite3.SQLiteContext, col int) error { val, err := c.cursor.Column(col) if err != nil { return err } resultValue(ctx, val) return nil } // resultValue 将 Go 值写入 SQLite 上下文。 func resultValue(ctx *gosqlite3.SQLiteContext, val any) { if val == nil { ctx.ResultNull() return } switch v := val.(type) { case int: ctx.ResultInt(v) case int32: ctx.ResultInt(int(v)) case int64: ctx.ResultInt64(v) case float32: ctx.ResultDouble(float64(v)) case float64: ctx.ResultDouble(v) case bool: if v { ctx.ResultInt(1) } else { ctx.ResultInt(0) } case string: ctx.ResultText(v) case []byte: ctx.ResultBlob(v) default: ctx.ResultText(fmt.Sprint(v)) } }