influxql: add LIST MEASUREMENTS, TAGS, & FIELDS

pull/1222/head
David Norton 2014-12-15 21:48:22 -05:00
parent ddc1352073
commit 4ce9b2bd74
3 changed files with 568 additions and 7 deletions

View File

@ -50,6 +50,11 @@ func (_ Statements) node() {}
func (_ *SelectStatement) node() {}
func (_ *DeleteStatement) node() {}
func (_ *ListSeriesStatement) node() {}
func (_ *ListMeasurementsStatement) node() {}
func (_ *ListTagKeysStatement) node() {}
func (_ *ListTagValuesStatement) node() {}
func (_ *ListFieldKeysStatement) node() {}
func (_ *ListFieldValuesStatement) node() {}
func (_ *DropSeriesStatement) node() {}
func (_ *ListContinuousQueriesStatement) node() {}
func (_ *CreateContinuousQueryStatement) node() {}
@ -108,6 +113,11 @@ func (_ *DropSeriesStatement) stmt() {}
func (_ *ListContinuousQueriesStatement) stmt() {}
func (_ *CreateContinuousQueryStatement) stmt() {}
func (_ *DropContinuousQueryStatement) stmt() {}
func (_ *ListMeasurementsStatement) stmt() {}
func (_ *ListTagKeysStatement) stmt() {}
func (_ *ListTagValuesStatement) stmt() {}
func (_ *ListFieldKeysStatement) stmt() {}
func (_ *ListFieldValuesStatement) stmt() {}
// Expr represents an expression that can be evaluated to a value.
type Expr interface {
@ -203,13 +213,13 @@ func (s *SelectStatement) String() string {
_, _ = buf.WriteString(" GROUP BY ")
_, _ = buf.WriteString(s.Dimensions.String())
}
if s.Limit > 0 {
_, _ = fmt.Fprintf(&buf, " LIMIT %d", s.Limit)
}
if len(s.SortFields) > 0 {
_, _ = buf.WriteString(" ORDER BY ")
_, _ = buf.WriteString(s.SortFields.String())
}
if s.Limit > 0 {
_, _ = fmt.Fprintf(&buf, " LIMIT %d", s.Limit)
}
return buf.String()
}
@ -376,16 +386,33 @@ type ListSeriesStatement struct{
// An expression evaluated on a series name or tag.
Condition Expr
// Fields to sort results by
SortFields SortFields
// 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.
func (s *ListSeriesStatement) String() string { return "LIST SERIES" }
func (s *ListSeriesStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("LIST SERIES")
if s.Condition != nil {
_, _ = buf.WriteString(" WHERE ")
_, _ = buf.WriteString(s.Condition.String())
}
if len(s.SortFields) > 0 {
_, _ = buf.WriteString(" ORDER BY ")
_, _ = buf.WriteString(s.SortFields.String())
}
if s.Limit > 0 {
_, _ = buf.WriteString(" LIMIT ")
_, _ = buf.WriteString(strconv.Itoa(s.Limit))
}
return buf.String()
}
// DropSeriesStatement represents a command for removing a series from the database.
type DropSeriesStatement struct {
@ -423,6 +450,198 @@ func (s *DropContinuousQueryStatement) String() string {
return fmt.Sprintf("DROP CONTINUOUS QUERY %s", s.Name)
}
// ListMeasurementsStatement represents a command for listing measurements.
type ListMeasurementsStatement 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
}
// String returns a string representation of the statement.
func (s *ListMeasurementsStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("LIST MEASUREMENTS")
if s.Condition != nil {
_, _ = buf.WriteString(" WHERE ")
_, _ = buf.WriteString(s.Condition.String())
}
if len(s.SortFields) > 0 {
_, _ = buf.WriteString(" ORDER BY ")
_, _ = buf.WriteString(s.SortFields.String())
}
if s.Limit > 0 {
_, _ = buf.WriteString(" LIMIT ")
_, _ = buf.WriteString(strconv.Itoa(s.Limit))
}
return buf.String()
}
// ListTagKeysStatement represents a command for listing tag keys.
type ListTagKeysStatement struct {
// Data source that fields are extracted from.
Source Source
// 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
}
// String returns a string representation of the statement.
func (s *ListTagKeysStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("LIST TAG KEYS")
if s.Source != nil {
_, _ = buf.WriteString(" FROM ")
_, _ = buf.WriteString(s.Source.String())
}
if s.Condition != nil {
_, _ = buf.WriteString(" WHERE ")
_, _ = buf.WriteString(s.Condition.String())
}
if len(s.SortFields) > 0 {
_, _ = buf.WriteString(" ORDER BY ")
_, _ = buf.WriteString(s.SortFields.String())
}
if s.Limit > 0 {
_, _ = buf.WriteString(" LIMIT ")
_, _ = buf.WriteString(strconv.Itoa(s.Limit))
}
return buf.String()
}
// ListTagValuesStatement represents a command for listing tag values.
type ListTagValuesStatement struct {
// Data source that fields are extracted from.
Source Source
// 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
}
// String returns a string representation of the statement.
func (s *ListTagValuesStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("LIST TAG VALUES")
if s.Source != nil {
_, _ = buf.WriteString(" FROM ")
_, _ = buf.WriteString(s.Source.String())
}
if s.Condition != nil {
_, _ = buf.WriteString(" WHERE ")
_, _ = buf.WriteString(s.Condition.String())
}
if len(s.SortFields) > 0 {
_, _ = buf.WriteString(" ORDER BY ")
_, _ = buf.WriteString(s.SortFields.String())
}
if s.Limit > 0 {
_, _ = buf.WriteString(" LIMIT ")
_, _ = buf.WriteString(strconv.Itoa(s.Limit))
}
return buf.String()
}
// ListFieldKeyStatement represents a command for listing field keys.
type ListFieldKeysStatement struct {
// Data source that fields are extracted from.
Source Source
// 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
}
// String returns a string representation of the statement.
func (s *ListFieldKeysStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("LIST FIELD KEYS")
if s.Source != nil {
_, _ = buf.WriteString(" FROM ")
_, _ = buf.WriteString(s.Source.String())
}
if s.Condition != nil {
_, _ = buf.WriteString(" WHERE ")
_, _ = buf.WriteString(s.Condition.String())
}
if len(s.SortFields) > 0 {
_, _ = buf.WriteString(" ORDER BY ")
_, _ = buf.WriteString(s.SortFields.String())
}
if s.Limit > 0 {
_, _ = buf.WriteString(" LIMIT ")
_, _ = buf.WriteString(strconv.Itoa(s.Limit))
}
return buf.String()
}
// ListFieldValuesStatement represents a command for listing field values.
type ListFieldValuesStatement struct {
// Data source that fields are extracted from.
Source Source
// 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
}
// String returns a string representation of the statement.
func (s *ListFieldValuesStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("LIST FIELD VALUES")
if s.Source != nil {
_, _ = buf.WriteString(" FROM ")
_, _ = buf.WriteString(s.Source.String())
}
if s.Condition != nil {
_, _ = buf.WriteString(" WHERE ")
_, _ = buf.WriteString(s.Condition.String())
}
if len(s.SortFields) > 0 {
_, _ = buf.WriteString(" ORDER BY ")
_, _ = buf.WriteString(s.SortFields.String())
}
if s.Limit > 0 {
_, _ = buf.WriteString(" LIMIT ")
_, _ = buf.WriteString(strconv.Itoa(s.Limit))
}
return buf.String()
}
// Fields represents a list of fields.
type Fields []*Field

View File

@ -64,6 +64,24 @@ func (p *Parser) ParseStatement() (Statement, error) {
return p.parseListSeriesStatement()
} else if tok == CONTINUOUS {
return p.parseListContinuousQueriesStatement()
} else if tok == MEASUREMENTS {
return p.parseListMeasurementsStatement()
} else if tok == TAG {
if tok, pos, lit := p.scanIgnoreWhitespace(); tok == KEYS {
return p.parseListTagKeysStatement()
} else if tok == VALUES {
return p.parseListTagValuesStatement()
} else {
return nil, newParseError(tokstr(tok, lit), []string{"KEYS", "VALUES"}, pos)
}
} else if tok == FIELD {
if tok, pos, lit := p.scanIgnoreWhitespace(); tok == KEYS {
return p.parseListFieldKeysStatement()
} else if tok == VALUES {
return p.parseListFieldValuesStatement()
} else {
return nil, newParseError(tokstr(tok, lit), []string{"KEYS", "VALUES"}, pos)
}
} else {
return nil, newParseError(tokstr(tok, lit), []string{"SERIES", "CONTINUOUS"}, pos)
}
@ -211,6 +229,236 @@ func (p *Parser) parseListSeriesStatement() (*ListSeriesStatement, error) {
return stmt, nil
}
// parseListMeasurementsStatement parses a string and returns a ListSeriesStatement.
// This function assumes the "LIST MEASUREMENTS" tokens have already been consumed.
func (p *Parser) parseListMeasurementsStatement() (*ListMeasurementsStatement, error) {
stmt := &ListMeasurementsStatement{}
// Parse condition: "WHERE EXPR".
condition, err := p.parseCondition()
if err != nil {
return nil, err
}
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 {
return nil, err
}
stmt.Limit = limit
return stmt, nil
}
// parseListTagKeysStatement parses a string and returns a ListSeriesStatement.
// This function assumes the "LIST TAG KEYS" tokens have already been consumed.
func (p *Parser) parseListTagKeysStatement() (*ListTagKeysStatement, error) {
stmt := &ListTagKeysStatement{}
// Parse source.
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != FROM {
return nil, newParseError(tokstr(tok, lit), []string{"FROM"}, pos)
}
source, err := p.parseSource()
if err != nil {
return nil, err
}
stmt.Source = source
// Parse condition: "WHERE EXPR".
condition, err := p.parseCondition()
if err != nil {
return nil, err
}
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 {
return nil, err
}
stmt.Limit = limit
return stmt, nil
}
// parseListTagValuesStatement parses a string and returns a ListSeriesStatement.
// This function assumes the "LIST TAG VALUES" tokens have already been consumed.
func (p *Parser) parseListTagValuesStatement() (*ListTagValuesStatement, error) {
stmt := &ListTagValuesStatement{}
// Parse source.
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != FROM {
return nil, newParseError(tokstr(tok, lit), []string{"FROM"}, pos)
}
source, err := p.parseSource()
if err != nil {
return nil, err
}
stmt.Source = source
// Parse condition: "WHERE EXPR".
condition, err := p.parseCondition()
if err != nil {
return nil, err
}
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 {
return nil, err
}
stmt.Limit = limit
return stmt, nil
}
// parseListFieldKeysStatement parses a string and returns a ListSeriesStatement.
// This function assumes the "LIST FIELD KEYS" tokens have already been consumed.
func (p *Parser) parseListFieldKeysStatement() (*ListFieldKeysStatement, error) {
stmt := &ListFieldKeysStatement{}
// Parse source.
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != FROM {
return nil, newParseError(tokstr(tok, lit), []string{"FROM"}, pos)
}
source, err := p.parseSource()
if err != nil {
return nil, err
}
stmt.Source = source
// Parse condition: "WHERE EXPR".
condition, err := p.parseCondition()
if err != nil {
return nil, err
}
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 {
return nil, err
}
stmt.Limit = limit
return stmt, nil
}
// parseListFieldValuesStatement parses a string and returns a ListSeriesStatement.
// This function assumes the "LIST FIELD VALUES" tokens have already been consumed.
func (p *Parser) parseListFieldValuesStatement() (*ListFieldValuesStatement, error) {
stmt := &ListFieldValuesStatement{}
// Parse source.
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != FROM {
return nil, newParseError(tokstr(tok, lit), []string{"FROM"}, pos)
}
source, err := p.parseSource()
if err != nil {
return nil, err
}
stmt.Source = source
// Parse condition: "WHERE EXPR".
condition, err := p.parseCondition()
if err != nil {
return nil, err
}
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 {
return nil, err
}
stmt.Limit = limit
return stmt, nil
}
// parseDropSeriesStatement parses a string and returns a DropSeriesStatement.
// This function assumes the "DROP SERIES" tokens have already been consumed.
func (p *Parser) parseDropSeriesStatement() (*DropSeriesStatement, error) {

View File

@ -172,6 +172,100 @@ func TestParser_ParseStatement(t *testing.T) {
},
},
// LIST MEASUREMENTS WHERE with ORDER BY and LIMIT
{
s: `LIST MEASUREMENTS WHERE region = 'uswest' ORDER BY ASC, field1, field2 DESC LIMIT 10`,
stmt: &influxql.ListMeasurementsStatement{
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,
},
},
// LIST TAG KEYS
{
s: `LIST TAG KEYS FROM src WHERE region = 'uswest' ORDER BY ASC, field1, field2 DESC LIMIT 10`,
stmt: &influxql.ListTagKeysStatement{
Source: &influxql.Series{Name: "src"},
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,
},
},
// LIST TAG VALUES
{
s: `LIST TAG VALUES FROM src WHERE region = 'uswest' ORDER BY ASC, field1, field2 DESC LIMIT 10`,
stmt: &influxql.ListTagValuesStatement{
Source: &influxql.Series{Name: "src"},
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,
},
},
// LIST FIELD KEYS
{
s: `LIST FIELD KEYS FROM src WHERE region = 'uswest' ORDER BY ASC, field1, field2 DESC LIMIT 10`,
stmt: &influxql.ListFieldKeysStatement{
Source: &influxql.Series{Name: "src"},
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,
},
},
// LIST FIELD VALUES
{
s: `LIST FIELD VALUES FROM src WHERE region = 'uswest' ORDER BY ASC, field1, field2 DESC LIMIT 10`,
stmt: &influxql.ListFieldValuesStatement{
Source: &influxql.Series{Name: "src"},
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,
},
},
// DROP SERIES statement
{
s: `DROP SERIES myseries`,