influxql: change ORDER BY to accept a field list
Change ORDER BY to accept a field list and change LIST SERIES statement to allow ORDER BY.pull/1222/head
parent
0f3ea136be
commit
6f3ba3efdb
|
@ -72,8 +72,10 @@ func (_ *DurationLiteral) node() {}
|
|||
func (_ *BinaryExpr) node() {}
|
||||
func (_ *ParenExpr) node() {}
|
||||
func (_ *Wildcard) node() {}
|
||||
func (_ SortFields) node() {}
|
||||
func (_ *SortField) node() {}
|
||||
|
||||
// Query represents a collection of order statements.
|
||||
// Query represents a collection of ordered statements.
|
||||
type Query struct {
|
||||
Statements Statements
|
||||
}
|
||||
|
@ -84,7 +86,7 @@ func (q *Query) String() string { return q.Statements.String() }
|
|||
// Statements represents a list of statements.
|
||||
type Statements []Statement
|
||||
|
||||
// String returns a string representation of the statements
|
||||
// String returns a string representation of the statements.
|
||||
func (a Statements) String() string {
|
||||
var str []string
|
||||
for _, stmt := range a {
|
||||
|
@ -134,6 +136,36 @@ func (_ *Series) source() {}
|
|||
func (_ *Join) source() {}
|
||||
func (_ *Merge) source() {}
|
||||
|
||||
// SortField represens a field to sort results by.
|
||||
type SortField struct {
|
||||
// Name of the field
|
||||
Name string
|
||||
|
||||
// Sort order.
|
||||
Ascending bool
|
||||
}
|
||||
|
||||
// String returns a string representation of a sort field
|
||||
func (field *SortField) String() string {
|
||||
var buf bytes.Buffer
|
||||
_, _ = buf.WriteString(field.Name)
|
||||
_, _ = buf.WriteString(" ")
|
||||
_, _ = buf.WriteString(strconv.FormatBool(field.Ascending))
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// SortFields represents an ordered list of ORDER BY fields
|
||||
type SortFields []*SortField
|
||||
|
||||
// String returns a string representation of sort fields
|
||||
func (a SortFields) String() string {
|
||||
fields := make([]string, 0, len(a))
|
||||
for _, field := range a {
|
||||
fields = append(fields, field.String())
|
||||
}
|
||||
return strings.Join(fields, ", ")
|
||||
}
|
||||
|
||||
// SelectStatement represents a command for extracting data from the database.
|
||||
type SelectStatement struct {
|
||||
// Expressions returned from the selection.
|
||||
|
@ -148,12 +180,12 @@ type SelectStatement struct {
|
|||
// An expression evaluated on data point.
|
||||
Condition Expr
|
||||
|
||||
// Fields to sort results by
|
||||
SortFields SortFields
|
||||
|
||||
// Maximum number of rows to be returned.
|
||||
// Unlimited if zero.
|
||||
Limit int
|
||||
|
||||
// Sort order.
|
||||
Ascending bool
|
||||
}
|
||||
|
||||
// String returns a string representation of the select statement.
|
||||
|
@ -174,8 +206,9 @@ func (s *SelectStatement) String() string {
|
|||
if s.Limit > 0 {
|
||||
_, _ = fmt.Fprintf(&buf, " LIMIT %d", s.Limit)
|
||||
}
|
||||
if s.Ascending {
|
||||
_, _ = buf.WriteString(" ORDER BY ASC")
|
||||
if len(s.SortFields) > 0 {
|
||||
_, _ = buf.WriteString(" ORDER BY ")
|
||||
_, _ = buf.WriteString(s.SortFields.String())
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
@ -230,7 +263,7 @@ func (s *SelectStatement) Substatement(ref *VarRef) (*SelectStatement, error) {
|
|||
Fields: Fields{{Expr: ref}},
|
||||
Dimensions: s.Dimensions,
|
||||
Limit: s.Limit,
|
||||
Ascending: s.Ascending,
|
||||
SortFields: s.SortFields,
|
||||
}
|
||||
|
||||
// If there is only one series source then return it with the whole condition.
|
||||
|
@ -346,6 +379,9 @@ type ListSeriesStatement struct{
|
|||
// Maximum number of rows to be returned.
|
||||
// Unlimited if zero.
|
||||
Limit int
|
||||
|
||||
// Fields to sort results by
|
||||
SortFields SortFields
|
||||
}
|
||||
|
||||
// String returns a string representation of the list series statement.
|
||||
|
|
|
@ -122,6 +122,22 @@ func (p *Parser) parseSelectStatement() (*SelectStatement, error) {
|
|||
}
|
||||
stmt.Dimensions = dimensions
|
||||
|
||||
// Parse sort: "ORDER BY FIELD+".
|
||||
if tok, _, _ := p.scanIgnoreWhitespace(); tok == ORDER {
|
||||
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != BY {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"BY"}, pos)
|
||||
}
|
||||
|
||||
sortFields, err := p.parseSortFields()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stmt.SortFields = sortFields
|
||||
} else {
|
||||
p.unscan()
|
||||
}
|
||||
|
||||
// Parse limit: "LIMIT INT".
|
||||
limit, err := p.parseLimit()
|
||||
if err != nil {
|
||||
|
@ -129,13 +145,6 @@ func (p *Parser) parseSelectStatement() (*SelectStatement, error) {
|
|||
}
|
||||
stmt.Limit = limit
|
||||
|
||||
// Parse ordering: "ORDER BY (ASC|DESC)".
|
||||
ascending, err := p.parseOrderBy()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt.Ascending = ascending
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
|
@ -176,6 +185,22 @@ func (p *Parser) parseListSeriesStatement() (*ListSeriesStatement, error) {
|
|||
}
|
||||
stmt.Condition = condition
|
||||
|
||||
// Parse sort: "ORDER BY FIELD+".
|
||||
if tok, _, _ := p.scanIgnoreWhitespace(); tok == ORDER {
|
||||
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != BY {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"BY"}, pos)
|
||||
}
|
||||
|
||||
sortFields, err := p.parseSortFields()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stmt.SortFields = sortFields
|
||||
} else {
|
||||
p.unscan()
|
||||
}
|
||||
|
||||
// Parse limit: "LIMIT INT".
|
||||
limit, err := p.parseLimit()
|
||||
if err != nil {
|
||||
|
@ -468,26 +493,58 @@ func (p *Parser) parseLimit() (int, error) {
|
|||
return int(n), nil
|
||||
}
|
||||
|
||||
// parseOrderBy parses the "ORDER BY" clause of the query, if it exists.
|
||||
func (p *Parser) parseOrderBy() (bool, error) {
|
||||
// Check if the ORDER token exists.
|
||||
if tok, _, _ := p.scanIgnoreWhitespace(); tok != ORDER {
|
||||
p.unscan()
|
||||
return false, nil
|
||||
// parseSortFields parses all fields of and ORDER BY clause.
|
||||
func (p *Parser) parseSortFields() (SortFields, error) {
|
||||
var fields SortFields
|
||||
|
||||
// At least one field is required.
|
||||
field, err := p.parseSortField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields = append(fields, field)
|
||||
|
||||
// Parse additional fields.
|
||||
for {
|
||||
tok, _, _ := p.scanIgnoreWhitespace()
|
||||
|
||||
if tok != COMMA {
|
||||
p.unscan()
|
||||
break
|
||||
}
|
||||
|
||||
field, err := p.parseSortField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fields = append(fields, field)
|
||||
}
|
||||
|
||||
// Ensure the next token is BY.
|
||||
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != BY {
|
||||
return false, newParseError(tokstr(tok, lit), []string{"BY"}, pos)
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// Ensure the next token is ASC OR DESC.
|
||||
// parseSortField parses one field of an ORDER BY clause.
|
||||
func (p *Parser) parseSortField() (*SortField, error) {
|
||||
field := &SortField{}
|
||||
|
||||
// Next token should be ASC, DESC, or IDENT | STRING.
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok != ASC && tok != DESC {
|
||||
return false, newParseError(tokstr(tok, lit), []string{"ASC", "DESC"}, pos)
|
||||
if tok == IDENT || tok == STRING {
|
||||
field.Name = lit
|
||||
// Check for optional ASC or DESC token.
|
||||
tok, pos, lit = p.scanIgnoreWhitespace()
|
||||
if tok != ASC && tok != DESC {
|
||||
p.unscan()
|
||||
return field, nil
|
||||
}
|
||||
} else if tok != ASC && tok != DESC {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"identifier, ASC, or DESC"}, pos)
|
||||
}
|
||||
|
||||
return tok == ASC, nil
|
||||
field.Ascending = (tok == ASC)
|
||||
|
||||
return field, nil
|
||||
}
|
||||
|
||||
// ParseExpr parses an expression.
|
||||
|
|
|
@ -58,7 +58,7 @@ func TestParser_ParseStatement(t *testing.T) {
|
|||
|
||||
// SELECT statement
|
||||
{
|
||||
s: `SELECT field1, field2 ,field3 AS field_x FROM myseries WHERE host = 'hosta.influxdb.org' GROUP BY 10h LIMIT 20 ORDER BY ASC;`,
|
||||
s: `SELECT field1, field2 ,field3 AS field_x FROM myseries WHERE host = 'hosta.influxdb.org' GROUP BY 10h ORDER BY ASC LIMIT 20;`,
|
||||
stmt: &influxql.SelectStatement{
|
||||
Fields: influxql.Fields{
|
||||
&influxql.Field{Expr: &influxql.VarRef{Val: "field1"}},
|
||||
|
@ -75,7 +75,9 @@ func TestParser_ParseStatement(t *testing.T) {
|
|||
&influxql.Dimension{Expr: &influxql.DurationLiteral{Val: 10 * time.Hour}},
|
||||
},
|
||||
Limit: 20,
|
||||
Ascending: true,
|
||||
SortFields: influxql.SortFields{
|
||||
&influxql.SortField{Ascending: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -118,6 +120,21 @@ func TestParser_ParseStatement(t *testing.T) {
|
|||
},
|
||||
},
|
||||
|
||||
// SELECT statement with multiple ORDER BY fields
|
||||
{
|
||||
s: `SELECT field1 FROM myseries ORDER BY ASC, field1, field2 DESC LIMIT 10`,
|
||||
stmt: &influxql.SelectStatement{
|
||||
Fields: influxql.Fields{&influxql.Field{Expr: &influxql.VarRef{Val: "field1"}}},
|
||||
Source: &influxql.Series{Name: "myseries"},
|
||||
SortFields: influxql.SortFields{
|
||||
&influxql.SortField{Ascending: true,},
|
||||
&influxql.SortField{Name: "field1",},
|
||||
&influxql.SortField{Name: "field2",},
|
||||
},
|
||||
Limit: 10,
|
||||
},
|
||||
},
|
||||
|
||||
// DELETE statement
|
||||
{
|
||||
s: `DELETE FROM myseries WHERE host = 'hosta.influxdb.org'`,
|
||||
|
@ -137,15 +154,20 @@ func TestParser_ParseStatement(t *testing.T) {
|
|||
stmt: &influxql.ListSeriesStatement{},
|
||||
},
|
||||
|
||||
// LIST SERIES WHERE statement
|
||||
// LIST SERIES WHERE with ORDER BY and LIMIT
|
||||
{
|
||||
s: `LIST SERIES WHERE region = 'uswest' LIMIT 10`,
|
||||
s: `LIST SERIES WHERE region = 'uswest' ORDER BY ASC, field1, field2 DESC LIMIT 10`,
|
||||
stmt: &influxql.ListSeriesStatement{
|
||||
Condition: &influxql.BinaryExpr{
|
||||
Op: influxql.EQ,
|
||||
LHS: &influxql.VarRef{Val: "region"},
|
||||
RHS: &influxql.StringLiteral{Val: "uswest"},
|
||||
},
|
||||
SortFields: influxql.SortFields{
|
||||
&influxql.SortField{Ascending: true,},
|
||||
&influxql.SortField{Name: "field1",},
|
||||
&influxql.SortField{Name: "field2",},
|
||||
},
|
||||
Limit: 10,
|
||||
},
|
||||
},
|
||||
|
@ -185,16 +207,17 @@ func TestParser_ParseStatement(t *testing.T) {
|
|||
{s: ``, err: `found EOF, expected SELECT at line 1, char 1`},
|
||||
{s: `SELECT`, err: `found EOF, expected identifier, string, number, bool at line 1, char 8`},
|
||||
{s: `blah blah`, err: `found blah, expected SELECT at line 1, char 1`},
|
||||
{s: `SELECT field X`, err: `found X, expected FROM at line 1, char 14`},
|
||||
{s: `SELECT field FROM "series" WHERE X +;`, err: `found ;, expected identifier, string, number, bool at line 1, char 37`},
|
||||
{s: `SELECT field FROM myseries GROUP`, err: `found EOF, expected BY at line 1, char 34`},
|
||||
{s: `SELECT field FROM myseries LIMIT`, err: `found EOF, expected number at line 1, char 34`},
|
||||
{s: `SELECT field FROM myseries LIMIT 10.5`, err: `fractional parts not allowed in limit at line 1, char 34`},
|
||||
{s: `SELECT field FROM myseries ORDER`, err: `found EOF, expected BY at line 1, char 34`},
|
||||
{s: `SELECT field FROM myseries ORDER BY /`, err: `found /, expected ASC, DESC at line 1, char 37`},
|
||||
{s: `SELECT field AS`, err: `found EOF, expected identifier, string at line 1, char 17`},
|
||||
{s: `SELECT field FROM 12`, err: `found 12, expected identifier, string at line 1, char 19`},
|
||||
{s: `SELECT field FROM myseries GROUP BY *`, err: `found *, expected identifier, string, number, bool at line 1, char 37`},
|
||||
{s: `SELECT field1 X`, err: `found X, expected FROM at line 1, char 15`},
|
||||
{s: `SELECT field1 FROM "series" WHERE X +;`, err: `found ;, expected identifier, string, number, bool at line 1, char 38`},
|
||||
{s: `SELECT field1 FROM myseries GROUP`, err: `found EOF, expected BY at line 1, char 35`},
|
||||
{s: `SELECT field1 FROM myseries LIMIT`, err: `found EOF, expected number at line 1, char 35`},
|
||||
{s: `SELECT field1 FROM myseries LIMIT 10.5`, err: `fractional parts not allowed in limit at line 1, char 35`},
|
||||
{s: `SELECT field1 FROM myseries ORDER`, err: `found EOF, expected BY at line 1, char 35`},
|
||||
{s: `SELECT field1 FROM myseries ORDER BY /`, err: `found /, expected identifier, ASC, or DESC at line 1, char 38`},
|
||||
{s: `SELECT field1 FROM myseries ORDER BY 1`, err: `found 1, expected identifier, ASC, or DESC at line 1, char 38`},
|
||||
{s: `SELECT field1 AS`, err: `found EOF, expected identifier, string at line 1, char 18`},
|
||||
{s: `SELECT field1 FROM 12`, err: `found 12, expected identifier, string at line 1, char 20`},
|
||||
{s: `SELECT field1 FROM myseries GROUP BY *`, err: `found *, expected identifier, string, number, bool at line 1, char 38`},
|
||||
{s: `SELECT 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 FROM myseries`, err: `unable to parse number at line 1, char 8`},
|
||||
{s: `SELECT 10.5h FROM myseries`, err: `found h, expected FROM at line 1, char 12`},
|
||||
{s: `DELETE`, err: `found EOF, expected FROM at line 1, char 8`},
|
||||
|
|
|
@ -190,150 +190,4 @@ func TestScanner_Scan_Multi(t *testing.T) {
|
|||
t.Fatalf("%d. token mismatch:\n\nexp=%#v\n\ngot=%#v", i, exp[i], act[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test LIST MEASUREMENTS WHERE
|
||||
func TestScanner_Scan_LIST_MEASUREMENTS_WHERE(t *testing.T) {
|
||||
type result struct {
|
||||
tok influxql.Token
|
||||
pos influxql.Pos
|
||||
lit string
|
||||
}
|
||||
exp := []result{
|
||||
{tok: influxql.LIST, pos: influxql.Pos{Line: 0, Char: 0}, lit: ""},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 4}, lit: " "},
|
||||
{tok: influxql.MEASUREMENTS, pos: influxql.Pos{Line: 0, Char: 5}, lit: ""},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 17}, lit: " "},
|
||||
{tok: influxql.WHERE, pos: influxql.Pos{Line: 0, Char: 18}, lit: ""},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 23}, lit: " "},
|
||||
{tok: influxql.IDENT, pos: influxql.Pos{Line: 0, Char: 24}, lit: "service"},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 31}, lit: " "},
|
||||
{tok: influxql.EQ, pos: influxql.Pos{Line: 0, Char: 32}, lit: ""},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 33}, lit: " "},
|
||||
{tok: influxql.STRING, pos: influxql.Pos{Line: 0, Char: 34}, lit: "redis"},
|
||||
{tok: influxql.EOF, pos: influxql.Pos{Line: 0, Char: 41}, lit: ""},
|
||||
}
|
||||
|
||||
// Create a scanner.
|
||||
v := `LIST MEASUREMENTS WHERE service = 'redis'`
|
||||
s := influxql.NewScanner(strings.NewReader(v))
|
||||
|
||||
// Continually scan until we reach the end.
|
||||
var act []result
|
||||
for {
|
||||
tok, pos, lit := s.Scan()
|
||||
act = append(act, result{tok, pos, lit})
|
||||
if tok == influxql.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the token counts match.
|
||||
if len(exp) != len(act) {
|
||||
t.Fatalf("token count mismatch: exp=%d, got=%d", len(exp), len(act))
|
||||
}
|
||||
|
||||
// Verify each token matches.
|
||||
for i := range exp {
|
||||
if !reflect.DeepEqual(exp[i], act[i]) {
|
||||
t.Fatalf("%d. token mismatch:\n\nexp=%#v\n\ngot=%#v", i, exp[i], act[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test LIST SERIES WHERE
|
||||
func TestScanner_Scan_LIST_SERIES_WHERE(t *testing.T) {
|
||||
type result struct {
|
||||
tok influxql.Token
|
||||
pos influxql.Pos
|
||||
lit string
|
||||
}
|
||||
exp := []result{
|
||||
{tok: influxql.LIST, pos: influxql.Pos{Line: 0, Char: 0}, lit: ""},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 4}, lit: " "},
|
||||
{tok: influxql.SERIES, pos: influxql.Pos{Line: 0, Char: 5}, lit: ""},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 11}, lit: " "},
|
||||
{tok: influxql.WHERE, pos: influxql.Pos{Line: 0, Char: 12}, lit: ""},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 17}, lit: " "},
|
||||
{tok: influxql.IDENT, pos: influxql.Pos{Line: 0, Char: 18}, lit: "service42"},
|
||||
{tok: influxql.EQ, pos: influxql.Pos{Line: 0, Char: 27}, lit: ""},
|
||||
{tok: influxql.STRING, pos: influxql.Pos{Line: 0, Char: 28}, lit: "redis"},
|
||||
{tok: influxql.EOF, pos: influxql.Pos{Line: 0, Char: 35}, lit: ""},
|
||||
}
|
||||
|
||||
// Create a scanner.
|
||||
v := `LIST SERIES WHERE service42="redis"`
|
||||
s := influxql.NewScanner(strings.NewReader(v))
|
||||
|
||||
// Continually scan until we reach the end.
|
||||
var act []result
|
||||
for {
|
||||
tok, pos, lit := s.Scan()
|
||||
act = append(act, result{tok, pos, lit})
|
||||
if tok == influxql.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the token counts match.
|
||||
if len(exp) != len(act) {
|
||||
t.Fatalf("token count mismatch: exp=%d, got=%d", len(exp), len(act))
|
||||
}
|
||||
|
||||
// Verify each token matches.
|
||||
for i := range exp {
|
||||
if !reflect.DeepEqual(exp[i], act[i]) {
|
||||
t.Fatalf("%d. token mismatch:\n\nexp=%#v\n\ngot=%#v", i, exp[i], act[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test LIST TAG KEYS FROM
|
||||
func TestScanner_Scan_LIST_TAG_KEYS_FROM(t *testing.T) {
|
||||
type result struct {
|
||||
tok influxql.Token
|
||||
pos influxql.Pos
|
||||
lit string
|
||||
}
|
||||
exp := []result{
|
||||
{tok: influxql.LIST, pos: influxql.Pos{Line: 0, Char: 0}, lit: ""},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 4}, lit: " "},
|
||||
{tok: influxql.TAG, pos: influxql.Pos{Line: 0, Char: 5}, lit: ""},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 8}, lit: " "},
|
||||
{tok: influxql.KEYS, pos: influxql.Pos{Line: 0, Char: 9}, lit: ""},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 13}, lit: " "},
|
||||
{tok: influxql.FROM, pos: influxql.Pos{Line: 0, Char: 14}, lit: ""},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 18}, lit: " "},
|
||||
{tok: influxql.IDENT, pos: influxql.Pos{Line: 0, Char: 19}, lit: "temperature"},
|
||||
{tok: influxql.COMMA, pos: influxql.Pos{Line: 0, Char: 30}, lit: ""},
|
||||
{tok: influxql.WS, pos: influxql.Pos{Line: 0, Char: 31}, lit: " "},
|
||||
{tok: influxql.IDENT, pos: influxql.Pos{Line: 0, Char: 32}, lit: "wind_speed"},
|
||||
{tok: influxql.EOF, pos: influxql.Pos{Line: 0, Char: 43}, lit: ""},
|
||||
}
|
||||
|
||||
// Create a scanner.
|
||||
v := `LIST TAG KEYS FROM temperature, wind_speed`
|
||||
s := influxql.NewScanner(strings.NewReader(v))
|
||||
|
||||
// Continually scan until we reach the end.
|
||||
var act []result
|
||||
for {
|
||||
tok, pos, lit := s.Scan()
|
||||
act = append(act, result{tok, pos, lit})
|
||||
if tok == influxql.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the token counts match.
|
||||
if len(exp) != len(act) {
|
||||
t.Fatalf("token count mismatch: exp=%d, got=%d", len(exp), len(act))
|
||||
}
|
||||
|
||||
// Verify each token matches.
|
||||
for i := range exp {
|
||||
if !reflect.DeepEqual(exp[i], act[i]) {
|
||||
t.Fatalf("%d. token mismatch:\n\nexp=%#v\n\ngot=%#v", i, exp[i], act[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue