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) { // }