You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
3.5 KiB
175 lines
3.5 KiB
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) {
|
|
|
|
// }
|