diff --git a/builder2.go b/builder2.go new file mode 100644 index 0000000..316a4ff --- /dev/null +++ b/builder2.go @@ -0,0 +1,294 @@ +package vql + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/tidwall/gjson" +) + +type Builder2 struct { + As string `json:"as"` + Select interface{} `json:"select"` + From interface{} `json:"from"` + Join interface{} `json:"join"` + Where interface{} `json:"where"` + Group interface{} `json:"group"` + Having interface{} `json:"having"` + Order interface{} `json:"order"` +} + +func NewBuilder2() *Builder2 { + return &Builder2{} +} + +func (b *Builder2) Select2(selects ...interface{}) *Builder2 { + b.Select = selects + return b +} + +func (b *Builder2) Sql() (string, error) { + selectSql, err := toSelectSql(b.Select) + if err != nil { + return "", err + } + + fromSql, err := toFromSql(b.From) + if err != nil { + return "", err + } + + joinSql, err := toJoinSql(b.Join) + if err != nil { + return "", err + } + + whereSql, err := toWhereSql(b.Where) + if err != nil { + return "", err + } + + groupSql, err := toGroupSql(b.Group) + if err != nil { + return "", err + } + + havingSql, err := toHavingSql(b.Having) + if err != nil { + return "", err + } + + orderSql, err := toOrderSql(b.Order) + if err != nil { + return "", err + } + + sql := fmt.Sprintf("select %s from %s ", selectSql, fromSql) + if joinSql != "" { + sql += fmt.Sprintf(" %s ", joinSql) + } + if whereSql != "" { + sql += fmt.Sprintf(" where %s ", whereSql) + } + if groupSql != "" { + sql += fmt.Sprintf(" group by %s ", groupSql) + } + if havingSql != "" { + sql += fmt.Sprintf(" having %s ", havingSql) + } + if orderSql != "" { + sql += fmt.Sprintf(" order by %s ", orderSql) + } + + return sql, nil + +} + +func toSelectSql(selects interface{}) (string, error) { + + selectSql := "" + if selectSql, ok := selects.(string); ok { + return selectSql, nil + } + + selectGjson := gjson.Parse(jsonEncode(selects)) + if selectGjson.IsArray() { + ses := []string{} + for _, se := range selectGjson.Array() { + ses = append(ses, se.String()) + } + selectSql = strings.Join(ses, ",") + } else { + selectSql = selectGjson.String() + } + return selectSql, nil +} + +func toFromSql(from interface{}) (string, error) { + fromSql := "" + if fromSql, ok := from.(string); ok { + return fromSql, nil + } + + fromGjson := gjson.Parse(jsonEncode(from)) + if fromGjson.IsObject() { + nb := NewBuilder2() + json.Unmarshal([]byte(fromGjson.Raw), &nb) + ns, err := nb.Sql() + if err != nil { + return "", err + } + if nb.As != "" { + fromSql = fmt.Sprintf("( %s ) as %s", ns, nb.As) + } else { + fromSql = fmt.Sprintf("( %s )", ns) + } + } else { + fromSql = fromGjson.String() + } + return fromSql, nil +} + +func toJoinSql(join interface{}) (string, error) { + if join == nil { + return "", nil + } + // joinSql := "" + if joinSql, ok := join.(string); ok { + return joinSql, nil + } + + joinGjson := gjson.Parse(jsonEncode(join)) + if joinGjson.IsArray() { + js := []string{} + for _, j := range joinGjson.Array() { + nj, err := toJoinSql(j.Value()) + if err != nil { + return "", err + } + js = append(js, nj) + } + return strings.Join(js, " "), nil + } else { + return "", fmt.Errorf("目前仅支持数组或者字符串") + } + + // joinGjson := gjson.Parse(jsonEncode(join)) + // if joinGjson.IsArray() { + // js := []string{} + // for _, j := range joinGjson.Array() { + // if j.IsObject() { + // return + // } else { + // js = append(js, j.String()) + // } + // } + // joinSql = strings.Join(js, " ") + // } else { + // joinSql = joinGjson.String() + // } + // return joinSql +} + +func toWhereSql(where interface{}) (string, error) { + if where == nil { + return "", nil + } + whereSql := "" + if whereSql, ok := where.(string); ok { + return whereSql, nil + } + + whereGjson := gjson.Parse(jsonEncode(where)) + + //[ "a=","1","and",[["c=","3"],"and",["d=","4"]],"or",["b=","2"]] + if whereGjson.IsArray() { + ws := []string{} + expression := whereGjson.Array() + + if len(expression) == 0 { + return "", nil + } + + if expression[0].String() == "and" || expression[0].String() == "or" { + + operator := "and" + + for _, w := range expression { + if w.Type == gjson.String { + if whereSql == "" && (w.String() == "and" || w.String() == "or") { + operator = w.String() + } else { + ws = append(ws, w.String()) + } + } else { + newExp, err := toWhereSql(w.Value()) + if err != nil { + return "", err + } + ws = append(ws, newExp) + } + + whereSql = strings.Join(ws, fmt.Sprintf(" %s ", operator)) + } + } else { + for _, w := range expression { + if w.Type == gjson.String { + ws = append(ws, w.String()) + } else { + newExp, err := toWhereSql(w.Value()) + if err != nil { + return "", err + } + ws = append(ws, newExp) + } + + whereSql = strings.Join(ws, " ") + } + } + } else if whereGjson.IsObject() { + newValue := []interface{}{ + "and", + } + + ops := []string{"=", ">", ">=", "<", "<=", "<>", "!="} + + for k, v := range whereGjson.Map() { + if v.Value() == nil { + continue + } + + prefix := k + mop := "=" + + for _, op := range ops { + if strings.HasSuffix(k, op) { + prefix = strings.TrimSuffix(k, op) + mop = op + break + } + } + + newValue = append(newValue, []string{prefix, mop, v.String()}) + } + + return toWhereSql(newValue) + } else { + whereSql = whereGjson.String() + } + return fmt.Sprintf("( %s )", whereSql), nil +} + +func toGroupSql(group interface{}) (string, error) { + if group == nil { + return "", nil + } + // groupSql := "" + if groupSql, ok := group.(string); ok { + return groupSql, nil + } + return "", fmt.Errorf("暂时不支持非字符串类型的group语句") +} + +func toHavingSql(having interface{}) (string, error) { + if having == nil { + return "", nil + } + // havingSql := "" + if havingSql, ok := having.(string); ok { + return havingSql, nil + } + return "", fmt.Errorf("暂时不支持非字符串类型的having语句") +} + +func toOrderSql(order interface{}) (string, error) { + if order == nil { + return "", nil + } + // orderSql := "" + if orderSql, ok := order.(string); ok { + return orderSql, nil + } + return "", fmt.Errorf("暂时不支持非字符串类型的order语句") +} diff --git a/builder2_test.go b/builder2_test.go new file mode 100644 index 0000000..fd20f32 --- /dev/null +++ b/builder2_test.go @@ -0,0 +1,44 @@ +package vql + +import ( + "encoding/json" + "log" + "testing" +) + +func Test_toOrderSql(t *testing.T) { + text := `{ + "as": "test", + "select": [ + "col1", + "col2" + ], + "from": { + "as": "test", + "select": [ + "col1", + "col2" + ], + "from": "table1" + }, + "join": "left join table2 on table1.id = table2.id", + "where": { + "col1": "value1", + "col2": "value2", + "col3": 12 + } + }` + + var builder Builder2 + err := json.Unmarshal([]byte(text), &builder) + if err != nil { + t.Error(err) + } + + sql, err := builder.Sql() + if err != nil { + t.Error(err) + } + + log.Println(sql) +}