- 新增 dialect/sqlite3/vtab 包,提供干净的虚拟表接口(Module/Table/Cursor) - 适配层自动将 BestIndex 约束与 Filter 值绑定(ConstraintInfo.Value),无需手动编解码 IdxStr - 支持 OpLIMIT/OpOFFSET 约束下推 - 新增 SupportsDistinct 方言选项,控制 SELECT 级和表达式级 DISTINCT 生成 - sqlite3 方言注册 IF() 函数支持
205 lines
5.3 KiB
Go
205 lines
5.3 KiB
Go
//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))
|
||
}
|
||
}
|