Merge pull request #1270 from influxdb/muchoQL
This PR adds to InfluxQL: - CREATE DATABASE, USER, and RETENTION POLICY - ALTER RETENTION POLICY - GRANT and REVOKE for privileges - DROP for DATABASE and USERpull/1276/merge
commit
d044d5ffa6
42
QUERIES.md
42
QUERIES.md
|
@ -1,5 +1,47 @@
|
|||
The top level name is called a measurement. These names can contain any characters. Then there are field names, field values, tag keys and tag values, which can also contain any characters. Because of this, anywhere a measurement name, field name, field value, tag name, or tag value appears should be able to get wrapped in double quotes to deal with special characters.
|
||||
|
||||
# Databases & retention policies
|
||||
|
||||
```sql
|
||||
-- create a database
|
||||
CREATE DATABASE <name>
|
||||
|
||||
-- create a retention policy
|
||||
CREATE RETENTION POLICY <rp-name> ON <db-name> DURATION <duration> REPLICATION <n> [DEFAULT]
|
||||
|
||||
-- alter retention policy
|
||||
ALTER RETENTION POLICY <rp-name> ON <db-name> (DURATION <duration> | REPLICATION <n> | DEFAULT)+
|
||||
|
||||
-- drop a database
|
||||
DROP DATABASE <name>
|
||||
```
|
||||
|
||||
# Users and permissions
|
||||
|
||||
```sql
|
||||
-- create user
|
||||
CREATE USER <name> WITH PASSWORD <password>
|
||||
|
||||
-- grant privilege on a database
|
||||
GRANT <privilege> ON <db> TO <user>
|
||||
|
||||
-- grant cluster admin privileges
|
||||
GRANT ALL [PRIVILEGES] TO <user>
|
||||
|
||||
-- revoke privilege
|
||||
REVOKE <privilege> ON <db> FROM <user>
|
||||
|
||||
-- revoke all privileges for a DB
|
||||
REVOKE ALL [PRIVILEGES] ON <db> FROM <user>
|
||||
|
||||
-- revoke all of user's privileges (all DBs and/or cluster admin)
|
||||
REVOKE ALL [PRIVILEGES] FROM <user>
|
||||
|
||||
-- delete a user
|
||||
DROP USER <name>
|
||||
```
|
||||
<privilege> := READ | WRITE | All [PRIVILEGES]
|
||||
|
||||
# Select
|
||||
|
||||
```sql
|
||||
|
|
233
influxql/ast.go
233
influxql/ast.go
|
@ -55,10 +55,18 @@ func (_ *ListTagKeysStatement) node() {}
|
|||
func (_ *ListTagValuesStatement) node() {}
|
||||
func (_ *ListFieldKeysStatement) node() {}
|
||||
func (_ *ListFieldValuesStatement) node() {}
|
||||
func (_ *DropSeriesStatement) node() {}
|
||||
func (_ *ListContinuousQueriesStatement) node() {}
|
||||
func (_ *CreateContinuousQueryStatement) node() {}
|
||||
func (_ *DropSeriesStatement) node() {}
|
||||
func (_ *DropContinuousQueryStatement) node() {}
|
||||
func (_ *DropDatabaseStatement) node() {}
|
||||
func (_ *DropUserStatement) node() {}
|
||||
func (_ *CreateContinuousQueryStatement) node() {}
|
||||
func (_ *CreateDatabaseStatement) node() {}
|
||||
func (_ *CreateUserStatement) node() {}
|
||||
func (_ *CreateRetentionPolicyStatement) node() {}
|
||||
func (_ *GrantStatement) node() {}
|
||||
func (_ *RevokeStatement) node() {}
|
||||
func (_ *AlterRetentionPolicyStatement) node() {}
|
||||
|
||||
func (_ Fields) node() {}
|
||||
func (_ *Field) node() {}
|
||||
|
@ -119,6 +127,14 @@ func (_ *ListTagKeysStatement) stmt() {}
|
|||
func (_ *ListTagValuesStatement) stmt() {}
|
||||
func (_ *ListFieldKeysStatement) stmt() {}
|
||||
func (_ *ListFieldValuesStatement) stmt() {}
|
||||
func (_ *CreateDatabaseStatement) stmt() {}
|
||||
func (_ *CreateUserStatement) stmt() {}
|
||||
func (_ *GrantStatement) stmt() {}
|
||||
func (_ *RevokeStatement) stmt() {}
|
||||
func (_ *CreateRetentionPolicyStatement) stmt() {}
|
||||
func (_ *DropDatabaseStatement) stmt() {}
|
||||
func (_ *DropUserStatement) stmt() {}
|
||||
func (_ *AlterRetentionPolicyStatement) stmt() {}
|
||||
|
||||
// Expr represents an expression that can be evaluated to a value.
|
||||
type Expr interface {
|
||||
|
@ -177,6 +193,219 @@ func (a SortFields) String() string {
|
|||
return strings.Join(fields, ", ")
|
||||
}
|
||||
|
||||
// CreateDatabaseStatement represents a command for creating a new database.
|
||||
type CreateDatabaseStatement struct {
|
||||
// Name of the database to be created.
|
||||
Name string
|
||||
}
|
||||
|
||||
// String returns a string representation of the create database statement.
|
||||
func (s *CreateDatabaseStatement) String() string {
|
||||
var buf bytes.Buffer
|
||||
_, _ = buf.WriteString("CREATE DATABASE ")
|
||||
_, _ = buf.WriteString(s.Name)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// DropDatabaseStatement represents a command to drop a database.
|
||||
type DropDatabaseStatement struct {
|
||||
// Name of the database to be dropped.
|
||||
Name string
|
||||
}
|
||||
|
||||
// String returns a string representation of the drop database statement.
|
||||
func (s *DropDatabaseStatement) String() string {
|
||||
var buf bytes.Buffer
|
||||
_, _ = buf.WriteString("DROP DATABASE ")
|
||||
_, _ = buf.WriteString(s.Name)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// CreateUserStatement represents a command for creating a new user.
|
||||
type CreateUserStatement struct {
|
||||
// Name of the user to be created.
|
||||
Name string
|
||||
|
||||
// User's password
|
||||
Password string
|
||||
}
|
||||
|
||||
// String returns a string representation of the create user statement.
|
||||
func (s *CreateUserStatement) String() string {
|
||||
var buf bytes.Buffer
|
||||
_, _ = buf.WriteString("CREATE USER ")
|
||||
_, _ = buf.WriteString(s.Name)
|
||||
_, _ = buf.WriteString(" WITH PASSWORD ")
|
||||
_, _ = buf.WriteString(s.Password)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// DropUserStatement represents a command for dropping a user.
|
||||
type DropUserStatement struct {
|
||||
// Name of the user to drop.
|
||||
Name string
|
||||
}
|
||||
|
||||
// String returns a string representation of the drop user statement.
|
||||
func (s *DropUserStatement) String() string {
|
||||
var buf bytes.Buffer
|
||||
_, _ = buf.WriteString("DROP USER ")
|
||||
_, _ = buf.WriteString(s.Name)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Privilege is a type of action a user can be granted the right to use.
|
||||
type Privilege int
|
||||
|
||||
const (
|
||||
ReadPrivilege Privilege = iota
|
||||
WritePrivilege
|
||||
AllPrivileges
|
||||
)
|
||||
|
||||
// String returns a string representation of a Privilege.
|
||||
func (p Privilege) String() string {
|
||||
switch p {
|
||||
case ReadPrivilege:
|
||||
return "READ"
|
||||
case WritePrivilege:
|
||||
return "WRITE"
|
||||
case AllPrivileges:
|
||||
return "ALL PRIVILEGES"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GrantStatement represents a command for granting a privilege.
|
||||
type GrantStatement struct {
|
||||
// The privilege to be granted.
|
||||
Privilege Privilege
|
||||
|
||||
// Thing to grant privilege on (e.g., a DB).
|
||||
On string
|
||||
|
||||
// Who to grant the privilege to.
|
||||
User string
|
||||
}
|
||||
|
||||
// String returns a string representation of the grant statement.
|
||||
func (s *GrantStatement) String() string {
|
||||
var buf bytes.Buffer
|
||||
_, _ = buf.WriteString("GRANT ")
|
||||
_, _ = buf.WriteString(s.Privilege.String())
|
||||
if s.On != "" {
|
||||
_, _ = buf.WriteString(" ON ")
|
||||
_, _ = buf.WriteString(s.On)
|
||||
}
|
||||
_, _ = buf.WriteString(" TO ")
|
||||
_, _ = buf.WriteString(s.User)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// RevokeStatement represents a command to revoke a privilege from a user.
|
||||
type RevokeStatement struct {
|
||||
// Privilege to be revoked.
|
||||
Privilege Privilege
|
||||
|
||||
// Thing to revoke privilege to (e.g., a DB)
|
||||
On string
|
||||
|
||||
// Who to revoke privilege from.
|
||||
User string
|
||||
}
|
||||
|
||||
// String returns a string representation of the revoke statement.
|
||||
func (s *RevokeStatement) String() string {
|
||||
var buf bytes.Buffer
|
||||
_, _ = buf.WriteString("REVOKE ")
|
||||
_, _ = buf.WriteString(s.Privilege.String())
|
||||
if s.On != "" {
|
||||
_, _ = buf.WriteString(" ON ")
|
||||
_, _ = buf.WriteString(s.On)
|
||||
}
|
||||
_, _ = buf.WriteString(" FROM ")
|
||||
_, _ = buf.WriteString(s.User)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// CreateRetentionPolicyStatement represents a command to create a retention policy.
|
||||
type CreateRetentionPolicyStatement struct {
|
||||
// Name of policy to create.
|
||||
Name string
|
||||
|
||||
// Name of database this policy belongs to.
|
||||
DB string
|
||||
|
||||
// Duration data written to this policy will be retained.
|
||||
Duration time.Duration
|
||||
|
||||
// Replication factor for data written to this policy.
|
||||
Replication int
|
||||
|
||||
// Should this policy be set as default for the database?
|
||||
Default bool
|
||||
}
|
||||
|
||||
// String returns a string representation of the create retention policy.
|
||||
func (s *CreateRetentionPolicyStatement) String() string {
|
||||
var buf bytes.Buffer
|
||||
_, _ = buf.WriteString("CREATE RETENTION POLICY ")
|
||||
_, _ = buf.WriteString(s.Name)
|
||||
_, _ = buf.WriteString(" ON ")
|
||||
_, _ = buf.WriteString(s.DB)
|
||||
_, _ = buf.WriteString(" DURATION ")
|
||||
_, _ = buf.WriteString(FormatDuration(s.Duration))
|
||||
_, _ = buf.WriteString(" REPLICATION ")
|
||||
_, _ = buf.WriteString(strconv.Itoa(s.Replication))
|
||||
if s.Default {
|
||||
_, _ = buf.WriteString(" DEFAULT")
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// AlterRetentionPolicyStatement represents a command to alter an existing retention policy.
|
||||
type AlterRetentionPolicyStatement struct {
|
||||
// Name of policy to alter.
|
||||
Name string
|
||||
|
||||
// Name of the database this policy belongs to.
|
||||
DB string
|
||||
|
||||
// Duration data written to this policy will be retained.
|
||||
Duration *time.Duration
|
||||
|
||||
// Replication factor for data written to this policy.
|
||||
Replication *int
|
||||
|
||||
// Should this policy be set as defalut for the database?
|
||||
Default bool
|
||||
}
|
||||
|
||||
// String returns a string representation of the alter retention policy statement.
|
||||
func (s *AlterRetentionPolicyStatement) String() string {
|
||||
var buf bytes.Buffer
|
||||
_, _ = buf.WriteString("ALTER RETENTION POLICY ")
|
||||
_, _ = buf.WriteString(s.Name)
|
||||
_, _ = buf.WriteString(" ON ")
|
||||
_, _ = buf.WriteString(s.DB)
|
||||
|
||||
if s.Duration != nil {
|
||||
_, _ = buf.WriteString(" DURATION ")
|
||||
_, _ = buf.WriteString(FormatDuration(*s.Duration))
|
||||
}
|
||||
|
||||
if s.Replication != nil {
|
||||
_, _ = buf.WriteString(" REPLICATION ")
|
||||
_, _ = buf.WriteString(strconv.Itoa(*s.Replication))
|
||||
}
|
||||
|
||||
if s.Default {
|
||||
_, _ = buf.WriteString(" DEFAULT")
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// SelectStatement represents a command for extracting data from the database.
|
||||
type SelectStatement struct {
|
||||
// Expressions returned from the selection.
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -68,50 +69,377 @@ func (p *Parser) ParseStatement() (Statement, error) {
|
|||
case DELETE:
|
||||
return p.parseDeleteStatement()
|
||||
case LIST:
|
||||
if tok, pos, lit := p.scanIgnoreWhitespace(); tok == SERIES {
|
||||
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)
|
||||
}
|
||||
return p.parseListStatement()
|
||||
case CREATE:
|
||||
if tok, pos, lit := p.scanIgnoreWhitespace(); tok == CONTINUOUS {
|
||||
return p.parseCreateContinuousQueryStatement()
|
||||
} else {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"CONTINUOUS"}, pos)
|
||||
}
|
||||
return p.parseCreateStatement()
|
||||
case DROP:
|
||||
if tok, pos, lit := p.scanIgnoreWhitespace(); tok == SERIES {
|
||||
return p.parseDropSeriesStatement()
|
||||
} else if tok == CONTINUOUS {
|
||||
return p.parseDropContinuousQueryStatement()
|
||||
} else {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"SERIES", "CONTINUOUS"}, pos)
|
||||
}
|
||||
return p.parseDropStatement()
|
||||
case GRANT:
|
||||
return p.parseGrantStatement()
|
||||
case REVOKE:
|
||||
return p.parseRevokeStatement()
|
||||
case ALTER:
|
||||
return p.parseAlterStatement()
|
||||
default:
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"SELECT"}, pos)
|
||||
}
|
||||
}
|
||||
|
||||
// parseListStatement parses a string and returns a list statement.
|
||||
// This function assumes the LIST token has already been consumed.
|
||||
func (p *Parser) parseListStatement() (Statement, error) {
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok == SERIES {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"SERIES", "CONTINUOUS", "MEASUREMENTS", "TAG", "FIELD"}, pos)
|
||||
}
|
||||
|
||||
// parseCreateStatement parses a string and returns a create statement.
|
||||
// This function assumes the CREATE token has already been consumned.
|
||||
func (p *Parser) parseCreateStatement() (Statement, error) {
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok == CONTINUOUS {
|
||||
return p.parseCreateContinuousQueryStatement()
|
||||
} else if tok == DATABASE {
|
||||
return p.parseCreateDatabaseStatement()
|
||||
} else if tok == USER {
|
||||
return p.parseCreateUserStatement()
|
||||
} else if tok == RETENTION {
|
||||
tok, pos, lit = p.scanIgnoreWhitespace()
|
||||
if tok != POLICY {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"POLICY"}, pos)
|
||||
}
|
||||
return p.parseCreateRetentionPolicyStatement()
|
||||
}
|
||||
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"CONTINUOUS", "DATABASE", "USER", "RETENTION"}, pos)
|
||||
}
|
||||
|
||||
// parseDropStatement parses a string and returns a drop statement.
|
||||
// This function assumes the DROP token has already been consumed.
|
||||
func (p *Parser) parseDropStatement() (Statement, error) {
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok == SERIES {
|
||||
return p.parseDropSeriesStatement()
|
||||
} else if tok == CONTINUOUS {
|
||||
return p.parseDropContinuousQueryStatement()
|
||||
} else if tok == DATABASE {
|
||||
return p.parseDropDatabaseStatement()
|
||||
} else if tok == USER {
|
||||
return p.parseDropUserStatement()
|
||||
}
|
||||
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"SERIES", "CONTINUOUS"}, pos)
|
||||
}
|
||||
|
||||
// parseAlterStatement parses a string and returns an alter statement.
|
||||
// This function assumes the ALTER token has already been consumed.
|
||||
func (p *Parser) parseAlterStatement() (Statement, error) {
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok == RETENTION {
|
||||
if tok, pos, lit = p.scanIgnoreWhitespace(); tok != POLICY {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"POLICY"}, pos)
|
||||
}
|
||||
return p.parseAlterRetentionPolicyStatement()
|
||||
}
|
||||
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"RETENTION"}, pos)
|
||||
}
|
||||
|
||||
// parseCreateRetentionPolicyStatement parses a string and returns a create retention policy statement.
|
||||
// This function assumes the CREATE RETENTION POLICY tokens have already been consumed.
|
||||
func (p *Parser) parseCreateRetentionPolicyStatement() (*CreateRetentionPolicyStatement, error) {
|
||||
stmt := &CreateRetentionPolicyStatement{}
|
||||
|
||||
// Parse the retention policy name.
|
||||
ident, err := p.parseIdentifier()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt.Name = ident
|
||||
|
||||
// Consume the required ON token.
|
||||
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != ON {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"ON"}, pos)
|
||||
}
|
||||
|
||||
// Parse the database name.
|
||||
ident, err = p.parseIdentifier()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt.DB = ident
|
||||
|
||||
// Parse required DURATION token.
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok != DURATION {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"DURATION"}, pos)
|
||||
}
|
||||
|
||||
// Parse duration value
|
||||
d, err := p.parseDuration()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt.Duration = d
|
||||
|
||||
// Parse required REPLICATION token.
|
||||
if tok, pos, lit = p.scanIgnoreWhitespace(); tok != REPLICATION {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"REPLICATION"}, pos)
|
||||
}
|
||||
|
||||
// Parse replication value.
|
||||
n, err := p.parseInt(1, math.MaxInt32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt.Replication = n
|
||||
|
||||
// Parse optional DEFAULT token.
|
||||
if tok, pos, lit = p.scanIgnoreWhitespace(); tok == DEFAULT {
|
||||
stmt.Default = true
|
||||
} else {
|
||||
p.unscan()
|
||||
}
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
// parseAlterRetentionPolicyStatement parses a string and returns an alter retention policy statement.
|
||||
// This function assumes the ALTER RETENTION POLICY tokens have already been consumned.
|
||||
func (p *Parser) parseAlterRetentionPolicyStatement() (*AlterRetentionPolicyStatement, error) {
|
||||
stmt := &AlterRetentionPolicyStatement{}
|
||||
|
||||
// Parse the retention policy name.
|
||||
ident, err := p.parseIdentifier()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt.Name = ident
|
||||
|
||||
// Consume the required ON token.
|
||||
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != ON {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"ON"}, pos)
|
||||
}
|
||||
|
||||
// Parse the database name.
|
||||
ident, err = p.parseIdentifier()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt.DB = ident
|
||||
|
||||
// Loop through option tokens (DURATION, RETENTION, DEFAULT, etc.).
|
||||
maxNumOptions := 3
|
||||
Loop:
|
||||
for i := 0; i < maxNumOptions; i++ {
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
switch tok {
|
||||
case DURATION:
|
||||
d, err := p.parseDuration()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt.Duration = &d
|
||||
case REPLICATION:
|
||||
n, err := p.parseInt(1, math.MaxInt32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt.Replication = &n
|
||||
case DEFAULT:
|
||||
stmt.Default = true
|
||||
default:
|
||||
if i < 1 {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"DURATION", "RETENTION", "DEFAULT"}, pos)
|
||||
}
|
||||
p.unscan()
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
// parseInt parses a string and returns an integer literal.
|
||||
func (p *Parser) parseInt(min, max int) (int, error) {
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok != NUMBER {
|
||||
return 0, newParseError(tokstr(tok, lit), []string{"number"}, pos)
|
||||
}
|
||||
|
||||
// Return an error if the number has a fractional part.
|
||||
if strings.Contains(lit, ".") {
|
||||
return 0, &ParseError{Message: "number must be an integer", Pos: pos}
|
||||
}
|
||||
|
||||
// Convert string to int.
|
||||
n, err := strconv.Atoi(lit)
|
||||
if err != nil {
|
||||
return 0, &ParseError{Message: err.Error(), Pos: pos}
|
||||
} else if min > n || n > max {
|
||||
return 0, &ParseError{
|
||||
Message: fmt.Sprintf("invalid value %d: must be %d <= n <= %d", n, min, max),
|
||||
Pos: pos,
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// parseDuration parses a string and returns a duration literal.
|
||||
// This function assumes the DURATION token has already been consumed.
|
||||
func (p *Parser) parseDuration() (time.Duration, error) {
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok != DURATION_VAL {
|
||||
return 0, newParseError(tokstr(tok, lit), []string{"duration"}, pos)
|
||||
}
|
||||
d, err := ParseDuration(lit)
|
||||
if err != nil {
|
||||
return 0, &ParseError{Message: err.Error(), Pos: pos}
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// parserIdentifier parses a string and returns an identifier.
|
||||
func (p *Parser) parseIdentifier() (string, error) {
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok != IDENT && tok != STRING {
|
||||
return "", newParseError(tokstr(tok, lit), []string{"identifier"}, pos)
|
||||
}
|
||||
return lit, nil
|
||||
}
|
||||
|
||||
// parseRevokeStatement parses a string and returns a revoke statement.
|
||||
// This function assumes the REVOKE token has already been consumend.
|
||||
func (p *Parser) parseRevokeStatement() (*RevokeStatement, error) {
|
||||
stmt := &RevokeStatement{}
|
||||
|
||||
// Parse the privilege to be granted.
|
||||
priv, err := p.parsePrivilege()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt.Privilege = priv
|
||||
|
||||
// Parse ON clause.
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok == ON {
|
||||
// Parse the name of the thing we're granting a privilege to use.
|
||||
tok, pos, lit = p.scanIgnoreWhitespace()
|
||||
if tok != IDENT && tok != STRING {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
|
||||
}
|
||||
stmt.On = lit
|
||||
|
||||
tok, pos, lit = p.scanIgnoreWhitespace()
|
||||
} else if priv != AllPrivileges {
|
||||
// ALL PRIVILEGES is the only privilege allowed cluster-wide.
|
||||
// No ON clause means query is requesting cluster-wide.
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"ON"}, pos)
|
||||
}
|
||||
|
||||
// Check for required FROM token.
|
||||
if tok != FROM {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"FROM"}, pos)
|
||||
}
|
||||
|
||||
// Parse the name of the user we're granting the privilege to.
|
||||
tok, pos, lit = p.scanIgnoreWhitespace()
|
||||
if tok != IDENT && tok != STRING {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
|
||||
}
|
||||
stmt.User = lit
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
// parseGrantStatement parses a string and returns a grant statement.
|
||||
// This function assumes the GRANT token has already been consumed.
|
||||
func (p *Parser) parseGrantStatement() (*GrantStatement, error) {
|
||||
stmt := &GrantStatement{}
|
||||
|
||||
// Parse the privilege to be granted.
|
||||
priv, err := p.parsePrivilege()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stmt.Privilege = priv
|
||||
|
||||
// Parse ON clause.
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok == ON {
|
||||
// Parse the name of the thing we're granting a privilege to use.
|
||||
tok, pos, lit = p.scanIgnoreWhitespace()
|
||||
if tok != IDENT && tok != STRING {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
|
||||
}
|
||||
stmt.On = lit
|
||||
|
||||
tok, pos, lit = p.scanIgnoreWhitespace()
|
||||
} else if priv != AllPrivileges {
|
||||
// ALL PRIVILEGES is the only privilege allowed cluster-wide.
|
||||
// No ON clause means query is requesting cluster-wide.
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"ON"}, pos)
|
||||
}
|
||||
|
||||
// Check for required TO token.
|
||||
if tok != TO {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"TO"}, pos)
|
||||
}
|
||||
|
||||
// Parse the name of the user we're granting the privilege to.
|
||||
tok, pos, lit = p.scanIgnoreWhitespace()
|
||||
if tok != IDENT && tok != STRING {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
|
||||
}
|
||||
stmt.User = lit
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
// parsePrivilege parses a string and returns a Privilege
|
||||
func (p *Parser) parsePrivilege() (Privilege, error) {
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
switch tok {
|
||||
case READ:
|
||||
return ReadPrivilege, nil
|
||||
case WRITE:
|
||||
return WritePrivilege, nil
|
||||
case ALL:
|
||||
// Consume optional PRIVILEGES token
|
||||
tok, pos, lit = p.scanIgnoreWhitespace()
|
||||
if tok != PRIVILEGES {
|
||||
p.unscan()
|
||||
}
|
||||
return AllPrivileges, nil
|
||||
}
|
||||
return 0, newParseError(tokstr(tok, lit), []string{"READ", "WRITE", "ALL [PRIVILEGES]"}, pos)
|
||||
}
|
||||
|
||||
// parseSelectStatement parses a select string and returns a Statement AST object.
|
||||
// This function assumes the SELECT token has already been consumed.
|
||||
func (p *Parser) parseSelectStatement() (*SelectStatement, error) {
|
||||
|
@ -479,6 +807,110 @@ func (p *Parser) parseCreateContinuousQueryStatement() (*CreateContinuousQuerySt
|
|||
return stmt, nil
|
||||
}
|
||||
|
||||
// parseCreateDatabaseStatement parses a string and returns a CreateDatabaseStatement.
|
||||
// This function assumes the "CREATE DATABASE" tokens have already been consumed.
|
||||
func (p *Parser) parseCreateDatabaseStatement() (*CreateDatabaseStatement, error) {
|
||||
stmt := &CreateDatabaseStatement{}
|
||||
|
||||
// Parse the name of the database to be created.
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok != IDENT && tok != STRING {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"identifier"}, pos)
|
||||
}
|
||||
stmt.Name = lit
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
// parseDropDatabaseStatement parses a string and returns a DropDatabaseStatement.
|
||||
// This function assumes the DROP DATABASE tokens have already been consumed.
|
||||
func (p *Parser) parseDropDatabaseStatement() (*DropDatabaseStatement, error) {
|
||||
stmt := &DropDatabaseStatement{}
|
||||
|
||||
// Parse the name of the database to be dropped.
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok != IDENT && tok != STRING {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"identifier"}, pos)
|
||||
}
|
||||
stmt.Name = lit
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
// parseCreateUserStatement parses a string and returns a CreateUserStatement.
|
||||
// This function assumes the "CREATE USER" tokens have already been consumed.
|
||||
func (p *Parser) parseCreateUserStatement() (*CreateUserStatement, error) {
|
||||
stmt := &CreateUserStatement{}
|
||||
|
||||
// Parse name of the user to be created.
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok != IDENT && tok != STRING {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
|
||||
}
|
||||
stmt.Name = lit
|
||||
|
||||
// Consume "WITH PASSWORD" tokens
|
||||
if err := p.parseTokens([]Token{WITH, PASSWORD}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse new user's password
|
||||
tok, pos, lit = p.scanIgnoreWhitespace()
|
||||
if tok != IDENT && tok != STRING {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"identifier", "string"}, pos)
|
||||
}
|
||||
stmt.Password = lit
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
// parseDropUserStatement parses a string and returns a DropUserStatement.
|
||||
// This function assumes the DROP USER tokens have already been consumed.
|
||||
func (p *Parser) parseDropUserStatement() (*DropUserStatement, error) {
|
||||
stmt := &DropUserStatement{}
|
||||
|
||||
// Parse the name of the user to be dropped.
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok != IDENT && tok != STRING {
|
||||
return nil, newParseError(tokstr(tok, lit), []string{"identifier"}, pos)
|
||||
}
|
||||
stmt.Name = lit
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
// parseRetentionPolicy parses a string and returns a retention policy name.
|
||||
// This function assumes the "WITH" token has already been consumed.
|
||||
func (p *Parser) parseRetentionPolicy() (name string, dfault bool, err error) {
|
||||
// Check for optional DEFAULT token.
|
||||
tok, pos, lit := p.scanIgnoreWhitespace()
|
||||
if tok == DEFAULT {
|
||||
dfault = true
|
||||
tok, pos, lit = p.scanIgnoreWhitespace()
|
||||
}
|
||||
|
||||
// Check for required RETENTION token.
|
||||
if tok != RETENTION {
|
||||
err = newParseError(tokstr(tok, lit), []string{"RETENTION"}, pos)
|
||||
return
|
||||
}
|
||||
|
||||
// Check of required POLICY token.
|
||||
if tok, pos, lit = p.scanIgnoreWhitespace(); tok != POLICY {
|
||||
err = newParseError(tokstr(tok, lit), []string{"POLICY"}, pos)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse retention policy name.
|
||||
tok, pos, name = p.scanIgnoreWhitespace()
|
||||
if tok != IDENT && tok != STRING {
|
||||
err = newParseError(tokstr(tok, name), []string{"identifier"}, pos)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// parseDropContinuousQueriesStatement parses a string and returns a DropContinuousQueryStatement.
|
||||
// This function assumes the "DROP CONTINUOUS" tokens have already been consumed.
|
||||
func (p *Parser) parseDropContinuousQueryStatement() (*DropContinuousQueryStatement, error) {
|
||||
|
@ -879,7 +1311,7 @@ func (p *Parser) parseUnaryExpr() (Expr, error) {
|
|||
return &NumberLiteral{Val: v}, nil
|
||||
case TRUE, FALSE:
|
||||
return &BooleanLiteral{Val: (tok == TRUE)}, nil
|
||||
case DURATION:
|
||||
case DURATION_VAL:
|
||||
v, _ := ParseDuration(lit)
|
||||
return &DurationLiteral{Val: v}, nil
|
||||
default:
|
||||
|
@ -1023,6 +1455,16 @@ func FormatDuration(d time.Duration) string {
|
|||
}
|
||||
}
|
||||
|
||||
// parseTokens consumes an expected sequence of tokens.
|
||||
func (p *Parser) parseTokens(toks []Token) error {
|
||||
for _, expected := range toks {
|
||||
if tok, pos, lit := p.scanIgnoreWhitespace(); tok != expected {
|
||||
return newParseError(tokstr(tok, lit), []string{tokens[expected]}, pos)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Quote returns a quoted string.
|
||||
func Quote(s string) string {
|
||||
return `"` + strings.NewReplacer("\n", `\n`, `\`, `\\`, `"`, `\"`).Replace(s) + `"`
|
||||
|
|
|
@ -290,12 +290,192 @@ func TestParser_ParseStatement(t *testing.T) {
|
|||
},
|
||||
},
|
||||
|
||||
// CREATE DATABASE statement
|
||||
{
|
||||
s: `CREATE DATABASE testdb`,
|
||||
stmt: &influxql.CreateDatabaseStatement{
|
||||
Name: "testdb",
|
||||
},
|
||||
},
|
||||
|
||||
// CREATE USER statement
|
||||
{
|
||||
s: `CREATE USER testuser WITH PASSWORD pwd1337`,
|
||||
stmt: &influxql.CreateUserStatement{
|
||||
Name: "testuser",
|
||||
Password: "pwd1337",
|
||||
},
|
||||
},
|
||||
|
||||
// DROP CONTINUOUS QUERY statement
|
||||
{
|
||||
s: `DROP CONTINUOUS QUERY myquery`,
|
||||
stmt: &influxql.DropContinuousQueryStatement{Name: "myquery"},
|
||||
},
|
||||
|
||||
// DROP DATABASE statement
|
||||
{
|
||||
s: `DROP DATABASE testdb`,
|
||||
stmt: &influxql.DropDatabaseStatement{Name: "testdb"},
|
||||
},
|
||||
|
||||
// DROP USER statement
|
||||
{
|
||||
s: `DROP USER jdoe`,
|
||||
stmt: &influxql.DropUserStatement{Name: "jdoe"},
|
||||
},
|
||||
|
||||
// GRANT READ
|
||||
{
|
||||
s: `GRANT READ ON testdb TO jdoe`,
|
||||
stmt: &influxql.GrantStatement{
|
||||
Privilege: influxql.ReadPrivilege,
|
||||
On: "testdb",
|
||||
User: "jdoe",
|
||||
},
|
||||
},
|
||||
|
||||
// GRANT WRITE
|
||||
{
|
||||
s: `GRANT WRITE ON testdb TO jdoe`,
|
||||
stmt: &influxql.GrantStatement{
|
||||
Privilege: influxql.WritePrivilege,
|
||||
On: "testdb",
|
||||
User: "jdoe",
|
||||
},
|
||||
},
|
||||
|
||||
// GRANT ALL
|
||||
{
|
||||
s: `GRANT ALL ON testdb TO jdoe`,
|
||||
stmt: &influxql.GrantStatement{
|
||||
Privilege: influxql.AllPrivileges,
|
||||
On: "testdb",
|
||||
User: "jdoe",
|
||||
},
|
||||
},
|
||||
|
||||
// GRANT ALL PRIVILEGES
|
||||
{
|
||||
s: `GRANT ALL PRIVILEGES ON testdb TO jdoe`,
|
||||
stmt: &influxql.GrantStatement{
|
||||
Privilege: influxql.AllPrivileges,
|
||||
On: "testdb",
|
||||
User: "jdoe",
|
||||
},
|
||||
},
|
||||
|
||||
// GRANT cluster admin
|
||||
{
|
||||
s: `GRANT ALL PRIVILEGES TO jdoe`,
|
||||
stmt: &influxql.GrantStatement{
|
||||
Privilege: influxql.AllPrivileges,
|
||||
User: "jdoe",
|
||||
},
|
||||
},
|
||||
|
||||
// REVOKE READ
|
||||
{
|
||||
s: `REVOKE READ on testdb FROM jdoe`,
|
||||
stmt: &influxql.RevokeStatement{
|
||||
Privilege: influxql.ReadPrivilege,
|
||||
On: "testdb",
|
||||
User: "jdoe",
|
||||
},
|
||||
},
|
||||
|
||||
// REVOKE WRITE
|
||||
{
|
||||
s: `REVOKE WRITE ON testdb FROM jdoe`,
|
||||
stmt: &influxql.RevokeStatement{
|
||||
Privilege: influxql.WritePrivilege,
|
||||
On: "testdb",
|
||||
User: "jdoe",
|
||||
},
|
||||
},
|
||||
|
||||
// REVOKE ALL
|
||||
{
|
||||
s: `REVOKE ALL ON testdb FROM jdoe`,
|
||||
stmt: &influxql.RevokeStatement{
|
||||
Privilege: influxql.AllPrivileges,
|
||||
On: "testdb",
|
||||
User: "jdoe",
|
||||
},
|
||||
},
|
||||
|
||||
// REVOKE ALL PRIVILEGES
|
||||
{
|
||||
s: `REVOKE ALL PRIVILEGES ON testdb FROM jdoe`,
|
||||
stmt: &influxql.RevokeStatement{
|
||||
Privilege: influxql.AllPrivileges,
|
||||
On: "testdb",
|
||||
User: "jdoe",
|
||||
},
|
||||
},
|
||||
|
||||
// REVOKE cluster admin
|
||||
{
|
||||
s: `REVOKE ALL FROM jdoe`,
|
||||
stmt: &influxql.RevokeStatement{
|
||||
Privilege: influxql.AllPrivileges,
|
||||
User: "jdoe",
|
||||
},
|
||||
},
|
||||
|
||||
// CREATE RETENTION POLICY
|
||||
{
|
||||
s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION 2`,
|
||||
stmt: &influxql.CreateRetentionPolicyStatement{
|
||||
Name: "policy1",
|
||||
DB: "testdb",
|
||||
Duration: time.Hour,
|
||||
Replication: 2,
|
||||
},
|
||||
},
|
||||
|
||||
// CREATE RETENTION POLICY ... DEFAULT
|
||||
{
|
||||
s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 2m REPLICATION 4 DEFAULT`,
|
||||
stmt: &influxql.CreateRetentionPolicyStatement{
|
||||
Name: "policy1",
|
||||
DB: "testdb",
|
||||
Duration: 2 * time.Minute,
|
||||
Replication: 4,
|
||||
Default: true,
|
||||
},
|
||||
},
|
||||
|
||||
// ALTER RETENTION POLICY
|
||||
{
|
||||
s: `ALTER RETENTION POLICY policy1 ON testdb DURATION 1m REPLICATION 4 DEFAULT`,
|
||||
stmt: newAlterRetentionPolicyStatement("policy1", "testdb", time.Minute, 4, true),
|
||||
},
|
||||
|
||||
// ALTER RETENTION POLICY with options in reverse order
|
||||
{
|
||||
s: `ALTER RETENTION POLICY policy1 ON testdb DEFAULT REPLICATION 4 DURATION 1m`,
|
||||
stmt: newAlterRetentionPolicyStatement("policy1", "testdb", time.Minute, 4, true),
|
||||
},
|
||||
|
||||
// ALTER RETENTION POLICY without optional DURATION
|
||||
{
|
||||
s: `ALTER RETENTION POLICY policy1 ON testdb DEFAULT REPLICATION 4`,
|
||||
stmt: newAlterRetentionPolicyStatement("policy1", "testdb", -1, 4, true),
|
||||
},
|
||||
|
||||
// ALTER RETENTION POLICY without optional REPLICATION
|
||||
{
|
||||
s: `ALTER RETENTION POLICY policy1 ON testdb DEFAULT`,
|
||||
stmt: newAlterRetentionPolicyStatement("policy1", "testdb", -1, -1, true),
|
||||
},
|
||||
|
||||
// ALTER RETENTION POLICY without optional DEFAULT
|
||||
{
|
||||
s: `ALTER RETENTION POLICY policy1 ON testdb REPLICATION 4`,
|
||||
stmt: newAlterRetentionPolicyStatement("policy1", "testdb", -1, 4, false),
|
||||
},
|
||||
|
||||
// Errors
|
||||
{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`},
|
||||
|
@ -319,10 +499,44 @@ func TestParser_ParseStatement(t *testing.T) {
|
|||
{s: `DELETE FROM myseries WHERE`, err: `found EOF, expected identifier, string, number, bool at line 1, char 28`},
|
||||
{s: `DROP SERIES`, err: `found EOF, expected identifier, string at line 1, char 13`},
|
||||
{s: `LIST CONTINUOUS`, err: `found EOF, expected QUERIES at line 1, char 17`},
|
||||
{s: `LIST FOO`, err: `found FOO, expected SERIES, CONTINUOUS at line 1, char 6`},
|
||||
{s: `LIST FOO`, err: `found FOO, expected SERIES, CONTINUOUS, MEASUREMENTS, TAG, FIELD 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 FOO`, err: `found FOO, expected SERIES, CONTINUOUS at line 1, char 6`},
|
||||
{s: `DROP DATABASE`, err: `found EOF, expected identifier at line 1, char 15`},
|
||||
{s: `DROP USER`, err: `found EOF, expected identifier at line 1, char 11`},
|
||||
{s: `CREATE USER testuser`, err: `found EOF, expected WITH at line 1, char 22`},
|
||||
{s: `GRANT`, err: `found EOF, expected READ, WRITE, ALL [PRIVILEGES] at line 1, char 7`},
|
||||
{s: `GRANT BOGUS`, err: `found BOGUS, expected READ, WRITE, ALL [PRIVILEGES] at line 1, char 7`},
|
||||
{s: `GRANT READ`, err: `found EOF, expected ON at line 1, char 12`},
|
||||
{s: `GRANT READ TO jdoe`, err: `found TO, expected ON at line 1, char 12`},
|
||||
{s: `GRANT READ ON`, err: `found EOF, expected identifier, string at line 1, char 15`},
|
||||
{s: `GRANT READ ON testdb`, err: `found EOF, expected TO at line 1, char 22`},
|
||||
{s: `GRANT READ ON testdb TO`, err: `found EOF, expected identifier, string at line 1, char 25`}, {s: `GRANT`, err: `found EOF, expected READ, WRITE, ALL [PRIVILEGES] at line 1, char 7`},
|
||||
{s: `REVOKE BOGUS`, err: `found BOGUS, expected READ, WRITE, ALL [PRIVILEGES] at line 1, char 8`},
|
||||
{s: `REVOKE READ`, err: `found EOF, expected ON at line 1, char 13`},
|
||||
{s: `REVOKE READ TO jdoe`, err: `found TO, expected ON at line 1, char 13`},
|
||||
{s: `REVOKE READ ON`, err: `found EOF, expected identifier, string at line 1, char 16`},
|
||||
{s: `REVOKE READ ON testdb`, err: `found EOF, expected FROM at line 1, char 23`},
|
||||
{s: `REVOKE READ ON testdb FROM`, err: `found EOF, expected identifier, string at line 1, char 28`},
|
||||
{s: `CREATE RETENTION`, err: `found EOF, expected POLICY at line 1, char 18`},
|
||||
{s: `CREATE RETENTION POLICY`, err: `found EOF, expected identifier at line 1, char 25`},
|
||||
{s: `CREATE RETENTION POLICY policy1`, err: `found EOF, expected ON at line 1, char 33`},
|
||||
{s: `CREATE RETENTION POLICY policy1 ON`, err: `found EOF, expected identifier at line 1, char 36`},
|
||||
{s: `CREATE RETENTION POLICY policy1 ON testdb`, err: `found EOF, expected DURATION at line 1, char 43`},
|
||||
{s: `CREATE RETENTION POLICY policy1 ON testdb DURATION`, err: `found EOF, expected duration at line 1, char 52`},
|
||||
{s: `CREATE RETENTION POLICY policy1 ON testdb DURATION bad`, err: `found bad, expected duration at line 1, char 52`},
|
||||
{s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h`, err: `found EOF, expected REPLICATION at line 1, char 54`},
|
||||
{s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION`, err: `found EOF, expected number at line 1, char 67`},
|
||||
{s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION 3.14`, err: `number must be an integer at line 1, char 67`},
|
||||
{s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION 0`, err: `invalid value 0: must be 1 <= n <= 2147483647 at line 1, char 67`},
|
||||
{s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION bad`, err: `found bad, expected number at line 1, char 67`},
|
||||
{s: `ALTER`, err: `found EOF, expected RETENTION at line 1, char 7`},
|
||||
{s: `ALTER RETENTION`, err: `found EOF, expected POLICY at line 1, char 17`},
|
||||
{s: `ALTER RETENTION POLICY`, err: `found EOF, expected identifier at line 1, char 24`},
|
||||
{s: `ALTER RETENTION POLICY policy1`, err: `found EOF, expected ON at line 1, char 32`},
|
||||
{s: `ALTER RETENTION POLICY policy1 ON`, err: `found EOF, expected identifier at line 1, char 35`},
|
||||
{s: `ALTER RETENTION POLICY policy1 ON testdb`, err: `found EOF, expected DURATION, RETENTION, DEFAULT at line 1, char 42`},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
|
@ -615,3 +829,22 @@ func errstring(err error) string {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// newAlterRetentionPolicyStatement creates an initialized AlterRetentionPolicyStatement.
|
||||
func newAlterRetentionPolicyStatement(name string, DB string, d time.Duration, replication int, dfault bool) *influxql.AlterRetentionPolicyStatement {
|
||||
stmt := &influxql.AlterRetentionPolicyStatement{
|
||||
Name: name,
|
||||
DB: DB,
|
||||
Default: dfault,
|
||||
}
|
||||
|
||||
if d > -1 {
|
||||
stmt.Duration = &d
|
||||
}
|
||||
|
||||
if replication > -1 {
|
||||
stmt.Replication = &replication
|
||||
}
|
||||
|
||||
return stmt
|
||||
}
|
||||
|
|
|
@ -221,7 +221,7 @@ func (s *Scanner) scanNumber() (tok Token, pos Pos, lit string) {
|
|||
// If the next rune is a duration unit (u,µ,ms,s) then return a duration token
|
||||
if ch0, _ := s.r.read(); ch0 == 'u' || ch0 == 'µ' || ch0 == 's' || ch0 == 'h' || ch0 == 'd' || ch0 == 'w' {
|
||||
_, _ = buf.WriteRune(ch0)
|
||||
return DURATION, pos, buf.String()
|
||||
return DURATION_VAL, pos, buf.String()
|
||||
} else if ch0 == 'm' {
|
||||
_, _ = buf.WriteRune(ch0)
|
||||
if ch1, _ := s.r.read(); ch1 == 's' {
|
||||
|
@ -229,7 +229,7 @@ func (s *Scanner) scanNumber() (tok Token, pos Pos, lit string) {
|
|||
} else {
|
||||
s.r.unread()
|
||||
}
|
||||
return DURATION, pos, buf.String()
|
||||
return DURATION_VAL, pos, buf.String()
|
||||
}
|
||||
s.r.unread()
|
||||
}
|
||||
|
|
|
@ -85,27 +85,37 @@ func TestScanner_Scan(t *testing.T) {
|
|||
{s: `10.3s`, tok: influxql.NUMBER, lit: `10.3`},
|
||||
|
||||
// Durations
|
||||
{s: `10u`, tok: influxql.DURATION, lit: `10u`},
|
||||
{s: `10µ`, tok: influxql.DURATION, lit: `10µ`},
|
||||
{s: `10ms`, tok: influxql.DURATION, lit: `10ms`},
|
||||
{s: `-1s`, tok: influxql.DURATION, lit: `-1s`},
|
||||
{s: `10m`, tok: influxql.DURATION, lit: `10m`},
|
||||
{s: `10h`, tok: influxql.DURATION, lit: `10h`},
|
||||
{s: `10d`, tok: influxql.DURATION, lit: `10d`},
|
||||
{s: `10w`, tok: influxql.DURATION, lit: `10w`},
|
||||
{s: `10u`, tok: influxql.DURATION_VAL, lit: `10u`},
|
||||
{s: `10µ`, tok: influxql.DURATION_VAL, lit: `10µ`},
|
||||
{s: `10ms`, tok: influxql.DURATION_VAL, lit: `10ms`},
|
||||
{s: `-1s`, tok: influxql.DURATION_VAL, lit: `-1s`},
|
||||
{s: `10m`, tok: influxql.DURATION_VAL, lit: `10m`},
|
||||
{s: `10h`, tok: influxql.DURATION_VAL, lit: `10h`},
|
||||
{s: `10d`, tok: influxql.DURATION_VAL, lit: `10d`},
|
||||
{s: `10w`, tok: influxql.DURATION_VAL, lit: `10w`},
|
||||
{s: `10x`, tok: influxql.NUMBER, lit: `10`}, // non-duration unit
|
||||
|
||||
// Keywords
|
||||
{s: `ALL`, tok: influxql.ALL},
|
||||
{s: `ALTER`, tok: influxql.ALTER},
|
||||
{s: `AS`, tok: influxql.AS},
|
||||
{s: `ASC`, tok: influxql.ASC},
|
||||
{s: `BY`, tok: influxql.BY},
|
||||
{s: `CREATE`, tok: influxql.CREATE},
|
||||
{s: `CONTINUOUS`, tok: influxql.CONTINUOUS},
|
||||
{s: `DATABASE`, tok: influxql.DATABASE},
|
||||
{s: `DEFAULT`, tok: influxql.DEFAULT},
|
||||
{s: `DELETE`, tok: influxql.DELETE},
|
||||
{s: `DESC`, tok: influxql.DESC},
|
||||
{s: `DROP`, tok: influxql.DROP},
|
||||
{s: `DURATION`, tok: influxql.DURATION},
|
||||
{s: `EXISTS`, tok: influxql.EXISTS},
|
||||
{s: `EXPLAIN`, tok: influxql.EXPLAIN},
|
||||
{s: `FIELD`, tok: influxql.FIELD},
|
||||
{s: `FROM`, tok: influxql.FROM},
|
||||
{s: `GRANT`, tok: influxql.GRANT},
|
||||
{s: `GROUP`, tok: influxql.GROUP},
|
||||
{s: `IF`, tok: influxql.IF},
|
||||
{s: `INNER`, tok: influxql.INNER},
|
||||
{s: `INSERT`, tok: influxql.INSERT},
|
||||
{s: `INTO`, tok: influxql.INTO},
|
||||
|
@ -114,13 +124,25 @@ func TestScanner_Scan(t *testing.T) {
|
|||
{s: `LIST`, tok: influxql.LIST},
|
||||
{s: `MEASUREMENT`, tok: influxql.MEASUREMENT},
|
||||
{s: `MEASUREMENTS`, tok: influxql.MEASUREMENTS},
|
||||
{s: `ON`, tok: influxql.ON},
|
||||
{s: `ORDER`, tok: influxql.ORDER},
|
||||
{s: `PASSWORD`, tok: influxql.PASSWORD},
|
||||
{s: `POLICY`, tok: influxql.POLICY},
|
||||
{s: `PRIVILEGES`, tok: influxql.PRIVILEGES},
|
||||
{s: `QUERIES`, tok: influxql.QUERIES},
|
||||
{s: `QUERY`, tok: influxql.QUERY},
|
||||
{s: `READ`, tok: influxql.READ},
|
||||
{s: `RETENTION`, tok: influxql.RETENTION},
|
||||
{s: `REVOKE`, tok: influxql.REVOKE},
|
||||
{s: `SELECT`, tok: influxql.SELECT},
|
||||
{s: `SERIES`, tok: influxql.SERIES},
|
||||
{s: `TAG`, tok: influxql.TAG},
|
||||
{s: `TO`, tok: influxql.TO},
|
||||
{s: `USER`, tok: influxql.USER},
|
||||
{s: `VALUES`, tok: influxql.VALUES},
|
||||
{s: `WHERE`, tok: influxql.WHERE},
|
||||
{s: `WITH`, tok: influxql.WITH},
|
||||
{s: `WRITE`, tok: influxql.WRITE},
|
||||
{s: `explain`, tok: influxql.EXPLAIN}, // case insensitive
|
||||
}
|
||||
|
||||
|
|
|
@ -15,14 +15,14 @@ const (
|
|||
|
||||
literal_beg
|
||||
// Literals
|
||||
IDENT // main
|
||||
NUMBER // 12345.67
|
||||
DURATION // 13h
|
||||
STRING // "abc"
|
||||
BADSTRING // "abc
|
||||
BADESCAPE // \q
|
||||
TRUE // true
|
||||
FALSE // false
|
||||
IDENT // main
|
||||
NUMBER // 12345.67
|
||||
DURATION_VAL // 13h
|
||||
STRING // "abc"
|
||||
BADSTRING // "abc
|
||||
BADESCAPE // \q
|
||||
TRUE // true
|
||||
FALSE // false
|
||||
literal_end
|
||||
|
||||
operator_beg
|
||||
|
@ -50,18 +50,26 @@ const (
|
|||
|
||||
keyword_beg
|
||||
// Keywords
|
||||
ALL
|
||||
ALTER
|
||||
AS
|
||||
ASC
|
||||
BY
|
||||
CREATE
|
||||
CONTINUOUS
|
||||
DATABASE
|
||||
DEFAULT
|
||||
DELETE
|
||||
DESC
|
||||
DROP
|
||||
DURATION
|
||||
EXISTS
|
||||
EXPLAIN
|
||||
FIELD
|
||||
FROM
|
||||
GRANT
|
||||
GROUP
|
||||
IF
|
||||
INNER
|
||||
INSERT
|
||||
INTO
|
||||
|
@ -70,14 +78,26 @@ const (
|
|||
LIST
|
||||
MEASUREMENT
|
||||
MEASUREMENTS
|
||||
ON
|
||||
ORDER
|
||||
PASSWORD
|
||||
POLICY
|
||||
PRIVILEGES
|
||||
QUERIES
|
||||
QUERY
|
||||
READ
|
||||
REPLICATION
|
||||
RETENTION
|
||||
REVOKE
|
||||
SELECT
|
||||
SERIES
|
||||
TAG
|
||||
TO
|
||||
USER
|
||||
VALUES
|
||||
WHERE
|
||||
WITH
|
||||
WRITE
|
||||
keyword_end
|
||||
)
|
||||
|
||||
|
@ -86,11 +106,12 @@ var tokens = [...]string{
|
|||
EOF: "EOF",
|
||||
WS: "WS",
|
||||
|
||||
IDENT: "IDENT",
|
||||
NUMBER: "NUMBER",
|
||||
STRING: "STRING",
|
||||
TRUE: "TRUE",
|
||||
FALSE: "FALSE",
|
||||
IDENT: "IDENT",
|
||||
NUMBER: "NUMBER",
|
||||
DURATION_VAL: "DURATION_VAL",
|
||||
STRING: "STRING",
|
||||
TRUE: "TRUE",
|
||||
FALSE: "FALSE",
|
||||
|
||||
ADD: "+",
|
||||
SUB: "-",
|
||||
|
@ -112,18 +133,26 @@ var tokens = [...]string{
|
|||
COMMA: ",",
|
||||
SEMICOLON: ";",
|
||||
|
||||
ALL: "ALL",
|
||||
ALTER: "ALTER",
|
||||
AS: "AS",
|
||||
ASC: "ASC",
|
||||
BY: "BY",
|
||||
CREATE: "CREATE",
|
||||
CONTINUOUS: "CONTINUOUS",
|
||||
DATABASE: "DATABASE",
|
||||
DEFAULT: "DEFAULT",
|
||||
DELETE: "DELETE",
|
||||
DESC: "DESC",
|
||||
DROP: "DROP",
|
||||
DURATION: "DURATION",
|
||||
EXISTS: "EXISTS",
|
||||
EXPLAIN: "EXPLAIN",
|
||||
FIELD: "FIELD",
|
||||
FROM: "FROM",
|
||||
GRANT: "GRANT",
|
||||
GROUP: "GROUP",
|
||||
IF: "IF",
|
||||
INNER: "INNER",
|
||||
INSERT: "INSERT",
|
||||
INTO: "INTO",
|
||||
|
@ -132,14 +161,26 @@ var tokens = [...]string{
|
|||
LIST: "LIST",
|
||||
MEASUREMENT: "MEASUREMENT",
|
||||
MEASUREMENTS: "MEASUREMENTS",
|
||||
ON: "ON",
|
||||
ORDER: "ORDER",
|
||||
PASSWORD: "PASSWORD",
|
||||
POLICY: "POLICY",
|
||||
PRIVILEGES: "PRIVILEGES",
|
||||
QUERIES: "QUERIES",
|
||||
QUERY: "QUERY",
|
||||
READ: "READ",
|
||||
REPLICATION: "REPLICATION",
|
||||
RETENTION: "RETENTION",
|
||||
REVOKE: "REVOKE",
|
||||
SELECT: "SELECT",
|
||||
SERIES: "SERIES",
|
||||
TAG: "TAG",
|
||||
TO: "TO",
|
||||
USER: "USER",
|
||||
VALUES: "VALUES",
|
||||
WHERE: "WHERE",
|
||||
WITH: "WITH",
|
||||
WRITE: "WRITE",
|
||||
}
|
||||
|
||||
var keywords map[string]Token
|
||||
|
|
Loading…
Reference in New Issue