github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/event/query/builder.go (about) 1 package query 2 3 import ( 4 "bytes" 5 "encoding" 6 "fmt" 7 "reflect" 8 "strconv" 9 "strings" 10 "text/template" 11 "time" 12 ) 13 14 const ( 15 MultipleValueTagSeparator = ";" 16 17 // Operators 18 equalString = "=" 19 notEqualString = "!=" 20 greaterThanString = ">" 21 lessThanString = "<" 22 greaterOrEqualString = ">=" 23 lessOrEqualString = "<=" 24 containsString = "CONTAINS" 25 andString = "AND" 26 orString = "OR" 27 notString = "NOT" 28 29 // Values 30 trueString = "true" 31 falseString = "false" 32 timeString = "TIME" 33 dateString = "DATE" 34 ) 35 36 type Queryable interface { 37 Query() (Query, error) 38 } 39 40 type parsedQuery struct { 41 query Query 42 } 43 44 func AsQueryable(query Query) parsedQuery { 45 return parsedQuery{query: query} 46 } 47 48 func (pq parsedQuery) Query() (Query, error) { 49 return pq.query, nil 50 } 51 52 func Must(qry Query, err error) Query { 53 if err != nil { 54 panic(fmt.Errorf("could not compile: %v", qry)) 55 } 56 return qry 57 } 58 59 // A yet-to-be-parsed query 60 type String string 61 62 func (qs String) Query() (Query, error) { 63 if isEmpty(string(qs)) { 64 return Empty{}, nil 65 } 66 return New(string(qs)) 67 } 68 69 func MatchAllQueryable() Queryable { 70 return Empty{} 71 } 72 73 // A fluent query builder 74 type Builder struct { 75 queryString string 76 condition 77 // reusable buffer for building queryString 78 bytes.Buffer 79 error 80 } 81 82 // Templates 83 type condition struct { 84 Tag string 85 Op string 86 Operand string 87 } 88 89 var conditionTemplate = template.Must(template.New("condition").Parse("{{.Tag}} {{.Op}} {{.Operand}}")) 90 91 // Creates a new query builder with a base query that is the conjunction of all queries passed 92 func NewBuilder(queries ...string) *Builder { 93 qb := new(Builder) 94 qb.queryString = qb.binary(stringIterator(queries...), andString) 95 return qb 96 } 97 98 func (qb *Builder) String() string { 99 return qb.queryString 100 } 101 102 func (qb *Builder) Query() (Query, error) { 103 if qb.error != nil { 104 return nil, qb.error 105 } 106 return NewOrEmpty(qb.queryString) 107 } 108 109 func NewOrEmpty(queryString string) (Query, error) { 110 if isEmpty(queryString) { 111 return Empty{}, nil 112 } 113 return New(queryString) 114 } 115 116 // Creates the conjunction of Builder and rightQuery 117 func (qb *Builder) And(queryBuilders ...*Builder) *Builder { 118 return NewBuilder(qb.binary(queryBuilderIterator(queryBuilders...), andString)) 119 } 120 121 func (qb *Builder) Or(queryBuilders ...*Builder) *Builder { 122 return NewBuilder(qb.binary(queryBuilderIterator(queryBuilders...), orString)) 123 } 124 125 func (qb *Builder) Not() *Builder { 126 defer qb.Buffer.Reset() 127 qb.Buffer.WriteString(notString) 128 qb.Buffer.WriteByte(' ') 129 qb.Buffer.WriteByte('(') 130 qb.Buffer.WriteString(qb.queryString) 131 qb.Buffer.WriteByte(')') 132 return NewBuilder(qb.Buffer.String()) 133 } 134 135 // Creates the conjunction of Builder and tag = operand 136 func (qb *Builder) AndEquals(tag string, operand interface{}) *Builder { 137 qb.condition.Tag = tag 138 qb.condition.Op = equalString 139 qb.condition.Operand = operandString(operand) 140 return NewBuilder(qb.binary(stringIterator(qb.conditionString()), andString)) 141 } 142 143 func (qb *Builder) AndNotEquals(tag string, operand interface{}) *Builder { 144 qb.condition.Tag = tag 145 qb.condition.Op = notEqualString 146 qb.condition.Operand = operandString(operand) 147 return NewBuilder(qb.binary(stringIterator(qb.conditionString()), andString)) 148 } 149 150 func (qb *Builder) AndGreaterThanOrEqual(tag string, operand interface{}) *Builder { 151 qb.condition.Tag = tag 152 qb.condition.Op = greaterOrEqualString 153 qb.condition.Operand = operandString(operand) 154 return NewBuilder(qb.binary(stringIterator(qb.conditionString()), andString)) 155 } 156 157 func (qb *Builder) AndLessThanOrEqual(tag string, operand interface{}) *Builder { 158 qb.condition.Tag = tag 159 qb.condition.Op = lessOrEqualString 160 qb.condition.Operand = operandString(operand) 161 return NewBuilder(qb.binary(stringIterator(qb.conditionString()), andString)) 162 } 163 164 func (qb *Builder) AndStrictlyGreaterThan(tag string, operand interface{}) *Builder { 165 qb.condition.Tag = tag 166 qb.condition.Op = greaterThanString 167 qb.condition.Operand = operandString(operand) 168 return NewBuilder(qb.binary(stringIterator(qb.conditionString()), andString)) 169 } 170 171 func (qb *Builder) AndStrictlyLessThan(tag string, operand interface{}) *Builder { 172 qb.condition.Tag = tag 173 qb.condition.Op = lessThanString 174 qb.condition.Operand = operandString(operand) 175 return NewBuilder(qb.binary(stringIterator(qb.conditionString()), andString)) 176 } 177 178 func (qb *Builder) AndContains(tag string, operand interface{}) *Builder { 179 qb.condition.Tag = tag 180 qb.condition.Op = containsString 181 qb.condition.Operand = operandString(operand) 182 return NewBuilder(qb.binary(stringIterator(qb.conditionString()), andString)) 183 } 184 185 func (qb *Builder) binary(queryIterator func(func(string)), operator string) string { 186 defer qb.Buffer.Reset() 187 qb.Buffer.WriteString(qb.queryString) 188 queryIterator(func(q string) { 189 if !isEmpty(q) { 190 if qb.Buffer.Len() > 0 { 191 qb.Buffer.WriteByte(' ') 192 qb.Buffer.WriteString(operator) 193 qb.Buffer.WriteByte(' ') 194 } 195 qb.Buffer.WriteString(q) 196 } 197 }) 198 return qb.Buffer.String() 199 } 200 201 func operandString(value interface{}) string { 202 buf := new(bytes.Buffer) 203 switch v := value.(type) { 204 case string: 205 buf.WriteByte('\'') 206 buf.WriteString(v) 207 buf.WriteByte('\'') 208 return buf.String() 209 case fmt.Stringer: 210 return operandString(v.String()) 211 default: 212 return StringFromValue(v) 213 } 214 } 215 216 func StringFromValue(value interface{}) string { 217 rv := reflect.ValueOf(value) 218 if rv.Kind() == reflect.Ptr && rv.IsNil() { 219 return "nil" 220 } 221 switch v := value.(type) { 222 case string: 223 return v 224 case time.Time: 225 return timeString + " " + v.Format(time.RFC3339) 226 case encoding.TextMarshaler: 227 bs, _ := v.MarshalText() 228 return string(bs) 229 case fmt.Stringer: 230 return v.String() 231 case bool: 232 if v { 233 return trueString 234 } 235 return falseString 236 case int: 237 return strconv.FormatInt(int64(v), 10) 238 case int32: 239 return strconv.FormatInt(int64(v), 10) 240 case int64: 241 return strconv.FormatInt(v, 10) 242 case uint: 243 return strconv.FormatUint(uint64(v), 10) 244 case uint32: 245 return strconv.FormatUint(uint64(v), 10) 246 case uint64: 247 return strconv.FormatUint(v, 10) 248 case float32: 249 return strconv.FormatFloat(float64(v), 'f', -1, 32) 250 case float64: 251 return strconv.FormatFloat(float64(v), 'f', -1, 64) 252 default: 253 if rv.Kind() == reflect.Slice { 254 values := make([]string, rv.Len()) 255 for i := 0; i < rv.Len(); i++ { 256 values[i] = StringFromValue(rv.Index(i).Interface()) 257 } 258 return strings.Join(values, MultipleValueTagSeparator) 259 } 260 return fmt.Sprintf("%v", v) 261 } 262 } 263 264 func (qb *Builder) conditionString() string { 265 defer qb.Buffer.Reset() 266 err := conditionTemplate.Execute(&qb.Buffer, qb.condition) 267 if err != nil && qb.error == nil { 268 qb.error = err 269 } 270 return qb.Buffer.String() 271 } 272 273 func isEmpty(queryString string) bool { 274 return queryString == "" 275 } 276 277 // Iterators over some strings 278 func stringIterator(strs ...string) func(func(string)) { 279 return func(callback func(string)) { 280 for _, s := range strs { 281 callback(s) 282 } 283 } 284 } 285 286 func queryBuilderIterator(qbs ...*Builder) func(func(string)) { 287 return func(callback func(string)) { 288 for _, qb := range qbs { 289 callback(qb.String()) 290 } 291 } 292 }