diff --git a/expr/comparison.go b/expr/comparison.go index 4bf4a9359..494341d1e 100644 --- a/expr/comparison.go +++ b/expr/comparison.go @@ -53,58 +53,34 @@ func (op *cmpOp) compare(l, r document.Value) (bool, error) { } } -type EqOperator struct { - *cmpOp -} - // Eq creates an expression that returns true if a equals b. func Eq(a, b Expr) Expr { - return &EqOperator{newCmpOp(a, b, scanner.EQ)} -} - -type NeqOperator struct { - *cmpOp + return newCmpOp(a, b, scanner.EQ) } // Neq creates an expression that returns true if a equals b. func Neq(a, b Expr) Expr { - return &NeqOperator{newCmpOp(a, b, scanner.NEQ)} -} - -type GtOperator struct { - *cmpOp + return newCmpOp(a, b, scanner.NEQ) } // Gt creates an expression that returns true if a is greater than b. func Gt(a, b Expr) Expr { - return &GtOperator{newCmpOp(a, b, scanner.GT)} -} - -type GteOperator struct { - *cmpOp + return newCmpOp(a, b, scanner.GT) } // Gte creates an expression that returns true if a is greater than or equal to b. func Gte(a, b Expr) Expr { - return &GteOperator{newCmpOp(a, b, scanner.GTE)} -} - -type LtOperator struct { - *cmpOp + return newCmpOp(a, b, scanner.GTE) } // Lt creates an expression that returns true if a is lesser than b. func Lt(a, b Expr) Expr { - return &LtOperator{newCmpOp(a, b, scanner.LT)} -} - -type LteOperator struct { - *cmpOp + return newCmpOp(a, b, scanner.LT) } // Lte creates an expression that returns true if a is lesser than or equal to b. func Lte(a, b Expr) Expr { - return &LteOperator{newCmpOp(a, b, scanner.LTE)} + return newCmpOp(a, b, scanner.LTE) } type BetweenOperator struct { @@ -153,50 +129,13 @@ func (op *BetweenOperator) String() string { // =, !=, >, >=, <, <=, IS, IS NOT, IN, or NOT IN operators. func IsComparisonOperator(op Operator) bool { switch op.(type) { - case *EqOperator, *NeqOperator, *GtOperator, *GteOperator, *LtOperator, *LteOperator, - *IsOperator, *IsNotOperator, *InOperator, *NotInOperator, *LikeOperator, *NotLikeOperator, *BetweenOperator: + case *cmpOp, *IsOperator, *IsNotOperator, *InOperator, *NotInOperator, *LikeOperator, *NotLikeOperator, *BetweenOperator: return true } return false } -// IsEqualOperator returns true if e is the = operator -func IsEqualOperator(op Operator) bool { - _, ok := op.(*EqOperator) - return ok -} - -// IsAndOperator reports if e is the AND operator. -func IsAndOperator(op Operator) bool { - _, ok := op.(*AndOp) - return ok -} - -// IsOrOperator reports if e is the OR operator. -func IsOrOperator(e Expr) bool { - _, ok := e.(*OrOp) - return ok -} - -// IsInOperator reports if e is the IN operator. -func IsInOperator(e Expr) bool { - _, ok := e.(*InOperator) - return ok -} - -// IsNotInOperator reports if e is the NOT IN operator. -func IsNotInOperator(e Expr) bool { - _, ok := e.(*NotInOperator) - return ok -} - -// IsBetweenOperator reports if e is the BETWEEN operator. -func IsBetweenOperator(e Expr) bool { - _, ok := e.(*BetweenOperator) - return ok -} - type InOperator struct { *simpleOperator } @@ -234,7 +173,7 @@ type NotInOperator struct { // NotIn creates an expression that evaluates to the result of a NOT IN b. func NotIn(a, b Expr) Expr { - return &NotInOperator{InOperator{&simpleOperator{a, b, scanner.IN}}} + return &NotInOperator{InOperator{&simpleOperator{a, b, scanner.NIN}}} } func (op *NotInOperator) Eval(env *Environment) (document.Value, error) { @@ -274,7 +213,7 @@ type IsNotOperator struct { // IsNot creates an expression that evaluates to the result of a IS NOT b. func IsNot(a, b Expr) Expr { - return &IsNotOperator{&simpleOperator{a, b, scanner.IN}} + return &IsNotOperator{&simpleOperator{a, b, scanner.ISN}} } func (op *IsNotOperator) Eval(env *Environment) (document.Value, error) { diff --git a/expr/operator.go b/expr/operator.go index 839d45742..a3bc17bb8 100644 --- a/expr/operator.go +++ b/expr/operator.go @@ -85,8 +85,8 @@ type Operator interface { // OperatorIsIndexCompatible returns whether the operator can be used to read from an index. func OperatorIsIndexCompatible(op Operator) bool { - switch op.(type) { - case *EqOperator, *GtOperator, *GteOperator, *LtOperator, *LteOperator, *InOperator: + switch op.Token() { + case scanner.EQ, scanner.GT, scanner.GTE, scanner.LT, scanner.LTE, scanner.IN: return true } diff --git a/planner/optimizer.go b/planner/optimizer.go index 08bfc9335..cd11daea9 100644 --- a/planner/optimizer.go +++ b/planner/optimizer.go @@ -4,6 +4,7 @@ import ( "github.com/genjidb/genji/database" "github.com/genjidb/genji/document" "github.com/genjidb/genji/expr" + "github.com/genjidb/genji/sql/scanner" "github.com/genjidb/genji/stream" "github.com/genjidb/genji/stringutil" ) @@ -59,7 +60,7 @@ func SplitANDConditionRule(s *stream.Stream, _ *database.Transaction, _ []expr.P // only OR has a lower precedence, // which means that if AND is used without OR, it will be at // the top of the expression tree. - if op, ok := cond.(expr.Operator); ok && expr.IsAndOperator(op) { + if op, ok := cond.(expr.Operator); ok && op.Token() == scanner.AND { exprs := splitANDExpr(cond) cur := n.GetPrev() @@ -85,7 +86,7 @@ func SplitANDConditionRule(s *stream.Stream, _ *database.Transaction, _ []expr.P // splitANDExpr takes an expression and splits it by AND operator. func splitANDExpr(cond expr.Expr) (exprs []expr.Expr) { op, ok := cond.(expr.Operator) - if ok && expr.IsAndOperator(op) { + if ok && op.Token() == scanner.AND { exprs = append(exprs, splitANDExpr(op.LeftHand())...) exprs = append(exprs, splitANDExpr(op.RightHand())...) return @@ -192,8 +193,9 @@ func precalculateExpr(e expr.Expr, params []expr.Param) (expr.Expr, error) { // since expr.Operator is an interface, // this optimization must only be applied to // a few selected operators that we know about. - if !expr.IsAndOperator(t) && - !expr.IsOrOperator(t) && + tok := t.Token() + if tok != scanner.AND && + tok != scanner.OR && !expr.IsArithmeticOperator(t) && !expr.IsComparisonOperator(t) { return e, nil @@ -498,7 +500,7 @@ func UseIndexBasedOnFilterNodeRule(s *stream.Stream, tx *database.Transaction, p isNodeEq := func(fno *filterNode) bool { op := fno.f.E.(expr.Operator) - return expr.IsEqualOperator(op) || expr.IsInOperator(op) + return op.Token() == scanner.EQ || op.Token() == scanner.IN } isNodeComp := func(fno *filterNode) bool { op := fno.f.E.(expr.Operator) @@ -688,7 +690,7 @@ func operatorCanUseIndex(op expr.Operator) (bool, document.Path, expr.Expr) { // Special case for IN operator: only left operand is valid for index usage // valid: a IN [1, 2, 3] // invalid: 1 IN a - if expr.IsInOperator(op) { + if op.Token() == scanner.IN { if leftIsField && !rightIsField { rh := op.RightHand() // The IN operator can use indexes only if the right hand side is an array with constants. @@ -746,10 +748,8 @@ func getRangesFromFilterNodes(fnodes []*filterNode) (stream.IndexRanges, error) op := fno.f.E.(expr.Operator) v := fno.v - switch op.(type) { - case *expr.EqOperator, *expr.GtOperator, *expr.GteOperator, *expr.LtOperator, *expr.LteOperator: - vb = vb.Append(v) - case *expr.InOperator: + switch { + case op.Token() == scanner.IN: // mark where the IN operator values are supposed to go is in the buffer // and what are the value needed to generate the ranges. // operatorCanUseIndex made sure v is an array. @@ -757,6 +757,8 @@ func getRangesFromFilterNodes(fnodes []*filterNode) (stream.IndexRanges, error) // placeholder for when we'll explode the IN operands in multiple ranges vb = vb.Append(document.Value{}) + case expr.IsComparisonOperator(op): + vb = vb.Append(v) default: panic(stringutil.Sprintf("unknown operator %#v", op)) } @@ -771,19 +773,19 @@ func getRangesFromFilterNodes(fnodes []*filterNode) (stream.IndexRanges, error) buildRange := func(op expr.Operator, vb *document.ValueBuffer) stream.IndexRange { var rng stream.IndexRange - switch op.(type) { - case *expr.EqOperator, *expr.InOperator: + switch op.Token() { + case scanner.EQ, scanner.IN: rng.Exact = true rng.Min = vb - case *expr.GtOperator: + case scanner.GT: rng.Exclusive = true rng.Min = vb - case *expr.GteOperator: + case scanner.GTE: rng.Min = vb - case *expr.LtOperator: + case scanner.LT: rng.Exclusive = true rng.Max = vb - case *expr.LteOperator: + case scanner.LTE: rng.Max = vb } @@ -836,31 +838,31 @@ func getRangesFromFilterNodes(fnodes []*filterNode) (stream.IndexRanges, error) func getRangesFromOp(op expr.Operator, v document.Value) (stream.ValueRanges, error) { var ranges stream.ValueRanges - switch op.(type) { - case *expr.EqOperator: + switch op.Token() { + case scanner.EQ: ranges = ranges.Append(stream.ValueRange{ Min: v, Exact: true, }) - case *expr.GtOperator: + case scanner.GT: ranges = ranges.Append(stream.ValueRange{ Min: v, Exclusive: true, }) - case *expr.GteOperator: + case scanner.GTE: ranges = ranges.Append(stream.ValueRange{ Min: v, }) - case *expr.LtOperator: + case scanner.LT: ranges = ranges.Append(stream.ValueRange{ Max: v, Exclusive: true, }) - case *expr.LteOperator: + case scanner.LTE: ranges = ranges.Append(stream.ValueRange{ Max: v, }) - case *expr.InOperator: + case scanner.IN: // operatorCanUseIndex made sure e is an array. a := v.V.(document.Array) err := a.Iterate(func(i int, value document.Value) error { diff --git a/sql/scanner/token.go b/sql/scanner/token.go index cc94f3e0c..5923c6272 100644 --- a/sql/scanner/token.go +++ b/sql/scanner/token.go @@ -55,7 +55,9 @@ const ( GT // > GTE // >= IN // IN + NIN // NOT IN IS // IS + ISN // IS NOT LIKE // LIKE CONCAT // || operatorEnd