mirror of
https://github.com/chaisql/chai.git
synced 2025-10-13 03:14:04 +08:00
236 lines
5.7 KiB
Go
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)
|
|
}
|