github.com/jxskiss/gopkg@v0.17.3/sqlutil/condition.go (about) 1 package sqlutil 2 3 import ( 4 "fmt" 5 "strings" 6 "unsafe" 7 ) 8 9 // And returns a new *Condition which is combination of given conditions 10 // using the "AND" operator. 11 func And(conds ...*Condition) *Condition { 12 f := new(Condition) 13 for _, c := range conds { 14 clause, args := c.Build() 15 f.And(clause, args...) 16 } 17 return f 18 } 19 20 // Or returns a new *Condition which is combination of given conditions 21 // using the "OR" operator. 22 func Or(conds ...*Condition) *Condition { 23 f := new(Condition) 24 for _, c := range conds { 25 clause, args := c.Build() 26 f.Or(clause, args...) 27 } 28 return f 29 } 30 31 // Cond creates a new *Condition from the given params. 32 func Cond(clause string, args ...interface{}) *Condition { 33 return new(Condition).And(clause, args...) 34 } 35 36 // Condition represents a query filter to work with SQL query. 37 type Condition struct { 38 builder strings.Builder 39 prefix []byte 40 args []interface{} 41 } 42 43 // And combines the given query filter to Condition using "AND" operator. 44 func (p *Condition) And(clause string, args ...interface{}) *Condition { 45 if clause == "" { 46 return p 47 } 48 49 // encapsulate with brackets to avoid misuse 50 clause = strings.TrimSpace(clause) 51 if containsOr(clause) { 52 clause = "(" + clause + ")" 53 } 54 55 if p.builder.Len() == 0 { 56 p.builder.WriteString(clause) 57 } else { 58 p.builder.WriteString(" AND ") 59 p.builder.WriteString(clause) 60 } 61 p.args = append(p.args, args...) 62 return p 63 } 64 65 // AndCond combines the given Condition using "AND" operator. 66 func (p *Condition) AndCond(c *Condition) *Condition { 67 clause, args := c.Build() 68 return p.And(clause, args...) 69 } 70 71 // Or combines the given query filter to Condition using "OR" operator. 72 func (p *Condition) Or(clause string, args ...interface{}) *Condition { 73 if clause == "" { 74 return p 75 } 76 77 // encapsulate with brackets to avoid misuse 78 clause = strings.TrimSpace(clause) 79 if containsAnd(clause) { 80 clause = "(" + clause + ")" 81 } 82 83 if p.builder.Len() == 0 { 84 p.builder.WriteString(clause) 85 } else { 86 p.prefix = append(p.prefix, '(') 87 p.builder.WriteString(" OR ") 88 p.builder.WriteString(clause) 89 p.builder.WriteByte(')') 90 } 91 p.args = append(p.args, args...) 92 return p 93 } 94 95 // OrCond combines the given Condition using "OR" operator. 96 func (p *Condition) OrCond(c *Condition) *Condition { 97 clause, args := c.Build() 98 return p.Or(clause, args...) 99 } 100 101 // IfAnd checks cond, if cond is true, it combines the query filter 102 // to Condition using "AND" operator. 103 func (p *Condition) IfAnd(cond bool, clause string, args ...interface{}) *Condition { 104 if cond { 105 return p.And(clause, args...) 106 } 107 return p 108 } 109 110 // IfAndCond checks cond, if cond is true, it combines the given Condition 111 // using "AND" operator. 112 func (p *Condition) IfAndCond(cond bool, c *Condition) *Condition { 113 if cond { 114 return p.AndCond(c) 115 } 116 return p 117 } 118 119 // IfOr checks cond, it cond is true, it combines the query filter 120 // to Condition using "OR" operator. 121 func (p *Condition) IfOr(cond bool, clause string, args ...interface{}) *Condition { 122 if cond { 123 return p.Or(clause, args...) 124 } 125 return p 126 } 127 128 // IfOrCond checks cond, if cond is true, it combines the given Condition 129 // using "OR" operator. 130 func (p *Condition) IfOrCond(cond bool, c *Condition) *Condition { 131 if cond { 132 return p.OrCond(c) 133 } 134 return p 135 } 136 137 // Build returns the query filter clause and parameters of the Condition. 138 func (p *Condition) Build() (string, []interface{}) { 139 buf := make([]byte, len(p.prefix)+p.builder.Len()) 140 copy(buf, p.prefix) 141 copy(buf[len(p.prefix):], p.builder.String()) 142 clause := *(*string)(unsafe.Pointer(&buf)) 143 return clause, p.args 144 } 145 146 // String returns the string representation of the Condition. 147 func (p *Condition) String() string { 148 clause, args := p.Build() 149 format := strings.Replace(clause, "?", "%v", -1) 150 return fmt.Sprintf(format, args...) 151 } 152 153 func containsAnd(clause string) bool { 154 parenCnt := 0 155 clause = strings.ToLower(clause) 156 for i := 0; i < len(clause)-4; i++ { 157 switch clause[i] { 158 case '(': 159 parenCnt++ 160 case ')': 161 parenCnt-- 162 case 'a': 163 if clause[i:i+3] == "and" && 164 i > 0 && isWhitespace(clause[i-1]) && isWhitespace(clause[i+3]) && 165 parenCnt == 0 { 166 return true 167 } 168 } 169 } 170 return false 171 } 172 173 func containsOr(clause string) bool { 174 parenCnt := 0 175 clause = strings.ToLower(clause) 176 for i := 0; i < len(clause)-3; i++ { 177 switch clause[i] { 178 case '(': 179 parenCnt++ 180 case ')': 181 parenCnt-- 182 case 'o': 183 if clause[i:i+2] == "or" && 184 i > 0 && isWhitespace(clause[i-1]) && isWhitespace(clause[i+2]) && 185 parenCnt == 0 { 186 return true 187 } 188 } 189 } 190 return false 191 } 192 193 func isWhitespace(b byte) bool { 194 switch b { 195 case '\t', '\n', '\v', '\f', '\r', ' ': 196 return true 197 } 198 return false 199 }