386 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			386 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package clause
 | |
| 
 | |
| import (
 | |
| 	"database/sql"
 | |
| 	"database/sql/driver"
 | |
| 	"go/ast"
 | |
| 	"reflect"
 | |
| )
 | |
| 
 | |
| // Expression expression interface
 | |
| type Expression interface {
 | |
| 	Build(builder Builder)
 | |
| }
 | |
| 
 | |
| // NegationExpressionBuilder negation expression builder
 | |
| type NegationExpressionBuilder interface {
 | |
| 	NegationBuild(builder Builder)
 | |
| }
 | |
| 
 | |
| // Expr raw expression
 | |
| type Expr struct {
 | |
| 	SQL                string
 | |
| 	Vars               []interface{}
 | |
| 	WithoutParentheses bool
 | |
| }
 | |
| 
 | |
| // Build build raw expression
 | |
| func (expr Expr) Build(builder Builder) {
 | |
| 	var (
 | |
| 		afterParenthesis bool
 | |
| 		idx              int
 | |
| 	)
 | |
| 
 | |
| 	for _, v := range []byte(expr.SQL) {
 | |
| 		if v == '?' && len(expr.Vars) > idx {
 | |
| 			if afterParenthesis || expr.WithoutParentheses {
 | |
| 				if _, ok := expr.Vars[idx].(driver.Valuer); ok {
 | |
| 					builder.AddVar(builder, expr.Vars[idx])
 | |
| 				} else {
 | |
| 					switch rv := reflect.ValueOf(expr.Vars[idx]); rv.Kind() {
 | |
| 					case reflect.Slice, reflect.Array:
 | |
| 						if rv.Len() == 0 {
 | |
| 							builder.AddVar(builder, nil)
 | |
| 						} else {
 | |
| 							for i := 0; i < rv.Len(); i++ {
 | |
| 								if i > 0 {
 | |
| 									builder.WriteByte(',')
 | |
| 								}
 | |
| 								builder.AddVar(builder, rv.Index(i).Interface())
 | |
| 							}
 | |
| 						}
 | |
| 					default:
 | |
| 						builder.AddVar(builder, expr.Vars[idx])
 | |
| 					}
 | |
| 				}
 | |
| 			} else {
 | |
| 				builder.AddVar(builder, expr.Vars[idx])
 | |
| 			}
 | |
| 
 | |
| 			idx++
 | |
| 		} else {
 | |
| 			if v == '(' {
 | |
| 				afterParenthesis = true
 | |
| 			} else {
 | |
| 				afterParenthesis = false
 | |
| 			}
 | |
| 			builder.WriteByte(v)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if idx < len(expr.Vars) {
 | |
| 		for _, v := range expr.Vars[idx:] {
 | |
| 			builder.AddVar(builder, sql.NamedArg{Value: v})
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // NamedExpr raw expression for named expr
 | |
| type NamedExpr struct {
 | |
| 	SQL  string
 | |
| 	Vars []interface{}
 | |
| }
 | |
| 
 | |
| // Build build raw expression
 | |
| func (expr NamedExpr) Build(builder Builder) {
 | |
| 	var (
 | |
| 		idx              int
 | |
| 		inName           bool
 | |
| 		afterParenthesis bool
 | |
| 		namedMap         = make(map[string]interface{}, len(expr.Vars))
 | |
| 	)
 | |
| 
 | |
| 	for _, v := range expr.Vars {
 | |
| 		switch value := v.(type) {
 | |
| 		case sql.NamedArg:
 | |
| 			namedMap[value.Name] = value.Value
 | |
| 		case map[string]interface{}:
 | |
| 			for k, v := range value {
 | |
| 				namedMap[k] = v
 | |
| 			}
 | |
| 		default:
 | |
| 			var appendFieldsToMap func(reflect.Value)
 | |
| 			appendFieldsToMap = func(reflectValue reflect.Value) {
 | |
| 				reflectValue = reflect.Indirect(reflectValue)
 | |
| 				switch reflectValue.Kind() {
 | |
| 				case reflect.Struct:
 | |
| 					modelType := reflectValue.Type()
 | |
| 					for i := 0; i < modelType.NumField(); i++ {
 | |
| 						if fieldStruct := modelType.Field(i); ast.IsExported(fieldStruct.Name) {
 | |
| 							namedMap[fieldStruct.Name] = reflectValue.Field(i).Interface()
 | |
| 
 | |
| 							if fieldStruct.Anonymous {
 | |
| 								appendFieldsToMap(reflectValue.Field(i))
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			appendFieldsToMap(reflect.ValueOf(value))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	name := make([]byte, 0, 10)
 | |
| 
 | |
| 	for _, v := range []byte(expr.SQL) {
 | |
| 		if v == '@' && !inName {
 | |
| 			inName = true
 | |
| 			name = name[:0]
 | |
| 		} else if v == ' ' || v == ',' || v == ')' || v == '"' || v == '\'' || v == '`' || v == '\r' || v == '\n' || v == ';' {
 | |
| 			if inName {
 | |
| 				if nv, ok := namedMap[string(name)]; ok {
 | |
| 					builder.AddVar(builder, nv)
 | |
| 				} else {
 | |
| 					builder.WriteByte('@')
 | |
| 					builder.WriteString(string(name))
 | |
| 				}
 | |
| 				inName = false
 | |
| 			}
 | |
| 
 | |
| 			afterParenthesis = false
 | |
| 			builder.WriteByte(v)
 | |
| 		} else if v == '?' && len(expr.Vars) > idx {
 | |
| 			if afterParenthesis {
 | |
| 				if _, ok := expr.Vars[idx].(driver.Valuer); ok {
 | |
| 					builder.AddVar(builder, expr.Vars[idx])
 | |
| 				} else {
 | |
| 					switch rv := reflect.ValueOf(expr.Vars[idx]); rv.Kind() {
 | |
| 					case reflect.Slice, reflect.Array:
 | |
| 						if rv.Len() == 0 {
 | |
| 							builder.AddVar(builder, nil)
 | |
| 						} else {
 | |
| 							for i := 0; i < rv.Len(); i++ {
 | |
| 								if i > 0 {
 | |
| 									builder.WriteByte(',')
 | |
| 								}
 | |
| 								builder.AddVar(builder, rv.Index(i).Interface())
 | |
| 							}
 | |
| 						}
 | |
| 					default:
 | |
| 						builder.AddVar(builder, expr.Vars[idx])
 | |
| 					}
 | |
| 				}
 | |
| 			} else {
 | |
| 				builder.AddVar(builder, expr.Vars[idx])
 | |
| 			}
 | |
| 
 | |
| 			idx++
 | |
| 		} else if inName {
 | |
| 			name = append(name, v)
 | |
| 		} else {
 | |
| 			if v == '(' {
 | |
| 				afterParenthesis = true
 | |
| 			} else {
 | |
| 				afterParenthesis = false
 | |
| 			}
 | |
| 			builder.WriteByte(v)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if inName {
 | |
| 		if nv, ok := namedMap[string(name)]; ok {
 | |
| 			builder.AddVar(builder, nv)
 | |
| 		} else {
 | |
| 			builder.WriteByte('@')
 | |
| 			builder.WriteString(string(name))
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // IN Whether a value is within a set of values
 | |
| type IN struct {
 | |
| 	Column interface{}
 | |
| 	Values []interface{}
 | |
| }
 | |
| 
 | |
| func (in IN) Build(builder Builder) {
 | |
| 	builder.WriteQuoted(in.Column)
 | |
| 
 | |
| 	switch len(in.Values) {
 | |
| 	case 0:
 | |
| 		builder.WriteString(" IN (NULL)")
 | |
| 	case 1:
 | |
| 		if _, ok := in.Values[0].([]interface{}); !ok {
 | |
| 			builder.WriteString(" = ")
 | |
| 			builder.AddVar(builder, in.Values[0])
 | |
| 			break
 | |
| 		}
 | |
| 
 | |
| 		fallthrough
 | |
| 	default:
 | |
| 		builder.WriteString(" IN (")
 | |
| 		builder.AddVar(builder, in.Values...)
 | |
| 		builder.WriteByte(')')
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (in IN) NegationBuild(builder Builder) {
 | |
| 	builder.WriteQuoted(in.Column)
 | |
| 	switch len(in.Values) {
 | |
| 	case 0:
 | |
| 		builder.WriteString(" IS NOT NULL")
 | |
| 	case 1:
 | |
| 		if _, ok := in.Values[0].([]interface{}); !ok {
 | |
| 			builder.WriteString(" <> ")
 | |
| 			builder.AddVar(builder, in.Values[0])
 | |
| 			break
 | |
| 		}
 | |
| 
 | |
| 		fallthrough
 | |
| 	default:
 | |
| 		builder.WriteString(" NOT IN (")
 | |
| 		builder.AddVar(builder, in.Values...)
 | |
| 		builder.WriteByte(')')
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Eq equal to for where
 | |
| type Eq struct {
 | |
| 	Column interface{}
 | |
| 	Value  interface{}
 | |
| }
 | |
| 
 | |
| func (eq Eq) Build(builder Builder) {
 | |
| 	builder.WriteQuoted(eq.Column)
 | |
| 
 | |
| 	switch eq.Value.(type) {
 | |
| 	case []string, []int, []int32, []int64, []uint, []uint32, []uint64, []interface{}:
 | |
| 		rv := reflect.ValueOf(eq.Value)
 | |
| 		if rv.Len() == 0 {
 | |
| 			builder.WriteString(" IN (NULL)")
 | |
| 		} else {
 | |
| 			builder.WriteString(" IN (")
 | |
| 			for i := 0; i < rv.Len(); i++ {
 | |
| 				if i > 0 {
 | |
| 					builder.WriteByte(',')
 | |
| 				}
 | |
| 				builder.AddVar(builder, rv.Index(i).Interface())
 | |
| 			}
 | |
| 			builder.WriteByte(')')
 | |
| 		}
 | |
| 	default:
 | |
| 		if eqNil(eq.Value) {
 | |
| 			builder.WriteString(" IS NULL")
 | |
| 		} else {
 | |
| 			builder.WriteString(" = ")
 | |
| 			builder.AddVar(builder, eq.Value)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (eq Eq) NegationBuild(builder Builder) {
 | |
| 	Neq(eq).Build(builder)
 | |
| }
 | |
| 
 | |
| // Neq not equal to for where
 | |
| type Neq Eq
 | |
| 
 | |
| func (neq Neq) Build(builder Builder) {
 | |
| 	builder.WriteQuoted(neq.Column)
 | |
| 
 | |
| 	switch neq.Value.(type) {
 | |
| 	case []string, []int, []int32, []int64, []uint, []uint32, []uint64, []interface{}:
 | |
| 		builder.WriteString(" NOT IN (")
 | |
| 		rv := reflect.ValueOf(neq.Value)
 | |
| 		for i := 0; i < rv.Len(); i++ {
 | |
| 			if i > 0 {
 | |
| 				builder.WriteByte(',')
 | |
| 			}
 | |
| 			builder.AddVar(builder, rv.Index(i).Interface())
 | |
| 		}
 | |
| 		builder.WriteByte(')')
 | |
| 	default:
 | |
| 		if eqNil(neq.Value) {
 | |
| 			builder.WriteString(" IS NOT NULL")
 | |
| 		} else {
 | |
| 			builder.WriteString(" <> ")
 | |
| 			builder.AddVar(builder, neq.Value)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (neq Neq) NegationBuild(builder Builder) {
 | |
| 	Eq(neq).Build(builder)
 | |
| }
 | |
| 
 | |
| // Gt greater than for where
 | |
| type Gt Eq
 | |
| 
 | |
| func (gt Gt) Build(builder Builder) {
 | |
| 	builder.WriteQuoted(gt.Column)
 | |
| 	builder.WriteString(" > ")
 | |
| 	builder.AddVar(builder, gt.Value)
 | |
| }
 | |
| 
 | |
| func (gt Gt) NegationBuild(builder Builder) {
 | |
| 	Lte(gt).Build(builder)
 | |
| }
 | |
| 
 | |
| // Gte greater than or equal to for where
 | |
| type Gte Eq
 | |
| 
 | |
| func (gte Gte) Build(builder Builder) {
 | |
| 	builder.WriteQuoted(gte.Column)
 | |
| 	builder.WriteString(" >= ")
 | |
| 	builder.AddVar(builder, gte.Value)
 | |
| }
 | |
| 
 | |
| func (gte Gte) NegationBuild(builder Builder) {
 | |
| 	Lt(gte).Build(builder)
 | |
| }
 | |
| 
 | |
| // Lt less than for where
 | |
| type Lt Eq
 | |
| 
 | |
| func (lt Lt) Build(builder Builder) {
 | |
| 	builder.WriteQuoted(lt.Column)
 | |
| 	builder.WriteString(" < ")
 | |
| 	builder.AddVar(builder, lt.Value)
 | |
| }
 | |
| 
 | |
| func (lt Lt) NegationBuild(builder Builder) {
 | |
| 	Gte(lt).Build(builder)
 | |
| }
 | |
| 
 | |
| // Lte less than or equal to for where
 | |
| type Lte Eq
 | |
| 
 | |
| func (lte Lte) Build(builder Builder) {
 | |
| 	builder.WriteQuoted(lte.Column)
 | |
| 	builder.WriteString(" <= ")
 | |
| 	builder.AddVar(builder, lte.Value)
 | |
| }
 | |
| 
 | |
| func (lte Lte) NegationBuild(builder Builder) {
 | |
| 	Gt(lte).Build(builder)
 | |
| }
 | |
| 
 | |
| // Like whether string matches regular expression
 | |
| type Like Eq
 | |
| 
 | |
| func (like Like) Build(builder Builder) {
 | |
| 	builder.WriteQuoted(like.Column)
 | |
| 	builder.WriteString(" LIKE ")
 | |
| 	builder.AddVar(builder, like.Value)
 | |
| }
 | |
| 
 | |
| func (like Like) NegationBuild(builder Builder) {
 | |
| 	builder.WriteQuoted(like.Column)
 | |
| 	builder.WriteString(" NOT LIKE ")
 | |
| 	builder.AddVar(builder, like.Value)
 | |
| }
 | |
| 
 | |
| func eqNil(value interface{}) bool {
 | |
| 	if valuer, ok := value.(driver.Valuer); ok && !eqNilReflect(valuer) {
 | |
| 		value, _ = valuer.Value()
 | |
| 	}
 | |
| 
 | |
| 	return value == nil || eqNilReflect(value)
 | |
| }
 | |
| 
 | |
| func eqNilReflect(value interface{}) bool {
 | |
| 	reflectValue := reflect.ValueOf(value)
 | |
| 	return reflectValue.Kind() == reflect.Ptr && reflectValue.IsNil()
 | |
| }
 |