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

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