Parse tables and @things better
This commit is contained in:
parent
974f75eea0
commit
1fc814bb43
4 changed files with 229 additions and 84 deletions
40
sql/exprs.go
40
sql/exprs.go
|
@ -18,9 +18,47 @@ import (
|
|||
"regexp"
|
||||
)
|
||||
|
||||
func (p *parser) parseWhat() (mul []Expr, err error) {
|
||||
|
||||
for {
|
||||
|
||||
tok, lit, err := p.shouldBe(IDENT, NUMBER, DOUBLE, THING, PARAM)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"table name or record id"}}
|
||||
}
|
||||
|
||||
if p.is(tok, IDENT, NUMBER, DOUBLE) {
|
||||
one, _ := p.declare(TABLE, lit)
|
||||
mul = append(mul, one)
|
||||
}
|
||||
|
||||
if p.is(tok, THING) {
|
||||
one, _ := p.declare(THING, lit)
|
||||
mul = append(mul, one)
|
||||
}
|
||||
|
||||
if p.is(tok, PARAM) {
|
||||
one, err := p.declare(PARAM, lit)
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
_, lit, err := p.shouldBe(IDENT, NUMBER, DOUBLE, DATE, TIME)
|
||||
func (p *parser) parseName() (string, error) {
|
||||
|
||||
_, lit, err := p.shouldBe(IDENT, NUMBER, DOUBLE)
|
||||
if err != nil {
|
||||
return string(""), &ParseError{Found: lit, Expected: []string{"name"}}
|
||||
}
|
||||
|
|
|
@ -325,13 +325,33 @@ func (s *scanner) scanCommentMultiple(chp ...rune) (tok Token, lit string, val i
|
|||
|
||||
func (s *scanner) scanParams(chp ...rune) (tok Token, lit string, val interface{}) {
|
||||
|
||||
tok, lit, val = s.scanIdent(chp...)
|
||||
tok, lit, _ = s.scanIdent()
|
||||
|
||||
if s.p.is(tok, IDENT) {
|
||||
return PARAM, lit, nil
|
||||
if s.p.is(tok, REGION) {
|
||||
return ILLEGAL, lit, val
|
||||
}
|
||||
|
||||
return
|
||||
if s.p.is(tok, ILLEGAL) {
|
||||
return ILLEGAL, lit, val
|
||||
}
|
||||
|
||||
return PARAM, lit, val
|
||||
|
||||
}
|
||||
|
||||
func (s *scanner) scanQuoted(chp ...rune) (tok Token, lit string, val interface{}) {
|
||||
|
||||
tok, lit, _ = s.scanString(chp...)
|
||||
|
||||
if s.p.is(tok, REGION) {
|
||||
return ILLEGAL, lit, val
|
||||
}
|
||||
|
||||
if s.p.is(tok, ILLEGAL) {
|
||||
return ILLEGAL, lit, val
|
||||
}
|
||||
|
||||
return IDENT, lit, val
|
||||
|
||||
}
|
||||
|
||||
|
@ -393,28 +413,29 @@ func (s *scanner) scanThing(chp ...rune) (tok Token, lit string, val interface{}
|
|||
for {
|
||||
if ch := s.next(); ch == eof {
|
||||
break
|
||||
} else if isLetter(ch) {
|
||||
} else if isThingChar(ch) {
|
||||
tok, lit, _ = s.scanIdent(ch)
|
||||
beg.WriteString(lit)
|
||||
} else if isNumber(ch) {
|
||||
tok, lit, _ = s.scanNumber(ch)
|
||||
beg.WriteString(lit)
|
||||
break
|
||||
} else if ch == '`' {
|
||||
tok, lit, _ = s.scanQuoted(ch)
|
||||
beg.WriteString(lit)
|
||||
break
|
||||
} else if ch == '{' {
|
||||
tok, lit, _ = s.scanQuoted(ch)
|
||||
beg.WriteString(lit)
|
||||
break
|
||||
} else if ch == '⟨' {
|
||||
tok, lit, _ = s.scanQuoted(ch)
|
||||
beg.WriteString(lit)
|
||||
break
|
||||
} else {
|
||||
s.undo()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if beg.Len() < 1 {
|
||||
if beg.Len() < 1 || tok == ILLEGAL {
|
||||
return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val
|
||||
}
|
||||
|
||||
|
@ -435,42 +456,33 @@ func (s *scanner) scanThing(chp ...rune) (tok Token, lit string, val interface{}
|
|||
for {
|
||||
if ch := s.next(); ch == eof {
|
||||
break
|
||||
} else if isLetter(ch) {
|
||||
} else if isThingChar(ch) {
|
||||
tok, lit, _ = s.scanIdent(ch)
|
||||
end.WriteString(lit)
|
||||
} else if isNumber(ch) {
|
||||
tok, lit, _ = s.scanNumber(ch)
|
||||
end.WriteString(lit)
|
||||
break
|
||||
} else if ch == '`' {
|
||||
tok, lit, _ = s.scanQuoted(ch)
|
||||
beg.WriteString(lit)
|
||||
end.WriteString(lit)
|
||||
break
|
||||
} else if ch == '{' {
|
||||
tok, lit, _ = s.scanQuoted(ch)
|
||||
end.WriteString(lit)
|
||||
break
|
||||
} else if ch == '⟨' {
|
||||
tok, lit, _ = s.scanQuoted(ch)
|
||||
end.WriteString(lit)
|
||||
break
|
||||
} else {
|
||||
s.undo()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if end.Len() < 1 {
|
||||
if end.Len() < 1 || tok == ILLEGAL {
|
||||
return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val
|
||||
}
|
||||
|
||||
val = &Thing{
|
||||
TB: beg.String(),
|
||||
ID: end.String(),
|
||||
}
|
||||
|
||||
switch tok {
|
||||
case DATE, TIME, NUMBER, DOUBLE:
|
||||
val.(*Thing).ID, _ = s.p.declare(tok, end.String())
|
||||
case REGION:
|
||||
return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val
|
||||
}
|
||||
val = NewThing(beg.String(), end.String())
|
||||
|
||||
// Otherwise return as a regular thing.
|
||||
return THING, buf.String() + beg.String() + mid.String() + end.String(), val
|
||||
|
@ -516,18 +528,6 @@ func (s *scanner) scanNumber(chp ...rune) (tok Token, lit string, val interface{
|
|||
|
||||
}
|
||||
|
||||
func (s *Scanner) scanQuoted(chp ...rune) (tok Token, lit string, val interface{}) {
|
||||
|
||||
tok, lit, val = s.scanString(chp...)
|
||||
|
||||
if s.p.is(tok, STRING) {
|
||||
return IDENT, lit, nil
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (s *scanner) scanString(chp ...rune) (tok Token, lit string, val interface{}) {
|
||||
|
||||
beg := chp[0]
|
||||
|
@ -760,5 +760,10 @@ func isIdentChar(ch rune) bool {
|
|||
return isLetter(ch) || isNumber(ch) || ch == '.' || ch == '_' || ch == '*'
|
||||
}
|
||||
|
||||
// isThingChar returns true if the rune is allowed in a THING.
|
||||
func isThingChar(ch rune) bool {
|
||||
return isLetter(ch) || isNumber(ch) || ch == '_'
|
||||
}
|
||||
|
||||
// eof represents a marker rune for the end of the reader.
|
||||
var eof = rune(0)
|
||||
|
|
147
sql/sql_test.go
147
sql/sql_test.go
|
@ -270,6 +270,9 @@ func Test_Parse_Queries_Explain(t *testing.T) {
|
|||
|
||||
func Test_Parse_Queries_Select(t *testing.T) {
|
||||
|
||||
date, _ := time.Parse("2006-01-02", "1987-06-22")
|
||||
nano, _ := time.Parse(time.RFC3339, "1987-06-22T08:30:30.511Z")
|
||||
|
||||
var tests = []tester{
|
||||
{
|
||||
sql: `SELECT`,
|
||||
|
@ -325,6 +328,41 @@ func Test_Parse_Queries_Select(t *testing.T) {
|
|||
sql: `SELECT * FROM person:uuid`,
|
||||
err: "Found `:` but expected `EOF, ;`",
|
||||
},
|
||||
{
|
||||
sql: "SELECT * FROM 111",
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Table{"111"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: "SELECT * FROM `111`",
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Table{"111"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: "SELECT * FROM `2006-01-02`",
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Table{"2006-01-02"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: "SELECT * FROM `2006-01-02T15:04:05+07:00`",
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Table{"2006-01-02T15:04:05+07:00"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: "SELECT * FROM `2006-01-02T15:04:05.999999999+07:00`",
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Table{"2006-01-02T15:04:05.999999999+07:00"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM person`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
|
@ -339,6 +377,13 @@ func Test_Parse_Queries_Select(t *testing.T) {
|
|||
What: []Expr{&Table{"person"}, &Table{"tweet"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @111:1a`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Thing{TB: "111", ID: "1a"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:1a`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
|
@ -346,6 +391,20 @@ func Test_Parse_Queries_Select(t *testing.T) {
|
|||
What: []Expr{&Thing{TB: "person", ID: "1a"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:⟨1a⟩`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Thing{TB: "person", ID: "1a"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:{1a}`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Thing{TB: "person", ID: "1a"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:123456`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
|
@ -353,6 +412,20 @@ func Test_Parse_Queries_Select(t *testing.T) {
|
|||
What: []Expr{&Thing{TB: "person", ID: float64(123456)}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:⟨123456⟩`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Thing{TB: "person", ID: float64(123456)}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:{123456}`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Thing{TB: "person", ID: float64(123456)}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:123.456`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
|
@ -360,6 +433,20 @@ func Test_Parse_Queries_Select(t *testing.T) {
|
|||
What: []Expr{&Thing{TB: "person", ID: float64(123.456)}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:⟨123.456⟩`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Thing{TB: "person", ID: float64(123.456)}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:{123.456}`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Thing{TB: "person", ID: float64(123.456)}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:123.456.789.012`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
|
@ -381,6 +468,34 @@ func Test_Parse_Queries_Select(t *testing.T) {
|
|||
What: []Expr{&Thing{TB: "person", ID: "123.456.789.012"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:⟨1987-06-22⟩`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Thing{TB: "person", ID: date}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:{1987-06-22}`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Thing{TB: "person", ID: date}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:⟨1987-06-22T08:30:30.511Z⟩`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Thing{TB: "person", ID: nano}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:{1987-06-22T08:30:30.511Z}`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
Expr: []*Field{{Expr: &All{}, Alias: "*"}},
|
||||
What: []Expr{&Thing{TB: "person", ID: nano}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @person:⟨A250C5A3-948F-4657-88AD-FF5F27B5B24E⟩`,
|
||||
res: &Query{Statements: []Statement{&SelectStatement{
|
||||
|
@ -451,6 +566,10 @@ func Test_Parse_Queries_Select(t *testing.T) {
|
|||
What: []Expr{&Thing{TB: "email addresses", ID: "tobie+spam@abcum.com"}},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @{person}test:id`,
|
||||
err: "Found `@person` but expected `table name or record id`",
|
||||
},
|
||||
{
|
||||
sql: `SELECT * FROM @⟨email addresses⟩:⟨this\qis\nodd⟩`,
|
||||
err: "Found `@email addresses:thisqis\nodd` but expected `table name or record id`",
|
||||
|
@ -1201,6 +1320,18 @@ func Test_Parse_Queries_Define(t *testing.T) {
|
|||
sql: `DEFINE TABLE`,
|
||||
err: "Found `` but expected `name`",
|
||||
},
|
||||
{
|
||||
sql: `DEFINE TABLE 111`,
|
||||
res: &Query{Statements: []Statement{&DefineTableStatement{
|
||||
What: []string{"111"},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `DEFINE TABLE 111.111`,
|
||||
res: &Query{Statements: []Statement{&DefineTableStatement{
|
||||
What: []string{"111.111"},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `DEFINE TABLE person`,
|
||||
res: &Query{Statements: []Statement{&DefineTableStatement{
|
||||
|
@ -1729,12 +1860,28 @@ func Test_Parse_Queries_Remove(t *testing.T) {
|
|||
sql: `REMOVE TABLE`,
|
||||
err: "Found `` but expected `name`",
|
||||
},
|
||||
{
|
||||
sql: `REMOVE TABLE 111`,
|
||||
res: &Query{Statements: []Statement{&RemoveTableStatement{
|
||||
What: []string{"111"},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `REMOVE TABLE 111.111`,
|
||||
res: &Query{Statements: []Statement{&RemoveTableStatement{
|
||||
What: []string{"111.111"},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `REMOVE TABLE person`,
|
||||
res: &Query{Statements: []Statement{&RemoveTableStatement{
|
||||
What: []string{"person"},
|
||||
}}},
|
||||
},
|
||||
{
|
||||
sql: `REMOVE TABLE person something`,
|
||||
err: "Found `something` but expected `EOF, ;`",
|
||||
},
|
||||
// ----------------------------------------------------------------------
|
||||
{
|
||||
sql: `REMOVE RULES`,
|
||||
|
|
45
sql/what.go
45
sql/what.go
|
@ -1,45 +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) parseWhat() (mul []Expr, err error) {
|
||||
|
||||
for {
|
||||
|
||||
tok, lit, err := p.shouldBe(IDENT, THING)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Found: lit, Expected: []string{"table name or record id"}}
|
||||
}
|
||||
|
||||
if p.is(tok, IDENT) {
|
||||
one, _ := p.declare(TABLE, lit)
|
||||
mul = append(mul, one)
|
||||
}
|
||||
|
||||
if p.is(tok, THING) {
|
||||
one, _ := p.declare(THING, lit)
|
||||
mul = append(mul, one)
|
||||
}
|
||||
|
||||
// If the next token is not a comma then break the loop.
|
||||
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
Loading…
Reference in a new issue