Files
chaisql/internal/expr/comparison.go
Asdine El Hrychy 37ebf47ea8 Move packages under internal package
These packages are not part of the public API and can break at any time.
2021-05-23 11:05:41 +04:00

236 lines
5.7 KiB
Go

package expr
import (
"github.com/genjidb/genji/document"
"github.com/genjidb/genji/internal/stringutil"
"github.com/genjidb/genji/sql/scanner"
)
// A cmpOp is a comparison operator.
type cmpOp struct {
*simpleOperator
}
// newCmpOp creates a comparison operator.
func newCmpOp(a, b Expr, t scanner.Token) *cmpOp {
return &cmpOp{&simpleOperator{a, b, t}}
}
// Eval compares a and b together using the operator specified when constructing the CmpOp
// and returns the result of the comparison.
// Comparing with NULL always evaluates to NULL.
func (op *cmpOp) Eval(env *Environment) (document.Value, error) {
return op.simpleOperator.eval(env, func(a, b document.Value) (document.Value, error) {
if a.Type == document.NullValue || b.Type == document.NullValue {
return nullLitteral, nil
}
ok, err := op.compare(a, b)
if ok {
return trueLitteral, err
}
return falseLitteral, err
})
}
func (op *cmpOp) compare(l, r document.Value) (bool, error) {
switch op.Tok {
case scanner.EQ:
return l.IsEqual(r)
case scanner.NEQ:
return l.IsNotEqual(r)
case scanner.GT:
return l.IsGreaterThan(r)
case scanner.GTE:
return l.IsGreaterThanOrEqual(r)
case scanner.LT:
return l.IsLesserThan(r)
case scanner.LTE:
return l.IsLesserThanOrEqual(r)
default:
panic(stringutil.Sprintf("unknown token %v", op.Tok))
}
}
// Eq creates an expression that returns true if a equals b.
func Eq(a, b Expr) Expr {
return newCmpOp(a, b, scanner.EQ)
}
// Neq creates an expression that returns true if a equals b.
func Neq(a, b Expr) Expr {
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 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 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 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 newCmpOp(a, b, scanner.LTE)
}
type BetweenOperator struct {
*simpleOperator
X Expr
}
// Between returns a function that creates a BETWEEN operator that
// returns true if x is between a and b.
func Between(a Expr) func(x, b Expr) Expr {
return func(x, b Expr) Expr {
return &BetweenOperator{&simpleOperator{a, b, scanner.BETWEEN}, x}
}
}
func (op *BetweenOperator) Eval(env *Environment) (document.Value, error) {
x, err := op.X.Eval(env)
if err != nil {
return falseLitteral, err
}
return op.simpleOperator.eval(env, func(a, b document.Value) (document.Value, error) {
if a.Type == document.NullValue || b.Type == document.NullValue {
return nullLitteral, nil
}
ok, err := x.IsGreaterThanOrEqual(a)
if !ok || err != nil {
return falseLitteral, err
}
ok, err = x.IsLesserThanOrEqual(b)
if !ok || err != nil {
return falseLitteral, err
}
return trueLitteral, nil
})
}
func (op *BetweenOperator) String() string {
return stringutil.Sprintf("%v BETWEEN %v AND %v", op.X, op.a, op.b)
}
// IsComparisonOperator returns true if e is one of
// =, !=, >, >=, <, <=, IS, IS NOT, IN, or NOT IN operators.
func IsComparisonOperator(op Operator) bool {
switch op.(type) {
case *cmpOp, *IsOperator, *IsNotOperator, *InOperator, *NotInOperator, *LikeOperator, *NotLikeOperator, *BetweenOperator:
return true
}
return false
}
type InOperator struct {
*simpleOperator
}
// In creates an expression that evaluates to the result of a IN b.
func In(a, b Expr) Expr {
return &InOperator{&simpleOperator{a, b, scanner.IN}}
}
func (op *InOperator) Eval(env *Environment) (document.Value, error) {
return op.simpleOperator.eval(env, func(a, b document.Value) (document.Value, error) {
if a.Type == document.NullValue || b.Type == document.NullValue {
return nullLitteral, nil
}
if b.Type != document.ArrayValue {
return falseLitteral, nil
}
ok, err := document.ArrayContains(b.V.(document.Array), a)
if err != nil {
return nullLitteral, err
}
if ok {
return trueLitteral, nil
}
return falseLitteral, nil
})
}
type NotInOperator struct {
InOperator
}
// 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.NIN}}}
}
func (op *NotInOperator) Eval(env *Environment) (document.Value, error) {
return invertBoolResult(op.InOperator.Eval)(env)
}
func (op *NotInOperator) String() string {
return stringutil.Sprintf("%v NOT IN %v", op.a, op.b)
}
type IsOperator struct {
*simpleOperator
}
// Is creates an expression that evaluates to the result of a IS b.
func Is(a, b Expr) Expr {
return &IsOperator{&simpleOperator{a, b, scanner.IN}}
}
func (op *IsOperator) Eval(env *Environment) (document.Value, error) {
return op.simpleOperator.eval(env, func(a, b document.Value) (document.Value, error) {
ok, err := a.IsEqual(b)
if err != nil {
return nullLitteral, err
}
if ok {
return trueLitteral, nil
}
return falseLitteral, nil
})
}
type IsNotOperator struct {
*simpleOperator
}
// 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.ISN}}
}
func (op *IsNotOperator) Eval(env *Environment) (document.Value, error) {
return op.simpleOperator.eval(env, func(a, b document.Value) (document.Value, error) {
ok, err := a.IsNotEqual(b)
if err != nil {
return nullLitteral, err
}
if ok {
return trueLitteral, nil
}
return falseLitteral, nil
})
}
func (op *IsNotOperator) String() string {
return stringutil.Sprintf("%v IS NOT %v", op.a, op.b)
}