|
|
package vql
import ( "encoding/json" "fmt" "strings" "sync" )
type VirtualQlConvert func(val map[string]interface{}) (string, error)
func NoParameConvertFunc(vTable string, convertSql string) *VirtualTable { return &VirtualTable{ MappingTableName: vTable, SqlConvert: func(val map[string]interface{}) (string, error) { return convertSql, nil }, } }
type VirtualTable struct { MappingTableName string //example: user(db:db1,id:1)
ArgNames []string //example: [db,id]
SqlConvert VirtualQlConvert }
func parseTableName(tableNameExpression string) (tableName string, args map[string]string, err error) { args = make(map[string]string) source := []rune(tableNameExpression)
flag := "table" argStr := "" for i := range source { switch flag { case "table": if source[i] == '(' { flag = "arg" continue } tableName += string(source[i]) case "arg": if source[i] == ')' { flag = "end" continue } argStr += string(source[i]) case "end": break } }
argsArr := []string{} if argStr != "" { argsArr = strings.Split(strings.TrimSpace(argStr), ",") } for i := range argsArr { arg := strings.Split(argsArr[i], ":") if len(arg) != 2 { err = fmt.Errorf("table name expression error") return } args[arg[0]] = arg[1] }
return }
var DefaultVirtualQL = &VirtualQL{}
type VirtualQL struct { tables map[string]VirtualTable locker sync.RWMutex }
func (vql *VirtualQL) getTable(tableName string) (vt VirtualTable, args map[string]string, err error) { vql.locker.RLock() defer vql.locker.RUnlock()
tableName, args, err = parseTableName(tableName) vqlTable := vql.tables[tableName] return vqlTable, args, err }
func (vql *VirtualQL) Register(v *VirtualTable) { if vql.tables == nil { vql.tables = make(map[string]VirtualTable) } vql.locker.Lock() defer vql.locker.Unlock() vql.tables[v.MappingTableName] = *v }
func (vql *VirtualQL) Compile(sql string, param map[string]interface{}) (*Query, error) { query, err := Analyze(sql) if err != nil { return nil, err } err = vql.convert(query, param) if err != nil { return nil, err } return query, nil }
func (vql *VirtualQL) convert(query *Query, val map[string]interface{}) error { if query.From.Table == "" && query.From.SubQuery == nil { return fmt.Errorf("from table is empty") } if query.From.Table != "" {
vtable, args, err := vql.getTable(query.From.Table) if err != nil { return err } if vtable.SqlConvert == nil { return fmt.Errorf("table %s is not register", query.From.Table) }
if val == nil { val = make(map[string]interface{}) }
for k := range vtable.ArgNames { if args[vtable.ArgNames[k]] != "" { val[vtable.ArgNames[k]] = args[vtable.ArgNames[k]] } }
convertSql, err := vtable.SqlConvert(val) if err != nil { return err } if convertSql == "" { return fmt.Errorf("table %s is not register!", query.From.Table) }
newAs := query.From.As if newAs == "" { newAs = query.From.Table } query.From = From{ raw: convertSql, As: newAs, } } else { err := vql.convert(query.From.SubQuery, val) if err != nil { return err } }
for i := range query.Join { err := vql.convert(query.Join[i].Query, val) if err != nil { return err } }
return nil }
func jsonEncode(val interface{}) string { raw, _ := json.Marshal(val) return string(raw) }
func Analyze(sql string) (*Query, error) { q := Query{} err := q.FromSql(sql) return &q, err }
// func (vql *VirtualQL) CompileWithApijson(sql apijson.QueryNode, param map[string]interface{}) (*Query, error) {
// }
|