ddefed03e6
Beforehand it was possible to do SELECT * FROM $ which would show all of the variables defined. This is now not possible, and variables must be specified using their defined name.
1310 lines
25 KiB
Go
1310 lines
25 KiB
Go
// Copyright © 2016 Abcum Ltd
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
"math"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/text/language"
|
|
"golang.org/x/text/search"
|
|
|
|
"github.com/abcum/surreal/cnf"
|
|
"github.com/abcum/surreal/sql"
|
|
"github.com/abcum/surreal/util/data"
|
|
"github.com/abcum/surreal/util/deep"
|
|
"github.com/abcum/surreal/util/fncs"
|
|
)
|
|
|
|
var ign = data.New()
|
|
|
|
func (e *executor) fetch(ctx context.Context, val interface{}, doc *data.Doc) (out interface{}, err error) {
|
|
|
|
switch val := val.(type) {
|
|
default:
|
|
return val, nil
|
|
case *sql.Null:
|
|
return nil, nil
|
|
case *sql.Thing:
|
|
return val, nil
|
|
case *sql.Value:
|
|
return val.ID, nil
|
|
case int:
|
|
return float64(val), nil
|
|
case int8:
|
|
return float64(val), nil
|
|
case int16:
|
|
return float64(val), nil
|
|
case int32:
|
|
return float64(val), nil
|
|
case int64:
|
|
return float64(val), nil
|
|
case uint:
|
|
return float64(val), nil
|
|
case uint16:
|
|
return float64(val), nil
|
|
case uint32:
|
|
return float64(val), nil
|
|
case uint64:
|
|
return float64(val), nil
|
|
case []byte:
|
|
return string(val), nil
|
|
case []interface{}:
|
|
return deep.Copy(val), nil
|
|
case map[string]interface{}:
|
|
return deep.Copy(val), nil
|
|
|
|
case *sql.Regex:
|
|
|
|
return regexp.Compile(val.ID)
|
|
|
|
case *sql.Ident:
|
|
|
|
switch {
|
|
default:
|
|
return val, nil
|
|
case doc == ign:
|
|
return val, queryIdentFailed
|
|
case doc != nil:
|
|
|
|
fnc := func(key string, val interface{}, path []string) interface{} {
|
|
if len(path) > 0 {
|
|
switch res := val.(type) {
|
|
case []interface{}:
|
|
val, _ = e.fetchArray(ctx, res, doc)
|
|
return val
|
|
case *sql.Thing:
|
|
val, _ = e.fetchThing(ctx, res, doc)
|
|
return val
|
|
}
|
|
}
|
|
return val
|
|
}
|
|
|
|
res := doc.Fetch(fnc, val.ID).Data()
|
|
|
|
return e.fetch(ctx, res, doc)
|
|
|
|
}
|
|
|
|
case *sql.Param:
|
|
|
|
if len(val.ID) > 0 {
|
|
|
|
for _, s := range paramSearchKeys {
|
|
|
|
if obj, ok := ctx.Value(s).(*data.Doc); ok {
|
|
|
|
fnc := func(key string, val interface{}, path []string) interface{} {
|
|
if len(path) > 0 {
|
|
switch res := val.(type) {
|
|
case []interface{}:
|
|
val, _ = e.fetchArray(ctx, res, doc)
|
|
return val
|
|
case *sql.Thing:
|
|
val, _ = e.fetchThing(ctx, res, doc)
|
|
return val
|
|
}
|
|
}
|
|
return val
|
|
}
|
|
|
|
res := obj.Fetch(fnc, val.ID).Data()
|
|
|
|
if res != nil {
|
|
return e.fetch(ctx, res, doc)
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
case *sql.RunStatement:
|
|
|
|
return e.fetch(ctx, val.Expr, doc)
|
|
|
|
case *sql.IfStatement:
|
|
|
|
for k, v := range val.Cond {
|
|
ife, err := e.fetch(ctx, v, doc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if chk, ok := ife.(bool); ok && chk {
|
|
return e.fetch(ctx, val.Then[k], doc)
|
|
}
|
|
}
|
|
return e.fetch(ctx, val.Else, doc)
|
|
|
|
case *sql.IfelExpression:
|
|
|
|
for k, v := range val.Cond {
|
|
ife, err := e.fetch(ctx, v, doc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if chk, ok := ife.(bool); ok && chk {
|
|
return e.fetch(ctx, val.Then[k], doc)
|
|
}
|
|
}
|
|
return e.fetch(ctx, val.Else, doc)
|
|
|
|
case *sql.FuncExpression:
|
|
|
|
var args []interface{}
|
|
for _, v := range val.Args {
|
|
val, err := e.fetch(ctx, v, doc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
args = append(args, val)
|
|
}
|
|
res, err := fncs.Run(ctx, val.Name, args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return e.fetch(ctx, res, doc)
|
|
|
|
case *sql.SubExpression:
|
|
|
|
switch exp := val.Expr.(type) {
|
|
default:
|
|
return e.fetch(ctx, exp, doc)
|
|
case *sql.SelectStatement:
|
|
return e.fetchSelect(ctx, exp, doc)
|
|
case *sql.CreateStatement:
|
|
return e.fetchCreate(ctx, exp, doc)
|
|
case *sql.UpdateStatement:
|
|
return e.fetchUpdate(ctx, exp, doc)
|
|
case *sql.DeleteStatement:
|
|
return e.fetchDelete(ctx, exp, doc)
|
|
case *sql.RelateStatement:
|
|
return e.fetchRelate(ctx, exp, doc)
|
|
case *sql.InsertStatement:
|
|
return e.fetchInsert(ctx, exp, doc)
|
|
case *sql.UpsertStatement:
|
|
return e.fetchUpsert(ctx, exp, doc)
|
|
}
|
|
|
|
case *sql.MultExpression:
|
|
|
|
for _, exp := range val.Expr {
|
|
|
|
switch exp := exp.(type) {
|
|
default:
|
|
out, err = e.fetch(ctx, exp, doc)
|
|
case *sql.SelectStatement:
|
|
out, err = e.fetchSelect(ctx, exp, doc)
|
|
case *sql.CreateStatement:
|
|
out, err = e.fetchCreate(ctx, exp, doc)
|
|
case *sql.UpdateStatement:
|
|
out, err = e.fetchUpdate(ctx, exp, doc)
|
|
case *sql.DeleteStatement:
|
|
out, err = e.fetchDelete(ctx, exp, doc)
|
|
case *sql.RelateStatement:
|
|
out, err = e.fetchRelate(ctx, exp, doc)
|
|
case *sql.InsertStatement:
|
|
out, err = e.fetchInsert(ctx, exp, doc)
|
|
case *sql.UpsertStatement:
|
|
out, err = e.fetchUpsert(ctx, exp, doc)
|
|
}
|
|
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
case *sql.PathExpression:
|
|
|
|
return e.fetchPaths(ctx, doc, val.Expr...)
|
|
|
|
case *sql.BinaryExpression:
|
|
|
|
l, err := e.fetch(ctx, val.LHS, doc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch val.Op {
|
|
case sql.OR:
|
|
if calcAsBool(l) {
|
|
return true, nil
|
|
}
|
|
case sql.AND:
|
|
if !calcAsBool(l) {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
r, err := e.fetch(ctx, val.RHS, doc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch val.Op {
|
|
case sql.EEQ:
|
|
return l == r, nil
|
|
case sql.NEE:
|
|
return l != r, nil
|
|
case sql.AND, sql.OR:
|
|
return binaryBool(val.Op, l, r), nil
|
|
case sql.ADD, sql.SUB, sql.MUL, sql.DIV, sql.INC, sql.DEC:
|
|
return binaryMath(val.Op, l, r), nil
|
|
case sql.EQ, sql.NEQ, sql.ANY, sql.LT, sql.LTE, sql.GT, sql.GTE:
|
|
return binaryCheck(val.Op, l, r, val.LHS, val.RHS, doc), nil
|
|
case sql.SIN, sql.SNI, sql.INS, sql.NIS, sql.MAT, sql.NAT, sql.MAY:
|
|
return binaryCheck(val.Op, l, r, val.LHS, val.RHS, doc), nil
|
|
case sql.CONTAINSALL, sql.CONTAINSSOME, sql.CONTAINSNONE:
|
|
return binaryCheck(val.Op, l, r, val.LHS, val.RHS, doc), nil
|
|
case sql.ALLCONTAINEDIN, sql.SOMECONTAINEDIN, sql.NONECONTAINEDIN:
|
|
return binaryCheck(val.Op, l, r, val.LHS, val.RHS, doc), nil
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
func (e *executor) fetchPaths(ctx context.Context, doc *data.Doc, exprs ...sql.Expr) (interface{}, error) {
|
|
|
|
var expr sql.Expr
|
|
|
|
if len(exprs) == 0 {
|
|
return doc.Data(), nil
|
|
}
|
|
|
|
expr, exprs = exprs[0], exprs[1:]
|
|
|
|
switch val := expr.(type) {
|
|
case *sql.JoinExpression:
|
|
switch val.Join {
|
|
case sql.DOT:
|
|
return e.fetchPaths(ctx, doc, exprs...)
|
|
case sql.OEDGE:
|
|
return nil, errFeatureNotImplemented
|
|
case sql.IEDGE:
|
|
return nil, errFeatureNotImplemented
|
|
case sql.BEDGE:
|
|
return nil, errFeatureNotImplemented
|
|
}
|
|
case *sql.PartExpression:
|
|
switch val := val.Part.(type) {
|
|
case *sql.All:
|
|
return e.fetchPaths(ctx, doc, exprs...)
|
|
case *sql.Param:
|
|
res, err := e.fetch(ctx, val, doc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return e.fetchPaths(ctx, data.Consume(res), exprs...)
|
|
case *sql.Ident:
|
|
res, err := e.fetch(ctx, val, doc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return e.fetchPaths(ctx, data.Consume(res), exprs...)
|
|
case *sql.Thing:
|
|
res, err := e.fetchThing(ctx, val, doc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return e.fetchPaths(ctx, data.Consume(res), exprs...)
|
|
}
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
func (e *executor) fetchThing(ctx context.Context, val *sql.Thing, doc *data.Doc) (interface{}, error) {
|
|
|
|
ver, err := e.fetchVersion(ctx, ctx.Value(ctxKeyVersion))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res, err := e.executeSelect(ctx, &sql.SelectStatement{
|
|
KV: cnf.Settings.DB.Base,
|
|
NS: ctx.Value(ctxKeyNs).(string),
|
|
DB: ctx.Value(ctxKeyDb).(string),
|
|
Expr: []*sql.Field{{Expr: &sql.All{}}},
|
|
What: []sql.Expr{val},
|
|
Version: sql.Expr(ver),
|
|
Parallel: 1,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(res) > 0 {
|
|
return res[0], nil
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
func (e *executor) fetchArray(ctx context.Context, val []interface{}, doc *data.Doc) (interface{}, error) {
|
|
|
|
ver, err := e.fetchVersion(ctx, ctx.Value(ctxKeyVersion))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res, err := e.executeSelect(ctx, &sql.SelectStatement{
|
|
KV: cnf.Settings.DB.Base,
|
|
NS: ctx.Value(ctxKeyNs).(string),
|
|
DB: ctx.Value(ctxKeyDb).(string),
|
|
Expr: []*sql.Field{{Expr: &sql.All{}}},
|
|
What: []sql.Expr{val},
|
|
Version: sql.Expr(ver),
|
|
Parallel: 1,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
func (e *executor) fetchPerms(ctx context.Context, val sql.Expr, tb *sql.Ident) error {
|
|
|
|
// If the table does exist we reset the
|
|
// context to DB level so that no other
|
|
// embedded permissions are checked on
|
|
// records within these permissions.
|
|
|
|
ctx = context.WithValue(ctx, ctxKeyKind, cnf.AuthDB)
|
|
|
|
// We then try to process the relevant
|
|
// permissions expression, but only if
|
|
// the specified expression doesn't
|
|
// reference any document fields.
|
|
|
|
res, err := e.fetch(ctx, val, ign)
|
|
|
|
// If we receive an 'ident failed' error
|
|
// it is because the table permission
|
|
// expression contains a field check,
|
|
// and therefore we must check each
|
|
// record individually to see if it can
|
|
// be accessed or not.
|
|
|
|
if err != queryIdentFailed {
|
|
if res, ok := res.(bool); ok && !res {
|
|
return &PermsError{table: tb.ID}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (e *executor) fetchLimit(ctx context.Context, val sql.Expr) (int, error) {
|
|
|
|
v, err := e.fetch(ctx, val, nil)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
switch v := v.(type) {
|
|
case float64:
|
|
return int(v), nil
|
|
case int64:
|
|
return int(v), nil
|
|
case nil:
|
|
return -1, nil
|
|
default:
|
|
return -1, &LimitError{found: v}
|
|
}
|
|
|
|
}
|
|
|
|
func (e *executor) fetchStart(ctx context.Context, val sql.Expr) (int, error) {
|
|
|
|
v, err := e.fetch(ctx, val, nil)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
switch v := v.(type) {
|
|
case float64:
|
|
return int(v), nil
|
|
case int64:
|
|
return int(v), nil
|
|
case nil:
|
|
return -1, nil
|
|
default:
|
|
return -1, &StartError{found: v}
|
|
}
|
|
|
|
}
|
|
|
|
func (e *executor) fetchVersion(ctx context.Context, val sql.Expr) (int64, error) {
|
|
|
|
if v, ok := val.(int64); ok {
|
|
return v, nil
|
|
}
|
|
|
|
v, err := e.fetch(ctx, val, nil)
|
|
if err != nil {
|
|
return math.MaxInt64, err
|
|
}
|
|
|
|
switch v := v.(type) {
|
|
case time.Time:
|
|
return v.UnixNano(), nil
|
|
case nil:
|
|
return math.MaxInt64, nil
|
|
default:
|
|
return math.MaxInt64, &VersnError{found: v}
|
|
}
|
|
|
|
}
|
|
|
|
func (e *executor) fetchOutputs(ctx context.Context, stm *sql.SelectStatement) (int, error) {
|
|
|
|
l, err := e.fetchLimit(ctx, stm.Limit)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
if len(stm.What) == 1 {
|
|
if _, ok := stm.What[0].(*sql.Thing); ok {
|
|
l = 1
|
|
}
|
|
}
|
|
|
|
return l, nil
|
|
|
|
}
|
|
|
|
func calcAsBool(i interface{}) bool {
|
|
|
|
switch v := i.(type) {
|
|
default:
|
|
return false
|
|
case bool:
|
|
return v
|
|
case int64:
|
|
return v > 0
|
|
case float64:
|
|
return v > 0
|
|
case string:
|
|
return v != ""
|
|
case time.Time:
|
|
return v.UnixNano() > 0
|
|
case *sql.Thing:
|
|
return true
|
|
case []interface{}:
|
|
return len(v) > 0
|
|
case map[string]interface{}:
|
|
return len(v) > 0
|
|
}
|
|
|
|
}
|
|
|
|
func calcAsMath(i interface{}) float64 {
|
|
|
|
switch v := i.(type) {
|
|
default:
|
|
return 0
|
|
case bool:
|
|
if v {
|
|
return 1
|
|
}
|
|
return 0
|
|
case int64:
|
|
return float64(v)
|
|
case float64:
|
|
return v
|
|
case time.Time:
|
|
return float64(v.UnixNano())
|
|
}
|
|
|
|
}
|
|
|
|
func binaryBool(op sql.Token, l, r interface{}) interface{} {
|
|
|
|
a := calcAsBool(l)
|
|
b := calcAsBool(r)
|
|
|
|
switch op {
|
|
case sql.AND:
|
|
return a && b
|
|
case sql.OR:
|
|
return a || b
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func binaryMath(op sql.Token, l, r interface{}) interface{} {
|
|
|
|
a := calcAsMath(l)
|
|
b := calcAsMath(r)
|
|
|
|
switch op {
|
|
case sql.ADD, sql.INC:
|
|
return a + b
|
|
case sql.SUB, sql.DEC:
|
|
return a - b
|
|
case sql.MUL:
|
|
return a * b
|
|
case sql.DIV:
|
|
if b != 0 {
|
|
return a / b
|
|
}
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func binaryCheck(op sql.Token, l, r, lo, ro interface{}, d *data.Doc) interface{} {
|
|
|
|
switch lo.(type) {
|
|
case *sql.Void:
|
|
switch ro.(type) {
|
|
case *sql.Null:
|
|
return op == sql.NEQ
|
|
case *sql.Void:
|
|
return op == sql.EQ
|
|
case *sql.Empty:
|
|
return op == sql.EQ
|
|
}
|
|
case *sql.Empty:
|
|
switch ro.(type) {
|
|
case *sql.Null:
|
|
return op == sql.EQ
|
|
case *sql.Void:
|
|
return op == sql.EQ
|
|
case *sql.Empty:
|
|
return op == sql.EQ
|
|
}
|
|
}
|
|
|
|
switch ro.(type) {
|
|
case *sql.Void:
|
|
switch lo.(type) {
|
|
case *sql.Null:
|
|
return op == sql.NEQ
|
|
case *sql.Void:
|
|
return op == sql.EQ
|
|
case *sql.Empty:
|
|
return op == sql.EQ
|
|
}
|
|
case *sql.Empty:
|
|
switch lo.(type) {
|
|
case *sql.Null:
|
|
return op == sql.EQ
|
|
case *sql.Void:
|
|
return op == sql.EQ
|
|
case *sql.Empty:
|
|
return op == sql.EQ
|
|
}
|
|
}
|
|
|
|
if d != nil {
|
|
|
|
switch lo.(type) {
|
|
case *sql.Void:
|
|
switch r := ro.(type) {
|
|
case *sql.Ident:
|
|
if op == sql.EQ {
|
|
return d.Exists(r.ID) == false
|
|
} else if op == sql.NEQ {
|
|
return d.Exists(r.ID) == true
|
|
}
|
|
}
|
|
case *sql.Null:
|
|
switch r := ro.(type) {
|
|
case *sql.Ident:
|
|
if op == sql.EQ {
|
|
return d.Exists(r.ID) == true && d.Get(r.ID).Data() == nil
|
|
} else if op == sql.NEQ {
|
|
return d.Exists(r.ID) == false || d.Get(r.ID).Data() != nil
|
|
}
|
|
}
|
|
}
|
|
|
|
switch ro.(type) {
|
|
case *sql.Void:
|
|
switch l := lo.(type) {
|
|
case *sql.Ident:
|
|
if op == sql.EQ {
|
|
return d.Exists(l.ID) == false
|
|
} else if op == sql.NEQ {
|
|
return d.Exists(l.ID) == true
|
|
}
|
|
}
|
|
case *sql.Null:
|
|
switch l := lo.(type) {
|
|
case *sql.Ident:
|
|
if op == sql.EQ {
|
|
return d.Exists(l.ID) == true && d.Get(l.ID).Data() == nil
|
|
} else if op == sql.NEQ {
|
|
return d.Exists(l.ID) == false || d.Get(l.ID).Data() != nil
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
switch l := l.(type) {
|
|
|
|
case nil:
|
|
switch r := r.(type) {
|
|
case nil:
|
|
return op == sql.EQ
|
|
case *sql.Null:
|
|
return op == sql.EQ
|
|
case *sql.Empty:
|
|
return op == sql.EQ
|
|
case []interface{}:
|
|
return chkArrayR(op, l, r)
|
|
}
|
|
|
|
case *sql.Null:
|
|
switch r := r.(type) {
|
|
case nil:
|
|
return op == sql.EQ
|
|
case *sql.Null:
|
|
return op == sql.EQ
|
|
case *sql.Empty:
|
|
return op == sql.EQ
|
|
case []interface{}:
|
|
return chkArrayR(op, l, r)
|
|
}
|
|
|
|
case *sql.Empty:
|
|
switch r := r.(type) {
|
|
case nil:
|
|
return op == sql.EQ
|
|
case *sql.Null:
|
|
return op == sql.EQ
|
|
case *sql.Empty:
|
|
return op == sql.EQ
|
|
case string:
|
|
return chkLen(op, r)
|
|
case []interface{}:
|
|
return chkArrayR(op, l, r)
|
|
case map[string]interface{}:
|
|
return chkObject(op, r, l)
|
|
}
|
|
|
|
case *sql.Thing:
|
|
switch r := r.(type) {
|
|
case *sql.Thing:
|
|
return chkThing(op, l, r)
|
|
case string:
|
|
return chkString(op, r, l.String())
|
|
case []interface{}:
|
|
return chkArrayR(op, l, r)
|
|
}
|
|
|
|
case bool:
|
|
switch r := r.(type) {
|
|
case bool:
|
|
return chkBool(op, l, r)
|
|
case string:
|
|
if b, err := strconv.ParseBool(r); err == nil {
|
|
return chkBool(op, l, b)
|
|
}
|
|
case *regexp.Regexp:
|
|
return chkRegex(op, strconv.FormatBool(l), r)
|
|
case []interface{}:
|
|
return chkArrayR(op, l, r)
|
|
}
|
|
|
|
case string:
|
|
switch r := r.(type) {
|
|
case bool:
|
|
if b, err := strconv.ParseBool(l); err == nil {
|
|
return chkBool(op, r, b)
|
|
}
|
|
case string:
|
|
return chkString(op, l, r)
|
|
case int64:
|
|
if n, err := strconv.ParseInt(l, 10, 64); err == nil {
|
|
return chkInt(op, r, n)
|
|
}
|
|
case float64:
|
|
if n, err := strconv.ParseFloat(l, 64); err == nil {
|
|
return chkFloat(op, r, n)
|
|
}
|
|
case time.Time:
|
|
return chkString(op, l, r.String())
|
|
case *sql.Empty:
|
|
return chkLen(op, l)
|
|
case *sql.Thing:
|
|
return chkString(op, l, r.String())
|
|
case *regexp.Regexp:
|
|
return chkRegex(op, l, r)
|
|
case []interface{}:
|
|
return chkArrayR(op, l, r)
|
|
}
|
|
|
|
case int64:
|
|
switch r := r.(type) {
|
|
case string:
|
|
if n, err := strconv.ParseInt(r, 10, 64); err == nil {
|
|
return chkInt(op, l, n)
|
|
}
|
|
case int64:
|
|
return chkInt(op, l, r)
|
|
case float64:
|
|
return chkFloat(op, float64(l), r)
|
|
case time.Time:
|
|
return chkInt(op, l, r.UnixNano())
|
|
case *regexp.Regexp:
|
|
return chkRegex(op, strconv.FormatInt(l, 10), r)
|
|
case []interface{}:
|
|
return chkArrayR(op, l, r)
|
|
}
|
|
|
|
case float64:
|
|
switch r := r.(type) {
|
|
case string:
|
|
if n, err := strconv.ParseFloat(r, 64); err == nil {
|
|
return chkFloat(op, l, n)
|
|
}
|
|
case int64:
|
|
return chkFloat(op, l, float64(r))
|
|
case float64:
|
|
return chkFloat(op, l, r)
|
|
case time.Time:
|
|
return chkFloat(op, l, float64(r.UnixNano()))
|
|
case *regexp.Regexp:
|
|
return chkRegex(op, strconv.FormatFloat(l, 'g', -1, 64), r)
|
|
case []interface{}:
|
|
return chkArrayR(op, l, r)
|
|
}
|
|
|
|
case time.Time:
|
|
switch r := r.(type) {
|
|
case string:
|
|
return chkString(op, l.String(), r)
|
|
case int64:
|
|
return chkInt(op, l.UnixNano(), r)
|
|
case float64:
|
|
return chkFloat(op, float64(l.UnixNano()), r)
|
|
case time.Time:
|
|
return chkInt(op, l.UnixNano(), r.UnixNano())
|
|
case *regexp.Regexp:
|
|
return chkRegex(op, l.String(), r)
|
|
case []interface{}:
|
|
return chkArrayR(op, l, r)
|
|
}
|
|
|
|
case []interface{}:
|
|
switch r := r.(type) {
|
|
default:
|
|
return chkArrayL(op, l, r)
|
|
case bool:
|
|
return chkArrayL(op, l, r)
|
|
case string:
|
|
return chkArrayL(op, l, r)
|
|
case int64:
|
|
return chkArrayL(op, l, r)
|
|
case float64:
|
|
return chkArrayL(op, l, r)
|
|
case time.Time:
|
|
return chkArrayL(op, l, r)
|
|
case *regexp.Regexp:
|
|
return chkMatch(op, l, r)
|
|
case []interface{}:
|
|
return chkArray(op, l, r)
|
|
case map[string]interface{}:
|
|
return chkArrayL(op, l, r)
|
|
}
|
|
|
|
case map[string]interface{}:
|
|
switch r := r.(type) {
|
|
default:
|
|
return chkObject(op, l, r)
|
|
case []interface{}:
|
|
return chkArrayR(op, l, r)
|
|
case map[string]interface{}:
|
|
return chkObject(op, l, r)
|
|
}
|
|
|
|
}
|
|
|
|
return negOp(op)
|
|
|
|
}
|
|
|
|
func posOp(op sql.Token) bool {
|
|
return chkOp(op) > 1
|
|
}
|
|
|
|
func negOp(op sql.Token) bool {
|
|
return chkOp(op) < 0
|
|
}
|
|
|
|
func chkOp(op sql.Token) int8 {
|
|
switch op {
|
|
case sql.EQ, sql.SIN, sql.INS, sql.MAT, sql.ANY:
|
|
return +1
|
|
case sql.NEQ, sql.SNI, sql.NIS, sql.NAT, sql.MAY:
|
|
return -1
|
|
case sql.CONTAINSALL:
|
|
return +1
|
|
case sql.CONTAINSSOME:
|
|
return +1
|
|
case sql.CONTAINSNONE:
|
|
return -1
|
|
case sql.ALLCONTAINEDIN:
|
|
return +1
|
|
case sql.SOMECONTAINEDIN:
|
|
return +1
|
|
case sql.NONECONTAINEDIN:
|
|
return -1
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func chkLen(op sql.Token, s string) (val bool) {
|
|
switch op {
|
|
case sql.EQ:
|
|
return len(s) == 0
|
|
case sql.NEQ:
|
|
return len(s) != 0
|
|
}
|
|
return negOp(op)
|
|
}
|
|
|
|
func chkBool(op sql.Token, a, b bool) (val bool) {
|
|
switch op {
|
|
case sql.EQ:
|
|
return a == b
|
|
case sql.NEQ:
|
|
return a != b
|
|
}
|
|
return negOp(op)
|
|
}
|
|
|
|
func chkString(op sql.Token, a, b string) (val bool) {
|
|
switch op {
|
|
case sql.EQ:
|
|
return a == b
|
|
case sql.NEQ:
|
|
return a != b
|
|
case sql.LT:
|
|
return a < b
|
|
case sql.LTE:
|
|
return a <= b
|
|
case sql.GT:
|
|
return a > b
|
|
case sql.GTE:
|
|
return a >= b
|
|
case sql.INS:
|
|
return strings.Contains(b, a) == true
|
|
case sql.NIS:
|
|
return strings.Contains(b, a) == false
|
|
case sql.SIN:
|
|
return strings.Contains(a, b) == true
|
|
case sql.SNI:
|
|
return strings.Contains(a, b) == false
|
|
case sql.MAT:
|
|
b, e := search.New(language.Und, search.Loose).IndexString(a, b)
|
|
return b != -1 && e != -1
|
|
case sql.NAT:
|
|
b, e := search.New(language.Und, search.Loose).IndexString(a, b)
|
|
return b == -1 && e == -1
|
|
case sql.MAY:
|
|
b, e := search.New(language.Und, search.Loose).IndexString(a, b)
|
|
return b != -1 && e != -1
|
|
}
|
|
return negOp(op)
|
|
}
|
|
|
|
func chkInt(op sql.Token, a, b int64) (val bool) {
|
|
switch op {
|
|
case sql.EQ:
|
|
return a == b
|
|
case sql.NEQ:
|
|
return a != b
|
|
case sql.LT:
|
|
return a < b
|
|
case sql.LTE:
|
|
return a <= b
|
|
case sql.GT:
|
|
return a > b
|
|
case sql.GTE:
|
|
return a >= b
|
|
}
|
|
return negOp(op)
|
|
}
|
|
|
|
func chkFloat(op sql.Token, a, b float64) (val bool) {
|
|
switch op {
|
|
case sql.EQ:
|
|
return a == b
|
|
case sql.NEQ:
|
|
return a != b
|
|
case sql.LT:
|
|
return a < b
|
|
case sql.LTE:
|
|
return a <= b
|
|
case sql.GT:
|
|
return a > b
|
|
case sql.GTE:
|
|
return a >= b
|
|
}
|
|
return negOp(op)
|
|
}
|
|
|
|
func chkThing(op sql.Token, a, b *sql.Thing) (val bool) {
|
|
switch op {
|
|
case sql.EQ:
|
|
return a.TB == b.TB && a.ID == b.ID
|
|
case sql.NEQ:
|
|
return a.TB != b.TB || a.ID != b.ID
|
|
}
|
|
return negOp(op)
|
|
}
|
|
|
|
func chkRegex(op sql.Token, a string, r *regexp.Regexp) (val bool) {
|
|
switch op {
|
|
case sql.EQ:
|
|
return r.MatchString(a) == true
|
|
case sql.NEQ:
|
|
return r.MatchString(a) == false
|
|
case sql.ANY:
|
|
return r.MatchString(a) == true
|
|
}
|
|
return negOp(op)
|
|
}
|
|
|
|
func chkObject(op sql.Token, m map[string]interface{}, i interface{}) (val bool) {
|
|
switch op {
|
|
case sql.EQ:
|
|
switch i.(type) {
|
|
case *sql.Empty:
|
|
return len(m) == 0
|
|
default:
|
|
return reflect.TypeOf(m) == reflect.TypeOf(i) && reflect.DeepEqual(m, i) == true
|
|
}
|
|
case sql.NEQ:
|
|
switch i.(type) {
|
|
case *sql.Empty:
|
|
return len(m) != 0
|
|
default:
|
|
return reflect.TypeOf(m) != reflect.TypeOf(i) || reflect.DeepEqual(m, i) == false
|
|
}
|
|
}
|
|
return negOp(op)
|
|
}
|
|
|
|
func chkArrayL(op sql.Token, a []interface{}, i interface{}) (val bool) {
|
|
switch op {
|
|
case sql.EQ:
|
|
switch i.(type) {
|
|
case *sql.Empty:
|
|
return len(a) == 0
|
|
default:
|
|
return false
|
|
}
|
|
case sql.NEQ:
|
|
switch i.(type) {
|
|
case *sql.Empty:
|
|
return len(a) != 0
|
|
default:
|
|
return true
|
|
}
|
|
case sql.SIN:
|
|
switch i.(type) {
|
|
case nil, *sql.Null:
|
|
return data.Consume(a).Contains(nil) == true
|
|
default:
|
|
return data.Consume(a).Contains(i) == true
|
|
}
|
|
case sql.SNI:
|
|
switch i.(type) {
|
|
case nil, *sql.Null:
|
|
return data.Consume(a).Contains(nil) == false
|
|
default:
|
|
return data.Consume(a).Contains(i) == false
|
|
}
|
|
case sql.MAT:
|
|
switch s := i.(type) {
|
|
case string:
|
|
return chkSearch(op, a, s)
|
|
}
|
|
case sql.NAT:
|
|
switch s := i.(type) {
|
|
case string:
|
|
return chkSearch(op, a, s)
|
|
}
|
|
case sql.MAY:
|
|
switch s := i.(type) {
|
|
case string:
|
|
return chkSearch(op, a, s)
|
|
}
|
|
}
|
|
return negOp(op)
|
|
}
|
|
|
|
func chkArrayR(op sql.Token, i interface{}, a []interface{}) (val bool) {
|
|
switch op {
|
|
case sql.EQ:
|
|
switch i.(type) {
|
|
case *sql.Empty:
|
|
return len(a) == 0
|
|
default:
|
|
return false
|
|
}
|
|
case sql.NEQ:
|
|
switch i.(type) {
|
|
case *sql.Empty:
|
|
return len(a) != 0
|
|
default:
|
|
return true
|
|
}
|
|
case sql.INS:
|
|
switch i.(type) {
|
|
case nil, *sql.Null:
|
|
return data.Consume(a).Contains(nil) == true
|
|
default:
|
|
return data.Consume(a).Contains(i) == true
|
|
}
|
|
case sql.NIS:
|
|
switch i.(type) {
|
|
case nil, *sql.Null:
|
|
return data.Consume(a).Contains(nil) == false
|
|
default:
|
|
return data.Consume(a).Contains(i) == false
|
|
}
|
|
case sql.MAT:
|
|
switch s := i.(type) {
|
|
case string:
|
|
return chkSearch(op, a, s)
|
|
}
|
|
case sql.NAT:
|
|
switch s := i.(type) {
|
|
case string:
|
|
return chkSearch(op, a, s)
|
|
}
|
|
case sql.MAY:
|
|
switch s := i.(type) {
|
|
case string:
|
|
return chkSearch(op, a, s)
|
|
}
|
|
}
|
|
return negOp(op)
|
|
}
|
|
|
|
func chkArray(op sql.Token, a []interface{}, b []interface{}) (val bool) {
|
|
switch op {
|
|
case sql.EQ:
|
|
if reflect.TypeOf(a) == reflect.TypeOf(b) && reflect.DeepEqual(a, b) == true {
|
|
return true
|
|
}
|
|
case sql.NEQ:
|
|
if reflect.TypeOf(a) != reflect.TypeOf(b) || reflect.DeepEqual(a, b) == false {
|
|
return true
|
|
}
|
|
case sql.SIN:
|
|
return data.Consume(a).Contains(b) == true
|
|
case sql.SNI:
|
|
return data.Consume(a).Contains(b) == false
|
|
case sql.INS:
|
|
return data.Consume(b).Contains(a) == true
|
|
case sql.NIS:
|
|
return data.Consume(b).Contains(a) == false
|
|
case sql.CONTAINSALL:
|
|
for _, v := range b {
|
|
if data.Consume(a).Contains(v) == false {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case sql.CONTAINSSOME:
|
|
for _, v := range b {
|
|
if data.Consume(a).Contains(v) == true {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
case sql.CONTAINSNONE:
|
|
for _, v := range b {
|
|
if data.Consume(a).Contains(v) == true {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case sql.ALLCONTAINEDIN:
|
|
for _, v := range a {
|
|
if data.Consume(b).Contains(v) == false {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case sql.SOMECONTAINEDIN:
|
|
for _, v := range a {
|
|
if data.Consume(b).Contains(v) == true {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
case sql.NONECONTAINEDIN:
|
|
for _, v := range a {
|
|
if data.Consume(b).Contains(v) == true {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
return
|
|
}
|
|
|
|
func chkMatch(op sql.Token, a []interface{}, r *regexp.Regexp) (val bool) {
|
|
|
|
if len(a) == 0 {
|
|
return op == sql.NEQ
|
|
}
|
|
|
|
for _, v := range a {
|
|
|
|
var s string
|
|
|
|
switch c := v.(type) {
|
|
default:
|
|
return false
|
|
case string:
|
|
s = c
|
|
case bool:
|
|
s = strconv.FormatBool(c)
|
|
case int64:
|
|
s = strconv.FormatInt(c, 10)
|
|
case float64:
|
|
s = strconv.FormatFloat(c, 'g', -1, 64)
|
|
case time.Time:
|
|
s = c.String()
|
|
}
|
|
|
|
if op == sql.EQ {
|
|
if chkRegex(sql.EQ, s, r) == false {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if op == sql.NEQ {
|
|
if chkRegex(sql.EQ, s, r) == false {
|
|
return true
|
|
}
|
|
}
|
|
|
|
if op == sql.ANY {
|
|
if chkRegex(sql.EQ, s, r) == true {
|
|
return true
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
switch op {
|
|
case sql.EQ:
|
|
return true
|
|
case sql.NEQ:
|
|
return false
|
|
case sql.ANY:
|
|
return false
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
func chkSearch(op sql.Token, a []interface{}, r string) (val bool) {
|
|
|
|
if len(a) == 0 {
|
|
return op == sql.NAT
|
|
}
|
|
|
|
for _, v := range a {
|
|
|
|
var s string
|
|
|
|
switch c := v.(type) {
|
|
default:
|
|
return false
|
|
case string:
|
|
s = c
|
|
case bool:
|
|
s = strconv.FormatBool(c)
|
|
case int64:
|
|
s = strconv.FormatInt(c, 10)
|
|
case float64:
|
|
s = strconv.FormatFloat(c, 'g', -1, 64)
|
|
case time.Time:
|
|
s = c.String()
|
|
}
|
|
|
|
if op == sql.MAT {
|
|
b, e := search.New(language.Und, search.Loose).IndexString(s, r)
|
|
if b == -1 && e == -1 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if op == sql.NAT {
|
|
b, e := search.New(language.Und, search.Loose).IndexString(s, r)
|
|
if b == -1 && e == -1 {
|
|
return true
|
|
}
|
|
}
|
|
|
|
if op == sql.MAY {
|
|
b, e := search.New(language.Und, search.Loose).IndexString(s, r)
|
|
if b != -1 && e != -1 {
|
|
return true
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
switch op {
|
|
case sql.MAT:
|
|
return true
|
|
case sql.NAT:
|
|
return false
|
|
case sql.MAY:
|
|
return false
|
|
}
|
|
|
|
return
|
|
|
|
}
|