Improvements on sql parser
This commit is contained in:
parent
d76038652b
commit
08943946a9
17 changed files with 1447 additions and 733 deletions
199
sql/ast.go
199
sql/ast.go
|
@ -30,20 +30,45 @@ type Statement interface{}
|
|||
type Statements []Statement
|
||||
|
||||
// --------------------------------------------------
|
||||
// Select
|
||||
// Use
|
||||
// --------------------------------------------------
|
||||
|
||||
// UseStatement represents a SQL USE statement.
|
||||
type UseStatement struct {
|
||||
NS string // Namespace
|
||||
DB string // Database
|
||||
CK string // Cipherkey
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Select
|
||||
// Trans
|
||||
// --------------------------------------------------
|
||||
|
||||
type BeginStatement struct{}
|
||||
|
||||
type CancelStatement struct{}
|
||||
|
||||
type CommitStatement struct{}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Normal
|
||||
// --------------------------------------------------
|
||||
|
||||
// ActionStatement represents a SQL ACTION statement.
|
||||
type ActionStatement struct {
|
||||
EX bool // Explain
|
||||
KV string // Bucket
|
||||
NS string // Namespace
|
||||
DB string // Database
|
||||
Expr []*Field // Which fields
|
||||
What []Expr // What to select
|
||||
Cond []Expr // Select conditions
|
||||
Group []*Group // Group by
|
||||
Order []*Order // Order by
|
||||
Limit Expr // Limit by
|
||||
Start Expr // Start at
|
||||
Version Expr // Version
|
||||
}
|
||||
|
||||
// SelectStatement represents a SQL SELECT statement.
|
||||
type SelectStatement struct {
|
||||
EX bool // Explain
|
||||
|
@ -58,12 +83,9 @@ type SelectStatement struct {
|
|||
Limit Expr // Limit by
|
||||
Start Expr // Start at
|
||||
Version Expr // Version
|
||||
Echo Token // What to return
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Items
|
||||
// --------------------------------------------------
|
||||
|
||||
// CreateStatement represents a SQL CREATE statement.
|
||||
//
|
||||
// CREATE person SET column = 'value' RETURN ID
|
||||
|
@ -157,22 +179,26 @@ type RecordStatement struct {
|
|||
//
|
||||
// DEFINE RULES person
|
||||
type DefineRulesStatement struct {
|
||||
EX bool // Explain
|
||||
KV string // Bucket
|
||||
NS string // Namespace
|
||||
DB string // Database
|
||||
What []Table // Table names
|
||||
EX bool `json:"-" msgpack:"-"` // Explain
|
||||
KV string `json:"-" msgpack:"-"` // Bucket
|
||||
NS string `json:"-" msgpack:"-"` // Namespace
|
||||
DB string `json:"-" msgpack:"-"` // Database
|
||||
What []string `json:"-" msgpack:"-"` // Table names
|
||||
When []string `json:"-" msgpack:"-"` // Action names
|
||||
Rule string `json:"rule" msgpack:"rule"` // Rule behaviour
|
||||
Code string `json:"code" msgpack:"code"` // Rule custom code
|
||||
}
|
||||
|
||||
// RemoveRulesStatement represents an SQL REMOVE RULES statement.
|
||||
//
|
||||
// REMOVE RULES person
|
||||
type RemoveRulesStatement struct {
|
||||
EX bool // Explain
|
||||
KV string // Bucket
|
||||
NS string // Namespace
|
||||
DB string // Database
|
||||
What []Table // Table names
|
||||
EX bool `json:"-" msgpack:"-"` // Explain
|
||||
KV string `json:"-" msgpack:"-"` // Bucket
|
||||
NS string `json:"-" msgpack:"-"` // Namespace
|
||||
DB string `json:"-" msgpack:"-"` // Database
|
||||
What []string `json:"-" msgpack:"-"` // Table names
|
||||
When []string `json:"-" msgpack:"-"` // Action names
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
|
@ -183,22 +209,22 @@ type RemoveRulesStatement struct {
|
|||
//
|
||||
// DEFINE TABLE person
|
||||
type DefineTableStatement struct {
|
||||
EX bool // Explain
|
||||
KV string // Bucket
|
||||
NS string // Namespace
|
||||
DB string // Database
|
||||
What []Table // Table names
|
||||
EX bool `json:"-" msgpack:"-"` // Explain
|
||||
KV string `json:"-" msgpack:"-"` // Bucket
|
||||
NS string `json:"-" msgpack:"-"` // Namespace
|
||||
DB string `json:"-" msgpack:"-"` // Database
|
||||
What []string `json:"-" msgpack:"-"` // Table names
|
||||
}
|
||||
|
||||
// RemoveTableStatement represents an SQL REMOVE TABLE statement.
|
||||
//
|
||||
// REMOVE TABLE person
|
||||
type RemoveTableStatement struct {
|
||||
EX bool // Explain
|
||||
KV string // Bucket
|
||||
NS string // Namespace
|
||||
DB string // Database
|
||||
What []Table // Table names
|
||||
EX bool `json:"-" msgpack:"-"` // Explain
|
||||
KV string `json:"-" msgpack:"-"` // Bucket
|
||||
NS string `json:"-" msgpack:"-"` // Namespace
|
||||
DB string `json:"-" msgpack:"-"` // Database
|
||||
What []string `json:"-" msgpack:"-"` // Table names
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
|
@ -211,35 +237,35 @@ type RemoveTableStatement struct {
|
|||
// DEFINE FIELD name ON person TYPE number MIN 0 MAX 5 DEFAULT 0
|
||||
// DEFINE FIELD name ON person TYPE custom ENUM [0,1,2,3,4,5] DEFAULT 0
|
||||
type DefineFieldStatement struct {
|
||||
EX bool // Explain
|
||||
KV string // Bucket
|
||||
NS string // Namespace
|
||||
DB string // Database
|
||||
Name Ident // Field name
|
||||
What []Table // Table names
|
||||
Type Ident // Field type
|
||||
Enum []interface{} // Custom options
|
||||
Code string // Field code
|
||||
Min float64 // Minimum value / length
|
||||
Max float64 // Maximum value / length
|
||||
Match string // Regex value
|
||||
Default interface{} // Default value
|
||||
Notnull bool // Notnull - can not be NULL?
|
||||
Readonly bool // Readonly - can not be changed?
|
||||
Mandatory bool // Mandatory - can not be VOID?
|
||||
Validate bool // Validate - can not be INCORRECT?
|
||||
EX bool `json:"-" msgpack:"-"` // Explain
|
||||
KV string `json:"-" msgpack:"-"` // Bucket
|
||||
NS string `json:"-" msgpack:"-"` // Namespace
|
||||
DB string `json:"-" msgpack:"-"` // Database
|
||||
Name string `json:"name" msgpack:"name"` // Field name
|
||||
What []string `json:"-" msgpack:"-"` // Table names
|
||||
Type string `json:"type" msgpack:"type"` // Field type
|
||||
Enum []interface{} `json:"enum" msgpack:"enum"` // Custom options
|
||||
Code string `json:"code" msgpack:"code"` // Field code
|
||||
Min float64 `json:"min" msgpack:"min"` // Minimum value / length
|
||||
Max float64 `json:"max" msgpack:"max"` // Maximum value / length
|
||||
Match string `json:"match" msgpack:"match"` // Regex value
|
||||
Default interface{} `json:"default" msgpack:"default"` // Default value
|
||||
Notnull bool `json:"notnull" msgpack:"notnull"` // Notnull - can not be NULL?
|
||||
Readonly bool `json:"readonly" msgpack:"readonly"` // Readonly - can not be changed?
|
||||
Mandatory bool `json:"mandatory" msgpack:"mandatory"` // Mandatory - can not be VOID?
|
||||
Validate bool `json:"validate" msgpack:"validate"` // Validate - can not be INCORRECT?
|
||||
}
|
||||
|
||||
// RemoveFieldStatement represents an SQL REMOVE INDEX statement.
|
||||
//
|
||||
// REMOVE FIELD name ON person
|
||||
type RemoveFieldStatement struct {
|
||||
EX bool // Explain
|
||||
KV string // Bucket
|
||||
NS string // Namespace
|
||||
DB string // Database
|
||||
Name Ident // Field name
|
||||
What []Table // Table names
|
||||
EX bool `json:"-" msgpack:"-"` // Explain
|
||||
KV string `json:"-" msgpack:"-"` // Bucket
|
||||
NS string `json:"-" msgpack:"-"` // Namespace
|
||||
DB string `json:"-" msgpack:"-"` // Database
|
||||
Name string `json:"-" msgpack:"-"` // Field name
|
||||
What []string `json:"-" msgpack:"-"` // Table names
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
|
@ -250,38 +276,39 @@ type RemoveFieldStatement struct {
|
|||
//
|
||||
// DEFINE INDEX name ON person COLUMNS (account, age) UNIQUE
|
||||
type DefineIndexStatement struct {
|
||||
EX bool // Explain
|
||||
KV string // Bucket
|
||||
NS string // Namespace
|
||||
DB string // Database
|
||||
Name Ident // Index name
|
||||
What []Table // Table names
|
||||
Code string // Index code
|
||||
Cols []*Field // Index cols
|
||||
Uniq bool // Unique index
|
||||
EX bool `json:"-" msgpack:"-"` // Explain
|
||||
KV string `json:"-" msgpack:"-"` // Bucket
|
||||
NS string `json:"-" msgpack:"-"` // Namespace
|
||||
DB string `json:"-" msgpack:"-"` // Database
|
||||
Name string `json:"name" msgpack:"name"` // Index name
|
||||
What []string `json:"-" msgpack:"-"` // Table names
|
||||
Cols []string `json:"cols" msgpack:"cols"` // Index cols
|
||||
Uniq bool `json:"unique" msgpack:"unique"` // Unique index
|
||||
CI bool
|
||||
CS bool
|
||||
}
|
||||
|
||||
// RemoveIndexStatement represents an SQL REMOVE INDEX statement.
|
||||
//
|
||||
// REMOVE INDEX name ON person
|
||||
type RemoveIndexStatement struct {
|
||||
EX bool // Explain
|
||||
KV string // Bucket
|
||||
NS string // Namespace
|
||||
DB string // Database
|
||||
Name Ident // Index name
|
||||
What []Table // Table names
|
||||
EX bool `json:"-" msgpack:"-"` // Explain
|
||||
KV string `json:"-" msgpack:"-"` // Bucket
|
||||
NS string `json:"-" msgpack:"-"` // Namespace
|
||||
DB string `json:"-" msgpack:"-"` // Database
|
||||
Name string `json:"-" msgpack:"-"` // Index name
|
||||
What []string `json:"-" msgpack:"-"` // Table names
|
||||
}
|
||||
|
||||
// ResyncIndexStatement represents an SQL RESYNC INDEX statement.
|
||||
//
|
||||
// RESYNC INDEX name ON person
|
||||
type ResyncIndexStatement struct {
|
||||
EX bool // Explain
|
||||
KV string // Bucket
|
||||
NS string // Namespace
|
||||
DB string // Database
|
||||
What []Table // Table names
|
||||
EX bool `json:"-" msgpack:"-"` // Explain
|
||||
KV string `json:"-" msgpack:"-"` // Bucket
|
||||
NS string `json:"-" msgpack:"-"` // Namespace
|
||||
DB string `json:"-" msgpack:"-"` // Database
|
||||
What []string `json:"-" msgpack:"-"` // Table names
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
|
@ -309,9 +336,6 @@ type Void struct{}
|
|||
// Empty represents an expression which is null or "".
|
||||
type Empty struct{}
|
||||
|
||||
// Wildcard represents a wildcard expression.
|
||||
type Wildcard struct{}
|
||||
|
||||
// ClosedExpression represents a parenthesized expression.
|
||||
type ClosedExpression struct {
|
||||
Expr Expr
|
||||
|
@ -320,7 +344,7 @@ type ClosedExpression struct {
|
|||
// BinaryExpression represents a binary expression tree,
|
||||
type BinaryExpression struct {
|
||||
LHS Expr
|
||||
Op string
|
||||
Op Token
|
||||
RHS Expr
|
||||
}
|
||||
|
||||
|
@ -343,23 +367,14 @@ type ContentExpression struct {
|
|||
// Parts
|
||||
// --------------------------------------------------
|
||||
|
||||
type Table string
|
||||
|
||||
func (this Table) String() string {
|
||||
return string(this)
|
||||
// Ident comment
|
||||
type Ident struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (this Table) MarshalText() ([]byte, error) {
|
||||
return []byte(string(this)), nil
|
||||
}
|
||||
|
||||
type Ident string
|
||||
|
||||
func (this Ident) String() string {
|
||||
return string(this)
|
||||
}
|
||||
func (this Ident) MarshalText() ([]byte, error) {
|
||||
return []byte(string(this)), nil
|
||||
// Table comment
|
||||
type Table struct {
|
||||
TB string
|
||||
}
|
||||
|
||||
// Thing comment
|
||||
|
@ -371,15 +386,15 @@ type Thing struct {
|
|||
// Field comment
|
||||
type Field struct {
|
||||
Expr Expr
|
||||
Alias Expr
|
||||
Alias string
|
||||
}
|
||||
|
||||
// Group comment
|
||||
// Group represents an sql GROUP BY clause
|
||||
type Group struct {
|
||||
Expr Expr
|
||||
}
|
||||
|
||||
// Order comment
|
||||
// Order represents an sql ORDER BY clause
|
||||
type Order struct {
|
||||
Expr Expr
|
||||
Dir Expr
|
||||
|
|
35
sql/cond.go
35
sql/cond.go
|
@ -28,7 +28,7 @@ func (p *Parser) parseCond() (mul []Expr, err error) {
|
|||
|
||||
one := &BinaryExpression{}
|
||||
|
||||
tok, lit, err = p.shouldBe(IDENT, ID, TIME, TRUE, FALSE, STRING, NUMBER, DOUBLE)
|
||||
tok, lit, err = p.shouldBe(ID, IDENT, NULL, VOID, MISSING, EMPTY, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, REGEX, JSON, ARRAY, BOUNDPARAM)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
|
||||
}
|
||||
|
@ -38,13 +38,39 @@ func (p *Parser) parseCond() (mul []Expr, err error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
tok, lit, err = p.shouldBe(IN, EQ, NEQ, GT, LT, GTE, LTE, EQR, NER, SEQ, SNE)
|
||||
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 = lit
|
||||
one.Op = tok
|
||||
|
||||
tok, lit, err = p.shouldBe(IDENT, ID, NULL, EMPTY, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, REGEX, JSON, ARRAY)
|
||||
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, NULL, VOID, MISSING, EMPTY, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, REGEX, JSON, ARRAY, BOUNDPARAM)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"field value"}}
|
||||
}
|
||||
|
@ -56,7 +82,6 @@ func (p *Parser) parseCond() (mul []Expr, err error) {
|
|||
|
||||
mul = append(mul, one)
|
||||
|
||||
// Remove the WHERE keyword
|
||||
if _, _, exi := p.mightBe(AND, OR); !exi {
|
||||
break
|
||||
}
|
||||
|
|
|
@ -65,9 +65,9 @@ func (p *Parser) parseSet() (mul []Expr, err error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
one.Op = lit
|
||||
one.Op = tok
|
||||
|
||||
tok, lit, err = p.shouldBe(IDENT, NULL, VOID, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, JSON, ARRAY)
|
||||
tok, lit, err = p.shouldBe(IDENT, NULL, VOID, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, JSON, ARRAY, BOUNDPARAM)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"field value"}}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ func (p *Parser) parseDiff() (exp []Expr, err error) {
|
|||
|
||||
one := &DiffExpression{}
|
||||
|
||||
tok, lit, err := p.shouldBe(JSON)
|
||||
tok, lit, err := p.shouldBe(JSON, ARRAY)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"json"}}
|
||||
}
|
||||
|
|
76
sql/exprs.go
76
sql/exprs.go
|
@ -16,26 +16,59 @@ package sql
|
|||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (p *Parser) parseName() (string, error) {
|
||||
|
||||
_, lit, err := p.shouldBe(IDENT)
|
||||
if err != nil {
|
||||
return string(""), &ParseError{Found: lit, Expected: []string{"name"}}
|
||||
}
|
||||
|
||||
val, err := declare(STRING, lit)
|
||||
|
||||
return val.(string), err
|
||||
|
||||
}
|
||||
|
||||
func (p *Parser) parseNames() (mul []string, err error) {
|
||||
|
||||
for {
|
||||
|
||||
one, err := p.parseName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mul = append(mul, one)
|
||||
|
||||
// If the next token is not a comma then break the loop.
|
||||
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
//
|
||||
// --------------------------------------------------
|
||||
|
||||
func (p *Parser) parseTable() (Table, error) {
|
||||
func (p *Parser) parseTable() (*Table, error) {
|
||||
|
||||
_, lit, err := p.shouldBe(IDENT, NUMBER, DATE)
|
||||
if err != nil {
|
||||
return Table(""), &ParseError{Found: lit, Expected: []string{"table name"}}
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"table name"}}
|
||||
}
|
||||
|
||||
return Table(lit), err
|
||||
return &Table{lit}, err
|
||||
|
||||
}
|
||||
|
||||
func (p *Parser) parseTables() (mul []Table, err error) {
|
||||
func (p *Parser) parseTables() (mul []*Table, err error) {
|
||||
|
||||
for {
|
||||
|
||||
|
@ -92,14 +125,8 @@ func (p *Parser) parseThing() (one *Thing, err error) {
|
|||
switch tok {
|
||||
case IDENT:
|
||||
val = lit
|
||||
case NUMBER:
|
||||
val, err = strconv.ParseInt(lit, 10, 64)
|
||||
case DOUBLE:
|
||||
val, err = strconv.ParseFloat(lit, 64)
|
||||
case DATE:
|
||||
val, err = time.Parse("2006-01-02", lit)
|
||||
case TIME:
|
||||
val, err = time.Parse(time.RFC3339, lit)
|
||||
default:
|
||||
val, err = declare(tok, lit)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -138,16 +165,16 @@ func (p *Parser) parseThings() (mul []Expr, err error) {
|
|||
//
|
||||
// --------------------------------------------------
|
||||
|
||||
func (p *Parser) parseIdent() (Ident, error) {
|
||||
func (p *Parser) parseIdent() (*Ident, error) {
|
||||
|
||||
_, lit, err := p.shouldBe(IDENT)
|
||||
if err != nil {
|
||||
return Ident(""), &ParseError{Found: lit, Expected: []string{"name"}}
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"name"}}
|
||||
}
|
||||
|
||||
val, err := declare(IDENT, lit)
|
||||
|
||||
return val.(Ident), err
|
||||
return val.(*Ident), err
|
||||
|
||||
}
|
||||
|
||||
|
@ -220,7 +247,7 @@ func (p *Parser) parseScript() (string, error) {
|
|||
|
||||
tok, lit, err := p.shouldBe(STRING, REGION)
|
||||
if err != nil {
|
||||
return string(""), &ParseError{Found: lit, Expected: []string{"LUA script"}}
|
||||
return string(""), &ParseError{Found: lit, Expected: []string{"js/lua script"}}
|
||||
}
|
||||
|
||||
val, err := declare(tok, lit)
|
||||
|
@ -257,7 +284,7 @@ func (p *Parser) parseBoolean() (bool, error) {
|
|||
|
||||
func (p *Parser) parseDefault() (interface{}, error) {
|
||||
|
||||
tok, lit, err := p.shouldBe(NULL, NOW, DATE, TIME, TRUE, FALSE, NUMBER, STRING, REGION, ARRAY, JSON)
|
||||
tok, lit, err := p.shouldBe(NULL, NOW, DATE, TIME, TRUE, FALSE, NUMBER, DOUBLE, STRING, REGION, IDENT, ARRAY, JSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -271,12 +298,13 @@ func (p *Parser) parseExpr() (mul []*Field, err error) {
|
|||
var tok Token
|
||||
var lit string
|
||||
var exi bool
|
||||
var val interface{}
|
||||
|
||||
for {
|
||||
|
||||
one := &Field{}
|
||||
|
||||
tok, lit, err = p.shouldBe(IDENT, ID, OEDGE, IEDGE, BEDGE, NULL, ALL, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, JSON)
|
||||
tok, lit, err = p.shouldBe(IDENT, ID, NOW, PATH, NULL, ALL, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, JSON, ARRAY)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
|
||||
}
|
||||
|
@ -286,19 +314,23 @@ func (p *Parser) parseExpr() (mul []*Field, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
one.Alias = lit
|
||||
|
||||
// Next token might be AS
|
||||
if _, _, exi = p.mightBe(AS); exi {
|
||||
|
||||
tok, lit, err = p.shouldBe(IDENT)
|
||||
_, lit, err = p.shouldBe(IDENT)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"field alias"}}
|
||||
}
|
||||
|
||||
one.Alias, err = declare(tok, lit)
|
||||
val, err = declare(STRING, lit)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
one.Alias = val.(string)
|
||||
|
||||
}
|
||||
|
||||
mul = append(mul, one)
|
||||
|
|
|
@ -24,7 +24,7 @@ func (p *Parser) parseDefineFieldStatement(explain bool) (stmt *DefineFieldState
|
|||
stmt.NS = p.c.Get("NS").(string)
|
||||
stmt.DB = p.c.Get("DB").(string)
|
||||
|
||||
if stmt.Name, err = p.parseIdent(); err != nil {
|
||||
if stmt.Name, err = p.parseName(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ func (p *Parser) parseDefineFieldStatement(explain bool) (stmt *DefineFieldState
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.What, err = p.parseTables(); err != nil {
|
||||
if stmt.What, err = p.parseNames(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ func (p *Parser) parseRemoveFieldStatement(explain bool) (stmt *RemoveFieldState
|
|||
stmt.NS = p.c.Get("NS").(string)
|
||||
stmt.DB = p.c.Get("DB").(string)
|
||||
|
||||
if stmt.Name, err = p.parseIdent(); err != nil {
|
||||
if stmt.Name, err = p.parseName(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -153,7 +153,7 @@ func (p *Parser) parseRemoveFieldStatement(explain bool) (stmt *RemoveFieldState
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.What, err = p.parseTables(); err != nil {
|
||||
if stmt.What, err = p.parseNames(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
32
sql/index.go
32
sql/index.go
|
@ -24,7 +24,7 @@ func (p *Parser) parseDefineIndexStatement(explain bool) (stmt *DefineIndexState
|
|||
stmt.NS = p.c.Get("NS").(string)
|
||||
stmt.DB = p.c.Get("DB").(string)
|
||||
|
||||
if stmt.Name, err = p.parseIdent(); err != nil {
|
||||
if stmt.Name, err = p.parseName(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -32,28 +32,16 @@ func (p *Parser) parseDefineIndexStatement(explain bool) (stmt *DefineIndexState
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.What, err = p.parseTables(); err != nil {
|
||||
if stmt.What, err = p.parseNames(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tok, _, err := p.shouldBe(CODE, COLUMNS); tok != 0 {
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if is(tok, CODE) {
|
||||
if stmt.Code, err = p.parseScript(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if is(tok, COLUMNS) {
|
||||
if stmt.Cols, err = p.parseExpr(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if _, _, err = p.shouldBe(COLUMNS); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.Cols, err = p.parseNames(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, _, stmt.Uniq = p.mightBe(UNIQUE)
|
||||
|
@ -80,7 +68,7 @@ func (p *Parser) parseResyncIndexStatement(explain bool) (stmt *ResyncIndexState
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.What, err = p.parseTables(); err != nil {
|
||||
if stmt.What, err = p.parseNames(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -102,7 +90,7 @@ func (p *Parser) parseRemoveIndexStatement(explain bool) (stmt *RemoveIndexState
|
|||
stmt.NS = p.c.Get("NS").(string)
|
||||
stmt.DB = p.c.Get("DB").(string)
|
||||
|
||||
if stmt.Name, err = p.parseIdent(); err != nil {
|
||||
if stmt.Name, err = p.parseName(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -110,7 +98,7 @@ func (p *Parser) parseRemoveIndexStatement(explain bool) (stmt *RemoveIndexState
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.What, err = p.parseTables(); err != nil {
|
||||
if stmt.What, err = p.parseNames(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,10 @@ func (p *Parser) parseModifyStatement(explain bool) (stmt *ModifyStatement, err
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.Cond, err = p.parseCond(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.Echo, err = p.parseEcho(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -111,13 +111,20 @@ func (p *Parser) ParseSingle() (Statement, error) {
|
|||
explain = true
|
||||
}
|
||||
|
||||
tok, _, err := p.shouldBe(USE, SELECT, CREATE, UPDATE, INSERT, UPSERT, MODIFY, DELETE, RELATE, RECORD, DEFINE, RESYNC, REMOVE)
|
||||
tok, _, err := p.shouldBe(USE, LET, BEGIN, CANCEL, COMMIT, ROLLBACK, SELECT, CREATE, UPDATE, INSERT, UPSERT, MODIFY, DELETE, RELATE, RECORD, DEFINE, RESYNC, REMOVE)
|
||||
|
||||
switch tok {
|
||||
|
||||
case USE:
|
||||
return p.parseUseStatement(explain)
|
||||
|
||||
case BEGIN:
|
||||
return p.parseBeginStatement(explain)
|
||||
case CANCEL, ROLLBACK:
|
||||
return p.parseCancelStatement(explain)
|
||||
case COMMIT:
|
||||
return p.parseCommitStatement(explain)
|
||||
|
||||
case SELECT:
|
||||
return p.parseSelectStatement(explain)
|
||||
case CREATE, INSERT:
|
||||
|
|
72
sql/rules.go
72
sql/rules.go
|
@ -24,10 +24,51 @@ func (p *Parser) parseDefineRulesStatement(explain bool) (stmt *DefineRulesState
|
|||
stmt.NS = p.c.Get("NS").(string)
|
||||
stmt.DB = p.c.Get("DB").(string)
|
||||
|
||||
if stmt.What, err = p.parseTables(); err != nil {
|
||||
if _, _, err = p.shouldBe(ON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.What, err = p.parseNames(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, _, err = p.shouldBe(FOR); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for {
|
||||
|
||||
tok, _, exi := p.mightBe(SELECT, CREATE, UPDATE, DELETE, RELATE, RECORD)
|
||||
if !exi {
|
||||
break
|
||||
}
|
||||
|
||||
stmt.When = append(stmt.When, tok.String())
|
||||
|
||||
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(stmt.When) == 0 {
|
||||
return nil, &ParseError{Found: "", Expected: []string{"SELECT", "CREATE", "UPDATE", "DELETE", "RELATE", "RECORD"}}
|
||||
}
|
||||
|
||||
if tok, _, err := p.shouldBe(ACCEPT, REJECT, CUSTOM); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
|
||||
stmt.Rule = tok.String()
|
||||
|
||||
if is(tok, CUSTOM) {
|
||||
if stmt.Code, err = p.parseScript(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -46,10 +87,37 @@ func (p *Parser) parseRemoveRulesStatement(explain bool) (stmt *RemoveRulesState
|
|||
stmt.NS = p.c.Get("NS").(string)
|
||||
stmt.DB = p.c.Get("DB").(string)
|
||||
|
||||
if stmt.What, err = p.parseTables(); err != nil {
|
||||
if _, _, err = p.shouldBe(ON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.What, err = p.parseNames(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, _, err = p.shouldBe(FOR); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for {
|
||||
|
||||
tok, _, exi := p.mightBe(SELECT, CREATE, UPDATE, DELETE, RELATE, RECORD)
|
||||
if !exi {
|
||||
break
|
||||
}
|
||||
|
||||
stmt.When = append(stmt.When, tok.String())
|
||||
|
||||
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(stmt.When) == 0 {
|
||||
return nil, &ParseError{Found: "", Expected: []string{"SELECT", "CREATE", "UPDATE", "DELETE", "RELATE", "RECORD"}}
|
||||
}
|
||||
|
||||
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
463
sql/scanner.go
463
sql/scanner.go
|
@ -18,12 +18,15 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Scanner represents a lexical scanner.
|
||||
type Scanner struct {
|
||||
p []rune
|
||||
n []rune
|
||||
r *bufio.Reader
|
||||
}
|
||||
|
||||
|
@ -36,24 +39,21 @@ func NewScanner(r io.Reader) *Scanner {
|
|||
func (s *Scanner) Scan() (tok Token, lit string) {
|
||||
|
||||
// Read the next rune.
|
||||
ch := s.read()
|
||||
ch := s.next()
|
||||
|
||||
// If we see whitespace then consume all contiguous whitespace.
|
||||
if isWhitespace(ch) {
|
||||
s.unread()
|
||||
return s.scanWhitespace()
|
||||
return s.scanBlank(ch)
|
||||
}
|
||||
|
||||
// If we see a letter then consume as an string.
|
||||
if isLetter(ch) {
|
||||
s.unread()
|
||||
return s.scanIdent()
|
||||
return s.scanIdent(ch)
|
||||
}
|
||||
|
||||
// If we see a number then consume as a number.
|
||||
if isNumber(ch) {
|
||||
s.unread()
|
||||
return s.scanNumber()
|
||||
return s.scanNumber(ch)
|
||||
}
|
||||
|
||||
// Otherwise read the individual character.
|
||||
|
@ -63,6 +63,12 @@ func (s *Scanner) Scan() (tok Token, lit string) {
|
|||
return EOF, ""
|
||||
case '*':
|
||||
return ALL, string(ch)
|
||||
case '×':
|
||||
return MUL, string(ch)
|
||||
case '∙':
|
||||
return MUL, string(ch)
|
||||
case '÷':
|
||||
return DIV, string(ch)
|
||||
case '@':
|
||||
return EAT, string(ch)
|
||||
case ',':
|
||||
|
@ -70,23 +76,19 @@ func (s *Scanner) Scan() (tok Token, lit string) {
|
|||
case '.':
|
||||
return DOT, string(ch)
|
||||
case '"':
|
||||
s.unread()
|
||||
return s.scanString()
|
||||
return s.scanString(ch)
|
||||
case '\'':
|
||||
s.unread()
|
||||
return s.scanString()
|
||||
return s.scanString(ch)
|
||||
case '`':
|
||||
s.unread()
|
||||
return s.scanQuoted()
|
||||
return s.scanQuoted(ch)
|
||||
case '⟨':
|
||||
s.unread()
|
||||
return s.scanQuoted()
|
||||
return s.scanQuoted(ch)
|
||||
case '{':
|
||||
s.unread()
|
||||
return s.scanObject()
|
||||
return s.scanObject(ch)
|
||||
case '[':
|
||||
s.unread()
|
||||
return s.scanObject()
|
||||
return s.scanObject(ch)
|
||||
case '$':
|
||||
return s.scanParams(ch)
|
||||
case ':':
|
||||
return COLON, string(ch)
|
||||
case ';':
|
||||
|
@ -95,102 +97,262 @@ func (s *Scanner) Scan() (tok Token, lit string) {
|
|||
return LPAREN, string(ch)
|
||||
case ')':
|
||||
return RPAREN, string(ch)
|
||||
case '=':
|
||||
return EQ, string(ch)
|
||||
case '+':
|
||||
if chn := s.read(); chn == '=' {
|
||||
return INC, "+="
|
||||
}
|
||||
s.unread()
|
||||
return ADD, string(ch)
|
||||
case '-':
|
||||
if chn := s.read(); chn == '>' {
|
||||
return OEDGE, "->"
|
||||
case '¬':
|
||||
return NEQ, string(ch)
|
||||
case '≤':
|
||||
return LTE, string(ch)
|
||||
case '≥':
|
||||
return GTE, string(ch)
|
||||
case '~':
|
||||
return SIN, string(ch)
|
||||
case '∋':
|
||||
return SIN, string(ch)
|
||||
case '∌':
|
||||
return SNI, string(ch)
|
||||
case '⊇':
|
||||
return CONTAINSALL, string(ch)
|
||||
case '⊃':
|
||||
return CONTAINSSOME, string(ch)
|
||||
case '⊅':
|
||||
return CONTAINSNONE, string(ch)
|
||||
case '∈':
|
||||
return INS, string(ch)
|
||||
case '∉':
|
||||
return NIS, string(ch)
|
||||
case '⊆':
|
||||
return ALLCONTAINEDIN, string(ch)
|
||||
case '⊂':
|
||||
return SOMECONTAINEDIN, string(ch)
|
||||
case '⊄':
|
||||
return NONECONTAINEDIN, string(ch)
|
||||
case '#':
|
||||
return s.scanCommentSingle(ch)
|
||||
case '/':
|
||||
chn := s.next()
|
||||
switch {
|
||||
case chn == '*':
|
||||
return s.scanCommentMultiple(ch)
|
||||
case chn == ' ':
|
||||
s.undo()
|
||||
return DIV, string(ch)
|
||||
default:
|
||||
s.unread()
|
||||
s.undo()
|
||||
return s.scanRegexp(ch)
|
||||
}
|
||||
s.unread()
|
||||
if chn := s.read(); chn == '=' {
|
||||
return DEC, "-="
|
||||
case '=':
|
||||
chn := s.next()
|
||||
switch {
|
||||
case chn == '~':
|
||||
return SIN, "=~"
|
||||
case chn == '=':
|
||||
return EEQ, "=="
|
||||
default:
|
||||
s.undo()
|
||||
return EQ, string(ch)
|
||||
}
|
||||
case '?':
|
||||
chn := s.next()
|
||||
switch {
|
||||
case chn == '=':
|
||||
return ANY, "?="
|
||||
default:
|
||||
s.undo()
|
||||
return QMARK, string(ch)
|
||||
}
|
||||
s.unread()
|
||||
return SUB, string(ch)
|
||||
case '!':
|
||||
if chn := s.read(); chn == '=' {
|
||||
return NEQ, "!="
|
||||
}
|
||||
s.unread()
|
||||
case '<':
|
||||
if chn := s.read(); chn == '-' {
|
||||
if chn := s.read(); chn == '>' {
|
||||
return BEDGE, "<->"
|
||||
chn := s.next()
|
||||
switch {
|
||||
case chn == '=':
|
||||
if s.next() == '=' {
|
||||
return NEE, "!=="
|
||||
} else {
|
||||
s.undo()
|
||||
return NEQ, "!="
|
||||
}
|
||||
s.unread()
|
||||
return IEDGE, "<-"
|
||||
case chn == '~':
|
||||
return SNI, "!~"
|
||||
default:
|
||||
s.undo()
|
||||
return EXC, string(ch)
|
||||
}
|
||||
s.unread()
|
||||
if chn := s.read(); chn == '=' {
|
||||
return LTE, "<="
|
||||
case '+':
|
||||
chn := s.next()
|
||||
switch {
|
||||
case chn == '=':
|
||||
return INC, "+="
|
||||
case isNumber(chn):
|
||||
return s.scanNumber(ch, chn)
|
||||
default:
|
||||
s.undo()
|
||||
return ADD, string(ch)
|
||||
}
|
||||
case '-':
|
||||
chn := s.next()
|
||||
switch {
|
||||
case chn == '=':
|
||||
return DEC, "-="
|
||||
case chn == '>':
|
||||
return OEDGE, "->"
|
||||
case chn == '-':
|
||||
return s.scanCommentSingle(ch)
|
||||
case isNumber(chn):
|
||||
return s.scanNumber(ch, chn)
|
||||
default:
|
||||
s.undo()
|
||||
return SUB, string(ch)
|
||||
}
|
||||
s.unread()
|
||||
return LT, string(ch)
|
||||
case '>':
|
||||
if chn := s.read(); chn == '=' {
|
||||
chn := s.next()
|
||||
switch {
|
||||
case chn == '=':
|
||||
return GTE, ">="
|
||||
default:
|
||||
s.undo()
|
||||
return GT, string(ch)
|
||||
}
|
||||
case '<':
|
||||
chn := s.next()
|
||||
switch {
|
||||
case chn == '>':
|
||||
return NEQ, "<>"
|
||||
case chn == '=':
|
||||
return LTE, "<="
|
||||
case chn == '-':
|
||||
if s.next() == '>' {
|
||||
return BEDGE, "<->"
|
||||
} else {
|
||||
s.undo()
|
||||
return IEDGE, "<-"
|
||||
}
|
||||
default:
|
||||
s.undo()
|
||||
return LT, string(ch)
|
||||
}
|
||||
s.unread()
|
||||
return GT, string(ch)
|
||||
|
||||
}
|
||||
|
||||
return ILLEGAL, string(ch)
|
||||
|
||||
}
|
||||
|
||||
// scanWhitespace consumes the current rune and all contiguous whitespace.
|
||||
func (s *Scanner) scanWhitespace() (tok Token, lit string) {
|
||||
// scanBlank consumes the current rune and all contiguous whitespace.
|
||||
func (s *Scanner) scanBlank(chp ...rune) (tok Token, lit string) {
|
||||
|
||||
// Create a buffer and read the current character into it.
|
||||
tok = WS
|
||||
|
||||
// Create a buffer
|
||||
var buf bytes.Buffer
|
||||
buf.WriteRune(s.read())
|
||||
|
||||
// Read every subsequent whitespace character into the buffer.
|
||||
// Non-whitespace characters and EOF will cause the loop to exit.
|
||||
// Read passed in runes
|
||||
for _, ch := range chp {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
// Read subsequent characters
|
||||
for {
|
||||
if ch := s.read(); ch == eof {
|
||||
if ch := s.next(); ch == eof {
|
||||
break
|
||||
} else if !isWhitespace(ch) {
|
||||
s.unread()
|
||||
s.undo()
|
||||
break
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
|
||||
return WS, buf.String()
|
||||
return tok, buf.String()
|
||||
|
||||
}
|
||||
|
||||
// scanCommentSingle consumes the current rune and all contiguous whitespace.
|
||||
func (s *Scanner) scanCommentSingle(chp ...rune) (tok Token, lit string) {
|
||||
|
||||
tok = WS
|
||||
|
||||
// Create a buffer
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Read passed in runes
|
||||
for _, ch := range chp {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
// Read subsequent characters
|
||||
for {
|
||||
if ch := s.next(); ch == '\n' || ch == '\r' {
|
||||
buf.WriteRune(ch)
|
||||
break
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
|
||||
return tok, buf.String()
|
||||
|
||||
}
|
||||
|
||||
// scanCommentMultiple consumes the current rune and all contiguous whitespace.
|
||||
func (s *Scanner) scanCommentMultiple(chp ...rune) (tok Token, lit string) {
|
||||
|
||||
tok = WS
|
||||
|
||||
// Create a buffer
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Read passed in runes
|
||||
for _, ch := range chp {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
// Read subsequent characters
|
||||
for {
|
||||
if ch := s.next(); ch == eof {
|
||||
break
|
||||
} else if ch == '*' {
|
||||
if chn := s.next(); chn == '/' {
|
||||
buf.WriteRune(chn)
|
||||
break
|
||||
}
|
||||
buf.WriteRune(ch)
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
|
||||
return tok, buf.String()
|
||||
|
||||
}
|
||||
|
||||
func (s *Scanner) scanParams(chp ...rune) (Token, string) {
|
||||
|
||||
tok, lit := s.scanIdent(chp...)
|
||||
|
||||
if is(tok, IDENT) {
|
||||
return BOUNDPARAM, lit
|
||||
}
|
||||
|
||||
return tok, lit
|
||||
|
||||
}
|
||||
|
||||
// scanIdent consumes the current rune and all contiguous ident runes.
|
||||
func (s *Scanner) scanIdent() (tok Token, lit string) {
|
||||
func (s *Scanner) scanIdent(chp ...rune) (tok Token, lit string) {
|
||||
|
||||
// Create a buffer and read the current character into it.
|
||||
tok = IDENT
|
||||
|
||||
// Create a buffer
|
||||
var buf bytes.Buffer
|
||||
buf.WriteRune(s.read())
|
||||
|
||||
// Read every subsequent ident character into the buffer.
|
||||
// Non-ident characters and EOF will cause the loop to exit.
|
||||
// Read passed in runes
|
||||
for _, ch := range chp {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
// Read subsequent characters
|
||||
for {
|
||||
if ch := s.read(); ch == eof {
|
||||
if ch := s.next(); ch == eof {
|
||||
break
|
||||
} else if !isIdentChar(ch) {
|
||||
s.unread()
|
||||
s.undo()
|
||||
break
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
|
@ -207,22 +369,25 @@ func (s *Scanner) scanIdent() (tok Token, lit string) {
|
|||
}
|
||||
|
||||
// Otherwise return as a regular identifier.
|
||||
return IDENT, buf.String()
|
||||
return tok, buf.String()
|
||||
|
||||
}
|
||||
|
||||
func (s *Scanner) scanNumber() (tok Token, lit string) {
|
||||
func (s *Scanner) scanNumber(chp ...rune) (tok Token, lit string) {
|
||||
|
||||
tok = NUMBER
|
||||
|
||||
// Create a buffer and read the current character into it.
|
||||
// Create a buffer
|
||||
var buf bytes.Buffer
|
||||
buf.WriteRune(s.read())
|
||||
|
||||
// Read every subsequent ident character into the buffer.
|
||||
// Non-ident characters and EOF will cause the loop to exit.
|
||||
// Read passed in runes
|
||||
for _, ch := range chp {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
// Read subsequent characters
|
||||
for {
|
||||
if ch := s.read(); ch == eof {
|
||||
if ch := s.next(); ch == eof {
|
||||
break
|
||||
} else if isNumber(ch) {
|
||||
buf.WriteRune(ch)
|
||||
|
@ -238,7 +403,7 @@ func (s *Scanner) scanNumber() (tok Token, lit string) {
|
|||
}
|
||||
buf.WriteRune(ch)
|
||||
} else {
|
||||
s.unread()
|
||||
s.undo()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -247,9 +412,9 @@ func (s *Scanner) scanNumber() (tok Token, lit string) {
|
|||
|
||||
}
|
||||
|
||||
func (s *Scanner) scanQuoted() (Token, string) {
|
||||
func (s *Scanner) scanQuoted(chp ...rune) (Token, string) {
|
||||
|
||||
tok, lit := s.scanString()
|
||||
tok, lit := s.scanString(chp...)
|
||||
|
||||
if is(tok, STRING) {
|
||||
return IDENT, lit
|
||||
|
@ -259,13 +424,9 @@ func (s *Scanner) scanQuoted() (Token, string) {
|
|||
|
||||
}
|
||||
|
||||
func (s *Scanner) scanString() (tok Token, lit string) {
|
||||
func (s *Scanner) scanString(chp ...rune) (tok Token, lit string) {
|
||||
|
||||
tok = STRING
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
beg := s.read()
|
||||
beg := chp[0]
|
||||
end := beg
|
||||
|
||||
if beg == '"' {
|
||||
|
@ -280,8 +441,16 @@ func (s *Scanner) scanString() (tok Token, lit string) {
|
|||
end = '⟩'
|
||||
}
|
||||
|
||||
tok = STRING
|
||||
|
||||
// Create a buffer
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Ignore passed in runes
|
||||
|
||||
// Read subsequent characters
|
||||
for {
|
||||
if ch := s.read(); ch == end {
|
||||
if ch := s.next(); ch == end {
|
||||
break
|
||||
} else if ch == eof {
|
||||
return ILLEGAL, buf.String()
|
||||
|
@ -292,13 +461,13 @@ func (s *Scanner) scanString() (tok Token, lit string) {
|
|||
tok = REGION
|
||||
buf.WriteRune(ch)
|
||||
} else if ch == '\\' {
|
||||
chn := s.read()
|
||||
switch chn {
|
||||
switch chn := s.next(); chn {
|
||||
default:
|
||||
buf.WriteRune(chn)
|
||||
case 'b':
|
||||
continue
|
||||
case 't':
|
||||
tok = REGION
|
||||
buf.WriteRune('\t')
|
||||
case 'r':
|
||||
tok = REGION
|
||||
|
@ -328,13 +497,41 @@ func (s *Scanner) scanString() (tok Token, lit string) {
|
|||
|
||||
}
|
||||
|
||||
func (s *Scanner) scanObject() (tok Token, lit string) {
|
||||
func (s *Scanner) scanRegexp(chp ...rune) (tok Token, lit string) {
|
||||
|
||||
tok = IDENT
|
||||
|
||||
// Create a buffer
|
||||
var buf bytes.Buffer
|
||||
|
||||
beg := s.read()
|
||||
// Ignore passed in runes
|
||||
|
||||
// Read subsequent characters
|
||||
for {
|
||||
if ch := s.next(); ch == chp[0] {
|
||||
break
|
||||
} else if ch == eof {
|
||||
return ILLEGAL, buf.String()
|
||||
} else if ch == '\\' {
|
||||
chn := s.next()
|
||||
buf.WriteRune(ch)
|
||||
buf.WriteRune(chn)
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := regexp.Compile(buf.String()); err == nil {
|
||||
return REGEX, buf.String()
|
||||
}
|
||||
|
||||
return tok, buf.String()
|
||||
|
||||
}
|
||||
|
||||
func (s *Scanner) scanObject(chp ...rune) (tok Token, lit string) {
|
||||
|
||||
beg := chp[0]
|
||||
end := beg
|
||||
sub := 0
|
||||
|
||||
|
@ -346,8 +543,20 @@ func (s *Scanner) scanObject() (tok Token, lit string) {
|
|||
end = ']'
|
||||
}
|
||||
|
||||
tok = IDENT
|
||||
|
||||
// Create a buffer
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Read passed in runes
|
||||
for _, ch := range chp {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
// Read subsequent characters
|
||||
for {
|
||||
if ch := s.read(); ch == end && sub == 0 {
|
||||
if ch := s.next(); ch == end && sub == 0 {
|
||||
buf.WriteRune(ch)
|
||||
break
|
||||
} else if ch == beg {
|
||||
sub++
|
||||
|
@ -358,8 +567,7 @@ func (s *Scanner) scanObject() (tok Token, lit string) {
|
|||
} else if ch == eof {
|
||||
return ILLEGAL, buf.String()
|
||||
} else if ch == '\\' {
|
||||
chn := s.read()
|
||||
switch chn {
|
||||
switch chn := s.next(); chn {
|
||||
default:
|
||||
return ILLEGAL, buf.String()
|
||||
case 'b', 't', 'r', 'n', 'f', '"', '\\':
|
||||
|
@ -371,39 +579,49 @@ func (s *Scanner) scanObject() (tok Token, lit string) {
|
|||
}
|
||||
}
|
||||
|
||||
str := buf.String()
|
||||
|
||||
str = strings.Replace(str, "\n", "", -1)
|
||||
str = strings.Replace(str, "\r", "", -1)
|
||||
str = strings.Trim(str, " ")
|
||||
|
||||
if beg == '[' {
|
||||
return ARRAY, string(beg) + str + string(end)
|
||||
}
|
||||
|
||||
if beg == '{' {
|
||||
if len(str) == 0 || str[0] == '"' {
|
||||
return JSON, string(beg) + str + string(end)
|
||||
}
|
||||
return JSON, buf.String()
|
||||
}
|
||||
if beg == '[' {
|
||||
return ARRAY, buf.String()
|
||||
}
|
||||
|
||||
return tok, str
|
||||
return ILLEGAL, buf.String()
|
||||
|
||||
}
|
||||
|
||||
// read reads the next rune from the bufferred reader.
|
||||
// next reads the next rune from the bufferred reader.
|
||||
// Returns the rune(0) if an error occurs (or io.EOF is returned).
|
||||
func (s *Scanner) read() rune {
|
||||
ch, _, err := s.r.ReadRune()
|
||||
func (s *Scanner) next() rune {
|
||||
|
||||
if len(s.n) > 0 {
|
||||
var r rune
|
||||
r, s.n = s.n[len(s.n)-1], s.n[:len(s.n)-1]
|
||||
s.p = append(s.p, r)
|
||||
return r
|
||||
}
|
||||
|
||||
r, _, err := s.r.ReadRune()
|
||||
if err != nil {
|
||||
return eof
|
||||
}
|
||||
return ch
|
||||
s.p = append(s.p, r)
|
||||
return r
|
||||
|
||||
}
|
||||
|
||||
// unread places the previously read rune back on the reader.
|
||||
func (s *Scanner) unread() {
|
||||
// undo places the previously read rune back on the reader.
|
||||
func (s *Scanner) undo() {
|
||||
|
||||
if len(s.p) > 0 {
|
||||
var r rune
|
||||
r, s.p = s.p[len(s.p)-1], s.p[:len(s.p)-1]
|
||||
s.n = append(s.n, r)
|
||||
return
|
||||
}
|
||||
|
||||
_ = s.r.UnreadRune()
|
||||
|
||||
}
|
||||
|
||||
// isWhitespace returns true if the rune is a space, tab, or newline.
|
||||
|
@ -411,24 +629,19 @@ func isWhitespace(ch rune) bool {
|
|||
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'
|
||||
}
|
||||
|
||||
// isLetter returns true if the rune is a letter.
|
||||
func isLetter(ch rune) bool {
|
||||
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|
||||
}
|
||||
|
||||
// isNumber returns true if the rune is a number.
|
||||
func isNumber(ch rune) bool {
|
||||
return (ch >= '0' && ch <= '9')
|
||||
}
|
||||
|
||||
// isSeparator returns true if the rune is a separator expression.
|
||||
func isSeparator(ch rune) bool {
|
||||
return (ch == '.')
|
||||
// isLetter returns true if the rune is a letter.
|
||||
func isLetter(ch rune) bool {
|
||||
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|
||||
}
|
||||
|
||||
// isIdentChar returns true if the rune can be used in an unquoted identifier.
|
||||
// isIdentChar returns true if the rune is allowed in a IDENT.
|
||||
func isIdentChar(ch rune) bool {
|
||||
return isLetter(ch) || isNumber(ch) || isSeparator(ch) || ch == '_' || ch == '*' || ch == '?'
|
||||
return isLetter(ch) || isNumber(ch) || ch == '.' || ch == '_' || ch == '*'
|
||||
}
|
||||
|
||||
// eof represents a marker rune for the end of the reader.
|
||||
|
|
|
@ -61,6 +61,10 @@ func (p *Parser) parseSelectStatement(explain bool) (stmt *SelectStatement, err
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if stmt.Echo, err = p.parseEcho(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -113,8 +117,7 @@ func (p *Parser) parseOrder() (mul []*Order, err error) {
|
|||
|
||||
tok, lit, exi = p.mightBe(ASC, DESC)
|
||||
if !exi {
|
||||
tok = ASC
|
||||
lit = "ASC"
|
||||
tok, lit = ASC, "ASC"
|
||||
}
|
||||
|
||||
one.Dir, err = declare(tok, lit)
|
||||
|
|
909
sql/sql_test.go
909
sql/sql_test.go
File diff suppressed because it is too large
Load diff
|
@ -24,7 +24,7 @@ func (p *Parser) parseDefineTableStatement(explain bool) (stmt *DefineTableState
|
|||
stmt.NS = p.c.Get("NS").(string)
|
||||
stmt.DB = p.c.Get("DB").(string)
|
||||
|
||||
if stmt.What, err = p.parseTables(); err != nil {
|
||||
if stmt.What, err = p.parseNames(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ func (p *Parser) parseRemoveTableStatement(explain bool) (stmt *RemoveTableState
|
|||
stmt.NS = p.c.Get("NS").(string)
|
||||
stmt.DB = p.c.Get("DB").(string)
|
||||
|
||||
if stmt.What, err = p.parseTables(); err != nil {
|
||||
if stmt.What, err = p.parseNames(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
274
sql/token.go
274
sql/token.go
|
@ -29,22 +29,24 @@ const (
|
|||
|
||||
literalsBeg
|
||||
|
||||
DATE // 1970-01-01
|
||||
TIME // 1970-01-01T00:00:00+00:00
|
||||
PATH // :friend
|
||||
JSON // {"test":true}
|
||||
IDENT // something
|
||||
STRING // "something"
|
||||
REGION // "a multiline \n string"
|
||||
NUMBER // 123456
|
||||
DOUBLE // 123.456
|
||||
REGEX // /.*/
|
||||
ARRAY // [0,1,2]
|
||||
DURATION // 13h
|
||||
DATE // 1970-01-01
|
||||
TIME // 1970-01-01T00:00:00+00:00
|
||||
PATH // person->like->person
|
||||
JSON // {"test":true}
|
||||
IDENT // something
|
||||
STRING // "something"
|
||||
REGION // "a multiline \n string"
|
||||
NUMBER // 123456
|
||||
DOUBLE // 123.456
|
||||
REGEX // /.*/
|
||||
ARRAY // [0,1,2]
|
||||
DURATION // 13h
|
||||
BOUNDPARAM // $1
|
||||
|
||||
EAT // @
|
||||
DOT // .
|
||||
COMMA // ,
|
||||
QMARK // ?
|
||||
LPAREN // (
|
||||
RPAREN // )
|
||||
LBRACK // [
|
||||
|
@ -66,15 +68,19 @@ const (
|
|||
DEC // -=
|
||||
|
||||
EQ // =
|
||||
EEQ // ==
|
||||
EXC // !
|
||||
NEQ // !=
|
||||
NEE // !==
|
||||
ANY // ?=
|
||||
LT // <
|
||||
LTE // <=
|
||||
GT // >
|
||||
GTE // >=
|
||||
EQR // =~
|
||||
NER // !~
|
||||
SEQ // ∋
|
||||
SNE // ∌
|
||||
SIN // ∋
|
||||
SNI // ∌
|
||||
INS // ∈
|
||||
NIS // ∉
|
||||
|
||||
OEDGE // ->
|
||||
IEDGE // <-
|
||||
|
@ -89,17 +95,27 @@ const (
|
|||
ACCEPT
|
||||
AFTER
|
||||
ALL
|
||||
ALLCONTAINEDIN
|
||||
AND
|
||||
AS
|
||||
ASC
|
||||
AT
|
||||
BEFORE
|
||||
BEGIN
|
||||
BOTH
|
||||
BY
|
||||
CANCEL
|
||||
CODE
|
||||
COLLECT
|
||||
COLUMNS
|
||||
COMMIT
|
||||
CONTAINS
|
||||
CONTAINSALL
|
||||
CONTAINSNONE
|
||||
CONTAINSSOME
|
||||
CONTENT
|
||||
CREATE
|
||||
CUSTOM
|
||||
DATABASE
|
||||
DEFAULT
|
||||
DEFINE
|
||||
|
@ -113,6 +129,7 @@ const (
|
|||
EXPUNGE
|
||||
FALSE
|
||||
FIELD
|
||||
FOR
|
||||
FROM
|
||||
FULL
|
||||
GROUP
|
||||
|
@ -122,14 +139,21 @@ const (
|
|||
INDEX
|
||||
INSERT
|
||||
INTO
|
||||
IS
|
||||
LET
|
||||
LIMIT
|
||||
MANDATORY
|
||||
MATCH
|
||||
MAX
|
||||
MERGE
|
||||
MIN
|
||||
MISSING
|
||||
MODIFY
|
||||
MULTI
|
||||
NAMESPACE
|
||||
NONE
|
||||
NONECONTAINEDIN
|
||||
NOT
|
||||
NOTNULL
|
||||
NOW
|
||||
NULL
|
||||
|
@ -144,17 +168,22 @@ const (
|
|||
REMOVE
|
||||
RESYNC
|
||||
RETURN
|
||||
ROLLBACK
|
||||
RULES
|
||||
SELECT
|
||||
SET
|
||||
SOMECONTAINEDIN
|
||||
START
|
||||
TABLE
|
||||
TO
|
||||
TRANSACTION
|
||||
TRUE
|
||||
TYPE
|
||||
UNIQUE
|
||||
UPDATE
|
||||
UPSERT
|
||||
USE
|
||||
VALIDATE
|
||||
VERSION
|
||||
VOID
|
||||
WHERE
|
||||
|
@ -170,22 +199,24 @@ var tokens = [...]string{
|
|||
|
||||
// literals
|
||||
|
||||
DATE: "DATE",
|
||||
TIME: "TIME",
|
||||
PATH: "PATH",
|
||||
JSON: "JSON",
|
||||
IDENT: "IDENT",
|
||||
STRING: "STRING",
|
||||
REGION: "REGION",
|
||||
NUMBER: "NUMBER",
|
||||
DOUBLE: "DOUBLE",
|
||||
REGEX: "REGEX",
|
||||
ARRAY: "ARRAY",
|
||||
DURATION: "DURATION",
|
||||
DATE: "DATE",
|
||||
TIME: "TIME",
|
||||
PATH: "PATH",
|
||||
JSON: "JSON",
|
||||
IDENT: "IDENT",
|
||||
STRING: "STRING",
|
||||
REGION: "REGION",
|
||||
NUMBER: "NUMBER",
|
||||
DOUBLE: "DOUBLE",
|
||||
REGEX: "REGEX",
|
||||
ARRAY: "ARRAY",
|
||||
DURATION: "DURATION",
|
||||
BOUNDPARAM: "BOUNDPARAM",
|
||||
|
||||
EAT: "@",
|
||||
DOT: ".",
|
||||
COMMA: ",",
|
||||
QMARK: "?",
|
||||
LPAREN: "(",
|
||||
RPAREN: ")",
|
||||
LBRACK: "[",
|
||||
|
@ -203,89 +234,120 @@ var tokens = [...]string{
|
|||
DEC: "-=",
|
||||
|
||||
EQ: "=",
|
||||
EEQ: "==",
|
||||
EXC: "!",
|
||||
NEQ: "!=",
|
||||
NEE: "!==",
|
||||
ANY: "?=",
|
||||
LT: "<",
|
||||
LTE: "<=",
|
||||
GT: ">",
|
||||
GTE: ">=",
|
||||
EQR: "=~",
|
||||
NER: "!~",
|
||||
SEQ: "∋",
|
||||
SNE: "∌",
|
||||
SIN: "∋",
|
||||
SNI: "∌",
|
||||
INS: "∈",
|
||||
NIS: "∉",
|
||||
|
||||
OEDGE: "->",
|
||||
IEDGE: "<-",
|
||||
BEDGE: "<->",
|
||||
|
||||
// keywords
|
||||
|
||||
ACCEPT: "ACCEPT",
|
||||
AFTER: "AFTER",
|
||||
ALL: "ALL",
|
||||
AND: "AND",
|
||||
AS: "AS",
|
||||
ASC: "ASC",
|
||||
AT: "AT",
|
||||
BEFORE: "BEFORE",
|
||||
BOTH: "BOTH",
|
||||
BY: "BY",
|
||||
CODE: "CODE",
|
||||
COLUMNS: "COLUMNS",
|
||||
CONTENT: "CONTENT",
|
||||
CREATE: "CREATE",
|
||||
DATABASE: "DATABASE",
|
||||
DEFAULT: "DEFAULT",
|
||||
DEFINE: "DEFINE",
|
||||
DELETE: "DELETE",
|
||||
DESC: "DESC",
|
||||
DIFF: "DIFF",
|
||||
DISTINCT: "DISTINCT",
|
||||
EMPTY: "EMPTY",
|
||||
ENUM: "ENUM",
|
||||
EXPLAIN: "EXPLAIN",
|
||||
EXPUNGE: "EXPUNGE",
|
||||
FALSE: "FALSE",
|
||||
FIELD: "FIELD",
|
||||
FROM: "FROM",
|
||||
FULL: "FULL",
|
||||
GROUP: "GROUP",
|
||||
HISTORY: "HISTORY",
|
||||
ID: "ID",
|
||||
IN: "IN",
|
||||
INDEX: "INDEX",
|
||||
INSERT: "INSERT",
|
||||
INTO: "INTO",
|
||||
LIMIT: "LIMIT",
|
||||
MANDATORY: "MANDATORY",
|
||||
MAX: "MAX",
|
||||
MERGE: "MERGE",
|
||||
MIN: "MIN",
|
||||
MODIFY: "MODIFY",
|
||||
NAMESPACE: "NAMESPACE",
|
||||
NONE: "NONE",
|
||||
NOTNULL: "NOTNULL",
|
||||
NOW: "NOW",
|
||||
NULL: "NULL",
|
||||
ON: "ON",
|
||||
OR: "OR",
|
||||
ORDER: "ORDER",
|
||||
READONLY: "READONLY",
|
||||
RECORD: "RECORD",
|
||||
REJECT: "REJECT",
|
||||
RELATE: "RELATE",
|
||||
REMOVE: "REMOVE",
|
||||
RESYNC: "RESYNC",
|
||||
RETURN: "RETURN",
|
||||
SELECT: "SELECT",
|
||||
SET: "SET",
|
||||
START: "START",
|
||||
TABLE: "TABLE",
|
||||
TO: "TO",
|
||||
TRUE: "TRUE",
|
||||
TYPE: "TYPE",
|
||||
UNIQUE: "UNIQUE",
|
||||
UPDATE: "UPDATE",
|
||||
UPSERT: "UPSERT",
|
||||
USE: "USE",
|
||||
VERSION: "VERSION",
|
||||
VOID: "VOID",
|
||||
WHERE: "WHERE",
|
||||
ACCEPT: "ACCEPT",
|
||||
AFTER: "AFTER",
|
||||
ALL: "ALL",
|
||||
ALLCONTAINEDIN: "ALLCONTAINEDIN",
|
||||
AND: "AND",
|
||||
AS: "AS",
|
||||
ASC: "ASC",
|
||||
AT: "AT",
|
||||
BEFORE: "BEFORE",
|
||||
BEGIN: "BEGIN",
|
||||
BOTH: "BOTH",
|
||||
BY: "BY",
|
||||
CANCEL: "CANCEL",
|
||||
CODE: "CODE",
|
||||
COLLECT: "COLLECT",
|
||||
COLUMNS: "COLUMNS",
|
||||
COMMIT: "COMMIT",
|
||||
CONTAINS: "CONTAINS",
|
||||
CONTAINSALL: "CONTAINSALL",
|
||||
CONTAINSNONE: "CONTAINSNONE",
|
||||
CONTAINSSOME: "CONTAINSSOME",
|
||||
CONTENT: "CONTENT",
|
||||
CREATE: "CREATE",
|
||||
CUSTOM: "CUSTOM",
|
||||
DATABASE: "DATABASE",
|
||||
DEFAULT: "DEFAULT",
|
||||
DEFINE: "DEFINE",
|
||||
DELETE: "DELETE",
|
||||
DESC: "DESC",
|
||||
DIFF: "DIFF",
|
||||
DISTINCT: "DISTINCT",
|
||||
EMPTY: "EMPTY",
|
||||
ENUM: "ENUM",
|
||||
EXPLAIN: "EXPLAIN",
|
||||
EXPUNGE: "EXPUNGE",
|
||||
FALSE: "FALSE",
|
||||
FIELD: "FIELD",
|
||||
FOR: "FOR",
|
||||
FROM: "FROM",
|
||||
FULL: "FULL",
|
||||
GROUP: "GROUP",
|
||||
HISTORY: "HISTORY",
|
||||
ID: "ID",
|
||||
IN: "IN",
|
||||
INDEX: "INDEX",
|
||||
INSERT: "INSERT",
|
||||
INTO: "INTO",
|
||||
IS: "IS",
|
||||
LET: "LET",
|
||||
LIMIT: "LIMIT",
|
||||
MANDATORY: "MANDATORY",
|
||||
MATCH: "MATCH",
|
||||
MAX: "MAX",
|
||||
MERGE: "MERGE",
|
||||
MIN: "MIN",
|
||||
MISSING: "MISSING",
|
||||
MODIFY: "MODIFY",
|
||||
MULTI: "MULTI",
|
||||
NAMESPACE: "NAMESPACE",
|
||||
NONE: "NONE",
|
||||
NONECONTAINEDIN: "NONECONTAINEDIN",
|
||||
NOT: "NOT",
|
||||
NOTNULL: "NOTNULL",
|
||||
NOW: "NOW",
|
||||
NULL: "NULL",
|
||||
ON: "ON",
|
||||
OR: "OR",
|
||||
ORDER: "ORDER",
|
||||
READONLY: "READONLY",
|
||||
RECORD: "RECORD",
|
||||
REJECT: "REJECT",
|
||||
RELATE: "RELATE",
|
||||
REMOVE: "REMOVE",
|
||||
RESYNC: "RESYNC",
|
||||
RETURN: "RETURN",
|
||||
ROLLBACK: "ROLLBACK",
|
||||
RULES: "RULES",
|
||||
SELECT: "SELECT",
|
||||
SET: "SET",
|
||||
SOMECONTAINEDIN: "SOMECONTAINEDIN",
|
||||
START: "START",
|
||||
TABLE: "TABLE",
|
||||
TO: "TO",
|
||||
TRANSACTION: "TRANSACTION",
|
||||
TRUE: "TRUE",
|
||||
TYPE: "TYPE",
|
||||
UNIQUE: "UNIQUE",
|
||||
UPDATE: "UPDATE",
|
||||
UPSERT: "UPSERT",
|
||||
USE: "USE",
|
||||
VALIDATE: "VALIDATE",
|
||||
VERSION: "VERSION",
|
||||
VOID: "VOID",
|
||||
WHERE: "WHERE",
|
||||
}
|
||||
|
||||
var literals map[string]Token
|
||||
|
@ -325,7 +387,11 @@ func (tok Token) precedence() int {
|
|||
return 1
|
||||
case AND:
|
||||
return 2
|
||||
case EQ, NEQ, EQR, NER, LT, LTE, GT, GTE:
|
||||
case EQ, NEQ, EEQ, NEE,
|
||||
LT, LTE, GT, GTE,
|
||||
ANY, SIN, SNI, INS, NIS,
|
||||
CONTAINSALL, CONTAINSNONE, CONTAINSSOME,
|
||||
ALLCONTAINEDIN, NONECONTAINEDIN, SOMECONTAINEDIN:
|
||||
return 3
|
||||
case ADD, SUB:
|
||||
return 4
|
||||
|
|
57
sql/trans.go
Normal file
57
sql/trans.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
// 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) parseBeginStatement(explain bool) (stmt *BeginStatement, err error) {
|
||||
|
||||
stmt = &BeginStatement{}
|
||||
|
||||
_, _, _ = p.mightBe(TRANSACTION)
|
||||
|
||||
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (p *Parser) parseCancelStatement(explain bool) (stmt *CancelStatement, err error) {
|
||||
|
||||
stmt = &CancelStatement{}
|
||||
|
||||
_, _, _ = p.mightBe(TRANSACTION)
|
||||
|
||||
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (p *Parser) parseCommitStatement(explain bool) (stmt *CommitStatement, err error) {
|
||||
|
||||
stmt = &CommitStatement{}
|
||||
|
||||
_, _, _ = p.mightBe(TRANSACTION)
|
||||
|
||||
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
12
sql/type.go
12
sql/type.go
|
@ -14,21 +14,21 @@
|
|||
|
||||
package sql
|
||||
|
||||
func (p *Parser) parseType() (exp Ident, err error) {
|
||||
func (p *Parser) parseType() (exp string, err error) {
|
||||
|
||||
allowed := []string{"any", "url", "uuid", "color", "email", "phone", "array", "object", "domain", "string", "number", "custom", "boolean", "datetime", "latitude", "longitude"}
|
||||
|
||||
tok, lit, err := p.shouldBe(IDENT)
|
||||
_, lit, err := p.shouldBe(IDENT, CUSTOM)
|
||||
if err != nil {
|
||||
return Ident(""), err
|
||||
return string(""), &ParseError{Found: lit, Expected: allowed}
|
||||
}
|
||||
|
||||
if !contains(lit, allowed) {
|
||||
return Ident(""), &ParseError{Found: lit, Expected: allowed}
|
||||
return string(""), &ParseError{Found: lit, Expected: allowed}
|
||||
}
|
||||
|
||||
val, err := declare(tok, lit)
|
||||
val, err := declare(STRING, lit)
|
||||
|
||||
return val.(Ident), err
|
||||
return val.(string), err
|
||||
|
||||
}
|
||||
|
|
13
sql/util.go
13
sql/util.go
|
@ -80,6 +80,9 @@ func declare(tok Token, lit string) (interface{}, error) {
|
|||
case VOID:
|
||||
return &Void{}, nil
|
||||
|
||||
case MISSING:
|
||||
return &Void{}, nil
|
||||
|
||||
case EMPTY:
|
||||
return &Empty{}, nil
|
||||
|
||||
|
@ -93,10 +96,10 @@ func declare(tok Token, lit string) (interface{}, error) {
|
|||
return &Desc{}, nil
|
||||
|
||||
case ID:
|
||||
return Ident(lit), nil
|
||||
return &Ident{lit}, nil
|
||||
|
||||
case IDENT:
|
||||
return Ident(lit), nil
|
||||
return &Ident{lit}, nil
|
||||
|
||||
case NOW:
|
||||
return time.Now(), nil
|
||||
|
@ -111,7 +114,7 @@ func declare(tok Token, lit string) (interface{}, error) {
|
|||
return regexp.Compile(lit)
|
||||
|
||||
case NUMBER:
|
||||
return strconv.ParseInt(lit, 10, 64)
|
||||
return strconv.ParseFloat(lit, 64)
|
||||
|
||||
case DOUBLE:
|
||||
return strconv.ParseFloat(lit, 64)
|
||||
|
@ -120,7 +123,7 @@ func declare(tok Token, lit string) (interface{}, error) {
|
|||
return time.ParseDuration(lit)
|
||||
|
||||
case ARRAY:
|
||||
var j interface{}
|
||||
var j []interface{}
|
||||
json.Unmarshal([]byte(lit), &j)
|
||||
if j == nil {
|
||||
return j, fmt.Errorf("Invalid JSON: %s", lit)
|
||||
|
@ -128,7 +131,7 @@ func declare(tok Token, lit string) (interface{}, error) {
|
|||
return j, nil
|
||||
|
||||
case JSON:
|
||||
var j interface{}
|
||||
var j map[string]interface{}
|
||||
json.Unmarshal([]byte(lit), &j)
|
||||
if j == nil {
|
||||
return j, fmt.Errorf("Invalid JSON: %s", lit)
|
||||
|
|
Loading…
Reference in a new issue