| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| package query |
| |
| import ( |
| "errors" |
| "fmt" |
| "math/big" |
| "reflect" |
| "strconv" |
| |
| "v.io/syncbase/v23/syncbase/nosql/internal/query/query_checker" |
| "v.io/syncbase/v23/syncbase/nosql/internal/query/query_parser" |
| ) |
| |
| func Eval(k string, v interface{}, e *query_parser.Expression) bool { |
| if query_checker.IsLogicalOperator(e.Operator) { |
| return evalLogicalOperators(k, v, e) |
| } else { |
| return evalComparisonOperators(k, v, e) |
| } |
| } |
| |
| func evalLogicalOperators(k string, v interface{}, e *query_parser.Expression) bool { |
| switch e.Operator.Type { |
| case query_parser.And: |
| return Eval(k, v, e.Operand1.Expr) && Eval(k, v, e.Operand2.Expr) |
| default: // query_parser.Or |
| return Eval(k, v, e.Operand1.Expr) || Eval(k, v, e.Operand2.Expr) |
| } |
| } |
| |
| func evalComparisonOperators(k string, v interface{}, e *query_parser.Expression) bool { |
| // Key and type expressions are evaluated differently from value expression. |
| // Key expressions always have a string literal on the rhs and are currently |
| // limited to = and <>. |
| // Type expressions are limited to = and must have a stiring literal rhs. Also, |
| // they are evaluated via reflection on the value. |
| if query_checker.IsKey(e.Operand1) { |
| return evalKeyExpression(e, k) |
| } else if query_checker.IsType(e.Operand1) { |
| return evalTypeExpression(e, v) |
| } else { |
| return evalValueExpression(k, v, e) |
| } |
| } |
| |
| func evalValueExpression(k string, v interface{}, e *query_parser.Expression) bool { |
| // Any fields that are value fields that need to be resolved. |
| lhsValue := resolveOperand(v, e.Operand1) |
| if lhsValue == nil { |
| return false |
| } |
| rhsValue := resolveOperand(v, e.Operand2) |
| if rhsValue == nil { |
| return false |
| } |
| // coerce operands so they are comparable |
| var err error |
| lhsValue, rhsValue, err = coerceValues(lhsValue, rhsValue) |
| if err != nil { |
| return false // If operands can't be coerced to compare, expr evals to false. |
| } |
| // Do the compare |
| switch lhsValue.Type { |
| case query_parser.TypBigInt: |
| return compareBigInts(lhsValue, rhsValue, e.Operator) |
| case query_parser.TypBigRat: |
| return compareBigRats(lhsValue, rhsValue, e.Operator) |
| case query_parser.TypBool: |
| return compareBools(lhsValue, rhsValue, e.Operator) |
| case query_parser.TypFloat: |
| return compareFloats(lhsValue, rhsValue, e.Operator) |
| case query_parser.TypInt: |
| return compareInts(lhsValue, rhsValue, e.Operator) |
| case query_parser.TypLiteral: |
| return compareStrings(lhsValue, rhsValue, e.Operator) |
| case query_parser.TypUint: |
| return compareUints(lhsValue, rhsValue, e.Operator) |
| case query_parser.TypObject: |
| return compareObjects(lhsValue, rhsValue, e.Operator) |
| } |
| return false |
| } |
| |
| func coerceValues(lhsValue, rhsValue *query_parser.Operand) (*query_parser.Operand, *query_parser.Operand, error) { |
| var err error |
| // If either operand is a string, convert the other to a string. |
| if lhsValue.Type == query_parser.TypLiteral || rhsValue.Type == query_parser.TypLiteral { |
| if lhsValue, err = convertValueToString(lhsValue); err != nil { |
| return nil, nil, err |
| } |
| if rhsValue, err = convertValueToString(rhsValue); err != nil { |
| return nil, nil, err |
| } |
| } |
| // If either operand is a big rat, convert both to a big rat. |
| // Also, if one operand is a float and the other is a big int, |
| // convert both to big rats. |
| if lhsValue.Type == query_parser.TypBigRat || rhsValue.Type == query_parser.TypBigRat || (lhsValue.Type == query_parser.TypBigInt && rhsValue.Type == query_parser.TypFloat) || (lhsValue.Type == query_parser.TypFloat && rhsValue.Type == query_parser.TypBigInt) { |
| if lhsValue, err = convertValueToBigRat(lhsValue); err != nil { |
| return nil, nil, err |
| } |
| if rhsValue, err = convertValueToBigRat(rhsValue); err != nil { |
| return nil, nil, err |
| } |
| } |
| // If either operand is a float, convert the other to a float. |
| if lhsValue.Type == query_parser.TypFloat || rhsValue.Type == query_parser.TypFloat { |
| if lhsValue, err = convertValueToFloat(lhsValue); err != nil { |
| return nil, nil, err |
| } |
| if rhsValue, err = convertValueToFloat(rhsValue); err != nil { |
| return nil, nil, err |
| } |
| } |
| // If either operand is a big int, convert both to a big int. |
| // Also, if one operand is a uint64 and the other is an int64, convert both to big ints. |
| if lhsValue.Type == query_parser.TypBigInt || rhsValue.Type == query_parser.TypBigInt || (lhsValue.Type == query_parser.TypUint && rhsValue.Type == query_parser.TypInt) || (lhsValue.Type == query_parser.TypInt && rhsValue.Type == query_parser.TypUint) { |
| if lhsValue, err = convertValueToBigInt(lhsValue); err != nil { |
| return nil, nil, err |
| } |
| if rhsValue, err = convertValueToBigInt(rhsValue); err != nil { |
| return nil, nil, err |
| } |
| } |
| // If either operand is an int64, convert the other to int64. |
| if lhsValue.Type == query_parser.TypInt || rhsValue.Type == query_parser.TypInt { |
| if lhsValue, err = convertValueToInt(lhsValue); err != nil { |
| return nil, nil, err |
| } |
| if rhsValue, err = convertValueToInt(rhsValue); err != nil { |
| return nil, nil, err |
| } |
| } |
| // If either operand is an uint64, convert the other to uint64. |
| if lhsValue.Type == query_parser.TypUint || rhsValue.Type == query_parser.TypUint { |
| if lhsValue, err = convertValueToUint(lhsValue); err != nil { |
| return nil, nil, err |
| } |
| if rhsValue, err = convertValueToUint(rhsValue); err != nil { |
| return nil, nil, err |
| } |
| } |
| // Must be the same at this point. |
| if lhsValue.Type != rhsValue.Type { |
| return nil, nil, errors.New(fmt.Sprintf("Logic error: expeced like types, got: %v, %v", lhsValue, rhsValue)) |
| } |
| |
| return lhsValue, rhsValue, nil |
| } |
| |
| func convertValueToString(o *query_parser.Operand) (*query_parser.Operand, error) { |
| var c query_parser.Operand |
| c.Type = query_parser.TypLiteral |
| switch o.Type { |
| case query_parser.TypBigInt: |
| c.Literal = o.BigInt.String() |
| case query_parser.TypBigRat: |
| c.Literal = o.BigRat.String() |
| case query_parser.TypBool: |
| c.Literal = strconv.FormatBool(o.Bool) |
| case query_parser.TypFloat: |
| c.Literal = strconv.FormatFloat(o.Float, 'f', -1, 64) |
| case query_parser.TypInt: |
| c.Literal = strconv.FormatInt(o.Int, 10) |
| case query_parser.TypLiteral: |
| c.Literal = o.Literal |
| c.Regex = o.Regex // non-nil for rhs of like expressions |
| c.CompRegex = o.CompRegex // non-nil for rhs of like expressions |
| case query_parser.TypUint: |
| c.Literal = strconv.FormatUint(o.Uint, 10) |
| default: // query_parser.TypObject |
| return nil, errors.New("Cannot convert object to string for comparison.") |
| } |
| return &c, nil |
| } |
| |
| func convertValueToBigRat(o *query_parser.Operand) (*query_parser.Operand, error) { |
| // operand cannot be literal. |
| var c query_parser.Operand |
| c.Type = query_parser.TypBigRat |
| switch o.Type { |
| case query_parser.TypBigInt: |
| var b big.Rat |
| c.BigRat = b.SetInt(o.BigInt) |
| case query_parser.TypBigRat: |
| c.BigRat = o.BigRat |
| case query_parser.TypBool: |
| return nil, errors.New("Cannot convert bool to big.Rat for comparison.") |
| case query_parser.TypFloat: |
| var b big.Rat |
| c.BigRat = b.SetFloat64(o.Float) |
| case query_parser.TypInt: |
| c.BigRat = big.NewRat(o.Int, 1) |
| case query_parser.TypUint: |
| var bi big.Int |
| bi.SetUint64(o.Uint) |
| var br big.Rat |
| c.BigRat = br.SetInt(&bi) |
| default: // query_parser.TypObject |
| return nil, errors.New("Cannot convert object to big.Rat for comparison.") |
| } |
| return &c, nil |
| } |
| |
| func convertValueToFloat(o *query_parser.Operand) (*query_parser.Operand, error) { |
| // Operand cannot be literal, big.Rat or big.Int |
| var c query_parser.Operand |
| c.Type = query_parser.TypFloat |
| switch o.Type { |
| case query_parser.TypBool: |
| return nil, errors.New("Cannot convert bool to float64 for comparison.") |
| case query_parser.TypFloat: |
| c.Float = o.Float |
| case query_parser.TypInt: |
| c.Float = float64(o.Int) |
| case query_parser.TypUint: |
| c.Float = float64(o.Uint) |
| default: // query_parser.TypObject |
| return nil, errors.New("Cannot convert object to float64 for comparison.") |
| } |
| return &c, nil |
| } |
| |
| func convertValueToBigInt(o *query_parser.Operand) (*query_parser.Operand, error) { |
| // Operand cannot be literal, big.Rat or float. |
| var c query_parser.Operand |
| c.Type = query_parser.TypBigInt |
| switch o.Type { |
| case query_parser.TypBigInt: |
| c.BigInt = o.BigInt |
| case query_parser.TypBool: |
| return nil, errors.New("Cannot convert bool to big.Int for comparison.") |
| case query_parser.TypInt: |
| c.BigInt = big.NewInt(o.Int) |
| case query_parser.TypUint: |
| var b big.Int |
| b.SetUint64(o.Uint) |
| c.BigInt = &b |
| default: // case query_parser.TypObject |
| return nil, errors.New("Cannot convert object to big.Int for comparison.") |
| } |
| return &c, nil |
| } |
| |
| func convertValueToInt(o *query_parser.Operand) (*query_parser.Operand, error) { |
| // Operand cannot be literal, big.Rat or float or uint64. |
| var c query_parser.Operand |
| c.Type = query_parser.TypInt |
| switch o.Type { |
| case query_parser.TypBool: |
| return nil, errors.New("Cannot convert bool to int64 for comparison.") |
| case query_parser.TypInt: |
| c.Int = o.Int |
| default: //case query_parser.TypObject |
| return nil, errors.New("Cannot convert object to int64 for comparison.") |
| } |
| return &c, nil |
| } |
| |
| func convertValueToUint(o *query_parser.Operand) (*query_parser.Operand, error) { |
| // Operand cannot be literal, big.Rat or float or int64. |
| var c query_parser.Operand |
| c.Type = query_parser.TypUint |
| switch o.Type { |
| case query_parser.TypBool: |
| return nil, errors.New("Cannot convert bool to int64 for comparison.") |
| case query_parser.TypUint: |
| c.Uint = o.Uint |
| default: //case query_parser.TypObject |
| return nil, errors.New("Cannot convert object to int64 for comparison.") |
| } |
| return &c, nil |
| } |
| |
| func compareBools(lhsValue, rhsValue *query_parser.Operand, oper *query_parser.BinaryOperator) bool { |
| switch oper.Type { |
| case query_parser.Equal: |
| return lhsValue.Bool == rhsValue.Bool |
| default: // query_parser.NotEqual |
| return lhsValue.Bool != rhsValue.Bool |
| } |
| } |
| |
| func compareBigInts(lhsValue, rhsValue *query_parser.Operand, oper *query_parser.BinaryOperator) bool { |
| switch oper.Type { |
| case query_parser.Equal: |
| return lhsValue.BigInt.Cmp(rhsValue.BigInt) == 0 |
| case query_parser.NotEqual: |
| return lhsValue.BigInt.Cmp(rhsValue.BigInt) != 0 |
| case query_parser.LessThan: |
| return lhsValue.BigInt.Cmp(rhsValue.BigInt) < 0 |
| case query_parser.LessThanOrEqual: |
| return lhsValue.BigInt.Cmp(rhsValue.BigInt) <= 0 |
| case query_parser.GreaterThan: |
| return lhsValue.BigInt.Cmp(rhsValue.BigInt) > 0 |
| default: // case query_parser.GreaterThanOrEqual |
| return lhsValue.BigInt.Cmp(rhsValue.BigInt) >= 0 |
| } |
| } |
| |
| func compareBigRats(lhsValue, rhsValue *query_parser.Operand, oper *query_parser.BinaryOperator) bool { |
| switch oper.Type { |
| case query_parser.Equal: |
| return lhsValue.BigRat.Cmp(rhsValue.BigRat) == 0 |
| case query_parser.NotEqual: |
| return lhsValue.BigRat.Cmp(rhsValue.BigRat) != 0 |
| case query_parser.LessThan: |
| return lhsValue.BigRat.Cmp(rhsValue.BigRat) < 0 |
| case query_parser.LessThanOrEqual: |
| return lhsValue.BigRat.Cmp(rhsValue.BigRat) <= 0 |
| case query_parser.GreaterThan: |
| return lhsValue.BigRat.Cmp(rhsValue.BigRat) > 0 |
| default: // case query_parser.GreaterThanOrEqual |
| return lhsValue.BigRat.Cmp(rhsValue.BigRat) >= 0 |
| } |
| } |
| |
| func compareFloats(lhsValue, rhsValue *query_parser.Operand, oper *query_parser.BinaryOperator) bool { |
| switch oper.Type { |
| case query_parser.Equal: |
| return lhsValue.Float == rhsValue.Float |
| case query_parser.NotEqual: |
| return lhsValue.Float != rhsValue.Float |
| case query_parser.LessThan: |
| return lhsValue.Float < rhsValue.Float |
| case query_parser.LessThanOrEqual: |
| return lhsValue.Float <= rhsValue.Float |
| case query_parser.GreaterThan: |
| return lhsValue.Float > rhsValue.Float |
| default: // case query_parser.GreaterThanOrEqual |
| return lhsValue.Float >= rhsValue.Float |
| } |
| } |
| |
| func compareInts(lhsValue, rhsValue *query_parser.Operand, oper *query_parser.BinaryOperator) bool { |
| switch oper.Type { |
| case query_parser.Equal: |
| return lhsValue.Int == rhsValue.Int |
| case query_parser.NotEqual: |
| return lhsValue.Int != rhsValue.Int |
| case query_parser.LessThan: |
| return lhsValue.Int < rhsValue.Int |
| case query_parser.LessThanOrEqual: |
| return lhsValue.Int <= rhsValue.Int |
| case query_parser.GreaterThan: |
| return lhsValue.Int > rhsValue.Int |
| default: // case query_parser.GreaterThanOrEqual |
| return lhsValue.Int >= rhsValue.Int |
| } |
| } |
| |
| func compareUints(lhsValue, rhsValue *query_parser.Operand, oper *query_parser.BinaryOperator) bool { |
| switch oper.Type { |
| case query_parser.Equal: |
| return lhsValue.Uint == rhsValue.Uint |
| case query_parser.NotEqual: |
| return lhsValue.Uint != rhsValue.Uint |
| case query_parser.LessThan: |
| return lhsValue.Uint < rhsValue.Uint |
| case query_parser.LessThanOrEqual: |
| return lhsValue.Uint <= rhsValue.Uint |
| case query_parser.GreaterThan: |
| return lhsValue.Uint > rhsValue.Uint |
| default: // case query_parser.GreaterThanOrEqual |
| return lhsValue.Uint >= rhsValue.Uint |
| } |
| } |
| |
| func compareStrings(lhsValue, rhsValue *query_parser.Operand, oper *query_parser.BinaryOperator) bool { |
| switch oper.Type { |
| case query_parser.Equal: |
| return lhsValue.Literal == rhsValue.Literal |
| case query_parser.NotEqual: |
| return lhsValue.Literal != rhsValue.Literal |
| case query_parser.LessThan: |
| return lhsValue.Literal < rhsValue.Literal |
| case query_parser.LessThanOrEqual: |
| return lhsValue.Literal <= rhsValue.Literal |
| case query_parser.GreaterThan: |
| return lhsValue.Literal > rhsValue.Literal |
| case query_parser.GreaterThanOrEqual: |
| return lhsValue.Literal >= rhsValue.Literal |
| case query_parser.Like: |
| return rhsValue.CompRegex.MatchString(lhsValue.Literal) |
| default: // query_parser.NotLike: |
| return !rhsValue.CompRegex.MatchString(lhsValue.Literal) |
| } |
| } |
| |
| func compareObjects(lhsValue, rhsValue *query_parser.Operand, oper *query_parser.BinaryOperator) bool { |
| switch oper.Type { |
| case query_parser.Equal: |
| return reflect.DeepEqual(lhsValue.Object, rhsValue.Object) |
| case query_parser.NotEqual: |
| return !reflect.DeepEqual(lhsValue.Object, rhsValue.Object) |
| default: // other operands are non-sensical |
| return false |
| } |
| } |
| |
| func resolveOperand(v interface{}, o *query_parser.Operand) *query_parser.Operand { |
| if o.Type != query_parser.TypField { |
| return o |
| } |
| value := ResolveField(v, o.Column) |
| |
| // Convert value to an operand |
| var newOp query_parser.Operand |
| newOp.Off = o.Off |
| |
| switch value := value.(type) { |
| case bool: |
| newOp.Type = query_parser.TypBool |
| newOp.Bool = value |
| case int: |
| newOp.Type = query_parser.TypInt |
| newOp.Int = int64(value) |
| case int8: |
| newOp.Type = query_parser.TypInt |
| newOp.Int = int64(value) |
| case int16: |
| newOp.Type = query_parser.TypInt |
| newOp.Int = int64(value) |
| case int32: // rune |
| newOp.Type = query_parser.TypInt |
| newOp.Int = int64(value) |
| case int64: |
| newOp.Type = query_parser.TypInt |
| newOp.Int = value |
| case uint: |
| newOp.Type = query_parser.TypBigInt |
| var b big.Int |
| b.SetUint64(uint64(value)) |
| newOp.BigInt = &b |
| case uint8: // byte |
| newOp.Type = query_parser.TypInt |
| newOp.Int = int64(value) |
| case uint16: |
| newOp.Type = query_parser.TypInt |
| newOp.Int = int64(value) |
| case uint32: |
| newOp.Type = query_parser.TypInt |
| newOp.Int = int64(value) |
| case uint64: |
| newOp.Type = query_parser.TypBigInt |
| var b big.Int |
| b.SetUint64(value) |
| newOp.BigInt = &b |
| case float32: |
| newOp.Type = query_parser.TypFloat |
| newOp.Float = float64(value) |
| case float64: |
| newOp.Type = query_parser.TypFloat |
| newOp.Float = value |
| case string: |
| newOp.Type = query_parser.TypLiteral |
| newOp.Literal = value |
| case *big.Int: |
| newOp.Type = query_parser.TypBigInt |
| newOp.BigInt = value |
| case *big.Rat: |
| newOp.Type = query_parser.TypBigRat |
| newOp.BigRat = value |
| default: // OpObject for structs, arrays, maps, ... |
| newOp.Type = query_parser.TypObject |
| newOp.Object = value |
| } |
| return &newOp |
| } |
| |
| func ResolveField(v interface{}, f *query_parser.Field) interface{} { |
| var object interface{} |
| object = v |
| segments := f.Segments |
| // The first segment will always be v itself, skip it. |
| for i := 1; i < len(segments); i++ { |
| // object must be a struct in order to look for the next segment. |
| if reflect.ValueOf(object).Kind() != reflect.Struct { |
| return nil // field does not exist |
| } |
| // Look up the segment in object. |
| _, ok := reflect.ValueOf(object).Type().FieldByName(segments[i].Value) |
| if !ok { |
| return nil // field does not exist |
| } |
| if !reflect.ValueOf(object).FieldByName(segments[i].Value).CanInterface() { |
| return nil |
| } |
| object = reflect.ValueOf(object).FieldByName(segments[i].Value).Interface() |
| } |
| return object |
| } |
| |
| // Evaluate an expression where the first operand refers to the key. |
| func evalKeyExpression(e *query_parser.Expression, k string) bool { |
| // Need to evaluate the key expression. |
| // Currently, only = and like are allowed. |
| // Operand2 must be a string literal. |
| switch e.Operator.Type { |
| case query_parser.Equal: |
| return k == e.Operand2.Literal |
| default: // query_parse.Like |
| return e.Operand2.CompRegex.MatchString(k) |
| } |
| } |
| |
| func evalTypeExpression(e *query_parser.Expression, v interface{}) bool { |
| if v == nil { |
| // The type expression does not match. |
| return false |
| } |
| // First try to match on the full type. |
| if reflect.ValueOf(v).Type().PkgPath()+"."+reflect.ValueOf(v).Type().Name() == e.Operand2.Literal { |
| return true |
| } |
| // Try to match on just the name. |
| return reflect.ValueOf(v).Type().Name() == e.Operand2.Literal |
| } |
| |
| // Evaluate the where clause, substituting false for all expressions involving the key and |
| // true for all other expressions. If the answer is true, it is possible to satisfy the |
| // expression for any key. As such, all keys must be fetched. |
| func CheckIfAllKeysMustBeFetched(e *query_parser.Expression) bool { |
| switch e.Operator.Type { |
| case query_parser.And: |
| return CheckIfAllKeysMustBeFetched(e.Operand1.Expr) && CheckIfAllKeysMustBeFetched(e.Operand2.Expr) |
| case query_parser.Or: |
| return CheckIfAllKeysMustBeFetched(e.Operand1.Expr) || CheckIfAllKeysMustBeFetched(e.Operand2.Expr) |
| default: // =, > >=, <, <=, Like, <>, NotLike |
| if query_checker.IsKey(e.Operand1) { |
| return false |
| } else { |
| return true |
| } |
| } |
| } |
| |
| // Evaluate the where clause to determine if the row should be selected, but do so using only |
| // the key. Possible returns are: |
| // true: the row should included in the results |
| // false: the row should NOT be included |
| // error: the value and/or type of the value are required to determine if row should be included. |
| // The above decision is accomplished by evaluating all expressions which reference the key and |
| // substituing false for all other expressions. If the result is true, true is returned. |
| // If the result is false, but no other experssions (i.e., expressions which refer to the type |
| // of the value or the value itself) were encountered, false is returned; else, an error is |
| // returned indicating the value must be fetched in order to determine if the row should be included |
| // in the results. |
| func EvalWhereUsingOnlyKey(s *query_parser.SelectStatement, k string) (bool, error) { |
| if s.Where == nil { // all rows will be in result |
| return true, nil |
| } |
| return evalExprUsingOnlyKey(s.Where.Expr, k) |
| } |
| |
| func evalExprUsingOnlyKey(e *query_parser.Expression, k string) (bool, error) { |
| switch e.Operator.Type { |
| case query_parser.And: |
| op1Result, err1 := evalExprUsingOnlyKey(e.Operand1.Expr, k) |
| op2Result, err2 := evalExprUsingOnlyKey(e.Operand2.Expr, k) |
| if op1Result && op2Result { |
| return true, nil |
| } else if (op1Result == false && err1 == nil) || (op2Result == false && err2 == nil) { |
| // One of the operands evaluated to false with no error. |
| // As such, the value is not needed to reject the row. |
| return false, nil |
| } else { |
| if err1 != nil { |
| return false, err1 |
| } else { |
| return false, err2 |
| } |
| } |
| case query_parser.Or: |
| op1Result, err1 := evalExprUsingOnlyKey(e.Operand1.Expr, k) |
| op2Result, err2 := evalExprUsingOnlyKey(e.Operand2.Expr, k) |
| if op1Result || op2Result { |
| return true, nil |
| } else { |
| if err1 != nil { |
| return false, err1 |
| } else { |
| return false, err2 // err2 may or may not be nil |
| } |
| } |
| default: // =, > >=, <, <=, Like, <>, NotLike |
| if !query_checker.IsKey(e.Operand1) { |
| // Non-key expressions are evaluated as false. |
| return false, errors.New("Value required for answer.") // err text not used |
| } else { |
| return evalKeyExpression(e, k), nil |
| } |
| } |
| } |