Add subqueries, subexpressions, and paths to SQL
This commit is contained in:
parent
28d1806785
commit
68c22bc25c
17 changed files with 760 additions and 227 deletions
|
@ -21,11 +21,11 @@ import (
|
|||
|
||||
func (e *executor) executeLetStatement(txn kvs.TX, ast *sql.LetStatement) (out []interface{}, err error) {
|
||||
|
||||
switch expr := ast.Expr.(type) {
|
||||
switch what := ast.What.(type) {
|
||||
default:
|
||||
e.Set(ast.Name, expr)
|
||||
e.Set(ast.Name, what)
|
||||
case *sql.Param:
|
||||
e.Set(ast.Name, e.Get(expr.ID))
|
||||
e.Set(ast.Name, e.Get(what.ID))
|
||||
}
|
||||
|
||||
return
|
||||
|
|
|
@ -21,17 +21,13 @@ import (
|
|||
|
||||
func (e *executor) executeReturnStatement(txn kvs.TX, ast *sql.ReturnStatement) (out []interface{}, err error) {
|
||||
|
||||
for _, w := range ast.What {
|
||||
|
||||
switch what := w.(type) {
|
||||
switch what := ast.What.(type) {
|
||||
default:
|
||||
out = append(out, what)
|
||||
case *sql.Param:
|
||||
out = append(out, e.ctx.Get(what.ID).Data())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
|
84
sql/ast.go
84
sql/ast.go
|
@ -48,11 +48,6 @@ type CancelStatement struct{}
|
|||
// UseStatement represents a SQL COMMIT TRANSACTION statement.
|
||||
type CommitStatement struct{}
|
||||
|
||||
// ReturnStatement represents a SQL RETURN statement.
|
||||
type ReturnStatement struct {
|
||||
What []Expr
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Use
|
||||
// --------------------------------------------------
|
||||
|
@ -81,11 +76,13 @@ type InfoStatement struct {
|
|||
|
||||
// LetStatement represents a SQL LET statement.
|
||||
type LetStatement struct {
|
||||
KV string `cork:"-" codec:"-"`
|
||||
NS string `cork:"-" codec:"-"`
|
||||
DB string `cork:"-" codec:"-"`
|
||||
Name string `cork:"-" codec:"-"`
|
||||
Expr Expr `cork:"-" codec:"-"`
|
||||
What Expr `cork:"-" codec:"-"`
|
||||
}
|
||||
|
||||
// ReturnStatement represents a SQL RETURN statement.
|
||||
type ReturnStatement struct {
|
||||
What Expr `cork:"-" codec:"-"`
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
|
@ -99,7 +96,7 @@ type LiveStatement struct {
|
|||
DB string `cork:"-" codec:"-"`
|
||||
Expr []*Field `cork:"expr" codec:"expr"`
|
||||
What []Expr `cork:"what" codec:"what"`
|
||||
Cond []Expr `cork:"cond" codec:"cond"`
|
||||
Cond Expr `cork:"cond" codec:"cond"`
|
||||
Echo Token `cork:"echo" codec:"echo"`
|
||||
}
|
||||
|
||||
|
@ -110,7 +107,7 @@ type SelectStatement struct {
|
|||
DB string `cork:"-" codec:"-"`
|
||||
Expr []*Field `cork:"expr" codec:"expr"`
|
||||
What []Expr `cork:"what" codec:"what"`
|
||||
Cond []Expr `cork:"cond" codec:"cond"`
|
||||
Cond Expr `cork:"cond" codec:"cond"`
|
||||
Group []*Group `cork:"group" codec:"group"`
|
||||
Order []*Order `cork:"order" codec:"order"`
|
||||
Limit Expr `cork:"limit" codec:"limit"`
|
||||
|
@ -136,7 +133,7 @@ type UpdateStatement struct {
|
|||
DB string `cork:"-" codec:"-"`
|
||||
What []Expr `cork:"what" codec:"what"`
|
||||
Data []Expr `cork:"data" codec:"data"`
|
||||
Cond []Expr `cork:"cond" codec:"cond"`
|
||||
Cond Expr `cork:"cond" codec:"cond"`
|
||||
Echo Token `cork:"echo" codec:"echo"`
|
||||
}
|
||||
|
||||
|
@ -147,7 +144,7 @@ type DeleteStatement struct {
|
|||
DB string `cork:"-" codec:"-"`
|
||||
Hard bool `cork:"hard" codec:"hard"`
|
||||
What []Expr `cork:"what" codec:"what"`
|
||||
Cond []Expr `cork:"cond" codec:"cond"`
|
||||
Cond Expr `cork:"cond" codec:"cond"`
|
||||
Echo Token `cork:"echo" codec:"echo"`
|
||||
}
|
||||
|
||||
|
@ -156,10 +153,11 @@ type RelateStatement struct {
|
|||
KV string `cork:"-" codec:"-"`
|
||||
NS string `cork:"-" codec:"-"`
|
||||
DB string `cork:"-" codec:"-"`
|
||||
Type []Expr `cork:"type" codec:"type"`
|
||||
Type Expr `cork:"type" codec:"type"`
|
||||
From []Expr `cork:"from" codec:"from"`
|
||||
To []Expr `cork:"to" codec:"to"`
|
||||
With []Expr `cork:"with" codec:"with"`
|
||||
Data []Expr `cork:"data" codec:"data"`
|
||||
Uniq bool `cork:"uniq" codec:"uniq"`
|
||||
Echo Token `cork:"echo" codec:"echo"`
|
||||
}
|
||||
|
||||
|
@ -218,7 +216,7 @@ type DefineRulesStatement struct {
|
|||
What []string `cork:"-" codec:"-"`
|
||||
When []string `cork:"-" codec:"-"`
|
||||
Rule string `cork:"rule" codec:"rule"`
|
||||
Cond []Expr `cork:"cond" codec:"cond"`
|
||||
Cond Expr `cork:"cond" codec:"cond"`
|
||||
}
|
||||
|
||||
// RemoveRulesStatement represents an SQL REMOVE RULES statement.
|
||||
|
@ -299,7 +297,7 @@ type DefineViewStatement struct {
|
|||
Name string `cork:"name" codec:"name"`
|
||||
Expr []*Field `cork:"expr" codec:"expr"`
|
||||
What []Expr `cork:"what" codec:"what"`
|
||||
Cond []Expr `cork:"cond" codec:"cond"`
|
||||
Cond Expr `cork:"cond" codec:"cond"`
|
||||
Group []*Group `cork:"group" codec:"group"`
|
||||
}
|
||||
|
||||
|
@ -318,9 +316,12 @@ type RemoveViewStatement struct {
|
|||
// Expr represents a sql expression
|
||||
type Expr interface{}
|
||||
|
||||
// All represents a wildcard expression.
|
||||
// All represents a * expression.
|
||||
type All struct{}
|
||||
|
||||
// Any represents a ? expression.
|
||||
type Any struct{}
|
||||
|
||||
// Asc represents the ASC expression.
|
||||
type Asc struct{}
|
||||
|
||||
|
@ -373,19 +374,58 @@ type Order struct {
|
|||
Dir Expr
|
||||
}
|
||||
|
||||
// ClosedExpression represents a parenthesized expression.
|
||||
type ClosedExpression struct {
|
||||
// --------------------------------------------------
|
||||
// Expressions
|
||||
// --------------------------------------------------
|
||||
|
||||
// SubExpression represents a subquery.
|
||||
type SubExpression struct {
|
||||
Expr Expr
|
||||
}
|
||||
|
||||
// BinaryExpression represents a binary expression tree,
|
||||
// FuncExpression represents a function call.
|
||||
type FuncExpression struct {
|
||||
Name string
|
||||
Args []Expr
|
||||
}
|
||||
|
||||
// DataExpression represents a SET expression.
|
||||
type DataExpression struct {
|
||||
LHS Expr
|
||||
Op Token
|
||||
RHS Expr
|
||||
}
|
||||
|
||||
// BinaryExpression represents a WHERE expression.
|
||||
type BinaryExpression struct {
|
||||
LHS Expr
|
||||
Op Token
|
||||
RHS Expr
|
||||
}
|
||||
|
||||
// DiffExpression represents a JSON DIFF PATCH
|
||||
// PathExpression represents a path expression.
|
||||
type PathExpression struct {
|
||||
Expr []Expr
|
||||
}
|
||||
|
||||
// PartExpression represents a path part expression.
|
||||
type PartExpression struct {
|
||||
Part Expr
|
||||
}
|
||||
|
||||
// PartExpression represents a path join expression.
|
||||
type JoinExpression struct {
|
||||
Join Token
|
||||
}
|
||||
|
||||
// SubpExpression represents a sub path expression.
|
||||
type SubpExpression struct {
|
||||
What []Expr
|
||||
Name string
|
||||
Cond Expr
|
||||
}
|
||||
|
||||
// DiffExpression represents a JSON to DIFF
|
||||
type DiffExpression struct {
|
||||
JSON interface{}
|
||||
}
|
||||
|
|
93
sql/cond.go
93
sql/cond.go
|
@ -1,93 +0,0 @@
|
|||
// 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 sql
|
||||
|
||||
func (p *parser) parseCond() (mul []Expr, err error) {
|
||||
|
||||
var tok Token
|
||||
var lit string
|
||||
|
||||
// Remove the WHERE keyword
|
||||
if _, _, exi := p.mightBe(WHERE); !exi {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for {
|
||||
|
||||
one := &BinaryExpression{}
|
||||
|
||||
tok, lit, err = p.shouldBe(ID, IDENT, THING, NULL, VOID, MISSING, EMPTY, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, REGEX, JSON, ARRAY, PARAM)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
|
||||
}
|
||||
|
||||
one.LHS, err = p.declare(tok, lit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tok, lit, err = p.shouldBe(IS, IN, EQ, NEQ, EEQ, NEE, ANY, LT, LTE, GT, GTE, SIN, SNI, INS, NIS, CONTAINS, CONTAINSALL, CONTAINSNONE, CONTAINSSOME, ALLCONTAINEDIN, NONECONTAINEDIN, SOMECONTAINEDIN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
one.Op = tok
|
||||
|
||||
if tok == IN {
|
||||
one.Op = INS
|
||||
}
|
||||
|
||||
if tok == IS {
|
||||
one.Op = EQ
|
||||
if _, _, exi := p.mightBe(NOT); exi {
|
||||
one.Op = NEQ
|
||||
}
|
||||
if _, _, exi := p.mightBe(IN); exi {
|
||||
switch one.Op {
|
||||
case EQ:
|
||||
one.Op = INS
|
||||
case NEQ:
|
||||
one.Op = NIS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tok == CONTAINS {
|
||||
one.Op = SIN
|
||||
if _, _, exi := p.mightBe(NOT); exi {
|
||||
one.Op = SNI
|
||||
}
|
||||
}
|
||||
|
||||
tok, lit, err = p.shouldBe(ID, IDENT, THING, NULL, VOID, MISSING, EMPTY, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, REGEX, JSON, ARRAY, PARAM)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"field value"}}
|
||||
}
|
||||
|
||||
one.RHS, err = p.declare(tok, lit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mul = append(mul, one)
|
||||
|
||||
if _, _, exi := p.mightBe(AND, OR); !exi {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return mul, nil
|
||||
|
||||
}
|
34
sql/data.go
34
sql/data.go
|
@ -50,12 +50,16 @@ func (p *parser) parseData() (exp []Expr, err error) {
|
|||
|
||||
func (p *parser) parseSet() (mul []Expr, err error) {
|
||||
|
||||
for {
|
||||
|
||||
var tok Token
|
||||
var lit string
|
||||
|
||||
for {
|
||||
one := &DataExpression{}
|
||||
|
||||
one := &BinaryExpression{}
|
||||
// The first part of a SET expression must
|
||||
// always be an identifier, specifying a
|
||||
// record field to set.
|
||||
|
||||
tok, lit, err = p.shouldBe(IDENT)
|
||||
if err != nil {
|
||||
|
@ -67,32 +71,40 @@ func (p *parser) parseSet() (mul []Expr, err error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
tok, lit, err = p.shouldBe(EQ, INC, DEC)
|
||||
// The next query part must be a =, +=, or
|
||||
// -= operator, as this is a SET expression
|
||||
// and not a binary expression.
|
||||
|
||||
one.Op, lit, err = p.shouldBe(EQ, INC, DEC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
one.Op = tok
|
||||
|
||||
tok, lit, err = p.shouldBe(IDENT, THING, NULL, VOID, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, JSON, ARRAY, PARAM)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"field value"}}
|
||||
}
|
||||
// The next query part can be any expression
|
||||
// including a parenthesised expression or a
|
||||
// binary expression so handle accordingly.
|
||||
|
||||
one.RHS, err = p.declare(tok, lit)
|
||||
one.RHS, err = p.parseExpr()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Append the single SET data expression to
|
||||
// the array of data expressions.
|
||||
|
||||
mul = append(mul, one)
|
||||
|
||||
// Check to see if the next token is a comma
|
||||
// and if not, then break out of the loop,
|
||||
// otherwise repeat until we find no comma.
|
||||
|
||||
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||
p.unscan()
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return mul, nil
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,10 @@ package sql
|
|||
|
||||
func (p *parser) parseEcho() (exp Token, err error) {
|
||||
|
||||
// Next token might be RETURN
|
||||
// The next token that we expect to see is a
|
||||
// RETURN token, and if we don't find one then
|
||||
// return nil, with no error.
|
||||
|
||||
if _, _, exi := p.mightBe(RETURN); exi {
|
||||
|
||||
exp, _, err = p.shouldBe(ID, NONE, INFO, BOTH, DIFF, BEFORE, AFTER)
|
||||
|
|
503
sql/exprs.go
503
sql/exprs.go
|
@ -15,6 +15,7 @@
|
|||
package sql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
@ -43,7 +44,10 @@ func (p *parser) parseWhat() (mul []Expr, err error) {
|
|||
mul = append(mul, one)
|
||||
}
|
||||
|
||||
// If the next token is not a comma then break the loop.
|
||||
// Check to see if the next token is a comma
|
||||
// and if not, then break out of the loop,
|
||||
// otherwise repeat until we find no comma.
|
||||
|
||||
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||
break
|
||||
}
|
||||
|
@ -78,7 +82,10 @@ func (p *parser) parseNames() (mul []string, err error) {
|
|||
|
||||
mul = append(mul, one)
|
||||
|
||||
// If the next token is not a comma then break the loop.
|
||||
// Check to see if the next token is a comma
|
||||
// and if not, then break out of the loop,
|
||||
// otherwise repeat until we find no comma.
|
||||
|
||||
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||
break
|
||||
}
|
||||
|
@ -93,6 +100,24 @@ func (p *parser) parseNames() (mul []string, err error) {
|
|||
//
|
||||
// --------------------------------------------------
|
||||
|
||||
func (p *parser) parseCond() (exp Expr, err error) {
|
||||
|
||||
// The next token that we expect to see is a
|
||||
// WHERE token, and if we don't find one then
|
||||
// return nil, with no error.
|
||||
|
||||
if _, _, exi := p.mightBe(WHERE); !exi {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return p.parseExpr()
|
||||
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
//
|
||||
// --------------------------------------------------
|
||||
|
||||
func (p *parser) parseIdent() (*Ident, error) {
|
||||
|
||||
_, lit, err := p.shouldBe(IDENT)
|
||||
|
@ -249,66 +274,482 @@ func (p *parser) parseDuration() (time.Duration, error) {
|
|||
|
||||
}
|
||||
|
||||
func (p *parser) parseDefault() (interface{}, error) {
|
||||
func (p *parser) parseExpr() (exp Expr, err error) {
|
||||
|
||||
tok, lit, err := p.shouldBe(NULL, NOW, DATE, TIME, TRUE, FALSE, NUMBER, DOUBLE, STRING, REGION, IDENT, THING, ARRAY, JSON)
|
||||
// Create the root binary expression tree.
|
||||
|
||||
root := &BinaryExpression{}
|
||||
|
||||
// If the subsequent token is an in, out, or
|
||||
// multi way path expression, then parse all
|
||||
// following expressions as a path.
|
||||
|
||||
if tok, _, exi := p.mightBe(OEDGE, IEDGE, BEDGE); exi {
|
||||
return p.parsePath(tok)
|
||||
}
|
||||
|
||||
// Begin with parsing the first expression
|
||||
// as the root of the tree to start with.
|
||||
|
||||
root.RHS, err = p.parsePart()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.declare(tok, lit)
|
||||
// If the subsequent token is an in, out, or
|
||||
// multi way path expression, then parse all
|
||||
// following expressions as a path.
|
||||
|
||||
if tok, _, exi := p.mightBe(OEDGE, IEDGE, BEDGE); exi {
|
||||
return p.parsePath(root.RHS, tok)
|
||||
}
|
||||
|
||||
func (p *parser) parseExpr() (mul []*Field, err error) {
|
||||
|
||||
var tok Token
|
||||
var lit string
|
||||
var exi bool
|
||||
var val interface{}
|
||||
// Loop over the operations and expressions
|
||||
// and build a binary expression tree based
|
||||
// on the precedence of the operators.
|
||||
|
||||
for {
|
||||
|
||||
one := &Field{}
|
||||
var rhs Expr
|
||||
|
||||
tok, lit, err = p.shouldBe(IDENT, ID, NOW, PATH, NULL, ALL, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, JSON, ARRAY, PARAM)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
|
||||
// Get the next token from the scanner and
|
||||
// the literal value that it is scanned as.
|
||||
|
||||
tok, lit, _ := p.scan()
|
||||
|
||||
switch tok {
|
||||
|
||||
// If the token is an AND or OR expression
|
||||
// then skip to the next expression without
|
||||
// further checks.
|
||||
|
||||
case AND, OR:
|
||||
|
||||
// If the token is not an operator but can
|
||||
// be converted into an operator based on
|
||||
// logic, then convert it to an operator.
|
||||
|
||||
case IN:
|
||||
|
||||
tok = INS
|
||||
if _, _, exi := p.mightBe(NOT); exi {
|
||||
tok = NIS
|
||||
}
|
||||
|
||||
one.Expr, err = p.declare(tok, lit)
|
||||
case CONTAINS:
|
||||
|
||||
tok = SIN
|
||||
if _, _, exi := p.mightBe(NOT); exi {
|
||||
tok = SNI
|
||||
}
|
||||
|
||||
case IS:
|
||||
|
||||
tok = EQ
|
||||
if _, _, exi := p.mightBe(NOT); exi {
|
||||
tok = NEQ
|
||||
}
|
||||
if _, _, exi := p.mightBe(IN); exi {
|
||||
switch tok {
|
||||
case EQ:
|
||||
tok = INS
|
||||
case NEQ:
|
||||
tok = NIS
|
||||
}
|
||||
}
|
||||
|
||||
// If the token is a keyword which is also
|
||||
// actually an operator, then skip to the
|
||||
// next expression without further checks.
|
||||
|
||||
case CONTAINSALL, CONTAINSNONE, CONTAINSSOME:
|
||||
|
||||
case ALLCONTAINEDIN, NONECONTAINEDIN, SOMECONTAINEDIN:
|
||||
|
||||
// If the token is an int64 or a float64 then
|
||||
// check to see whether the first rune is a
|
||||
// + or a - and use it as a token instead.
|
||||
|
||||
case NUMBER, DOUBLE:
|
||||
|
||||
switch lit[0] {
|
||||
case '-':
|
||||
rhs, err = p.declare(tok, lit[1:])
|
||||
tok = SUB
|
||||
case '+':
|
||||
rhs, err = p.declare(tok, lit[1:])
|
||||
tok = ADD
|
||||
default:
|
||||
p.unscan()
|
||||
return root.RHS, nil
|
||||
}
|
||||
|
||||
// Check to see if the token is an operator
|
||||
// expression. If it is none of those then
|
||||
// unscan and break out of the loop.
|
||||
|
||||
default:
|
||||
|
||||
if !tok.isOperator() {
|
||||
p.unscan()
|
||||
return root.RHS, nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If the token was not an int64 or float64
|
||||
// signed value then retrieve the next part
|
||||
// of the expression and add it to the right.
|
||||
|
||||
if rhs == nil {
|
||||
rhs, err = p.parsePart()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Find the right place in the tree to add the
|
||||
// new expression, by descending the right side
|
||||
// of the tree until we reach the last binary
|
||||
// expression, or until we reach an expression
|
||||
// whose operator precendence >= this precedence.
|
||||
|
||||
for node := root; ; {
|
||||
r, ok := node.RHS.(*BinaryExpression)
|
||||
if !ok || r.Op.precedence() >= tok.precedence() {
|
||||
node.RHS = &BinaryExpression{LHS: node.RHS, Op: tok, RHS: rhs}
|
||||
break
|
||||
}
|
||||
node = r
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return root, err
|
||||
|
||||
}
|
||||
|
||||
func (p *parser) parsePart() (exp Expr, err error) {
|
||||
|
||||
toks := []Token{
|
||||
MUL, ID, IDENT, THING,
|
||||
NULL, VOID, EMPTY, MISSING,
|
||||
TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE,
|
||||
DATE, TIME, DURATION, JSON, ARRAY, PARAM, LPAREN,
|
||||
}
|
||||
|
||||
tok, lit, _ := p.scan()
|
||||
|
||||
// We need to declare the type up here instead
|
||||
// of at the bottom, as the held value might
|
||||
// be overwritten by the next token scan.
|
||||
|
||||
exp, err = p.declare(tok, lit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the current token is a left parenthesis
|
||||
// bracket, then we will parse this complete
|
||||
// expression part as a subquery.
|
||||
|
||||
if p.is(tok, LPAREN) {
|
||||
return p.parseSubq()
|
||||
}
|
||||
|
||||
// If the next token is a left parenthesis
|
||||
// bracket, then we will parse this complete
|
||||
// expression part as a function call.
|
||||
|
||||
if _, _, exi := p.mightBe(LPAREN); exi {
|
||||
return p.parseCall(lit)
|
||||
}
|
||||
|
||||
// If this expression is not a subquery or a
|
||||
// function call, then check to see if the
|
||||
// token is in the list of allowed tokens.
|
||||
|
||||
if !p.in(tok, toks) {
|
||||
err = &ParseError{Found: lit, Expected: []string{"expression"}}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
one.Alias = lit
|
||||
func (p *parser) parseSubq() (sub *SubExpression, err error) {
|
||||
|
||||
// Next token might be AS
|
||||
if _, _, exi = p.mightBe(AS); exi {
|
||||
var exp Expr
|
||||
var tok Token
|
||||
|
||||
_, lit, err = p.shouldBe(IDENT)
|
||||
tok, _, _ = p.mightBe(SELECT, CREATE, UPDATE, DELETE, RELATE)
|
||||
|
||||
switch tok {
|
||||
default:
|
||||
exp, err = p.parseExpr()
|
||||
case SELECT:
|
||||
exp, err = p.parseSelectStatement()
|
||||
case CREATE:
|
||||
exp, err = p.parseCreateStatement()
|
||||
case UPDATE:
|
||||
exp, err = p.parseUpdateStatement()
|
||||
case DELETE:
|
||||
exp, err = p.parseDeleteStatement()
|
||||
case RELATE:
|
||||
exp, err = p.parseRelateStatement()
|
||||
}
|
||||
|
||||
p.mightBe(RPAREN)
|
||||
|
||||
return &SubExpression{Expr: exp}, err
|
||||
|
||||
}
|
||||
|
||||
func (p *parser) parseCall(name string) (fnc *FuncExpression, err error) {
|
||||
|
||||
fnc = &FuncExpression{Name: name}
|
||||
|
||||
// Check to see if the immediate token
|
||||
// is a right parenthesis bracket, and if
|
||||
// it is then this function has no args.
|
||||
|
||||
if _, _, exi := p.mightBe(RPAREN); !exi {
|
||||
|
||||
for {
|
||||
|
||||
var arg Expr
|
||||
|
||||
arg, err = p.parseExpr()
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"field alias"}}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
val, err = p.declare(STRING, lit)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Append the single expression to the array
|
||||
// of function argument expressions.
|
||||
|
||||
one.Alias = val.(string)
|
||||
fnc.Args = append(fnc.Args, arg)
|
||||
|
||||
}
|
||||
// Check to see if the next token is a comma
|
||||
// and if not, then break out of the loop,
|
||||
// otherwise repeat until we find no comma.
|
||||
|
||||
mul = append(mul, one)
|
||||
|
||||
// If the next token is not a comma then break the loop.
|
||||
if _, _, exi = p.mightBe(COMMA); !exi {
|
||||
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_, _, err = p.shouldBe(RPAREN)
|
||||
|
||||
}
|
||||
|
||||
// Check to see if the used function name is
|
||||
// valid according to the currently supported
|
||||
// functions. If not then return an error.
|
||||
|
||||
if _, ok := funcs[fnc.Name]; !ok {
|
||||
|
||||
return nil, &ParseError{
|
||||
Found: fmt.Sprintf("%s()", name),
|
||||
Expected: []string{"valid function name"},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check to see if the number of arguments
|
||||
// is correct for the specified function name,
|
||||
// and if not, then return an error.
|
||||
|
||||
if _, ok := funcs[fnc.Name][len(fnc.Args)]; !ok {
|
||||
|
||||
s, t := "", len(funcs[fnc.Name])
|
||||
|
||||
for i := 0; i < t; i++ {
|
||||
switch {
|
||||
case i > 0 && i == t-1:
|
||||
s = s + " or "
|
||||
case i > 0:
|
||||
s = s + ", "
|
||||
}
|
||||
s = s + fmt.Sprintf("%d", i)
|
||||
}
|
||||
|
||||
switch {
|
||||
case t == 1:
|
||||
s = s + " argument"
|
||||
case t >= 2:
|
||||
s = s + " arguments"
|
||||
}
|
||||
|
||||
return nil, &ParseError{
|
||||
Found: fmt.Sprintf("%s() with %d arguments", fnc.Name, len(fnc.Args)),
|
||||
Expected: []string{s},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (p *parser) parsePath(expr ...Expr) (path *PathExpression, err error) {
|
||||
|
||||
path = &PathExpression{}
|
||||
|
||||
// Take the previosuly scanned expression
|
||||
// and append it to the path expression
|
||||
// tree as the first item.
|
||||
|
||||
for _, e := range expr {
|
||||
switch v := e.(type) {
|
||||
case Token:
|
||||
path.Expr = append(path.Expr, &JoinExpression{Join: v})
|
||||
default:
|
||||
path.Expr = append(path.Expr, &PartExpression{Part: v})
|
||||
}
|
||||
}
|
||||
|
||||
// If the last expression passed in was a
|
||||
// path joiner (->, <-, or <->), then we
|
||||
// need to process a path part first.
|
||||
|
||||
if _, ok := expr[len(expr)-1].(Token); ok {
|
||||
|
||||
var part Expr
|
||||
|
||||
part, err = p.parseStep()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if part == nil {
|
||||
return
|
||||
}
|
||||
|
||||
path.Expr = append(path.Expr, &PartExpression{Part: part})
|
||||
|
||||
}
|
||||
|
||||
for {
|
||||
|
||||
var join Expr
|
||||
var part Expr
|
||||
|
||||
// We expect the next token to be a join
|
||||
// operator (->, <-, or <->), otherwise we
|
||||
// are at the end of the path and will
|
||||
// ignore it and return.
|
||||
|
||||
join, err = p.parseJoin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if join == nil {
|
||||
return
|
||||
}
|
||||
|
||||
path.Expr = append(path.Expr, &JoinExpression{Join: join.(Token)})
|
||||
|
||||
// We expect the next token to be a path
|
||||
// part identifier, otherwise we are at
|
||||
// the end of the path and will ignore it
|
||||
// and return.
|
||||
|
||||
part, err = p.parseStep()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if part == nil {
|
||||
return
|
||||
}
|
||||
|
||||
path.Expr = append(path.Expr, &PartExpression{Part: part})
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (p *parser) parseJoin() (exp Expr, err error) {
|
||||
|
||||
toks := []Token{
|
||||
OEDGE, IEDGE, BEDGE,
|
||||
}
|
||||
|
||||
tok, _, _ := p.scan()
|
||||
|
||||
if !p.in(tok, toks) {
|
||||
p.unscan()
|
||||
return
|
||||
}
|
||||
|
||||
return tok, err
|
||||
|
||||
}
|
||||
|
||||
func (p *parser) parseStep() (exp Expr, err error) {
|
||||
|
||||
toks := []Token{
|
||||
QMARK, IDENT, THING, PARAM, LPAREN,
|
||||
}
|
||||
|
||||
tok, lit, _ := p.scan()
|
||||
|
||||
// We need to declare the type up here instead
|
||||
// of at the bottom, as the held value might
|
||||
// be overwritten by the next token scan.
|
||||
|
||||
exp, err = p.declare(tok, lit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the current token is a left parenthesis
|
||||
// bracket, then we will parse this complete
|
||||
// expression part as a subquery.
|
||||
|
||||
if p.is(tok, LPAREN) {
|
||||
return p.parseSubp()
|
||||
}
|
||||
|
||||
// If this expression is not a sub-path
|
||||
// expression, then check to see if the
|
||||
// token is in the list of allowed tokens.
|
||||
|
||||
if !p.in(tok, toks) {
|
||||
p.unscan()
|
||||
exp = nil
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (p *parser) parseSubp() (stmt *SubpExpression, err error) {
|
||||
|
||||
stmt = &SubpExpression{}
|
||||
|
||||
if stmt.What, err = p.parseWhat(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, _, exi := p.mightBe(AS); exi {
|
||||
if stmt.Name, err = p.parseName(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if stmt.Cond, err = p.parseCond(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, _, err = p.shouldBe(RPAREN); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ func (p *parser) parseDefineFieldStatement() (stmt *DefineFieldStatement, err er
|
|||
}
|
||||
|
||||
if p.is(tok, DEFAULT) {
|
||||
if stmt.Default, err = p.parseDefault(); err != nil {
|
||||
if stmt.Default, err = p.parseExpr(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
19
sql/let.go
19
sql/let.go
|
@ -21,26 +21,35 @@ func (p *parser) parseLetStatement() (stmt *LetStatement, err error) {
|
|||
stmt.KV = p.c.Get("KV").(string)
|
||||
stmt.NS = p.c.Get("NS").(string)
|
||||
stmt.DB = p.c.Get("DB").(string)
|
||||
// The first part of a LET expression must
|
||||
// always be an identifier, specifying a
|
||||
// variable name to set.
|
||||
|
||||
_, stmt.Name, err = p.shouldBe(IDENT)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The next query part must always be a =
|
||||
// operator, as this is a LET expression
|
||||
// and not a binary expression.
|
||||
|
||||
_, _, err = p.shouldBe(EQ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tok, lit, err := p.shouldBe(NULL, NOW, DATE, TIME, TRUE, FALSE, STRING, NUMBER, DOUBLE, THING, JSON, ARRAY, PARAM)
|
||||
// The next query part can be any expression
|
||||
// including a parenthesised expression or a
|
||||
// binary expression so handle accordingly.
|
||||
|
||||
stmt.What, err = p.parseExpr()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stmt.Expr, err = p.declare(tok, lit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Check that we have reached the end of the
|
||||
// statement with either a ';' or EOF.
|
||||
|
||||
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -115,7 +115,7 @@ func (p *parser) parseMulti() (*Query, error) {
|
|||
// parseSingle parses a single SQL SELECT statement.
|
||||
func (p *parser) parseSingle() (Statement, error) {
|
||||
|
||||
tok, _, err := p.shouldBe(USE, LET, INFO, BEGIN, CANCEL, COMMIT, RETURN, SELECT, CREATE, UPDATE, DELETE, RELATE, DEFINE, REMOVE)
|
||||
tok, _, err := p.shouldBe(USE, INFO, BEGIN, CANCEL, COMMIT, LET, RETURN, SELECT, CREATE, UPDATE, DELETE, RELATE, DEFINE, REMOVE)
|
||||
|
||||
switch tok {
|
||||
|
||||
|
|
|
@ -18,26 +18,21 @@ func (p *parser) parseReturnStatement() (stmt *ReturnStatement, err error) {
|
|||
|
||||
stmt = &ReturnStatement{}
|
||||
|
||||
for {
|
||||
if _, _, _, err = p.o.get(AuthTB); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tok, lit, err := p.shouldBe(NULL, NOW, DATE, TIME, TRUE, FALSE, STRING, NUMBER, DOUBLE, THING, JSON, ARRAY, PARAM)
|
||||
// The next query part can be any expression
|
||||
// including a parenthesised expression or a
|
||||
// binary expression so handle accordingly.
|
||||
|
||||
stmt.What, err = p.parseExpr()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
val, err := p.declare(tok, lit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stmt.What = append(stmt.What, val)
|
||||
|
||||
// If the next token is not a comma then break the loop.
|
||||
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
// Check that we have reached the end of the
|
||||
// statement with either a ';' or EOF.
|
||||
|
||||
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -63,7 +63,7 @@ func (s *scanner) scan() (tok Token, lit string, val interface{}) {
|
|||
case eof:
|
||||
return EOF, "", val
|
||||
case '*':
|
||||
return ALL, string(ch), val
|
||||
return MUL, string(ch), val
|
||||
case '×':
|
||||
return MUL, string(ch), val
|
||||
case '∙':
|
||||
|
@ -135,6 +135,9 @@ func (s *scanner) scan() (tok Token, lit string, val interface{}) {
|
|||
return s.scanCommentSingle(ch)
|
||||
case chn == '*':
|
||||
return s.scanCommentMultiple(ch)
|
||||
case isNumber(chn):
|
||||
s.undo()
|
||||
return DIV, string(ch), val
|
||||
case chn == ' ':
|
||||
s.undo()
|
||||
return DIV, string(ch), val
|
||||
|
@ -759,7 +762,7 @@ func isLetter(ch rune) bool {
|
|||
|
||||
// isIdentChar returns true if the rune is allowed in a IDENT.
|
||||
func isIdentChar(ch rune) bool {
|
||||
return isLetter(ch) || isNumber(ch) || ch == '.' || ch == '_' || ch == '*'
|
||||
return isLetter(ch) || isNumber(ch) || ch == '.' || ch == '_' || ch == '*' || ch == '[' || ch == ']'
|
||||
}
|
||||
|
||||
// isThingChar returns true if the rune is allowed in a THING.
|
||||
|
|
150
sql/select.go
150
sql/select.go
|
@ -71,32 +71,141 @@ func (p *parser) parseSelectStatement() (stmt *SelectStatement, err error) {
|
|||
|
||||
}
|
||||
|
||||
func (p *parser) parseField() (mul []*Field, err error) {
|
||||
|
||||
var lit string
|
||||
var exi bool
|
||||
|
||||
for {
|
||||
|
||||
one := &Field{}
|
||||
|
||||
one.Expr, err = p.parseExpr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
one.Alias = "*" // TODO need to implement default field name
|
||||
|
||||
// Chec to see if the next token is an AS
|
||||
// clause, and if it is read the defined
|
||||
// field alias name from the scanner.
|
||||
|
||||
if _, _, exi = p.mightBe(AS); exi {
|
||||
|
||||
if _, one.Alias, err = p.shouldBe(IDENT); err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"field alias"}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Append the single expression to the array
|
||||
// of return statement expressions.
|
||||
|
||||
mul = append(mul, one)
|
||||
|
||||
// Check to see if the next token is a comma
|
||||
// and if not, then break out of the loop,
|
||||
// otherwise repeat until we find no comma.
|
||||
|
||||
if _, _, exi = p.mightBe(COMMA); !exi {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (p *parser) parseWhere() (exp Expr, err error) {
|
||||
|
||||
// The next token that we expect to see is a
|
||||
// WHERE token, and if we don't find one then
|
||||
// return nil, with no error.
|
||||
|
||||
if _, _, exi := p.mightBe(WHERE); !exi {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return p.parseExpr()
|
||||
|
||||
}
|
||||
|
||||
func (p *parser) parseGroup() (mul []*Group, err error) {
|
||||
|
||||
// The next token that we expect to see is a
|
||||
// GROUP token, and if we don't find one then
|
||||
// return nil, with no error.
|
||||
|
||||
if _, _, exi := p.mightBe(GROUP); !exi {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// We don't need to have a BY token, but we
|
||||
// allow it so that the SQL query would read
|
||||
// better when compared to english.
|
||||
|
||||
_, _, _ = p.mightBe(BY)
|
||||
|
||||
for {
|
||||
|
||||
var tok Token
|
||||
var lit string
|
||||
|
||||
one := &Group{}
|
||||
|
||||
tok, lit, err = p.shouldBe(IDENT, ID)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
|
||||
}
|
||||
|
||||
one.Expr, err = p.declare(tok, lit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Append the single expression to the array
|
||||
// of return statement expressions.
|
||||
|
||||
mul = append(mul, one)
|
||||
|
||||
// Check to see if the next token is a comma
|
||||
// and if not, then break out of the loop,
|
||||
// otherwise repeat until we find no comma.
|
||||
|
||||
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (p *parser) parseOrder() (mul []*Order, err error) {
|
||||
|
||||
var tok Token
|
||||
var lit string
|
||||
var exi bool
|
||||
// The next token that we expect to see is a
|
||||
// ORDER token, and if we don't find one then
|
||||
// return nil, with no error.
|
||||
|
||||
if _, _, exi := p.mightBe(ORDER); !exi {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// We don't need to have a BY token, but we
|
||||
// allow it so that the SQL query would read
|
||||
// better when compared to english.
|
||||
|
||||
_, _, _ = p.mightBe(BY)
|
||||
|
||||
for {
|
||||
|
||||
var exi bool
|
||||
var tok Token
|
||||
var lit string
|
||||
|
||||
one := &Order{}
|
||||
|
||||
tok, lit, err = p.shouldBe(IDENT, ID)
|
||||
|
@ -109,10 +218,8 @@ func (p *parser) parseOrder() (mul []*Order, err error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
tok, lit, exi = p.mightBe(ASC, DESC)
|
||||
if !exi {
|
||||
if tok, lit, exi = p.mightBe(ASC, DESC); !exi {
|
||||
tok = ASC
|
||||
lit = tok.String()
|
||||
}
|
||||
|
||||
one.Dir, err = p.declare(tok, lit)
|
||||
|
@ -120,9 +227,15 @@ func (p *parser) parseOrder() (mul []*Order, err error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Append the single expression to the array
|
||||
// of return statement expressions.
|
||||
|
||||
mul = append(mul, one)
|
||||
|
||||
// If the next token is not a comma then break the loop.
|
||||
// Check to see if the next token is a comma
|
||||
// and if not, then break out of the loop,
|
||||
// otherwise repeat until we find no comma.
|
||||
|
||||
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||
break
|
||||
}
|
||||
|
@ -135,10 +248,18 @@ func (p *parser) parseOrder() (mul []*Order, err error) {
|
|||
|
||||
func (p *parser) parseLimit() (Expr, error) {
|
||||
|
||||
// The next token that we expect to see is a
|
||||
// LIMIT token, and if we don't find one then
|
||||
// return nil, with no error.
|
||||
|
||||
if _, _, exi := p.mightBe(LIMIT); !exi {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// We don't need to have a BY token, but we
|
||||
// allow it so that the SQL query would read
|
||||
// better when compared to english.
|
||||
|
||||
_, _, _ = p.mightBe(BY)
|
||||
|
||||
tok, lit, err := p.shouldBe(NUMBER)
|
||||
|
@ -152,17 +273,23 @@ func (p *parser) parseLimit() (Expr, error) {
|
|||
|
||||
func (p *parser) parseStart() (Expr, error) {
|
||||
|
||||
// Remove the START keyword
|
||||
// The next token that we expect to see is a
|
||||
// START token, and if we don't find one then
|
||||
// return nil, with no error.
|
||||
|
||||
if _, _, exi := p.mightBe(START); !exi {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Next token might be AT
|
||||
// We don't need to have a AT token, but we
|
||||
// allow it so that the SQL query would read
|
||||
// better when compared to english.
|
||||
|
||||
_, _, _ = p.mightBe(AT)
|
||||
|
||||
tok, lit, err := p.shouldBe(NUMBER, THING)
|
||||
tok, lit, err := p.shouldBe(NUMBER)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"number or record id"}}
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"start number"}}
|
||||
}
|
||||
|
||||
return p.declare(tok, lit)
|
||||
|
@ -171,7 +298,6 @@ func (p *parser) parseStart() (Expr, error) {
|
|||
|
||||
func (p *parser) parseVersion() (Expr, error) {
|
||||
|
||||
// Remove the VERSION keyword
|
||||
if _, _, exi := p.mightBe(VERSION, ON); !exi {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -90,9 +90,12 @@ func (p *parser) declare(tok Token, lit string) (interface{}, error) {
|
|||
case EMPTY:
|
||||
return &Empty{}, nil
|
||||
|
||||
case ALL:
|
||||
case MUL:
|
||||
return &All{}, nil
|
||||
|
||||
case QMARK:
|
||||
return &Any{}, nil
|
||||
|
||||
case ASC:
|
||||
return &Asc{}, nil
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ func (p *parser) parseDefineViewStatement() (stmt *DefineViewStatement, err erro
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.Expr, err = p.parseExpr(); err != nil {
|
||||
if stmt.Expr, err = p.parseField(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -24,16 +24,14 @@ import (
|
|||
"github.com/abcum/surreal/util/data"
|
||||
)
|
||||
|
||||
func (this *Doc) Check(cond []sql.Expr) (val bool) {
|
||||
func (this *Doc) Check(cond sql.Expr) (val bool) {
|
||||
|
||||
for _, part := range cond {
|
||||
switch expr := part.(type) {
|
||||
switch expr := cond.(type) {
|
||||
case *sql.BinaryExpression:
|
||||
if !this.chkOne(expr) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ func (this *Doc) Merge(data []sql.Expr) (err error) {
|
|||
switch expr := part.(type) {
|
||||
case *sql.DiffExpression:
|
||||
this.mrgDpm(expr)
|
||||
case *sql.BinaryExpression:
|
||||
case *sql.DataExpression:
|
||||
this.mrgOne(expr)
|
||||
case *sql.MergeExpression:
|
||||
this.mrgAny(expr)
|
||||
|
@ -149,7 +149,7 @@ func (this *Doc) mrgDpm(expr *sql.DiffExpression) {
|
|||
|
||||
}
|
||||
|
||||
func (this *Doc) mrgOne(expr *sql.BinaryExpression) {
|
||||
func (this *Doc) mrgOne(expr *sql.DataExpression) {
|
||||
|
||||
lhs := this.getMrgItemLHS(expr.LHS)
|
||||
rhs := this.getMrgItemRHS(expr.RHS)
|
||||
|
|
Loading…
Reference in a new issue