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  }