github.com/systematiccaos/gorm@v1.22.6/clause/where.go (about)

     1  package clause
     2  
     3  import (
     4  	"strings"
     5  )
     6  
     7  // Where where clause
     8  type Where struct {
     9  	Exprs []Expression
    10  }
    11  
    12  // Name where clause name
    13  func (where Where) Name() string {
    14  	return "WHERE"
    15  }
    16  
    17  // Build build where clause
    18  func (where Where) Build(builder Builder) {
    19  	// Switch position if the first query expression is a single Or condition
    20  	for idx, expr := range where.Exprs {
    21  		if v, ok := expr.(OrConditions); !ok || len(v.Exprs) > 1 {
    22  			if idx != 0 {
    23  				where.Exprs[0], where.Exprs[idx] = where.Exprs[idx], where.Exprs[0]
    24  			}
    25  			break
    26  		}
    27  	}
    28  
    29  	buildExprs(where.Exprs, builder, " AND ")
    30  }
    31  
    32  func buildExprs(exprs []Expression, builder Builder, joinCond string) {
    33  	wrapInParentheses := false
    34  
    35  	for idx, expr := range exprs {
    36  		if idx > 0 {
    37  			if v, ok := expr.(OrConditions); ok && len(v.Exprs) == 1 {
    38  				builder.WriteString(" OR ")
    39  			} else {
    40  				builder.WriteString(joinCond)
    41  			}
    42  		}
    43  
    44  		if len(exprs) > 1 {
    45  			switch v := expr.(type) {
    46  			case OrConditions:
    47  				if len(v.Exprs) == 1 {
    48  					if e, ok := v.Exprs[0].(Expr); ok {
    49  						sql := strings.ToLower(e.SQL)
    50  						wrapInParentheses = strings.Contains(sql, "and") || strings.Contains(sql, "or")
    51  					}
    52  				}
    53  			case AndConditions:
    54  				if len(v.Exprs) == 1 {
    55  					if e, ok := v.Exprs[0].(Expr); ok {
    56  						sql := strings.ToLower(e.SQL)
    57  						wrapInParentheses = strings.Contains(sql, "and") || strings.Contains(sql, "or")
    58  					}
    59  				}
    60  			case Expr:
    61  				sql := strings.ToLower(v.SQL)
    62  				wrapInParentheses = strings.Contains(sql, "and") || strings.Contains(sql, "or")
    63  			case NamedExpr:
    64  				sql := strings.ToLower(v.SQL)
    65  				wrapInParentheses = strings.Contains(sql, "and") || strings.Contains(sql, "or")
    66  			}
    67  		}
    68  
    69  		if wrapInParentheses {
    70  			builder.WriteString(`(`)
    71  			expr.Build(builder)
    72  			builder.WriteString(`)`)
    73  			wrapInParentheses = false
    74  		} else {
    75  			expr.Build(builder)
    76  		}
    77  	}
    78  }
    79  
    80  // MergeClause merge where clauses
    81  func (where Where) MergeClause(clause *Clause) {
    82  	if w, ok := clause.Expression.(Where); ok {
    83  		exprs := make([]Expression, len(w.Exprs)+len(where.Exprs))
    84  		copy(exprs, w.Exprs)
    85  		copy(exprs[len(w.Exprs):], where.Exprs)
    86  		where.Exprs = exprs
    87  	}
    88  
    89  	clause.Expression = where
    90  }
    91  
    92  func And(exprs ...Expression) Expression {
    93  	if len(exprs) == 0 {
    94  		return nil
    95  	} else if len(exprs) == 1 {
    96  		return exprs[0]
    97  	}
    98  	return AndConditions{Exprs: exprs}
    99  }
   100  
   101  type AndConditions struct {
   102  	Exprs []Expression
   103  }
   104  
   105  func (and AndConditions) Build(builder Builder) {
   106  	if len(and.Exprs) > 1 {
   107  		builder.WriteByte('(')
   108  		buildExprs(and.Exprs, builder, " AND ")
   109  		builder.WriteByte(')')
   110  	} else {
   111  		buildExprs(and.Exprs, builder, " AND ")
   112  	}
   113  }
   114  
   115  func Or(exprs ...Expression) Expression {
   116  	if len(exprs) == 0 {
   117  		return nil
   118  	}
   119  	return OrConditions{Exprs: exprs}
   120  }
   121  
   122  type OrConditions struct {
   123  	Exprs []Expression
   124  }
   125  
   126  func (or OrConditions) Build(builder Builder) {
   127  	if len(or.Exprs) > 1 {
   128  		builder.WriteByte('(')
   129  		buildExprs(or.Exprs, builder, " OR ")
   130  		builder.WriteByte(')')
   131  	} else {
   132  		buildExprs(or.Exprs, builder, " OR ")
   133  	}
   134  }
   135  
   136  func Not(exprs ...Expression) Expression {
   137  	if len(exprs) == 0 {
   138  		return nil
   139  	}
   140  	return NotConditions{Exprs: exprs}
   141  }
   142  
   143  type NotConditions struct {
   144  	Exprs []Expression
   145  }
   146  
   147  func (not NotConditions) Build(builder Builder) {
   148  	if len(not.Exprs) > 1 {
   149  		builder.WriteByte('(')
   150  	}
   151  
   152  	for idx, c := range not.Exprs {
   153  		if idx > 0 {
   154  			builder.WriteString(" AND ")
   155  		}
   156  
   157  		if negationBuilder, ok := c.(NegationExpressionBuilder); ok {
   158  			negationBuilder.NegationBuild(builder)
   159  		} else {
   160  			builder.WriteString("NOT ")
   161  			e, wrapInParentheses := c.(Expr)
   162  			if wrapInParentheses {
   163  				sql := strings.ToLower(e.SQL)
   164  				if wrapInParentheses = strings.Contains(sql, "and") || strings.Contains(sql, "or"); wrapInParentheses {
   165  					builder.WriteByte('(')
   166  				}
   167  			}
   168  
   169  			c.Build(builder)
   170  
   171  			if wrapInParentheses {
   172  				builder.WriteByte(')')
   173  			}
   174  		}
   175  	}
   176  
   177  	if len(not.Exprs) > 1 {
   178  		builder.WriteByte(')')
   179  	}
   180  }