246 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			246 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package clause
 | |
| 
 | |
| import (
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	AndWithSpace = " AND "
 | |
| 	OrWithSpace  = " OR "
 | |
| )
 | |
| 
 | |
| // Where where clause
 | |
| type Where struct {
 | |
| 	Exprs []Expression
 | |
| }
 | |
| 
 | |
| // Name where clause name
 | |
| func (where Where) Name() string {
 | |
| 	return "WHERE"
 | |
| }
 | |
| 
 | |
| // Build build where clause
 | |
| func (where Where) Build(builder Builder) {
 | |
| 	if len(where.Exprs) == 1 {
 | |
| 		if andCondition, ok := where.Exprs[0].(AndConditions); ok {
 | |
| 			where.Exprs = andCondition.Exprs
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Switch position if the first query expression is a single Or condition
 | |
| 	for idx, expr := range where.Exprs {
 | |
| 		if v, ok := expr.(OrConditions); !ok || len(v.Exprs) > 1 {
 | |
| 			if idx != 0 {
 | |
| 				where.Exprs[0], where.Exprs[idx] = where.Exprs[idx], where.Exprs[0]
 | |
| 			}
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	buildExprs(where.Exprs, builder, AndWithSpace)
 | |
| }
 | |
| 
 | |
| func buildExprs(exprs []Expression, builder Builder, joinCond string) {
 | |
| 	wrapInParentheses := false
 | |
| 
 | |
| 	for idx, expr := range exprs {
 | |
| 		if idx > 0 {
 | |
| 			if v, ok := expr.(OrConditions); ok && len(v.Exprs) == 1 {
 | |
| 				builder.WriteString(OrWithSpace)
 | |
| 			} else {
 | |
| 				builder.WriteString(joinCond)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if len(exprs) > 1 {
 | |
| 			switch v := expr.(type) {
 | |
| 			case OrConditions:
 | |
| 				if len(v.Exprs) == 1 {
 | |
| 					if e, ok := v.Exprs[0].(Expr); ok {
 | |
| 						sql := strings.ToUpper(e.SQL)
 | |
| 						wrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace)
 | |
| 					}
 | |
| 				}
 | |
| 			case AndConditions:
 | |
| 				if len(v.Exprs) == 1 {
 | |
| 					if e, ok := v.Exprs[0].(Expr); ok {
 | |
| 						sql := strings.ToUpper(e.SQL)
 | |
| 						wrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace)
 | |
| 					}
 | |
| 				}
 | |
| 			case Expr:
 | |
| 				sql := strings.ToUpper(v.SQL)
 | |
| 				wrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace)
 | |
| 			case NamedExpr:
 | |
| 				sql := strings.ToUpper(v.SQL)
 | |
| 				wrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if wrapInParentheses {
 | |
| 			builder.WriteByte('(')
 | |
| 			expr.Build(builder)
 | |
| 			builder.WriteByte(')')
 | |
| 			wrapInParentheses = false
 | |
| 		} else {
 | |
| 			expr.Build(builder)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // MergeClause merge where clauses
 | |
| func (where Where) MergeClause(clause *Clause) {
 | |
| 	if w, ok := clause.Expression.(Where); ok {
 | |
| 		exprs := make([]Expression, len(w.Exprs)+len(where.Exprs))
 | |
| 		copy(exprs, w.Exprs)
 | |
| 		copy(exprs[len(w.Exprs):], where.Exprs)
 | |
| 		where.Exprs = exprs
 | |
| 	}
 | |
| 
 | |
| 	clause.Expression = where
 | |
| }
 | |
| 
 | |
| func And(exprs ...Expression) Expression {
 | |
| 	if len(exprs) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if len(exprs) == 1 {
 | |
| 		if _, ok := exprs[0].(OrConditions); !ok {
 | |
| 			return exprs[0]
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return AndConditions{Exprs: exprs}
 | |
| }
 | |
| 
 | |
| type AndConditions struct {
 | |
| 	Exprs []Expression
 | |
| }
 | |
| 
 | |
| func (and AndConditions) Build(builder Builder) {
 | |
| 	if len(and.Exprs) > 1 {
 | |
| 		builder.WriteByte('(')
 | |
| 		buildExprs(and.Exprs, builder, AndWithSpace)
 | |
| 		builder.WriteByte(')')
 | |
| 	} else {
 | |
| 		buildExprs(and.Exprs, builder, AndWithSpace)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Or(exprs ...Expression) Expression {
 | |
| 	if len(exprs) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	return OrConditions{Exprs: exprs}
 | |
| }
 | |
| 
 | |
| type OrConditions struct {
 | |
| 	Exprs []Expression
 | |
| }
 | |
| 
 | |
| func (or OrConditions) Build(builder Builder) {
 | |
| 	if len(or.Exprs) > 1 {
 | |
| 		builder.WriteByte('(')
 | |
| 		buildExprs(or.Exprs, builder, OrWithSpace)
 | |
| 		builder.WriteByte(')')
 | |
| 	} else {
 | |
| 		buildExprs(or.Exprs, builder, OrWithSpace)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Not(exprs ...Expression) Expression {
 | |
| 	if len(exprs) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if len(exprs) == 1 {
 | |
| 		if andCondition, ok := exprs[0].(AndConditions); ok {
 | |
| 			exprs = andCondition.Exprs
 | |
| 		}
 | |
| 	}
 | |
| 	return NotConditions{Exprs: exprs}
 | |
| }
 | |
| 
 | |
| type NotConditions struct {
 | |
| 	Exprs []Expression
 | |
| }
 | |
| 
 | |
| func (not NotConditions) Build(builder Builder) {
 | |
| 	anyNegationBuilder := false
 | |
| 	for _, c := range not.Exprs {
 | |
| 		if _, ok := c.(NegationExpressionBuilder); ok {
 | |
| 			anyNegationBuilder = true
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if anyNegationBuilder {
 | |
| 		if len(not.Exprs) > 1 {
 | |
| 			builder.WriteByte('(')
 | |
| 		}
 | |
| 
 | |
| 		for idx, c := range not.Exprs {
 | |
| 			if idx > 0 {
 | |
| 				builder.WriteString(AndWithSpace)
 | |
| 			}
 | |
| 
 | |
| 			if negationBuilder, ok := c.(NegationExpressionBuilder); ok {
 | |
| 				negationBuilder.NegationBuild(builder)
 | |
| 			} else {
 | |
| 				builder.WriteString("NOT ")
 | |
| 				e, wrapInParentheses := c.(Expr)
 | |
| 				if wrapInParentheses {
 | |
| 					sql := strings.ToUpper(e.SQL)
 | |
| 					if wrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace); wrapInParentheses {
 | |
| 						builder.WriteByte('(')
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				c.Build(builder)
 | |
| 
 | |
| 				if wrapInParentheses {
 | |
| 					builder.WriteByte(')')
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if len(not.Exprs) > 1 {
 | |
| 			builder.WriteByte(')')
 | |
| 		}
 | |
| 	} else {
 | |
| 		builder.WriteString("NOT ")
 | |
| 		if len(not.Exprs) > 1 {
 | |
| 			builder.WriteByte('(')
 | |
| 		}
 | |
| 
 | |
| 		for idx, c := range not.Exprs {
 | |
| 			if idx > 0 {
 | |
| 				switch c.(type) {
 | |
| 				case OrConditions:
 | |
| 					builder.WriteString(OrWithSpace)
 | |
| 				default:
 | |
| 					builder.WriteString(AndWithSpace)
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			e, wrapInParentheses := c.(Expr)
 | |
| 			if wrapInParentheses {
 | |
| 				sql := strings.ToUpper(e.SQL)
 | |
| 				if wrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace); wrapInParentheses {
 | |
| 					builder.WriteByte('(')
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			c.Build(builder)
 | |
| 
 | |
| 			if wrapInParentheses {
 | |
| 				builder.WriteByte(')')
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if len(not.Exprs) > 1 {
 | |
| 			builder.WriteByte(')')
 | |
| 		}
 | |
| 	}
 | |
| }
 |