Files
db/dialect/sqlite3/vtab/adapter.go
T
what ac81f1ff0b feat: 新增 SQLite3 虚拟表框架及方言扩展
- 新增 dialect/sqlite3/vtab 包,提供干净的虚拟表接口(Module/Table/Cursor)
- 适配层自动将 BestIndex 约束与 Filter 值绑定(ConstraintInfo.Value),无需手动编解码 IdxStr
- 支持 OpLIMIT/OpOFFSET 约束下推
- 新增 SupportsDistinct 方言选项,控制 SELECT 级和表达式级 DISTINCT 生成
- sqlite3 方言注册 IF() 函数支持
2026-04-18 13:41:41 +08:00

205 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//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))
}
}