1 Star 1 Fork 20

吴文凯 / gen

forked from gorm / gen 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
clause.go 14.96 KB
一键复制 编辑 原始数据 按行查看 历史
吴文凯 提交于 2022-10-26 14:07 . update mod
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
package check
import (
"fmt"
"strings"
"gitee.com/whilew/gen/internal/model"
"gitee.com/whilew/gen/internal/parser"
)
// Clause a symbol of clause, it can be sql condition clause, if clause, where clause, set clause and else cluase
type Clause interface {
String() string
}
var (
_ Clause = new(SQLClause)
_ Clause = new(IfClause)
_ Clause = new(ElseClause)
_ Clause = new(WhereClause)
_ Clause = new(SetClause)
)
type clause struct {
VarName string
Type model.Status
}
// SQLClause sql condition clause
type SQLClause struct {
clause
Value []string
}
func (s SQLClause) String() string {
return strings.ReplaceAll(strings.Join(s.Value, "+"), `"+"`, "")
}
// IfClause if clause
type IfClause struct {
clause
Cond string
Value []Clause
Else []Clause
}
func (i IfClause) String() string {
return fmt.Sprintf("helper.IfClause(%s)", i.VarName)
}
// ElseClause else clause
type ElseClause struct {
clause
Cond string
Value []Clause
}
func (e ElseClause) String() (res string) {
condList := make([]string, len(e.Value))
for i, v := range e.Value {
condList[i] = v.String()
}
return strings.ReplaceAll(strings.Join(condList, "+"), `"+"`, "")
}
// WhereClause where clause
type WhereClause struct {
clause
Value []Clause
}
func (w WhereClause) String() string {
return fmt.Sprintf("helper.WhereClause(%s)", w.VarName)
}
// SetClause set clause
type SetClause struct {
clause
Value []Clause
}
func (w SetClause) String() string {
return fmt.Sprintf("helper.SetClause(%s)", w.VarName)
}
// Slices split sql into chunks
type Slices struct {
slices []slice
tmpl []string
currentIndex int
Names map[model.Status]int
}
// NewSlices create and initialize Slices
func NewSlices() *Slices {
return &Slices{
Names: map[model.Status]int{
model.IF: 0,
model.WHERE: 0,
model.SET: 0,
},
}
}
// Len return length of s.slices
func (s *Slices) Len() int {
return len(s.slices)
}
// Next return next slice and increase index by 1
func (s *Slices) Next() slice {
s.currentIndex++
return s.slices[s.currentIndex]
}
// SubIndex take index one step back
func (s *Slices) SubIndex() {
s.currentIndex--
}
// HasMore whether has more slice
func (s *Slices) HasMore() bool {
return s.currentIndex < len(s.slices)-1
}
// IsNull whether slice is empty
func (s *Slices) IsNull() bool {
return len(s.slices) == 0
}
// Current return current slice
func (s *Slices) Current() slice {
return s.slices[s.currentIndex]
}
// GetName ...
func (s *Slices) GetName(status model.Status) string {
switch status {
case model.IF:
defer func() { s.Names[model.IF]++ }()
return fmt.Sprintf("ifCond%d", s.Names[model.IF])
case model.WHERE:
defer func() { s.Names[model.WHERE]++ }()
return fmt.Sprintf("whereCond%d", s.Names[model.WHERE])
case model.SET:
defer func() { s.Names[model.SET]++ }()
return fmt.Sprintf("setCond%d", s.Names[model.SET])
default:
return fmt.Sprintf("Cond%d", s.currentIndex)
}
}
func (s *Slices) appendIfCond(name, cond, result string) {
s.tmpl = append(s.tmpl, fmt.Sprintf("%s = append(%s, helper.Cond{Cond: %s, Result: %s})", name, name, cond, result))
}
func (s *Slices) appendSetValue(name, result string) {
s.tmpl = append(s.tmpl, fmt.Sprintf("%s = append(%s, %s)", name, name, strings.TrimSpace(result)))
}
// CreateIf create if clause code
func (s *Slices) CreateIf(name string) {
s.tmpl = append(s.tmpl, fmt.Sprintf("%s := make([]helper.Cond, 0, 100)", name))
}
// CreateStringSet create string set
func (s *Slices) CreateStringSet(name string) {
s.tmpl = append(s.tmpl, fmt.Sprintf("%s := make([]string, 0, 100)", name))
}
// parse slice and append result to tmpl, return a Clause array
func (s *Slices) parse() ([]Clause, error) {
if s.IsNull() {
return nil, fmt.Errorf("sql is null")
}
name := "generateSQL"
res := make([]Clause, 0, s.Len())
for slice := s.Current(); ; slice = s.Next() {
s.tmpl = append(s.tmpl, "")
switch slice.Type {
case model.SQL, model.DATA, model.VARIABLE:
sqlClause := s.parseSQL(name)
res = append(res, sqlClause)
s.tmpl = append(s.tmpl, fmt.Sprintf("%s+=%s", name, sqlClause.String()))
case model.IF:
ifClause, err := s.parseIF()
if err != nil {
return nil, err
}
res = append(res, ifClause)
s.tmpl = append(s.tmpl, fmt.Sprintf("%s+=helper.IfClause(%s)", name, ifClause.VarName))
case model.WHERE:
whereClause, err := s.parseWhere()
if err != nil {
return nil, err
}
res = append(res, whereClause)
s.tmpl = append(s.tmpl, fmt.Sprintf("%s+=helper.WhereClause(%s)", name, whereClause.VarName))
case model.SET:
setClause, err := s.parseSet()
if err != nil {
return nil, err
}
res = append(res, setClause)
s.tmpl = append(s.tmpl, fmt.Sprintf("%s+=helper.SetClause(%s)", name, setClause.VarName))
case model.END:
default:
return nil, fmt.Errorf("unknow clause:%s", slice.Origin)
}
if !s.HasMore() {
break
}
}
return res, nil
}
// parseIF parse if clause
func (s *Slices) parseIF() (res IfClause, err error) {
slice := s.Current()
name := s.GetName(slice.Type)
s.CreateIf(name)
res.Type = slice.Type
res.Cond = slice.Value
res.VarName = name
cond := []string{res.Cond}
for s.HasMore() {
n := s.Next()
switch n.Type {
case model.SQL, model.DATA, model.VARIABLE:
str := s.parseSQL(name)
res.Value = append(res.Value, str)
s.appendIfCond(name, res.Cond, str.String())
case model.IF:
var ifClause IfClause
ifClause, err = s.parseIF()
if err != nil {
return
}
res.Value = append(res.Value, ifClause)
s.appendIfCond(name, res.Cond, ifClause.String())
case model.WHERE:
var whereClause WhereClause
whereClause, err = s.parseWhere()
if err != nil {
return
}
res.Value = append(res.Value, whereClause)
s.appendIfCond(name, res.Cond, whereClause.String())
case model.SET:
var setClause SetClause
setClause, err = s.parseSet()
if err != nil {
return
}
res.Value = append(res.Value, setClause)
s.appendIfCond(name, res.Cond, setClause.String())
case model.ELSEIF:
elseClause := s.parseElSE(name)
elseCond := elseClause.Cond
elseClause.Cond = fmt.Sprintf("!(%s) && %s", strings.Join(cond, " || "), elseCond)
res.Else = append(res.Else, elseClause)
s.appendIfCond(name, elseClause.Cond, elseClause.String())
cond = append(cond, elseCond)
case model.ELSE:
elseClause := s.parseElSE(name)
elseClause.Cond = fmt.Sprintf("!(%s)", strings.Join(cond, " || "))
res.Else = append(res.Else, elseClause)
s.appendIfCond(name, elseClause.Cond, elseClause.String())
case model.END:
return
default:
err = fmt.Errorf("unknow clause : %s", n.Origin)
return
}
}
if s.Current().Type == model.END {
return
}
err = fmt.Errorf("incomplete SQL,if not end")
return
}
// parseElSE parse else clause, the clause' type must be one of if, where, set, SQL condition
func (s *Slices) parseElSE(name string) (res ElseClause) {
slice := s.Current()
res.Cond = slice.Value
res.VarName = name
res.Type = slice.Type
if !s.HasMore() {
return
}
for n := s.Next(); s.HasMore(); n = s.Next() {
switch n.Type {
case model.SQL, model.DATA, model.VARIABLE:
res.Value = append(res.Value, s.parseSQL(name))
case model.IF:
ifClause, err := s.parseIF()
if err != nil {
return
}
res.Value = append(res.Value, ifClause)
case model.WHERE:
whereClause, err := s.parseWhere()
if err != nil {
return
}
res.Value = append(res.Value, whereClause)
case model.SET:
setClause, err := s.parseSet()
if err != nil {
return
}
res.Value = append(res.Value, setClause)
default:
s.SubIndex()
return
}
}
return
}
// parseWhere parse where clause, the clause' type must be one of if, SQL condition
func (s *Slices) parseWhere() (res WhereClause, err error) {
slice := s.Current()
name := s.GetName(slice.Type)
s.CreateStringSet(name)
res.VarName = name
res.Type = slice.Type
for s.HasMore() {
n := s.Next()
switch n.Type {
case model.SQL, model.DATA, model.VARIABLE:
strClause := s.parseSQL(name)
res.Value = append(res.Value, strClause)
s.appendSetValue(name, strClause.String())
case model.IF:
var ifClause IfClause
ifClause, err = s.parseIF()
if err != nil {
return
}
res.Value = append(res.Value, ifClause)
s.appendSetValue(name, ifClause.String())
case model.END:
return
default:
err = fmt.Errorf("unknow clause : %s", n.Origin)
return
}
}
if s.Current().Type == model.END {
return
}
err = fmt.Errorf("incomplete SQL,where not end")
return
}
// parseSet parse set clause, the clause' type must be one of if, SQL condition
func (s *Slices) parseSet() (res SetClause, err error) {
slice := s.Current()
name := s.GetName(slice.Type)
s.CreateStringSet(name)
res.VarName = name
res.Type = slice.Type
for s.HasMore() {
n := s.Next()
switch n.Type {
case model.SQL, model.DATA, model.VARIABLE:
strClause := s.parseSQL(name)
res.Value = append(res.Value, strClause)
s.appendSetValue(name, strClause.String())
case model.IF:
var ifClause IfClause
ifClause, err = s.parseIF()
if err != nil {
return
}
res.Value = append(res.Value, ifClause)
s.appendSetValue(name, ifClause.String())
case model.END:
return
default:
err = fmt.Errorf("unknow clause : %s", n.Origin)
return
}
}
if s.Current().Type == model.END {
return
}
err = fmt.Errorf("incomplete SQL,set not end")
return
}
// parseSQL parse sql condition, the clause' type must be one of SQL condition, VARIABLE, Data
func (s *Slices) parseSQL(name string) (res SQLClause) {
res.VarName = name
res.Type = model.SQL
for slice := s.Current(); ; slice = s.Next() {
switch slice.Type {
case model.SQL, model.VARIABLE, model.DATA:
res.Value = append(res.Value, slice.Value)
default:
s.SubIndex()
return
}
if !s.HasMore() {
return
}
}
}
// sql fragment
type fragment struct {
Type model.Status
Value string
IsArray bool
}
func checkFragment(s string, params []parser.Param) (f fragment, err error) {
digital := func(str string) string {
if isDigit(str) {
return "<integer>"
}
return str
}
f = fragment{Type: model.UNKNOWN, Value: strings.Trim(s, " ")}
str := strings.ToLower(strings.Trim(s, " "))
switch digital(str) {
case "<integer>":
f.Type = model.INT
case "&&", "||":
f.Type = model.LOGICAL
case ">", "<", ">=", "<=", "==", "!=":
f.Type = model.EXPRESSION
case "end":
f.Type = model.END
case "if":
f.Type = model.IF
case "set":
f.Type = model.SET
case "else":
f.Type = model.ELSE
case "where":
f.Type = model.WHERE
case "true", "false":
f.Type = model.BOOL
case "nil":
f.Type = model.NIL
default:
f.fragmentByParams(params)
if f.Type == model.UNKNOWN {
err = fmt.Errorf("unknow parameter: %s", s)
}
}
return
}
func (f *fragment) fragmentByParams(params []parser.Param) {
for _, param := range params {
if param.Name == f.Value {
f.IsArray = param.IsArray
switch param.Type {
case "bool":
f.Type = model.BOOL
return
case "int":
f.Type = model.INT
return
case "string":
f.Type = model.STRING
return
case "Time":
f.Type = model.TIME
default:
f.Type = model.OTHER
}
}
}
}
func splitTemplate(tmpl string, params []parser.Param) (fragList []fragment, err error) {
var buf model.SQLBuffer
var f fragment
for i := 0; !strOutrange(i, tmpl); i++ {
switch tmpl[i] {
case '"':
_ = buf.WriteByte(tmpl[i])
for i++; ; i++ {
if strOutrange(i, tmpl) {
return nil, fmt.Errorf("incomplete code:%s", tmpl)
}
_ = buf.WriteByte(tmpl[i])
if tmpl[i] == '"' && tmpl[i-1] != '\\' {
fragList = append(fragList, fragment{Type: model.STRING, Value: buf.Dump()})
break
}
}
case ' ':
if sqlClause := buf.Dump(); sqlClause != "" {
f, err = checkFragment(sqlClause, params)
if err != nil {
return nil, err
}
fragList = append(fragList, f)
}
case '>', '<', '=', '!':
if sqlClause := buf.Dump(); sqlClause != "" {
f, err = checkFragment(sqlClause, params)
if err != nil {
return nil, err
}
fragList = append(fragList, f)
}
_ = buf.WriteByte(tmpl[i])
if strOutrange(i+1, tmpl) {
return nil, fmt.Errorf("incomplete code:%s", tmpl)
}
if tmpl[i+1] == '=' {
_ = buf.WriteByte(tmpl[i+1])
i++
}
f, err = checkFragment(buf.Dump(), params)
if err != nil {
return nil, err
}
fragList = append(fragList, f)
case '&', '|':
if strOutrange(i+1, tmpl) {
return nil, fmt.Errorf("incomplete code:%s", tmpl)
}
if tmpl[i+1] == tmpl[i] {
i++
if sqlClause := buf.Dump(); sqlClause != "" {
f, err = checkFragment(sqlClause, params)
if err != nil {
return nil, err
}
fragList = append(fragList, f)
}
// write && or ||
fragList = append(fragList, fragment{
Type: model.LOGICAL,
Value: tmpl[i-1 : i+1],
})
}
default:
_ = buf.WriteByte(tmpl[i])
}
}
if sqlClause := buf.Dump(); sqlClause != "" {
f, err = checkFragment(sqlClause, params)
if err != nil {
return nil, err
}
fragList = append(fragList, f)
}
return fragList, nil
}
// check validition of clause's value
func checkTempleFragmentValid(list []fragment) error {
for i := 1; i < len(list); i++ {
switch list[i].Type {
case model.IF, model.ELSE, model.END, model.BOOL, model.LOGICAL, model.WHERE, model.SET:
continue
case model.INT, model.STRING, model.OTHER, model.TIME, model.NIL:
if i+2 < len(list) {
if isExpressionValid(list[i : i+3]) {
i += 2
} else {
return fmt.Errorf("condition type not match:%s", fragmentToString(list[i:i+3]))
}
}
default:
return fmt.Errorf("unknow fragment : %s ", list[i].Value)
}
}
return nil
}
// isExpressionValid check express valid
func isExpressionValid(expr []fragment) bool {
if len(expr) != 3 {
return false
}
if expr[1].Type != model.EXPRESSION {
return false
}
//Only arrays can be compared with nil
if expr[0].Type == model.NIL || expr[2].Type == model.NIL {
return expr[0].IsArray || expr[2].IsArray
}
return expr[0].Type == expr[2].Type
}
func fragmentToString(list []fragment) string {
var values []string
if len(list) == 0 {
return ""
}
for _, t := range list {
values = append(values, t.Value)
}
return strings.Join(values, " ")
}
func fragmentToSLice(list []fragment) (part slice, err error) {
var values []string
if len(list) == 0 {
return
}
for _, t := range list {
values = append(values, t.Value)
}
part.Origin = strings.Join(values, " ")
switch strings.ToLower(values[0]) {
case "if":
if len(values) > 1 {
part.Type = model.IF
part.Value = strings.Join(values[1:], " ")
return
}
case "else":
if len(values) == 1 {
part.Type = model.ELSE
return
} else {
if strings.ToLower(values[1]) == "if" && len(values) > 2 {
part.Value = strings.Join(values[2:], " ")
part.Type = model.ELSEIF
return
}
}
case "where":
part.Type = model.WHERE
return
case "set":
part.Type = model.SET
return
case "end":
part.Type = model.END
return
}
err = fmt.Errorf("syntax error:%s", strings.Join(values, " "))
return
}
Go
1
https://gitee.com/whilew/gen.git
git@gitee.com:whilew/gen.git
whilew
gen
gen
v0.1.38

搜索帮助