blob: af213d27d6ac34e55dcb517e6d1454852ff668ba [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package query_parser
import (
"fmt"
"math/big"
"regexp"
"strconv"
"strings"
"text/scanner"
"time"
"unicode/utf8"
"v.io/syncbase/v23/syncbase/nosql/query_db"
"v.io/syncbase/v23/syncbase/nosql/syncql"
"v.io/v23/vdl"
)
type TokenType int
const (
TokCHAR TokenType = 1 + iota
TokCOMMA
TokEOF
TokEQUAL
TokFLOAT
TokIDENT
TokINT
TokLEFTANGLEBRACKET
TokLEFTBRACKET
TokLEFTPAREN
TokMINUS
TokPERIOD
TokRIGHTANGLEBRACKET
TokRIGHTBRACKET
TokRIGHTPAREN
TokSTRING
)
type Token struct {
Tok TokenType
Value string
Off int64
}
type Node struct {
Off int64
}
type Statement interface {
Offset() int64
String() string
}
type Segment struct {
Value string
Keys []*Operand // Used as key(s) or index(es) to dereference map/set/array/list.
Node
}
type Field struct {
Segments []Segment
Node
}
type BinaryOperatorType int
const (
And BinaryOperatorType = 1 + iota
Equal
GreaterThan
GreaterThanOrEqual
Is
IsNot
LessThan
LessThanOrEqual
Like
NotEqual
NotLike
Or
)
type BinaryOperator struct {
Type BinaryOperatorType
Node
}
type OperandType int
const (
TypBigInt OperandType = 1 + iota // Only as a result of Resolve/Coerce Operand
TypBigRat // Only as a result of Resolve/Coerce Operand
TypBool
TypComplex
TypExpr
TypField
TypFloat
TypFunction
TypInt
TypNil
TypStr
TypTime
TypObject // Only as the result of a ResolveOperand.
TypUint // Only as a result of a ResolveOperand
)
type Operand struct {
Type OperandType
BigInt *big.Int
BigRat *big.Rat
Bool bool
Complex complex128
Column *Field
Float float64
Function *Function
Int int64
Str string
Time time.Time
Prefix string // Computed by checker for Like expressions
Regex string // Computed by checker for Like expressions
Uint uint64
CompRegex *regexp.Regexp
Expr *Expression
Object *vdl.Value
Node
}
type Function struct {
Name string
Args []*Operand
ArgTypes []OperandType // Filled in by checker.
RetType OperandType // Filled in by checker.
Computed bool // Checker sets to true and sets RetValue if function takes no args
RetValue *Operand
Node
}
type Expression struct {
Operand1 *Operand
Operator *BinaryOperator
Operand2 *Operand
Node
}
type SelectorType int
const (
TypSelField SelectorType = 1 + iota
TypSelFunc
)
// Selector: entries in the select clause.
// Entries can be functions for fields.
// The AS name, if present, will ONLY be used in the
// returned column header.
type Selector struct {
Type SelectorType
Field *Field
Function *Function
As *AsClause // If not nil, used in returned column header.
Node
}
type AsClause struct {
AltName Name
Node
}
type Name struct {
Value string
Node
}
type SelectClause struct {
Selectors []Selector
Node
}
type FromClause struct {
Table TableEntry
Node
}
type TableEntry struct {
Name string
DBTable query_db.Table // Checker gets table from db and sets this.
Node
}
type WhereClause struct {
Expr *Expression
Node
}
type Int64Value struct {
Value int64
Node
}
type LimitClause struct {
Limit *Int64Value
Node
}
type ResultsOffsetClause struct {
ResultsOffset *Int64Value
Node
}
type SelectStatement struct {
Select *SelectClause
From *FromClause
Where *WhereClause
Limit *LimitClause
ResultsOffset *ResultsOffsetClause
Node
}
func scanToken(s *scanner.Scanner) *Token {
// TODO(jkline): Replace golang text/scanner.
var token Token
tok := s.Scan()
token.Value = s.TokenText()
token.Off = int64(s.Position.Offset)
switch tok {
case '.':
token.Tok = TokPERIOD
case ',':
token.Tok = TokCOMMA
case '-':
token.Tok = TokMINUS
case '(':
token.Tok = TokLEFTPAREN
case ')':
token.Tok = TokRIGHTPAREN
case '=':
token.Tok = TokEQUAL
case '<':
token.Tok = TokLEFTANGLEBRACKET
case '>':
token.Tok = TokRIGHTANGLEBRACKET
case '[':
token.Tok = TokLEFTBRACKET
case ']':
token.Tok = TokRIGHTBRACKET
case scanner.EOF:
token.Tok = TokEOF
case scanner.Ident:
token.Tok = TokIDENT
case scanner.Int:
token.Tok = TokINT
case scanner.Float:
token.Tok = TokFLOAT
case scanner.Char:
token.Tok = TokCHAR
token.Value = token.Value[1 : len(token.Value)-1]
case scanner.String:
token.Tok = TokSTRING
token.Value = token.Value[1 : len(token.Value)-1]
}
return &token
}
// Text/scanner reports errors to stderr that are not errors in the query language.
// For example, to get the value where the key is "\",
// One would write the string:
// "select v where k = \"\\\""
// This will result in the scanner spewing "literal not terminated" to stderr if we don't
// set the Error field in Scanner. As such, Error is set to the following function which
// eats errors. In the longer term, there is still a TODO to replace text/scanner with
// our own scanner.
func scannerError(s *scanner.Scanner, msg string) {
// Do nothing.
}
// Parse a statement. Return it or an error.
func Parse(db query_db.Database, src string) (*Statement, error) {
r := strings.NewReader(src)
var s scanner.Scanner
s.Init(r)
s.Error = scannerError
token := scanToken(&s) // eat the select
if token.Tok == TokEOF {
return nil, syncql.NewErrNoStatementFound(db.GetContext(), token.Off)
}
if token.Tok != TokIDENT {
return nil, syncql.NewErrExpectedIdentifier(db.GetContext(), token.Off, token.Value)
}
switch strings.ToLower(token.Value) {
case "select":
var st Statement
var err error
st, token, err = selectStatement(db, &s, token)
return &st, err
default:
return nil, syncql.NewErrUnknownIdentifier(db.GetContext(), token.Off, token.Value)
}
}
// Parse the [currently] one and only supported statement: select.
func selectStatement(db query_db.Database, s *scanner.Scanner, token *Token) (Statement, *Token, error) {
// TODO(jkline): keep a count of recursions and limit it to some arbitrary number.
var st SelectStatement
st.Off = token.Off
// parse SelectClause
var err error
st.Select, token, err = parseSelectClause(db, s, token)
if err != nil {
return nil, nil, err
}
st.From, token, err = parseFromClause(db, s, token)
if err != nil {
return nil, nil, err
}
st.Where, token, err = parseWhereClause(db, s, token)
if err != nil {
return nil, nil, err
}
st.Limit, st.ResultsOffset, token, err = parseLimitResultsOffsetClauses(db, s, token)
if err != nil {
return nil, nil, err
}
// There can be nothing remaining for the current statement
if token.Tok != TokEOF {
return nil, nil, syncql.NewErrUnexpected(db.GetContext(), token.Off, token.Value)
}
return st, token, nil
}
// Parse the select clause (fields). Return *SelectClause, next token (or error).
func parseSelectClause(db query_db.Database, s *scanner.Scanner, token *Token) (*SelectClause, *Token, error) {
// must be at least one selector or it is an error
// field seclectors may be in dot notation
// selectors are separated by commas
var selectClause SelectClause
selectClause.Off = token.Off
token = scanToken(s) // eat the select
if token.Tok == TokEOF {
return nil, nil, syncql.NewErrUnexpectedEndOfStatement(db.GetContext(), token.Off)
}
var err error
// scan first selector
if token, err = parseSelector(db, s, &selectClause, token); err != nil {
return nil, nil, err
}
// More selectors?
for token.Tok == TokCOMMA {
token = scanToken(s)
if token, err = parseSelector(db, s, &selectClause, token); err != nil {
return nil, nil, err
}
}
return &selectClause, token, nil
}
// Parse a selector. Return next token (or error).
func parseSelector(db query_db.Database, s *scanner.Scanner, selectClause *SelectClause, token *Token) (*Token, error) {
if token.Tok != TokIDENT {
return nil, syncql.NewErrExpectedIdentifier(db.GetContext(), token.Off, token.Value)
}
var selector Selector
selector.Off = token.Off
selector.Type = TypSelField
var field Field
selector.Field = &field
selector.Field.Off = token.Off
selector.Field = &field
var segment *Segment
var err error
if segment, token, err = parseSegment(db, s, token); err != nil {
return nil, err
}
selector.Field.Segments = append(selector.Field.Segments, *segment)
// It might be a function.
if token.Tok == TokLEFTPAREN {
// Segments with a key(s) specified cannot be function calls.
if len(segment.Keys) != 0 {
return nil, syncql.NewErrUnexpected(db.GetContext(), token.Off, token.Value)
}
// switch selector to a function
selector.Type = TypSelFunc
var err error
if selector.Function, token, err = parseFunction(db, s, segment.Value, segment.Off, token); err != nil {
return nil, err
}
selector.Field = nil
} else {
for token.Tok != TokEOF && token.Tok == TokPERIOD {
token = scanToken(s)
if token.Tok != TokIDENT {
return nil, syncql.NewErrExpectedIdentifier(db.GetContext(), token.Off, token.Value)
}
if segment, token, err = parseSegment(db, s, token); err != nil {
return nil, err
}
selector.Field.Segments = append(selector.Field.Segments, *segment)
}
}
// Check for AS
if token.Tok == TokIDENT && strings.ToLower(token.Value) == "as" {
var asClause AsClause
asClause.Off = token.Off
token = scanToken(s)
if token.Tok != TokIDENT {
return nil, syncql.NewErrExpectedIdentifier(db.GetContext(), token.Off, token.Value)
}
asClause.AltName.Value = token.Value
asClause.AltName.Off = token.Off
selector.As = &asClause
token = scanToken(s)
}
selectClause.Selectors = append(selectClause.Selectors, selector)
return token, nil
}
// Parse a segment. Return the segment and the next token (or error).
// Check for a key (i.e., [<key>] following the segment).
func parseSegment(db query_db.Database, s *scanner.Scanner, token *Token) (*Segment, *Token, error) {
var segment Segment
segment.Value = token.Value
segment.Off = token.Off
token = scanToken(s)
for token.Tok == TokLEFTBRACKET {
// A key to the segment is specified.
token = scanToken(s)
var key *Operand
var err error
key, token, err = parseOperand(db, s, token)
if err != nil {
return nil, nil, err
}
segment.Keys = append(segment.Keys, key)
if token.Tok != TokRIGHTBRACKET {
return nil, nil, syncql.NewErrExpected(db.GetContext(), token.Off, "]")
}
token = scanToken(s)
}
return &segment, token, nil
}
// Parse the from clause, Return FromClause and next Token or error.
func parseFromClause(db query_db.Database, s *scanner.Scanner, token *Token) (*FromClause, *Token, error) {
if strings.ToLower(token.Value) != "from" {
return nil, nil, syncql.NewErrExpectedFrom(db.GetContext(), token.Off, token.Value)
}
var fromClause FromClause
fromClause.Off = token.Off
token = scanToken(s) // eat from
// must be a table specified
if token.Tok == TokEOF {
return nil, nil, syncql.NewErrUnexpectedEndOfStatement(db.GetContext(), token.Off)
}
if token.Tok != TokIDENT {
return nil, nil, syncql.NewErrExpectedIdentifier(db.GetContext(), token.Off, token.Value)
}
fromClause.Table.Off = token.Off
fromClause.Table.Name = token.Value
token = scanToken(s)
return &fromClause, token, nil
}
// Parse the where clause (if any). Return WhereClause (could be nil) and and next Token or error.
func parseWhereClause(db query_db.Database, s *scanner.Scanner, token *Token) (*WhereClause, *Token, error) {
// parse Optional where clause
if token.Tok != TokEOF {
if strings.ToLower(token.Value) != "where" {
return nil, token, nil
}
var where WhereClause
where.Off = token.Off
token = scanToken(s)
// parse expression
var err error
where.Expr, token, err = parseExpression(db, s, token)
if err != nil {
return nil, nil, err
}
return &where, token, nil
} else {
return nil, token, nil
}
}
// Parse a parenthesized expression. Return expression and next token (or error)
func parseParenthesizedExpression(db query_db.Database, s *scanner.Scanner, token *Token) (*Expression, *Token, error) {
// Only called when token == TokLEFTPAREN
token = scanToken(s) // eat '('
var expr *Expression
var err error
expr, token, err = parseExpression(db, s, token)
if err != nil {
return nil, nil, err
}
// Expect right paren
if token.Tok == TokEOF {
return nil, nil, syncql.NewErrUnexpectedEndOfStatement(db.GetContext(), token.Off)
}
if token.Tok != TokRIGHTPAREN {
return nil, nil, syncql.NewErrExpected(db.GetContext(), token.Off, ")")
}
token = scanToken(s) // eat ')'
return expr, token, nil
}
// Parse an expression. Return expression and next token (or error)
func parseExpression(db query_db.Database, s *scanner.Scanner, token *Token) (*Expression, *Token, error) {
if token.Tok == TokEOF {
return nil, nil, syncql.NewErrUnexpectedEndOfStatement(db.GetContext(), token.Off)
}
var err error
var expr *Expression
if token.Tok == TokLEFTPAREN {
expr, token, err = parseParenthesizedExpression(db, s, token)
} else {
// We expect a like/equal expression
expr, token, err = parseLikeEqualExpression(db, s, token)
}
if err != nil {
return nil, nil, err
}
for token.Tok != TokEOF && token.Tok != TokRIGHTPAREN {
// There is more. If not 'and', 'or' or ')', the where is over.
if strings.ToLower(token.Value) != "and" && strings.ToLower(token.Value) != "or" {
return expr, token, nil
}
var newExpression Expression
var operand1 Operand
operand1.Type = TypExpr
operand1.Expr = expr
operand1.Off = operand1.Expr.Off
newExpression.Operand1 = &operand1
newExpression.Off = operand1.Off
newExpression.Operator, token, err = parseLogicalOperator(db, s, token)
if err != nil {
return nil, nil, err
}
expr = &newExpression
// Need to set operand2.
var operand2 Operand
expr.Operand2 = &operand2
if token.Tok == TokLEFTPAREN {
expr.Operand2.Type = TypExpr
expr.Operand2.Expr, token, err = parseParenthesizedExpression(db, s, token)
} else {
expr.Operand2.Type = TypExpr
expr.Operand2.Expr, token, err = parseLikeEqualExpression(db, s, token)
}
if err != nil {
return nil, nil, err
}
expr.Operand2.Off = expr.Operand2.Expr.Off
}
return expr, token, nil
}
// Parse a binary expression. Return expression and next token (or error)
func parseLikeEqualExpression(db query_db.Database, s *scanner.Scanner, token *Token) (*Expression, *Token, error) {
var expression Expression
expression.Off = token.Off
// operand 1
var operand1 *Operand
var err error
operand1, token, err = parseOperand(db, s, token)
if err != nil {
return nil, nil, err
}
// operator
var operator *BinaryOperator
operator, token, err = parseBinaryOperator(db, s, token)
if err != nil {
return nil, nil, err
}
// operand 2
var operand2 *Operand
if token.Tok == TokEOF {
return nil, nil, syncql.NewErrUnexpectedEndOfStatement(db.GetContext(), token.Off)
}
operand2, token, err = parseOperand(db, s, token)
if err != nil {
return nil, nil, err
}
expression.Operand1 = operand1
expression.Operator = operator
expression.Operand2 = operand2
return &expression, token, nil
}
func parseFunction(db query_db.Database, s *scanner.Scanner, funcName string, funcOffset int64, token *Token) (*Function, *Token, error) {
var function Function
function.Name = funcName
function.Off = funcOffset
token = scanToken(s) // eat left paren
for token.Tok != TokRIGHTPAREN {
if token.Tok == TokEOF {
return nil, nil, syncql.NewErrExpected(db.GetContext(), token.Off, ")")
}
var arg *Operand
var err error
arg, token, err = parseOperand(db, s, token)
if err != nil {
return nil, nil, err
}
function.Args = append(function.Args, arg)
// A comma or right paren is expected, but a right paren cannot come after a comma.
if token.Tok == TokCOMMA {
token = scanToken(s)
if token.Tok == TokRIGHTPAREN {
// right paren cannot come after a comma
return nil, nil, syncql.NewErrExpectedOperand(db.GetContext(), token.Off, token.Value)
}
} else if token.Tok != TokRIGHTPAREN {
return nil, nil, syncql.NewErrUnexpected(db.GetContext(), token.Off, token.Value)
}
}
token = scanToken(s) // eat right paren
return &function, token, nil
}
// Parse an operand (field or literal) and return it and the next Token (or error)
func parseOperand(db query_db.Database, s *scanner.Scanner, token *Token) (*Operand, *Token, error) {
if token.Tok == TokEOF {
return nil, nil, syncql.NewErrUnexpectedEndOfStatement(db.GetContext(), token.Off)
}
var operand Operand
operand.Off = token.Off
switch token.Tok {
case TokIDENT:
operand.Type = TypField
var field Field
field.Off = token.Off
var segment *Segment
var err error
if segment, token, err = parseSegment(db, s, token); err != nil {
return nil, nil, err
}
field.Segments = append(field.Segments, *segment)
// If the next token is not a period, check for true/false/nil.
// If true/false or nil, change this operand to a bool or nil, respectively.
// Also, check for function call. If so, change to a function operand.
if token.Tok != TokPERIOD && (strings.ToLower(segment.Value) == "true" || strings.ToLower(segment.Value) == "false") {
operand.Type = TypBool
operand.Bool = strings.ToLower(segment.Value) == "true"
} else if token.Tok != TokPERIOD && strings.ToLower(segment.Value) == "nil" {
operand.Type = TypNil
} else if token.Tok == TokLEFTPAREN {
// Segments with a key specified cannot be function calls.
if len(segment.Keys) != 0 {
return nil, nil, syncql.NewErrUnexpected(db.GetContext(), token.Off, token.Value)
}
operand.Type = TypFunction
var err error
if operand.Function, token, err = parseFunction(db, s, segment.Value, segment.Off, token); err != nil {
return nil, nil, err
}
} else { // This is a field (column) operand.
// If the next token is a period, collect the rest of the segments in the column.
for token.Tok != TokEOF && token.Tok == TokPERIOD {
token = scanToken(s)
if token.Tok != TokIDENT {
return nil, nil, syncql.NewErrExpectedIdentifier(db.GetContext(), token.Off, token.Value)
}
if segment, token, err = parseSegment(db, s, token); err != nil {
return nil, nil, err
}
field.Segments = append(field.Segments, *segment)
}
operand.Column = &field
}
case TokINT:
operand.Type = TypInt
i, err := strconv.ParseInt(token.Value, 0, 64)
if err != nil {
return nil, nil, syncql.NewErrCouldNotConvert(db.GetContext(), token.Off, token.Value, "int64")
}
operand.Int = i
token = scanToken(s)
case TokFLOAT:
operand.Type = TypFloat
f, err := strconv.ParseFloat(token.Value, 64)
if err != nil {
return nil, nil, syncql.NewErrCouldNotConvert(db.GetContext(), token.Off, token.Value, "float64")
}
operand.Float = f
token = scanToken(s)
case TokCHAR:
operand.Type = TypInt
ch, _ := utf8.DecodeRuneInString(token.Value)
operand.Int = int64(ch)
token = scanToken(s)
case TokSTRING:
operand.Type = TypStr
operand.Str = token.Value
token = scanToken(s)
case TokMINUS:
// Could be negative int or negative float
off := token.Off
token = scanToken(s)
switch token.Tok {
case TokINT:
operand.Type = TypInt
i, err := strconv.ParseInt("-"+token.Value, 0, 64)
if err != nil {
return nil, nil, syncql.NewErrCouldNotConvert(db.GetContext(), off, "-"+token.Value, "int64")
}
operand.Int = i
case TokFLOAT:
operand.Type = TypFloat
f, err := strconv.ParseFloat("-"+token.Value, 64)
if err != nil {
return nil, nil, syncql.NewErrCouldNotConvert(db.GetContext(), off, "-"+token.Value, "float64")
}
operand.Float = f
default:
return nil, nil, syncql.NewErrExpected(db.GetContext(), token.Off, "int or float")
}
token = scanToken(s)
default:
return nil, nil, syncql.NewErrExpectedOperand(db.GetContext(), token.Off, token.Value)
}
return &operand, token, nil
}
// Parse binary operator and return it and the next Token (or error)
func parseBinaryOperator(db query_db.Database, s *scanner.Scanner, token *Token) (*BinaryOperator, *Token, error) {
if token.Tok == TokEOF {
return nil, nil, syncql.NewErrUnexpectedEndOfStatement(db.GetContext(), token.Off)
}
var operator BinaryOperator
operator.Off = token.Off
if token.Tok == TokIDENT {
switch strings.ToLower(token.Value) {
case "equal":
operator.Type = Equal
token = scanToken(s)
case "is":
operator.Type = Is
token = scanToken(s)
// if the next token is "not", change to IsNot
if token.Tok != TokEOF && strings.ToLower(token.Value) == "not" {
operator.Type = IsNot
token = scanToken(s)
}
case "like":
operator.Type = Like
token = scanToken(s)
case "not":
token = scanToken(s)
if token.Tok == TokEOF || (strings.ToLower(token.Value) != "equal" && strings.ToLower(token.Value) != "like") {
return nil, nil, syncql.NewErrExpected(db.GetContext(), token.Off, "'equal' or 'like'")
}
switch strings.ToLower(token.Value) {
case "equal":
operator.Type = NotEqual
default: //case "like":
operator.Type = NotLike
}
token = scanToken(s)
default:
return nil, nil, syncql.NewErrExpectedOperator(db.GetContext(), token.Off, token.Value)
}
} else {
switch token.Tok {
case TokEQUAL:
operator.Type = Equal
token = scanToken(s)
case TokLEFTANGLEBRACKET:
// Can be '<', '<=', '<>'.
token = scanToken(s)
switch token.Tok {
case TokRIGHTANGLEBRACKET:
operator.Type = NotEqual
token = scanToken(s)
case TokEQUAL:
operator.Type = LessThanOrEqual
token = scanToken(s)
default:
operator.Type = LessThan
}
case TokRIGHTANGLEBRACKET:
// Can be '>', '>='
token = scanToken(s)
switch token.Tok {
case TokEQUAL:
operator.Type = GreaterThanOrEqual
token = scanToken(s)
default:
operator.Type = GreaterThan
}
default:
return nil, nil, syncql.NewErrExpectedOperator(db.GetContext(), token.Off, token.Value)
}
}
return &operator, token, nil
}
// Parse logical operator and return it and the next Token (or error)
func parseLogicalOperator(db query_db.Database, s *scanner.Scanner, token *Token) (*BinaryOperator, *Token, error) {
if token.Tok == TokEOF {
return nil, nil, syncql.NewErrUnexpectedEndOfStatement(db.GetContext(), token.Off)
}
var operator BinaryOperator
operator.Off = token.Off
switch strings.ToLower(token.Value) {
case "and":
operator.Type = And
case "or":
operator.Type = Or
default:
return nil, nil, syncql.NewErrExpectedOperator(db.GetContext(), token.Off, token.Value)
}
token = scanToken(s)
return &operator, token, nil
}
// Parse and return LimitClause and ResultsOffsetClause (one or both can be nil) and next token (or error)
func parseLimitResultsOffsetClauses(db query_db.Database, s *scanner.Scanner, token *Token) (*LimitClause, *ResultsOffsetClause, *Token, error) {
var err error
var lc *LimitClause
var oc *ResultsOffsetClause
for token.Tok != TokEOF {
// Note: Can be in any order. If more than one limit or offset clause, the last one wins
if token.Tok == TokIDENT && strings.ToLower(token.Value) == "limit" {
lc, token, err = parseLimitClause(db, s, token)
} else if token.Tok == TokIDENT && strings.ToLower(token.Value) == "offset" {
oc, token, err = parseResultsOffsetClause(db, s, token)
} else {
return lc, oc, token, nil
}
if err != nil {
return nil, nil, nil, err
}
}
return lc, oc, token, nil
}
// Parse the limit clause. Return the LimitClause and the next Token (or error).
func parseLimitClause(db query_db.Database, s *scanner.Scanner, token *Token) (*LimitClause, *Token, error) {
var lc LimitClause
lc.Off = token.Off
token = scanToken(s)
var err error
lc.Limit, token, err = parseNonNegInt64(db, s, token)
if err != nil {
return nil, nil, err
}
return &lc, token, nil
}
// Parse the results offset clause. Return the ResultsOffsetClause and the next Token (or error).
func parseResultsOffsetClause(db query_db.Database, s *scanner.Scanner, token *Token) (*ResultsOffsetClause, *Token, error) {
var oc ResultsOffsetClause
oc.Off = token.Off
token = scanToken(s)
var err error
oc.ResultsOffset, token, err = parseNonNegInt64(db, s, token)
if err != nil {
return nil, nil, err
}
return &oc, token, nil
}
// Parse and return an Int64Value and next token (or error).
// This function is called by parseLimitClause and parseResultsOffsetClause. The integer
// values for both of these clauses cannot be negative.
func parseNonNegInt64(db query_db.Database, s *scanner.Scanner, token *Token) (*Int64Value, *Token, error) {
// We expect an integer literal
// Since we're looking for integers >= 0, don't allow TokMINUS.
if token.Tok == TokEOF {
return nil, nil, syncql.NewErrUnexpectedEndOfStatement(db.GetContext(), token.Off)
}
if token.Tok != TokINT {
return nil, nil, syncql.NewErrExpected(db.GetContext(), token.Off, "positive integer literal")
}
var v Int64Value
v.Off = token.Off
var err error
v.Value, err = strconv.ParseInt(token.Value, 0, 64)
if err != nil {
// The token value has already been checked, so this can't happen.
return nil, nil, syncql.NewErrCouldNotConvert(db.GetContext(), token.Off, token.Value, "int64")
}
token = scanToken(s)
return &v, token, nil
}
func (st SelectStatement) Offset() int64 {
return st.Off
}
// Pretty string of select statement.
func (st SelectStatement) String() string {
val := fmt.Sprintf("Off(%d):", st.Off)
if st.Select != nil {
val += st.Select.String()
}
if st.From != nil {
val += " " + st.From.String()
}
if st.Where != nil {
val += " " + st.Where.String()
}
if st.Limit != nil {
val += " " + st.Limit.String()
}
if st.ResultsOffset != nil {
val += " " + st.ResultsOffset.String()
}
return val
}
func (sel SelectClause) String() string {
val := fmt.Sprintf(" Off(%d):SELECT Columns(", sel.Off)
sep := ""
for _, selector := range sel.Selectors {
val += sep + selector.String()
sep = ","
}
val += ")"
return val
}
func (s Selector) String() string {
val := fmt.Sprintf(" Off(%d):", s.Off)
switch s.Type {
case TypSelField:
val += s.Field.String()
case TypSelFunc:
val += s.Function.String()
}
if s.As != nil {
val += s.As.String()
}
return val
}
func (a AsClause) String() string {
val := fmt.Sprintf(" Off(%d):", a.Off)
val += a.AltName.String()
return val
}
func (n Name) String() string {
val := fmt.Sprintf(" Off(%d):", n.Off)
val += n.Value
return val
}
func (f Field) String() string {
val := fmt.Sprintf(" Off(%d):", f.Off)
for i := range f.Segments {
if i != 0 {
val += "."
}
val += f.Segments[i].String()
}
return val
}
func (f Function) String() string {
val := fmt.Sprintf("Off(%d):", f.Off)
val += f.Name
val += "("
sep := ""
for _, a := range f.Args {
val += sep + a.String()
sep = ","
}
val += ")"
return val
}
func (s Segment) String() string {
val := fmt.Sprintf(" Off(%d):%s", s.Off, s.Value)
for _, k := range s.Keys {
val += "[" + k.String() + "]"
}
return val
}
func (f FromClause) String() string {
return fmt.Sprintf("Off(%d):FROM %s", f.Off, f.Table.String())
}
func (t TableEntry) String() string {
return fmt.Sprintf("Off(%d):%s", t.Off, t.Name)
}
func (w WhereClause) String() string {
return fmt.Sprintf(" Off(%d):WHERE %s", w.Off, w.Expr.String())
}
func (l LimitClause) String() string {
return fmt.Sprintf(" Off(%d):LIMIT %d", l.Off, l.Limit)
}
func (l ResultsOffsetClause) String() string {
return fmt.Sprintf(" Off(%d):OFFSET %d", l.Off, l.ResultsOffset)
}
func (o Operand) String() string {
val := fmt.Sprintf("Off(%d):", o.Off)
switch o.Type {
case TypBigInt:
val += "(BigInt)"
val += o.BigInt.String()
case TypBigRat:
val += "(BigRat)"
val += o.BigRat.String()
case TypComplex:
val += "(Complex)"
val += fmt.Sprintf("%g", o.Complex)
case TypField:
val += "(field)"
val += o.Column.String()
case TypBool:
val += "(bool)"
val += strconv.FormatBool(o.Bool)
case TypInt:
val += "(int)"
val += strconv.FormatInt(o.Int, 10)
case TypFloat:
val += "(float)"
val += strconv.FormatFloat(o.Float, 'f', -1, 64)
case TypFunction:
val += "(function)"
val += o.Function.String()
case TypStr:
val += "(string)"
val += o.Str
case TypExpr:
val += "(expr)"
val += o.Expr.String()
case TypTime:
val += "(time)"
val += o.Time.Format("Mon Jan 2 15:04:05 -0700 MST 2006")
case TypNil:
val += "<nil>"
case TypObject:
val += "(object)"
val += fmt.Sprintf("%v", o.Object)
default:
val += "<operand-type-undefined>"
}
return val
}
func (o BinaryOperator) String() string {
val := fmt.Sprintf("Off(%d):", o.Off)
switch o.Type {
case And:
val += "AND"
case Equal:
val += "="
case GreaterThan:
val += ">"
case GreaterThanOrEqual:
val += ">="
case Is:
val += "IS"
case IsNot:
val += "IS NOT"
case LessThan:
val += "<"
case LessThanOrEqual:
val += "<="
case Like:
val += "LIKE"
case NotEqual:
val += "<>"
case NotLike:
val += "NOT LIKE"
case Or:
val += "OR"
default:
val += "<operator-undefined>"
}
return val
}
func (e Expression) String() string {
return fmt.Sprintf("(Off(%d):%s %s %s)", e.Off, e.Operand1.String(), e.Operator.String(), e.Operand2.String())
}