Add CREATE CONTINUOUS QUERY parsing.

pull/1159/head
Ben Johnson 2014-11-24 21:49:09 -07:00
parent 7288a778c3
commit 143f8a0fd5
4 changed files with 90 additions and 40 deletions

View File

@ -28,6 +28,7 @@ func (_ *DeleteStatement) node() {}
func (_ *ListSeriesStatement) node() {}
func (_ *DropSeriesStatement) node() {}
func (_ *ListContinuousQueriesStatement) node() {}
func (_ *CreateContinuousQueryStatement) node() {}
func (_ *DropContinuousQueryStatement) node() {}
func (_ Fields) node() {}
@ -65,6 +66,7 @@ func (_ *DeleteStatement) stmt() {}
func (_ *ListSeriesStatement) stmt() {}
func (_ *DropSeriesStatement) stmt() {}
func (_ *ListContinuousQueriesStatement) stmt() {}
func (_ *CreateContinuousQueryStatement) stmt() {}
func (_ *DropContinuousQueryStatement) stmt() {}
// Expr represents an expression that can be evaluated to a value.
@ -134,6 +136,13 @@ type DropSeriesStatement struct {
// ListContinuousQueriesStatement represents a command for listing continuous queries.
type ListContinuousQueriesStatement struct{}
// CreateContinuousQueriesStatement represents a command for creating a continuous query.
type CreateContinuousQueryStatement struct {
Name string
Source *SelectStatement
Target string
}
// DropContinuousQueriesStatement represents a command for removing a continuous query.
type DropContinuousQueryStatement struct {
Name string

View File

@ -21,13 +21,15 @@ func NewParser(r io.Reader) *Parser {
// ParseQuery parses an InfluxQL string and returns a Query AST object.
func (p *Parser) ParseQuery() (*Query, error) {
// If there's only whitespace then return no statements.
if tok, _, _ := p.scanIgnoreWhitespace(); tok == EOF {
return &Query{}, nil
}
p.unscan()
// Otherwise parse statements until EOF.
var statements Statements
for {
// Read statements until we reach the end.
if tok, _, _ := p.scanIgnoreWhitespace(); tok == EOF {
break
}
p.unscan()
// Read the next statement.
s, err := p.ParseStatement()
@ -35,7 +37,15 @@ func (p *Parser) ParseQuery() (*Query, error) {
return nil, err
}
statements = append(statements, s)
// Expect a semicolon or EOF after the statement.
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != SEMICOLON && tok != EOF {
return nil, newParseError(tokstr(tok, lit), []string{";", "EOF"}, pos)
} else if tok == EOF {
break
}
}
return &Query{Statements: statements}, nil
}
@ -56,6 +66,12 @@ func (p *Parser) ParseStatement() (Statement, error) {
} else {
return nil, newParseError(tokstr(tok, lit), []string{"SERIES", "CONTINUOUS"}, pos)
}
case CREATE:
if tok, pos, lit := p.scanIgnoreWhitespace(); tok == CONTINUOUS {
return p.parseCreateContinuousQueryStatement()
} else {
return nil, newParseError(tokstr(tok, lit), []string{"CONTINUOUS"}, pos)
}
case DROP:
if tok, pos, lit := p.scanIgnoreWhitespace(); tok == SERIES {
return p.parseDropSeriesStatement()
@ -116,11 +132,6 @@ func (p *Parser) parseSelectStatement() (*SelectStatement, error) {
}
stmt.Ascending = ascending
// Expect a semicolon or EOF at the end
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != SEMICOLON && tok != EOF {
return nil, newParseError(tokstr(tok, lit), []string{";", "EOF"}, pos)
}
return stmt, nil
}
@ -143,11 +154,6 @@ func (p *Parser) parseDeleteStatement() (*DeleteStatement, error) {
}
stmt.Condition = condition
// Expect a semicolon or EOF at the end
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != SEMICOLON && tok != EOF {
return nil, newParseError(tokstr(tok, lit), []string{";", "EOF"}, pos)
}
return stmt, nil
}
@ -155,12 +161,6 @@ func (p *Parser) parseDeleteStatement() (*DeleteStatement, error) {
// This function assumes the "LIST SERIES" tokens have already been consumed.
func (p *Parser) parseListSeriesStatement() (*ListSeriesStatement, error) {
stmt := &ListSeriesStatement{}
// Expect a semicolon or EOF at the end
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != SEMICOLON && tok != EOF {
return nil, newParseError(tokstr(tok, lit), []string{";", "EOF"}, pos)
}
return stmt, nil
}
@ -176,11 +176,6 @@ func (p *Parser) parseDropSeriesStatement() (*DropSeriesStatement, error) {
}
stmt.Name = lit
// Expect a semicolon or EOF at the end
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != SEMICOLON && tok != EOF {
return nil, newParseError(tokstr(tok, lit), []string{";", "EOF"}, pos)
}
return stmt, nil
}
@ -194,11 +189,53 @@ func (p *Parser) parseListContinuousQueriesStatement() (*ListContinuousQueriesSt
return nil, newParseError(tokstr(tok, lit), []string{"QUERIES"}, pos)
}
// Expect a semicolon or EOF at the end
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != SEMICOLON && tok != EOF {
return nil, newParseError(tokstr(tok, lit), []string{";", "EOF"}, pos)
return stmt, nil
}
// parseCreateContinuousQueriesStatement parses a string and returns a CreateContinuousQueryStatement.
// This function assumes the "CREATE CONTINUOUS" tokens have already been consumed.
func (p *Parser) parseCreateContinuousQueryStatement() (*CreateContinuousQueryStatement, error) {
stmt := &CreateContinuousQueryStatement{}
// Expect a "QUERY" token.
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != QUERY {
return nil, newParseError(tokstr(tok, lit), []string{"QUERY"}, pos)
}
// Read the id of the query to create.
tok, pos, lit := p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
}
stmt.Name = lit
// Expect an "AS SELECT" keyword.
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != AS {
return nil, newParseError(tokstr(tok, lit), []string{"AS"}, pos)
}
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != SELECT {
return nil, newParseError(tokstr(tok, lit), []string{"SELECT"}, pos)
}
// Read the select statement to be used as the source.
source, err := p.parseSelectStatement()
if err != nil {
return nil, err
}
stmt.Source = source
// Expect an INTO keyword.
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != INTO {
return nil, newParseError(tokstr(tok, lit), []string{"INTO"}, pos)
}
// Read the target of the query.
tok, pos, lit = p.scanIgnoreWhitespace()
if tok != IDENT && tok != STRING {
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
}
stmt.Target = lit
return stmt, nil
}
@ -219,11 +256,6 @@ func (p *Parser) parseDropContinuousQueryStatement() (*DropContinuousQueryStatem
}
stmt.Name = lit
// Expect a semicolon or EOF at the end
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != SEMICOLON && tok != EOF {
return nil, newParseError(tokstr(tok, lit), []string{";", "EOF"}, pos)
}
return stmt, nil
}

