package influxql import ( "bytes" "errors" "fmt" "io" "math" "regexp" "sort" "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 params map[string]interface{} } // NewParser returns a new instance of Parser. func NewParser(r io.Reader) *Parser { return &Parser{s: newBufScanner(r)} } // SetParams sets the parameters that will be used for any bound parameter substitutions. func (p *Parser) SetParams(params map[string]interface{}) { p.params = params } // 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() } // MustParseExpr parses an expression string and returns its AST. Panic on error. func MustParseExpr(s string) Expr { expr, err := ParseExpr(s) if err != nil { panic(err.Error()) } return expr } // ParseQuery parses an InfluxQL string and returns a Query AST object. func (p *Parser) ParseQuery() (*Query, error) { var statements Statements semi := true for { if tok, pos, lit := p.scanIgnoreWhitespace(); tok == EOF { return &Query{Statements: statements}, nil } else if tok == SEMICOLON { semi = true } else { if !semi { return nil, newParseError(tokstr(tok, lit), []string{";"}, pos) } 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() case KILL: return p.parseKillQueryStatement() default: return nil, newParseError(tokstr(tok, lit), []string{"SELECT", "DELETE", "SHOW", "CREATE", "DROP", "GRANT", "REVOKE", "ALTER", "SET", "KILL"}, 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 FIELD: tok, pos, lit := p.scanIgnoreWhitespace() if tok == KEYS { return p.parseShowFieldKeysStatement() } return nil, newParseError(tokstr(tok, lit), []string{"KEYS"}, pos) case MEASUREMENTS: return p.parseShowMeasurementsStatement() case QUERIES: return p.parseShowQueriesStatement() 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 SHARD: tok, pos, lit := p.scanIgnoreWhitespace() if tok == GROUPS { return p.parseShowShardGroupsStatement() } return nil, newParseError(tokstr(tok, lit), []string{"GROUPS"}, pos) case SHARDS: return p.parseShowShardsStatement() 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() case SUBSCRIPTIONS: return p.parseShowSubscriptionsStatement() } showQueryKeywords := []string{ "CONTINUOUS", "DATABASES", "FIELD", "GRANTS", "MEASUREMENTS", "QUERIES", "RETENTION", "SERIES", "TAG", "USERS", "STATS", "DIAGNOSTICS", "SHARD", "SHARDS", "SUBSCRIPTIONS", } sort.Strings(showQueryKeywords) return nil, newParseError(tokstr(tok, lit), showQueryKeywords, 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() } else if tok == SUBSCRIPTION { return p.parseCreateSubscriptionStatement() } return nil, newParseError(tokstr(tok, lit), []string{"CONTINUOUS", "DATABASE", "USER", "RETENTION", "SUBSCRIPTION"}, 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() switch tok { case CONTINUOUS: return p.parseDropContinuousQueryStatement() case DATABASE: return p.parseDropDatabaseStatement() case MEASUREMENT: return p.parseDropMeasurementStatement() case RETENTION: if tok, pos, lit := p.scanIgnoreWhitespace(); tok != POLICY { return nil, newParseError(tokstr(tok, lit), []string{"POLICY"}, pos) } return p.parseDropRetentionPolicyStatement() case SERIES: return p.parseDropSeriesStatement() case SHARD: return p.parseDropShardStatement() case SUBSCRIPTION: return p.parseDropSubscriptionStatement() case USER: return p.parseDropUserStatement() default: return nil, newParseError(tokstr(tok, lit), []string{"CONTINUOUS", "MEASUREMENT", "RETENTION", "SERIES", "SHARD", "SUBSCRIPTION", "USER"}, 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 } // parseKillQueryStatement parses a string and returns a kill statement. // This function assumes the KILL token has already been consumed. func (p *Parser) parseKillQueryStatement() (*KillQueryStatement, error) { if err := p.parseTokens([]Token{QUERY}); err != nil { return nil, err } qid, err := p.parseUInt64() if err != nil { return nil, err } var host string if tok, _, _ := p.scanIgnoreWhitespace(); tok == ON { host, err = p.parseIdent() if err != nil { return nil, err } } else { p.unscan() } return &KillQueryStatement{QueryID: qid, Host: host}, nil } // parseCreateSubscriptionStatement parses a string and returns a CreateSubscriptionStatement. // This function assumes the "CREATE SUBSCRIPTION" tokens have already been consumed. func (p *Parser) parseCreateSubscriptionStatement() (*CreateSubscriptionStatement, error) { stmt := &CreateSubscriptionStatement{} // Read the id of the subscription to create. ident, err := p.parseIdent() if err != nil { return nil, err } stmt.Name = ident // Expect an "ON" keyword. if tok, pos, lit := p.scanIgnoreWhitespace(); tok != ON { return nil, newParseError(tokstr(tok, lit), []string{"ON"}, pos) } // Read the name of the database. if ident, err = p.parseIdent(); err != nil { return nil, err } stmt.Database = ident if tok, pos, lit := p.scan(); tok != DOT { return nil, newParseError(tokstr(tok, lit), []string{"."}, pos) } // Read the name of the retention policy. if ident, err = p.parseIdent(); err != nil { return nil, err } stmt.RetentionPolicy = ident // Expect a "DESTINATIONS" keyword. if tok, pos, lit := p.scanIgnoreWhitespace(); tok != DESTINATIONS { return nil, newParseError(tokstr(tok, lit), []string{"DESTINATIONS"}, pos) } // Expect one of "ANY ALL" keywords. if tok, pos, lit := p.scanIgnoreWhitespace(); tok == ALL || tok == ANY { stmt.Mode = tokens[tok] } else { return nil, newParseError(tokstr(tok, lit), []string{"ALL", "ANY"}, pos) } // Read list of destinations. var destinations []string if destinations, err = p.parseStringList(); err != nil { return nil, err } stmt.Destinations = destinations 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. if tok, pos, lit := p.scanIgnoreWhitespace(); 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 SHARD token. if tok, _, _ := p.scanIgnoreWhitespace(); tok == SHARD { if tok, pos, lit := p.scanIgnoreWhitespace(); tok != DURATION { return nil, newParseError(tokstr(tok, lit), []string{"DURATION"}, pos) } // Check to see if they used the INF keyword tok, pos, _ := p.scanIgnoreWhitespace() if tok == INF { return nil, &ParseError{ Message: "invalid duration INF for shard duration", Pos: pos, } } p.unscan() d, err := p.parseDuration() if err != nil { return nil, err } stmt.ShardGroupDuration = d } else { p.unscan() } // Parse optional DEFAULT token. if tok, _, _ := 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, SHARD DURATION, DEFAULT, etc.). found := make(map[Token]struct{}) Loop: for { tok, pos, lit := p.scanIgnoreWhitespace() if _, ok := found[tok]; ok { return nil, &ParseError{ Message: fmt.Sprintf("found duplicate %s option", tok), Pos: pos, } } 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 SHARD: tok, pos, lit := p.scanIgnoreWhitespace() if tok == DURATION { // Check to see if they used the INF keyword tok, pos, _ := p.scanIgnoreWhitespace() if tok == INF { return nil, &ParseError{ Message: "invalid duration INF for shard duration", Pos: pos, } } p.unscan() d, err := p.parseDuration() if err != nil { return nil, err } stmt.ShardGroupDuration = &d } else { return nil, newParseError(tokstr(tok, lit), []string{"DURATION"}, pos) } case DEFAULT: stmt.Default = true default: if len(found) == 0 { return nil, newParseError(tokstr(tok, lit), []string{"DURATION", "REPLICATION", "SHARD", "DEFAULT"}, pos) } p.unscan() break Loop } found[tok] = struct{}{} } return stmt, nil } // parseInt parses a string representing a base 10 integer and returns the number. // It returns an error if the parsed number is outside the range [min, max]. func (p *Parser) parseInt(min, max int) (int, error) { tok, pos, lit := p.scanIgnoreWhitespace() if tok != INTEGER { return 0, newParseError(tokstr(tok, lit), []string{"integer"}, 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 } // 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 != INTEGER { return 0, newParseError(tokstr(tok, lit), []string{"integer"}, 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 != DURATIONVAL && 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 == ':' { // Next segment is context-specific so let caller handle it. 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 } // parseString 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 } // parseStringList parses a list of strings separated by commas. func (p *Parser) parseStringList() ([]string, error) { // Parse first (required) string. str, err := p.parseString() if err != nil { return nil, err } strs := []string{str} // Parse remaining (optional) strings. for { if tok, _, _ := p.scanIgnoreWhitespace(); tok != COMMA { p.unscan() return strs, nil } if str, err = p.parseString(); err != nil { return nil, err } strs = append(strs, str) } } // parseRevokeStatement parses a string and returns a revoke statement. // This function assumes the REVOKE token has already been consumed. func (p *Parser) parseRevokeStatement() (Statement, error) { // Parse the privilege to be revoked. priv, err := p.parsePrivilege() if err != nil { return nil, err } // Check for ON or FROM clauses. tok, pos, lit := p.scanIgnoreWhitespace() if tok == ON { stmt, err := p.parseRevokeOnStatement() if err != nil { return nil, err } stmt.Privilege = priv return stmt, nil } else if tok == FROM { // Admin privilege is only revoked on ALL PRIVILEGES. if priv != AllPrivileges { return nil, newParseError(tokstr(tok, lit), []string{"ON"}, pos) } return p.parseRevokeAdminStatement() } // Only ON or FROM clauses are allowed after privilege. if priv == AllPrivileges { return nil, newParseError(tokstr(tok, lit), []string{"ON", "FROM"}, pos) } return nil, newParseError(tokstr(tok, lit), []string{"ON"}, pos) } // parseRevokeOnStatement parses a string and returns a revoke statement. // This function assumes the [PRIVILEGE] ON tokens have already been consumed. func (p *Parser) parseRevokeOnStatement() (*RevokeStatement, error) { stmt := &RevokeStatement{} // Parse the name of the database. lit, err := p.parseIdent() if err != nil { return nil, err } stmt.On = lit // Parse FROM clause. tok, pos, lit := p.scanIgnoreWhitespace() // Check for required FROM token. if tok != FROM { return nil, newParseError(tokstr(tok, lit), []string{"FROM"}, pos) } // Parse the name of the user. lit, err = p.parseIdent() if err != nil { return nil, err } stmt.User = lit return stmt, nil } // parseRevokeAdminStatement parses a string and returns a revoke admin statement. // This function assumes the ALL [PRVILEGES] FROM token has already been consumed. func (p *Parser) parseRevokeAdminStatement() (*RevokeAdminStatement, error) { // Admin privilege is always false when revoke admin clause is called. stmt := &RevokeAdminStatement{} // Parse the name of the user. 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() (Statement, error) { // Parse the privilege to be granted. priv, err := p.parsePrivilege() if err != nil { return nil, err } // Check for ON or TO clauses. tok, pos, lit := p.scanIgnoreWhitespace() if tok == ON { stmt, err := p.parseGrantOnStatement() if err != nil { return nil, err } stmt.Privilege = priv return stmt, nil } else if tok == TO { // Admin privilege is only granted on ALL PRIVILEGES. if priv != AllPrivileges { return nil, newParseError(tokstr(tok, lit), []string{"ON"}, pos) } return p.parseGrantAdminStatement() } // Only ON or TO clauses are allowed after privilege. if priv == AllPrivileges { return nil, newParseError(tokstr(tok, lit), []string{"ON", "TO"}, pos) } return nil, newParseError(tokstr(tok, lit), []string{"ON"}, pos) } // parseGrantOnStatement parses a string and returns a grant statement. // This function assumes the [PRIVILEGE] ON tokens have already been consumed. func (p *Parser) parseGrantOnStatement() (*GrantStatement, error) { stmt := &GrantStatement{} // Parse the name of the database. lit, err := p.parseIdent() if err != nil { return nil, err } stmt.On = lit // Parse TO clause. tok, pos, lit := p.scanIgnoreWhitespace() // Check for required TO token. if tok != TO { return nil, newParseError(tokstr(tok, lit), []string{"TO"}, pos) } // Parse the name of the user. lit, err = p.parseIdent() if err != nil { return nil, err } stmt.User = lit return stmt, nil } // parseGrantAdminStatement parses a string and returns a grant admin statement. // This function assumes the ALL [PRVILEGES] TO tokens have already been consumed. func (p *Parser) parseGrantAdminStatement() (*GrantAdminStatement, error) { // Admin privilege is always true when grant admin clause is called. stmt := &GrantAdminStatement{} // Parse the name of the user. 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(true); 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(