重构: Condition/Engine 迁移到 db-v2 的表达式树,替换掉旧的 db.Raw 拼字符串
ToSql 之前是手工拼接 SQL 字符串(string 类型的 conditions、手动加括号、 手动拼 AND/OR),改成用 exp.NewExpressionList 构造表达式树,跟 db-v2 其余部分统一走"先建表达式树、由方言渲染成 SQL"这条路,不再自己维护一份 字符串拼接逻辑。ConditionType/ConditionOperator 相应改成 exp.ExpressionListType/ exp.BooleanOperation 的类型别名。 TokenValue 从 condition_expr.go 里独立成 token_value.go,从 GetParam(k string) req.GlobalParams 改成 GetParam(k string) valuex.Accessor (配合 reflux/valuex),跟其它 "-v2" 项目的属性访问方式统一。engine 包 配合适配(sqlite3 用 db/engine 的新连接管理,SelectDataset 替代 db.Connection),删掉了不再需要的 EngineParam(老 GlobalParams 包装, 新 TokenValue 已经不需要这层)。 已知问题:engine.go 在没有外部传入 GlobalParams 时用 nil user 构造兜底 值,跟 NewTokenValue 现在强制要求非 nil user 冲突,TestEngine 会 panic ——这个先不修,等后面调整 Engine.Execute 让调用方能传 user 时一起解决。
This commit is contained in:
@@ -21,3 +21,4 @@
|
|||||||
# Go workspace file
|
# Go workspace file
|
||||||
go.work
|
go.work
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|||||||
+14
-22
@@ -5,15 +5,16 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.fsdpf.net/go/db"
|
"git.fsdpf.net/go/db"
|
||||||
|
"git.fsdpf.net/go/db/exp"
|
||||||
"git.fsdpf.net/go/utils"
|
"git.fsdpf.net/go/utils"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConditionType string
|
type ConditionType = exp.ExpressionListType
|
||||||
|
|
||||||
const (
|
const (
|
||||||
OR ConditionType = "OR"
|
AND ConditionType = exp.AndType
|
||||||
AND ConditionType = "AND"
|
OR ConditionType = exp.OrType
|
||||||
)
|
)
|
||||||
|
|
||||||
type Condition struct {
|
type Condition struct {
|
||||||
@@ -92,44 +93,35 @@ func (this Condition) IsAlwaysRight() bool {
|
|||||||
return !lo.Some(flag, []bool{false})
|
return !lo.Some(flag, []bool{false})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成 SQL 语句
|
// ToSql 生成 SQL 语句
|
||||||
func (this Condition) ToSql(m TokenValue) db.Expression {
|
func (this Condition) ToSql(m TokenValue) db.Expression {
|
||||||
conditions := []string{}
|
conditions := []exp.Expression{}
|
||||||
// 表达式
|
// 表达式
|
||||||
for _, item := range this.exprs {
|
for _, item := range this.exprs {
|
||||||
if item.IsIgnoreEmptyParma(m) {
|
if item.IsIgnoreEmptyParma(m) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
conditions = append(conditions, string(item.ToSql(m)))
|
conditions = append(conditions, item.ToSql(m))
|
||||||
}
|
}
|
||||||
// 条件子集
|
// 条件子集
|
||||||
for _, item := range this.childrens {
|
for _, item := range this.childrens {
|
||||||
conditions = append(conditions, string(item.ToSql(m)))
|
conditions = append(conditions, item.ToSql(m))
|
||||||
}
|
}
|
||||||
// 去除无效的表达式
|
|
||||||
conditions = lo.Filter(conditions, func(item string, _ int) bool { return item != "" })
|
|
||||||
// 组合 SQL
|
|
||||||
sql := strings.Join(conditions, " "+string(this.Type())+" ")
|
|
||||||
|
|
||||||
if sql == "" {
|
if this.IsEmpty() {
|
||||||
if this.parent == nil && this.Type() == OR {
|
if this.parent == nil && this.Type() == OR {
|
||||||
return db.Raw("false")
|
return db.V(false)
|
||||||
} else if this.parent == nil && this.Type() == AND {
|
} else if this.parent == nil && this.Type() == AND {
|
||||||
return db.Raw("true")
|
return db.V(true)
|
||||||
}
|
}
|
||||||
return db.Raw("")
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
// 包裹 SQL, 避免语法表达错误
|
|
||||||
if this.parent == nil || this.Type() == OR {
|
|
||||||
sql = "(" + sql + ")"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if this.describe != "" {
|
if this.describe != "" {
|
||||||
sql += "/*" + this.describe + "*/"
|
return db.L("? /* ? */", exp.NewExpressionList(this.typ, conditions...), db.L(this.describe))
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.Raw(sql)
|
return exp.NewExpressionList(this.typ, conditions...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置条件表达式
|
// 设置条件表达式
|
||||||
|
|||||||
+94
-98
@@ -7,13 +7,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.fsdpf.net/go/db"
|
"git.fsdpf.net/go/db"
|
||||||
"git.fsdpf.net/go/req"
|
"git.fsdpf.net/go/db/exp"
|
||||||
"github.com/samber/lo"
|
"git.fsdpf.net/go/db/sqlgen"
|
||||||
"github.com/spf13/cast"
|
"git.fsdpf.net/go/reflux/valuex"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TokenType string
|
type TokenType string
|
||||||
type ConditionOperator string
|
type ConditionOperator = exp.BooleanOperation
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SQL TokenType = "sql"
|
SQL TokenType = "sql"
|
||||||
@@ -23,29 +23,24 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IS_NULL ConditionOperator = "IS NULL"
|
IS_NULL ConditionOperator = exp.IsOp
|
||||||
IS_NOT_NULL ConditionOperator = "IS NOT NULL"
|
IS_NOT_NULL ConditionOperator = exp.IsNotOp
|
||||||
EQ ConditionOperator = "="
|
EQ ConditionOperator = exp.EqOp // =
|
||||||
NE ConditionOperator = "!="
|
NE ConditionOperator = exp.NeqOp // != or <>
|
||||||
GT ConditionOperator = ">"
|
GT ConditionOperator = exp.GtOp // >
|
||||||
GE ConditionOperator = ">="
|
GE ConditionOperator = exp.GteOp // >=
|
||||||
LT ConditionOperator = "<"
|
LT ConditionOperator = exp.LtOp // <
|
||||||
LE ConditionOperator = "<="
|
LE ConditionOperator = exp.LteOp // <=
|
||||||
LIKE ConditionOperator = "LIKE"
|
LIKE ConditionOperator = exp.LikeOp // LIKE
|
||||||
NOT_LIKE ConditionOperator = "NOT LIKE"
|
NOT_LIKE ConditionOperator = exp.NotLikeOp // NOT LIKE
|
||||||
IN ConditionOperator = "IN"
|
IN ConditionOperator = exp.InOp // IN
|
||||||
NOT_IN ConditionOperator = "NOT IN"
|
NOT_IN ConditionOperator = exp.NotInOp // NOT IN
|
||||||
REGEXP ConditionOperator = "REGEXP"
|
REGEXP ConditionOperator = exp.RegexpILikeOp // REGEXP
|
||||||
NOT_REGEXP ConditionOperator = "NOT REGEXP"
|
NOT_REGEXP ConditionOperator = exp.RegexpNotILikeOp // NOT REGEXP
|
||||||
)
|
)
|
||||||
|
|
||||||
type ExprOption func(option *ConditionExpr)
|
type ExprOption func(option *ConditionExpr)
|
||||||
|
|
||||||
type TokenValue interface {
|
|
||||||
GetParam(k string) req.GlobalParams
|
|
||||||
GetGlobalParamsUser() req.User
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConditionExpr struct {
|
type ConditionExpr struct {
|
||||||
parent *Condition
|
parent *Condition
|
||||||
operator ConditionOperator
|
operator ConditionOperator
|
||||||
@@ -81,66 +76,43 @@ func (this *ConditionExpr) AppendTo(c *Condition) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this ConditionExpr) ToSql(m TokenValue) db.Expression {
|
func (this ConditionExpr) ToSql(m TokenValue) db.Expression {
|
||||||
first := "`" + this.fieldResource + "`.`" + this.field + "`"
|
var first exp.Expression = db.T(this.fieldResource).Col(this.field)
|
||||||
|
|
||||||
if strings.Contains(this.field, "->") {
|
if strings.Contains(this.field, "->") {
|
||||||
first = "`" + this.fieldResource + "`." + this.field + ""
|
first = db.L("?."+this.field+"", db.T(this.fieldResource))
|
||||||
}
|
}
|
||||||
|
|
||||||
value := this.GetTokenSqlValue(m)
|
var value db.Expression = this.GetTokenSqlValue(m)
|
||||||
|
|
||||||
operator := ConditionOperator(strings.ToUpper(string(this.GetOperator())))
|
|
||||||
|
|
||||||
if value == "" {
|
|
||||||
// @todo return true
|
|
||||||
// value = "''"
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
secondary := ""
|
|
||||||
switch operator {
|
|
||||||
case IS_NULL:
|
|
||||||
case IS_NOT_NULL:
|
|
||||||
|
|
||||||
secondary = ""
|
|
||||||
case EQ, NE,
|
|
||||||
GT, GE,
|
|
||||||
LT, LE,
|
|
||||||
REGEXP, NOT_REGEXP:
|
|
||||||
|
|
||||||
if this.GetTokenType() == SQL {
|
|
||||||
secondary = value
|
|
||||||
} else {
|
|
||||||
secondary = "'" + strings.Trim(value, "'") + "'"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
switch this.GetOperator() {
|
||||||
case LIKE, NOT_LIKE:
|
case LIKE, NOT_LIKE:
|
||||||
secondary = "'%" + strings.Trim(value, "'") + "%'"
|
value = exp.NewSQLFunctionExpression("CONCAT", "%", value, "%")
|
||||||
case IN, NOT_IN:
|
|
||||||
secondary = "(" + lo.Ternary(value == "", "''", value) + ")"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if this.fieldSqlFunc == "json_member_of" {
|
if this.fieldSqlFunc == "" {
|
||||||
if this.fieldSqlFuncParam == "" {
|
return exp.NewBooleanExpression(this.GetOperator(), first, value)
|
||||||
return db.Raw(fmt.Sprintf("JSON_CONTAINS(%s, JSON_ARRAY(%s))", secondary, first))
|
|
||||||
// return db.Raw(fmt.Sprintf("%s MEMBER OF(%s)", secondary, first))
|
|
||||||
} else {
|
|
||||||
return db.Raw(fmt.Sprintf("JSON_CONTAINS(%s->>'%s', JSON_ARRAY(%s))", secondary, this.fieldSqlFuncParam, first))
|
|
||||||
// return db.Raw(fmt.Sprintf("%s MEMBER OF(%s->'%s')", secondary, first, this.FieldSqlFuncParam))
|
|
||||||
}
|
|
||||||
} else if this.fieldSqlFunc == "json_contains" {
|
|
||||||
if this.fieldSqlFuncParam == "" {
|
|
||||||
return db.Raw(fmt.Sprintf("JSON_CONTAINS(%s, JSON_ARRAY(%s))", first, secondary))
|
|
||||||
} else {
|
|
||||||
return db.Raw(fmt.Sprintf("JSON_CONTAINS(%s->>'%s', JSON_ARRAY(%s))", first, this.fieldSqlFuncParam, secondary))
|
|
||||||
}
|
|
||||||
} else if this.fieldSqlFunc != "" && this.fieldSqlFuncParam != "" {
|
|
||||||
first = this.fieldSqlFunc + "(" + first + ", " + this.fieldSqlFuncParam + ")"
|
|
||||||
} else if this.fieldSqlFunc != "" {
|
|
||||||
first = this.fieldSqlFunc + "(" + first + ")"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.Raw(strings.Trim(first+" "+string(operator)+" "+secondary, " "))
|
switch strings.ToLower(this.fieldSqlFunc) {
|
||||||
|
case "json_member_of":
|
||||||
|
if this.fieldSqlFuncParam != "" {
|
||||||
|
return exp.NewSQLFunctionExpression("JSON_CONTAINS", db.L(this.fieldSqlFuncParam, value), exp.NewSQLFunctionExpression("JSON_ARRAY", first))
|
||||||
|
}
|
||||||
|
return exp.NewSQLFunctionExpression("JSON_CONTAINS", value, exp.NewSQLFunctionExpression("JSON_ARRAY", first))
|
||||||
|
case "json_contains":
|
||||||
|
if this.fieldSqlFuncParam != "" {
|
||||||
|
return exp.NewSQLFunctionExpression("JSON_CONTAINS", db.L(this.fieldSqlFuncParam, first), exp.NewSQLFunctionExpression("JSON_ARRAY", value))
|
||||||
|
}
|
||||||
|
return exp.NewSQLFunctionExpression("JSON_CONTAINS", first, exp.NewSQLFunctionExpression("JSON_ARRAY", value))
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.fieldSqlFuncParam == "" {
|
||||||
|
first = exp.NewSQLFunctionExpression(this.fieldSqlFunc, first)
|
||||||
|
} else {
|
||||||
|
first = exp.NewSQLFunctionExpression(this.fieldSqlFunc, first, db.L(this.fieldSqlFuncParam))
|
||||||
|
}
|
||||||
|
|
||||||
|
return exp.NewBooleanExpression(this.GetOperator(), first, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this ConditionExpr) GetTokenName() string {
|
func (this ConditionExpr) GetTokenName() string {
|
||||||
@@ -151,12 +123,17 @@ func (this ConditionExpr) GetTokenType() TokenType {
|
|||||||
return this.tokenType
|
return this.tokenType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ConditionExpr) GetTokenSqlValue(m TokenValue) string {
|
func (this *ConditionExpr) GetTokenSqlValue(m TokenValue) exp.LiteralExpression {
|
||||||
if this.GetTokenType() == SQL {
|
if this.GetTokenType() == SQL {
|
||||||
return this.token
|
return db.L(this.token)
|
||||||
}
|
}
|
||||||
|
|
||||||
rv := reflect.ValueOf(this.GetTokenValue(m))
|
if this.operator == IS_NULL || this.operator == IS_NOT_NULL {
|
||||||
|
return db.V(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
v := this.GetTokenValue(m)
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
|
||||||
if rv.Kind() == reflect.Ptr {
|
if rv.Kind() == reflect.Ptr {
|
||||||
rv = reflect.Indirect(rv)
|
rv = reflect.Indirect(rv)
|
||||||
@@ -164,51 +141,54 @@ func (this *ConditionExpr) GetTokenSqlValue(m TokenValue) string {
|
|||||||
|
|
||||||
switch rv.Kind() {
|
switch rv.Kind() {
|
||||||
case reflect.Invalid:
|
case reflect.Invalid:
|
||||||
return ""
|
return db.V("")
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
aStr := cast.ToStringSlice(rv.Interface())
|
|
||||||
|
|
||||||
for i := 0; i < len(aStr); i++ {
|
|
||||||
if aStr[i] != "" {
|
|
||||||
aStr[i] = "'" + strings.Trim(db.MysqlRealEscapeString(aStr[i]), "'") + "'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 强制使用 in
|
// 强制使用 in
|
||||||
if this.operator == EQ {
|
if this.operator == EQ {
|
||||||
this.operator = IN
|
this.operator = IN
|
||||||
}
|
}
|
||||||
|
return db.V(v)
|
||||||
return strings.Join(aStr, ", ")
|
case reflect.String:
|
||||||
|
if this.operator == IN || this.operator == NOT_IN {
|
||||||
|
parts := strings.Split(rv.String(), ",")
|
||||||
|
for i, p := range parts {
|
||||||
|
parts[i] = strings.TrimSpace(p)
|
||||||
|
}
|
||||||
|
return db.V(parts)
|
||||||
|
}
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
b, _ := json.Marshal(rv.Interface())
|
b, _ := json.Marshal(v)
|
||||||
return fmt.Sprintf("'%s'", b)
|
return db.V(b)
|
||||||
default:
|
|
||||||
return db.MysqlRealEscapeString(fmt.Sprintf("%v", rv.Interface()))
|
|
||||||
}
|
}
|
||||||
|
return db.V(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this ConditionExpr) GetTokenValue(m TokenValue) any {
|
func (this ConditionExpr) GetTokenValue(m TokenValue) any {
|
||||||
switch this.GetTokenType() {
|
switch this.GetTokenType() {
|
||||||
case PARAM:
|
case PARAM:
|
||||||
|
var result valuex.Accessor
|
||||||
|
|
||||||
if this.matchPrefix != "" {
|
if this.matchPrefix != "" {
|
||||||
return m.GetParam(fmt.Sprintf("%s.%s", this.matchPrefix, this.token)).Value()
|
result = m.GetParam(fmt.Sprintf("%s.%s", this.matchPrefix, this.token))
|
||||||
|
} else {
|
||||||
|
result = m.GetParam(this.token)
|
||||||
}
|
}
|
||||||
return m.GetParam(this.token).Value()
|
|
||||||
|
return result.Any()
|
||||||
case STRING:
|
case STRING:
|
||||||
return this.token
|
return this.token
|
||||||
case FUNC:
|
case FUNC:
|
||||||
switch this.token {
|
switch this.token {
|
||||||
case "UserID":
|
case "UserID":
|
||||||
return m.GetGlobalParamsUser().ID()
|
return m.User().ID()
|
||||||
case "UserUuid":
|
case "UserUuid":
|
||||||
return m.GetGlobalParamsUser().Uuid()
|
return m.User().Uuid()
|
||||||
case "UserRolesUuid":
|
case "UserRolesUuid":
|
||||||
return m.GetGlobalParamsUser().Roles()
|
return m.User().Roles()
|
||||||
case "UserPlatform":
|
case "UserPlatform":
|
||||||
return m.GetGlobalParamsUser().Runtime().Platform()
|
return m.User().Runtime().Platform()
|
||||||
case "UserSaaS":
|
case "UserSaaS":
|
||||||
return m.GetGlobalParamsUser().Runtime().SaaS()
|
return m.User().Runtime().SaaS()
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
@@ -247,6 +227,12 @@ func Operator(v ConditionOperator) ExprOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StringOperator(op string) ExprOption {
|
||||||
|
return func(option *ConditionExpr) {
|
||||||
|
option.operator = ToConditionOperator(op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Token(token string, tType TokenType) ExprOption {
|
func Token(token string, tType TokenType) ExprOption {
|
||||||
return func(option *ConditionExpr) {
|
return func(option *ConditionExpr) {
|
||||||
option.token = token
|
option.token = token
|
||||||
@@ -281,3 +267,13 @@ func NewExpr(rResource, rField string, opts ...ExprOption) *ConditionExpr {
|
|||||||
expr.SetOption(opts...)
|
expr.SetOption(opts...)
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetDBOperator(op string) ConditionOperator {
|
||||||
|
ops := sqlgen.DefaultDialectOptions().BooleanOperatorLookup
|
||||||
|
for t, v := range ops {
|
||||||
|
if string(v) == strings.ToUpper(op) {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exp.EqOp
|
||||||
|
}
|
||||||
|
|||||||
+71
-70
@@ -4,16 +4,21 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strconv"
|
||||||
|
|
||||||
"git.fsdpf.net/go/condition"
|
"git.fsdpf.net/go/condition"
|
||||||
"git.fsdpf.net/go/db"
|
"git.fsdpf.net/go/db"
|
||||||
|
_ "git.fsdpf.net/go/db/dialect/sqlite3"
|
||||||
|
db_eng "git.fsdpf.net/go/db/engine"
|
||||||
|
"git.fsdpf.net/go/db/exp"
|
||||||
|
"git.fsdpf.net/go/reflux"
|
||||||
"git.fsdpf.net/go/req"
|
"git.fsdpf.net/go/req"
|
||||||
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
)
|
)
|
||||||
|
|
||||||
var conn *db.Connection
|
var conn *db.Database
|
||||||
|
|
||||||
var defaultEngineOptions = engineOptions{
|
var defaultEngineOptions = engineOptions{
|
||||||
debug: false,
|
debug: false,
|
||||||
@@ -21,76 +26,73 @@ var defaultEngineOptions = engineOptions{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
database := db.Open(map[string]db.DBConfig{
|
database := db_eng.Open(map[string]db_eng.DBConfig{
|
||||||
"condition-engine-sqlite3": {
|
"condition-engine-sqlite3": db_eng.NewDBConfig("sqlite3", db_eng.WithSQLiteFile(":memory:")),
|
||||||
Driver: "sqlite3",
|
|
||||||
File: ":memory:",
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
conn = database.Connection("condition-engine-sqlite3")
|
conn = database.Connection("condition-engine-sqlite3")
|
||||||
}
|
}
|
||||||
|
|
||||||
type Engine[T any] struct {
|
type Engine struct {
|
||||||
code string
|
code string
|
||||||
opts engineOptions
|
opts engineOptions
|
||||||
g req.GlobalParams
|
g req.UserAccessor
|
||||||
def func(data T, g req.GlobalParams) error
|
def func(data reflux.R, g req.UserAccessor) error
|
||||||
predicates []*EngineCase[T]
|
predicates []*EngineCase
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this Engine[T]) GetCode() string {
|
func (this Engine) GetCode() string {
|
||||||
return this.code
|
return this.code
|
||||||
}
|
}
|
||||||
|
|
||||||
// 公共参数
|
// 公共参数
|
||||||
func (this *Engine[T]) SetGlobalParams(g req.GlobalParams) *Engine[T] {
|
func (this *Engine) SetGlobalParams(g req.UserAccessor) *Engine {
|
||||||
this.g = g
|
this.g = g
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Engine[T]) Case(cond *condition.Condition, cb func(data T, g req.GlobalParams) error) *Engine[T] {
|
func (this *Engine) Case(cond *condition.Condition, cb func(data reflux.R, g req.UserAccessor) error) *Engine {
|
||||||
this.predicates = append(this.predicates, &EngineCase[T]{cond, cb})
|
this.predicates = append(this.predicates, &EngineCase{cond, cb})
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
// 基础条件
|
// 基础条件
|
||||||
func (this *Engine[T]) Default(cb func(data T, g req.GlobalParams) error) *Engine[T] {
|
func (this *Engine) Default(cb func(data reflux.R, g req.UserAccessor) error) *Engine {
|
||||||
this.def = cb
|
this.def = cb
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this Engine[T]) toField(rv reflect.Value) (string, error) {
|
func (this Engine) toField(rv reflect.Value, as string) (db.Expression, error) {
|
||||||
|
if rv.IsZero() {
|
||||||
|
return db.V(nil).As(as), nil
|
||||||
|
}
|
||||||
|
if rv.Kind() == reflect.Interface {
|
||||||
|
rv = rv.Elem()
|
||||||
|
}
|
||||||
if rv.Kind() == reflect.Ptr {
|
if rv.Kind() == reflect.Ptr {
|
||||||
rv = reflect.Indirect(rv)
|
rv = reflect.Indirect(rv)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rv.IsZero() {
|
|
||||||
return "NULL", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
v := rv.Interface()
|
v := rv.Interface()
|
||||||
|
|
||||||
switch reflect.TypeOf(v).Kind() {
|
switch reflect.TypeOf(v).Kind() {
|
||||||
case reflect.Map, reflect.Array, reflect.Slice, reflect.Struct:
|
case reflect.Map, reflect.Array, reflect.Slice, reflect.Struct:
|
||||||
b, err := json.Marshal(v)
|
b, err := json.Marshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return exp.NewSQLFunctionExpression("JSON", nil).As(as), err
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("CAST('%s' AS JSON1)", b), nil
|
return exp.NewSQLFunctionExpression("JSON", string(b)).As(as), err
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
return fmt.Sprintf("CAST('%s' AS TEXT)", v), nil
|
return db.Cast(db.V(v), "TEXT").As(as), nil
|
||||||
case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int8, reflect.Int16,
|
case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int8, reflect.Int16,
|
||||||
reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint8, reflect.Uint16:
|
reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint8, reflect.Uint16:
|
||||||
return fmt.Sprintf("CAST(%d AS INTEGER)", v), nil
|
return db.Cast(db.V(v), "INTEGER").As(as), nil
|
||||||
case reflect.Float64, reflect.Float32:
|
case reflect.Float64, reflect.Float32:
|
||||||
return fmt.Sprintf("CAST(%v AS REAL)", v), nil
|
return db.Cast(db.V(v), "REAL").As(as), nil
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
return fmt.Sprintf("CAST(%v AS BOOL)", v), nil
|
return db.Cast(db.V(v), "BOOL").As(as), nil
|
||||||
}
|
}
|
||||||
return "NULL", nil
|
return db.V(nil).As(as), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this Engine[T]) toTables(rv reflect.Value, table string) (tables map[string][]string, err error) {
|
func (this Engine) toTables(rv reflect.Value, table string) (tables map[string][]db.Expression, err error) {
|
||||||
rt := rv.Type()
|
rt := rv.Type()
|
||||||
|
|
||||||
if rv.Kind() == reflect.Ptr {
|
if rv.Kind() == reflect.Ptr {
|
||||||
@@ -99,11 +101,11 @@ func (this Engine[T]) toTables(rv reflect.Value, table string) (tables map[strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tables == nil {
|
if tables == nil {
|
||||||
tables = map[string][]string{}
|
tables = map[string][]db.Expression{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := tables[table]; !ok {
|
if _, ok := tables[table]; !ok {
|
||||||
tables[table] = []string{}
|
tables[table] = []db.Expression{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rv.Kind() == reflect.Struct {
|
if rv.Kind() == reflect.Struct {
|
||||||
@@ -115,10 +117,10 @@ func (this Engine[T]) toTables(rv reflect.Value, table string) (tables map[strin
|
|||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else if s, err := this.toField(rv.Field(i)); err != nil {
|
} else if col, err := this.toField(rv.Field(i), lo.Ternary(rt.Field(i).Tag.Get("db") != "", rt.Field(i).Tag.Get("db"), rt.Field(i).Name)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
tables[table] = append(tables[table], fmt.Sprintf("%s as `%s`", s, lo.Ternary(rt.Field(i).Tag.Get("db") != "", rt.Field(i).Tag.Get("db"), rt.Field(i).Name)))
|
tables[table] = append(tables[table], col)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if rv.Kind() == reflect.Map {
|
} else if rv.Kind() == reflect.Map {
|
||||||
@@ -130,10 +132,10 @@ func (this Engine[T]) toTables(rv reflect.Value, table string) (tables map[strin
|
|||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else if s, err := this.toField(iter.Value()); err != nil {
|
} else if col, err := this.toField(iter.Value(), iter.Key().String()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
tables[table] = append(tables[table], fmt.Sprintf("%s as `%s`", s, iter.Key().String()))
|
tables[table] = append(tables[table], col)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -143,66 +145,61 @@ func (this Engine[T]) toTables(rv reflect.Value, table string) (tables map[strin
|
|||||||
return tables, nil
|
return tables, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Engine[T]) Execute(data T) error {
|
func (this *Engine) Execute(data any) error {
|
||||||
|
rfx := reflux.New(data)
|
||||||
|
|
||||||
tables, err := this.toTables(reflect.ValueOf(data), this.GetCode())
|
tables, err := this.toTables(rfx.Raw(), this.GetCode())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
columns, ok := tables[this.GetCode()]
|
ts := []any{}
|
||||||
|
// 加载数据
|
||||||
if !ok {
|
for table, columns := range tables {
|
||||||
return fmt.Errorf("data is not")
|
ts = append(ts, db.Select(this.toSelectCols(columns)...).As(table))
|
||||||
}
|
|
||||||
|
|
||||||
sql := conn.Query().FromSub(fmt.Sprintf("SELECT %s", strings.Join(append(columns, "1 as `_`"), ", ")), this.GetCode())
|
|
||||||
|
|
||||||
for table, columns := range lo.OmitByKeys(tables, []string{this.GetCode()}) {
|
|
||||||
sql.JoinSub(
|
|
||||||
fmt.Sprintf("SELECT %s", strings.Join(append(columns, "1 as `_`"), ", ")),
|
|
||||||
table,
|
|
||||||
fmt.Sprintf("%s._", this.GetCode()),
|
|
||||||
fmt.Sprintf("%s._", table),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
_db := conn.From(ts...)
|
||||||
|
|
||||||
if this.g == nil {
|
if this.g == nil {
|
||||||
this.g = req.NewGlobalParam(`{}`, nil)
|
this.g = req.NewGlobalParams(`{}`, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
param := &EngineParam{this.g}
|
param := condition.NewTokenValue(this.g)
|
||||||
|
|
||||||
|
cols := []any{}
|
||||||
|
// 加载条件
|
||||||
for i, p := range this.predicates {
|
for i, p := range this.predicates {
|
||||||
sql.AddSelect(db.Raw(fmt.Sprintf("IFNULL(%s, 0) as `%d`", p.ToSql(param), i)))
|
cols = append(cols, exp.NewSQLFunctionExpression("IFNULL", p.ToSql(param), 0).As(strconv.Itoa(i)))
|
||||||
}
|
}
|
||||||
|
_db = _db.Select(cols...)
|
||||||
result := map[string]int64{}
|
|
||||||
|
|
||||||
if this.opts.debug {
|
if this.opts.debug {
|
||||||
fmt.Println("sql", sql.ToSql())
|
fmt.Println(_db.ToSQL())
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := sql.First(&result); err != nil {
|
result, err := _db.Executor().GetRecord()
|
||||||
return fmt.Errorf("%s => %s", err, sql.ToSql())
|
if err != nil {
|
||||||
|
sql, _, _ := _db.ToSQL()
|
||||||
|
return fmt.Errorf("%s => %s", err, sql)
|
||||||
}
|
}
|
||||||
|
|
||||||
isDefault := true
|
isDefault := true
|
||||||
for k, v := range result {
|
for fnIndex, v := range result {
|
||||||
if v == 0 {
|
b := cast.ToInt64(v)
|
||||||
|
if b == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
isDefault = false
|
isDefault = false
|
||||||
|
|
||||||
if err := this.predicates[cast.ToInt64(k)].Execute(data, this.g); err != nil {
|
if err := this.predicates[cast.ToInt64(fnIndex)].Execute(rfx, this.g); err != nil {
|
||||||
return fmt.Errorf("case %q error, %s", k, err)
|
return fmt.Errorf("case %q error, %s", fnIndex, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isDefault && this.def != nil {
|
if isDefault && this.def != nil {
|
||||||
if err := this.def(data, this.g); err != nil {
|
if err := this.def(rfx, this.g); err != nil {
|
||||||
return fmt.Errorf("case %q error, %s", "default", err)
|
return fmt.Errorf("case %q error, %s", "default", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,16 +207,20 @@ func (this *Engine[T]) Execute(data T) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func New[T any](table string, opt ...EngineOption) *Engine[T] {
|
func (this *Engine) toSelectCols(cols []db.Expression) []any {
|
||||||
|
return lo.Map(cols, func(item db.Expression, _ int) any { return item })
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(table string, opt ...EngineOption) *Engine {
|
||||||
opts := defaultEngineOptions
|
opts := defaultEngineOptions
|
||||||
|
|
||||||
for _, o := range opt {
|
for _, o := range opt {
|
||||||
o.apply(&opts)
|
o.apply(&opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Engine[T]{
|
return &Engine{
|
||||||
code: table,
|
code: table,
|
||||||
opts: opts,
|
opts: opts,
|
||||||
g: req.NewGlobalParam("", nil),
|
// g: req.NewParam("", nil),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,22 +3,23 @@ package engine
|
|||||||
import (
|
import (
|
||||||
"git.fsdpf.net/go/condition"
|
"git.fsdpf.net/go/condition"
|
||||||
"git.fsdpf.net/go/db"
|
"git.fsdpf.net/go/db"
|
||||||
|
"git.fsdpf.net/go/reflux"
|
||||||
"git.fsdpf.net/go/req"
|
"git.fsdpf.net/go/req"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EngineCase[T any] struct {
|
type EngineCase struct {
|
||||||
predicate *condition.Condition
|
predicate *condition.Condition
|
||||||
cb func(data T, g req.GlobalParams) error
|
cb func(data reflux.R, g req.UserAccessor) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this EngineCase[T]) ToSql(param condition.TokenValue) db.Expression {
|
func (this EngineCase) ToSql(param condition.TokenValue) db.Expression {
|
||||||
if this.predicate == nil || this.predicate.IsEmpty() {
|
if this.predicate == nil || this.predicate.IsEmpty() {
|
||||||
return db.Raw("NULL")
|
return db.V("NULL")
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.predicate.ToSql(param)
|
return this.predicate.ToSql(param)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this EngineCase[T]) Execute(data T, g req.GlobalParams) error {
|
func (this EngineCase) Execute(data reflux.R, g req.UserAccessor) error {
|
||||||
return this.cb(data, g)
|
return this.cb(data, g)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package engine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.fsdpf.net/go/req"
|
|
||||||
)
|
|
||||||
|
|
||||||
type EngineParam struct {
|
|
||||||
data req.GlobalParams
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this EngineParam) GetParam(k string) req.GlobalParams {
|
|
||||||
return this.data.Get(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this EngineParam) GetGlobalParamsUser() req.User {
|
|
||||||
return this.data.User()
|
|
||||||
}
|
|
||||||
+35
-20
@@ -4,6 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.fsdpf.net/go/condition"
|
"git.fsdpf.net/go/condition"
|
||||||
|
"git.fsdpf.net/go/reflux"
|
||||||
"git.fsdpf.net/go/req"
|
"git.fsdpf.net/go/req"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,26 +19,40 @@ type TestTable struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEngine(t *testing.T) {
|
func TestEngine(t *testing.T) {
|
||||||
engine := Engine[any]{
|
engine := Engine{
|
||||||
code: "TestTable",
|
code: "TestTable",
|
||||||
g: req.NewGlobalParam(`{"age": 30}`, nil),
|
g: req.NewGlobalParams(`{"age": 30}`, nil),
|
||||||
}
|
}
|
||||||
|
|
||||||
cond1 := condition.New(condition.Describe("条件1"))
|
cond1 := condition.New(condition.Describe("条件1"))
|
||||||
|
|
||||||
cond1.SetExpr(condition.NewExpr("TestTable", "age", condition.Token("age", condition.PARAM)))
|
cond1.SetExpr(condition.NewExpr("TestTable", "age", condition.Token("age", condition.PARAM)))
|
||||||
|
|
||||||
engine.Case(cond1, func(data any, g req.GlobalParams) error {
|
engine.Case(cond1, func(data reflux.R, g req.UserAccessor) error {
|
||||||
t.Log("cond1", data)
|
t.Log("cond1", data.Any())
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
engine.Default(func(data any, g req.GlobalParams) error {
|
engine.Default(func(data reflux.R, g req.UserAccessor) error {
|
||||||
t.Log("default", data)
|
t.Log("default", data.Any())
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
engine.Execute(map[string]any{
|
testData := map[string]any{
|
||||||
|
"null": nil,
|
||||||
|
"age": 30,
|
||||||
|
"name": "张三",
|
||||||
|
"map": map[string]any{
|
||||||
|
"field": 1,
|
||||||
|
},
|
||||||
|
"bool": false,
|
||||||
|
"struct": struct{ Name string }{"李四"},
|
||||||
|
"struct-ptr": &struct{ Name string }{"张三"},
|
||||||
|
"array": []any{1, "a", false},
|
||||||
|
}
|
||||||
|
engine.Execute(testData)
|
||||||
|
|
||||||
|
testData2 := map[string]any{
|
||||||
"null": nil,
|
"null": nil,
|
||||||
"age": 30,
|
"age": 30,
|
||||||
"name": "张三",
|
"name": "张三",
|
||||||
@@ -47,15 +62,14 @@ func TestEngine(t *testing.T) {
|
|||||||
"bool": false,
|
"bool": false,
|
||||||
"struct": &struct{ Name string }{"李四"},
|
"struct": &struct{ Name string }{"李四"},
|
||||||
"array": []any{1, "a", false},
|
"array": []any{1, "a", false},
|
||||||
})
|
}
|
||||||
|
t.Log("execute error", engine.Execute(testData2))
|
||||||
t.Log("execute error", engine.Execute(TestTable{nil, 30, "张三", map[string]any{"field": 1}, false, &struct{ Name string }{"李四"}, []any{1, "a", false}}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRelationEngine(t *testing.T) {
|
func TestRelationEngine(t *testing.T) {
|
||||||
engine := Engine[any]{
|
engine := Engine{
|
||||||
code: "TestTable",
|
code: "TestTable",
|
||||||
g: req.NewGlobalParam(`{"age": 30}`, nil),
|
g: req.NewGlobalParams(`{"age": 30}`, nil),
|
||||||
opts: engineOptions{
|
opts: engineOptions{
|
||||||
debug: false,
|
debug: false,
|
||||||
relations: []string{"TestTableA"},
|
relations: []string{"TestTableA"},
|
||||||
@@ -65,8 +79,8 @@ func TestRelationEngine(t *testing.T) {
|
|||||||
cond1 := condition.New(condition.Describe("条件1")).
|
cond1 := condition.New(condition.Describe("条件1")).
|
||||||
SetExpr(condition.NewExpr("TestTable", "age", condition.Token("age", condition.PARAM)))
|
SetExpr(condition.NewExpr("TestTable", "age", condition.Token("age", condition.PARAM)))
|
||||||
|
|
||||||
engine.Case(cond1, func(data any, g req.GlobalParams) error {
|
engine.Case(cond1, func(data reflux.R, g req.UserAccessor) error {
|
||||||
t.Log("cond1", data)
|
t.Log("cond1", data.Any())
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -74,17 +88,17 @@ func TestRelationEngine(t *testing.T) {
|
|||||||
SetExpr(condition.NewExpr("TestTableA", "age", condition.Token("age", condition.PARAM))).
|
SetExpr(condition.NewExpr("TestTableA", "age", condition.Token("age", condition.PARAM))).
|
||||||
SetExpr(condition.NewExpr("TestTable", "age", condition.Token("age", condition.PARAM)))
|
SetExpr(condition.NewExpr("TestTable", "age", condition.Token("age", condition.PARAM)))
|
||||||
|
|
||||||
engine.Case(cond2, func(data any, g req.GlobalParams) error {
|
engine.Case(cond2, func(data reflux.R, g req.UserAccessor) error {
|
||||||
t.Log("cond2", data)
|
t.Log("cond2", data.Any())
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
engine.Default(func(data any, g req.GlobalParams) error {
|
engine.Default(func(data reflux.R, g req.UserAccessor) error {
|
||||||
t.Log("default", data)
|
t.Log("default", data.Any())
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
engine.Execute(map[string]any{
|
testData := map[string]any{
|
||||||
"null": nil,
|
"null": nil,
|
||||||
"age": 31,
|
"age": 31,
|
||||||
"name": "张三",
|
"name": "张三",
|
||||||
@@ -100,7 +114,8 @@ func TestRelationEngine(t *testing.T) {
|
|||||||
"age": 22,
|
"age": 22,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
|
engine.Execute(testData)
|
||||||
|
|
||||||
// t.Log("execute error", engine.Execute(TestTable{nil, 30, "张三", map[string]any{"field": 1}, false, &struct{ Name string }{"李四"}, []any{1, "a", false}}))
|
// t.Log("execute error", engine.Execute(TestTable{nil, 30, "张三", map[string]any{"field": 1}, false, &struct{ Name string }{"李四"}, []any{1, "a", false}}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package condition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.fsdpf.net/go/reflux/valuex"
|
||||||
|
"git.fsdpf.net/go/req"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenValue interface {
|
||||||
|
GetParam(k string) valuex.Accessor
|
||||||
|
User() req.User
|
||||||
|
}
|
||||||
|
|
||||||
|
type tValueOpt func(option *tValue)
|
||||||
|
|
||||||
|
func WithUser(user req.User) tValueOpt {
|
||||||
|
return func(option *tValue) {
|
||||||
|
option.user = user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTokenValue(x valuex.Accessor, opts ...tValueOpt) TokenValue {
|
||||||
|
tv := &tValue{src: x}
|
||||||
|
if x == nil {
|
||||||
|
tv.src = valuex.Nil
|
||||||
|
} else if up, ok := x.(interface{ User() req.User }); ok {
|
||||||
|
tv.user = up.User()
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
if opt != nil {
|
||||||
|
opt(tv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tv.user == nil {
|
||||||
|
panic("user is required")
|
||||||
|
}
|
||||||
|
return tv
|
||||||
|
}
|
||||||
|
|
||||||
|
type tValue struct {
|
||||||
|
src valuex.Accessor
|
||||||
|
user req.User
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *tValue) GetParam(k string) valuex.Accessor {
|
||||||
|
if v, ok := this.src.Lookup(k); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return valuex.Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *tValue) User() req.User {
|
||||||
|
return this.user
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package condition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ToConditionOperator(op string) ConditionOperator {
|
||||||
|
switch strings.ToUpper(op) {
|
||||||
|
case "IS NULL":
|
||||||
|
return IS_NULL
|
||||||
|
case "IS NOT NULL":
|
||||||
|
return IS_NOT_NULL
|
||||||
|
case "=":
|
||||||
|
return EQ
|
||||||
|
case "!=", "<>":
|
||||||
|
return NE
|
||||||
|
case ">":
|
||||||
|
return GT
|
||||||
|
case ">=":
|
||||||
|
return GE
|
||||||
|
case "<":
|
||||||
|
return LT
|
||||||
|
case "<=":
|
||||||
|
return LE
|
||||||
|
case "LIKE":
|
||||||
|
return LIKE
|
||||||
|
case "NOT LIKE":
|
||||||
|
return NOT_LIKE
|
||||||
|
case "IN":
|
||||||
|
return IN
|
||||||
|
case "NOT IN":
|
||||||
|
return NOT_IN
|
||||||
|
case "REGEXP":
|
||||||
|
return REGEXP
|
||||||
|
case "NOT REGEXP":
|
||||||
|
return NOT_REGEXP
|
||||||
|
}
|
||||||
|
return EQ
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user