View File

@ -108,6 +108,19 @@ func TestParser_ParseStatement(t *testing.T) {
stmt: &influxql.ListContinuousQueriesStatement{},
},
// CREATE CONTINUOUS QUERY statement
{
s: `CREATE CONTINUOUS QUERY myquery AS SELECT count() FROM myseries INTO foo`,
stmt: &influxql.CreateContinuousQueryStatement{
Name: "myquery",
Source: &influxql.SelectStatement{
Fields: influxql.Fields{&influxql.Field{Expr: &influxql.Call{Name: "count"}}},
Source: &influxql.Series{Name: "myseries"},
},
Target: "foo",
},
},
// DROP CONTINUOUS QUERY statement
{
s: `DROP CONTINUOUS QUERY myquery`,
@ -119,7 +132,6 @@ func TestParser_ParseStatement(t *testing.T) {
{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 Y`, err: `found Y, expected ;, EOF at line 1, char 36`},
{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`},
@ -134,16 +146,11 @@ func TestParser_ParseStatement(t *testing.T) {
{s: `DELETE`, err: `found EOF, expected FROM at line 1, char 8`},
{s: `DELETE FROM`, err: `found EOF, expected identifier, string at line 1, char 13`},
{s: `DELETE FROM myseries WHERE`, err: `found EOF, expected identifier, string, number, bool at line 1, char 28`},
{s: `DELETE FROM myseries 123`, err: `found 123, expected ;, EOF at line 1, char 22`},
{s: `LIST SERIES x`, err: `found x, expected ;, EOF at line 1, char 13`},
{s: `DROP SERIES`, err: `found EOF, expected identifier, string at line 1, char 13`},
{s: `DROP SERIES myseries X`, err: `found X, expected ;, EOF at line 1, char 22`},
{s: `LIST CONTINUOUS`, err: `found EOF, expected QUERIES at line 1, char 17`},
{s: `LIST CONTINUOUS QUERIES x`, err: `found x, expected ;, EOF at line 1, char 25`},
{s: `LIST FOO`, err: `found FOO, expected SERIES, CONTINUOUS at line 1, char 6`},
{s: `DROP CONTINUOUS`, err: `found EOF, expected QUERY at line 1, char 17`},
{s: `DROP CONTINUOUS QUERY`, err: `found EOF, expected identifier, string at line 1, char 23`},
{s: `DROP CONTINUOUS QUERY myseries X`, err: `found X, expected ;, EOF at line 1, char 32`},
{s: `DROP FOO`, err: `found FOO, expected SERIES, CONTINUOUS at line 1, char 6`},
}

View File

@ -53,6 +53,7 @@ const (
AS
ASC
BY
CREATE
CONTINUOUS
DELETE
DESC
@ -110,6 +111,7 @@ var tokens = [...]string{
AS: "AS",
ASC: "ASC",
BY: "BY",
CREATE: "CREATE",
CONTINUOUS: "CONTINUOUS",
DELETE: "DELETE",
DESC: "DESC",