package influxql import ( "bytes" "errors" "fmt" "io" "math" "regexp" "strconv" "strings" "time" ) const ( // DateFormat represents the format for date literals. DateFormat = "2006-01-02" // DateTimeFormat represents the format for date time literals. DateTimeFormat = "2006-01-02 15:04:05.999999" ) // Parser represents an InfluxQL parser. type Parser struct { s *bufScanner } // NewParser returns a new instance of Parser. func NewParser(r io.Reader) *Parser { return &Parser{s: newBufScanner(r)} } // ParseQuery parses a query string and returns its AST representation. func ParseQuery(s string) (*Query, error) { return NewParser(strings.NewReader(s)).ParseQuery() } // ParseStatement parses a statement string and returns its AST representation. func ParseStatement(s string) (Statement, error) { return NewParser(strings.NewReader(s)).ParseStatement() } // MustParseStatement parses a statement string and returns its AST. Panic on error. func MustParseStatement(s string) Statement { stmt, err := ParseStatement(s) if err != nil { panic(err.Error()) } return stmt } // ParseExpr parses an expression string and returns its AST representation. func ParseExpr(s string) (Expr, error) { return NewParser(strings.NewReader(s)).ParseExpr() } // ParseQuery parses an InfluxQL string and returns a Query AST object. func (p *Parser) ParseQuery() (*Query, error) { var statements Statements var semi bool for { if tok, _, _ := p.scanIgnoreWhitespace(); tok == EOF { return &Query{Statements: statements}, nil } else if !semi && tok == SEMICOLON { semi = true } else { p.unscan() s, err := p.ParseStatement() if err != nil { return nil, err } statements = append(statements, s) semi = false } } } // ParseStatement parses an InfluxQL string and returns a Statement AST object. func (p *Parser) ParseStatement() (Statement, error) { // Inspect the first token. tok, pos, lit := p.scanIgnoreWhitespace() switch tok { case SELECT: return p.parseSelectStatement(targetNotRequired) case DELETE: return p.parseDeleteStatement() case SHOW: return p.parseShowStatement() case CREATE: return p.parseCreateStatement() case DROP: return p.parseDropStatement() case GRANT: return p.parseGrantStatement() case REVOKE: return p.parseRevokeStatement() case ALTER: return p.parseAlterStatement() case SET: return p.parseSetPasswordUserStatement() default: return nil, newParseError(tokstr(tok, lit), []string{"SELECT", "DELETE", "SHOW", "CREATE", "DROP", "GRANT", "REVOKE", "ALTER", "SET"}, pos) } } // parseShowStatement parses a string and returns a list statement. // This function assumes the SHOW token has already been consumed. func (p *Parser) parseShowStatement() (Statement, error) { tok, pos, lit := p.scanIgnoreWhitespace() switch tok { case CONTINUOUS: return p.parseShowContinuousQueriesStatement() case GRANTS: return p.parseGrantsForUserStatement() case DATABASES: return p.parseShowDatabasesStatement() case SERVERS: return p.parseShowServersStatement() case FIELD: tok, pos, lit := p.scanIgnoreWhitespace() if tok == KEYS { return p.parseShowFieldKeysStatement() } return nil, newParseError(tokstr(tok, lit), []string{"KEYS", "VALUES"}, pos) case MEASUREMENTS: return p.parseShowMeasurementsStatement() case RETENTION: tok, pos, lit := p.scanIgnoreWhitespace() if tok == POLICIES { return p.parseShowRetentionPoliciesStatement() } return nil, newParseError(tokstr(tok, lit), []string{"POLICIES"}, pos) case SERIES: return p.parseShowSeriesStatement() case STATS: return p.parseShowStatsStatement() case DIAGNOSTICS: return p.parseShowDiagnosticsStatement() case TAG: tok, pos, lit := p.scanIgnoreWhitespace() if tok == KEYS { return p.parseShowTagKeysStatement() } else if tok == VALUES { return p.parseShowTagValuesStatement() } return nil, newParseError(tokstr(tok, lit), []string{"KEYS", "VALUES"}, pos) case USERS: return p.parseShowUsersStatement() } return nil, newParseError(tokstr(tok, lit), []string{"CONTINUOUS", "DATABASES", "FIELD", "GRANTS", "MEASUREMENTS", "RETENTION", "SERIES", "SERVERS", "TAG", "USERS"}, pos) } // parseCreateStatement parses a string and returns a create statement. // This function assumes the CREATE token has already been consumed. 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 == MEASUREMENT { return p.parseDropMeasurementStatement() } else if tok == CONTINUOUS { return p.parseDropContinuousQueryStatement() } else if tok == DATABASE { return p.parseDropDatabaseStatement() } else if tok == RETENTION { if tok, pos, lit := p.scanIgnoreWhitespace(); tok != POLICY { return nil, newParseError(tokstr(tok, lit), []string{"POLICY"}, pos) } return p.parseDropRetentionPolicyStatement() } else if tok == USER { return p.parseDropUserStatement() } return nil, newParseError(tokstr(tok, lit), []string{"SERIES", "CONTINUOUS", "MEASUREMENT"}, 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) } // parseSetPasswordUserStatement parses a string and returns a set statement. // This function assumes the SET token has already been consumed. func (p *Parser) parseSetPasswordUserStatement() (*SetPasswordUserStatement, error) { stmt := &SetPasswordUserStatement{} // Consume the required PASSWORD FOR tokens. if err := p.parseTokens([]Token{PASSWORD, FOR}); err != nil { return nil, err } // Parse username ident, err := p.parseIdent() if err != nil { return nil, err } stmt.Name = ident // Consume the required = token. if tok, pos, lit := p.scanIgnoreWhitespace(); tok != EQ { return nil, newParseError(tokstr(tok, lit), []string{"="}, pos) } // Parse new user's password if ident, err = p.parseString(); err != nil { return nil, err } stmt.Password = ident return stmt, nil } // 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.parseIdent() 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.parseIdent() if err != nil { return nil, err } stmt.Database = 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 consumed. func (p *Parser) parseAlterRetentionPolicyStatement() (*AlterRetentionPolicyStatement, error) { stmt := &AlterRetentionPolicyStatement{} // Parse the retention policy name. tok, pos, lit := p.scanIgnoreWhitespace() if tok == DEFAULT { stmt.Name = "default" } else if tok == IDENT { stmt.Name = lit } else { return nil, newParseError(tokstr(tok, lit), []string{"identifier"}, pos) } // 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.parseIdent() if err != nil { return nil, err } stmt.Database = ident // Loop through option tokens (DURATION, REPLICATION, 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 } // parseUInt32 parses a string and returns a 32-bit unsigned integer literal. func (p *Parser) parseUInt32() (uint32, error) { tok, pos, lit := p.scanIgnoreWhitespace() if tok != NUMBER { return 0, newParseError(tokstr(tok, lit), []string{"number"}, pos) } // Convert string to unsigned 32-bit integer n, err := strconv.ParseUint(lit, 10, 32) if err != nil { return 0, &ParseError{Message: err.Error(), Pos: pos} } return uint32(n), nil } // parseUInt64 parses a string and returns a 64-bit unsigned integer literal. func (p *Parser) parseUInt64() (uint64, error) { tok, pos, lit := p.scanIgnoreWhitespace() if tok != NUMBER { return 0, newParseError(tokstr(tok, lit), []string{"number"}, pos) } // Convert string to unsigned 64-bit integer n, err := strconv.ParseUint(lit, 10, 64) if err != nil { return 0, &ParseError{Message: err.Error(), Pos: pos} } return uint64(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 && tok != INF { return 0, newParseError(tokstr(tok, lit), []string{"duration"}, pos) } if tok == INF { return 0, nil } d, err := ParseDuration(lit) if err != nil { return 0, &ParseError{Message: err.Error(), Pos: pos} } return d, nil } // parseIdent parses an identifier. func (p *Parser) parseIdent() (string, error) { tok, pos, lit := p.scanIgnoreWhitespace() if tok != IDENT { return "", newParseError(tokstr(tok, lit), []string{"identifier"}, pos) } return lit, nil } // parseIdentList parses a comma delimited list of identifiers. func (p *Parser) parseIdentList() ([]string, error) { // Parse first (required) identifier. ident, err := p.parseIdent() if err != nil { return nil, err } idents := []string{ident} // Parse remaining (optional) identifiers. for { if tok, _, _ := p.scanIgnoreWhitespace(); tok != COMMA { p.unscan() return idents, nil } if ident, err = p.parseIdent(); err != nil { return nil, err } idents = append(idents, ident) } } // parseSegmentedIdents parses a segmented identifiers. // e.g., "db"."rp".measurement or "db"..measurement func (p *Parser) parseSegmentedIdents() ([]string, error) { ident, err := p.parseIdent() if err != nil { return nil, err } idents := []string{ident} // Parse remaining (optional) identifiers. for { if tok, _, _ := p.scan(); tok != DOT { // No more segments so we're done. p.unscan() break } if ch := p.peekRune(); ch == '/' { // Next segment is a regex so we're done. break } else if ch == '.' { // Add an empty identifier. idents = append(idents, "") continue } // Parse the next identifier. if ident, err = p.parseIdent(); err != nil { return nil, err } idents = append(idents, ident) } if len(idents) > 3 { msg := fmt.Sprintf("too many segments in %s", QuoteIdent(idents...)) return nil, &ParseError{Message: msg} } return idents, nil } // parserString parses a string. func (p *Parser) parseString() (string, error) { tok, pos, lit := p.scanIgnoreWhitespace() if tok != STRING { return "", newParseError(tokstr(tok, lit), []string{"string"}, pos) } return lit, nil } // parseRevokeStatement parses a string and returns a revoke statement. // This function assumes the REVOKE token has already been consumed. func (p *Parser) parseRevokeStatement() (*RevokeStatement, error) { stmt := &RevokeStatement{} // Parse the privilege to be revoked. 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 revoking a privilege to use. lit, err := p.parseIdent() if err != nil { return nil, err } 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 revoking the privilege from. lit, err = p.parseIdent() if err != nil { return nil, err } 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. lit, err := p.parseIdent() if err != nil { return nil, err } 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. lit, err = p.parseIdent() if err != nil { return nil, err } 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(tr targetRequirement) (*SelectStatement, error) { stmt := &SelectStatement{} var err error // Parse fields: "FIELD+". if stmt.Fields, err = p.parseFields(); err != nil { return nil, err } // Parse target: "INTO" if stmt.Target, err = p.parseTarget(tr); err != nil { return nil, err } // Parse source: "FROM". if tok, pos, lit := p.scanIgnoreWhitespace(); tok != FROM { return nil, newParseError(tokstr(tok, lit), []string{"FROM"}, pos) } if stmt.Sources, err = p.parseSources(); err != nil { return nil, err } // Parse condition: "WHERE EXPR". if stmt.Condition, err = p.parseCondition(); err != nil { return nil, err } // Parse dimensions: "GROUP BY DIMENSION+". if stmt.Dimensions, err = p.parseDimensions(); err != nil { return nil, err } // Parse fill options: "fill(