influxdb/influxql/ast.go

924 lines
23 KiB
Go
Raw Normal View History

2014-11-15 19:04:30 +00:00
package influxql
import (
"bytes"
"fmt"
"strconv"
"strings"
2014-11-15 19:04:30 +00:00
"time"
)
// DataType represents the primitive data types available in InfluxQL.
type DataType string
const (
2014-12-09 15:45:29 +00:00
Unknown = DataType("")
2014-11-22 04:12:48 +00:00
Number = DataType("number")
2014-11-15 19:04:30 +00:00
Boolean = DataType("boolean")
String = DataType("string")
Time = DataType("time")
Duration = DataType("duration")
)
2014-12-09 15:45:29 +00:00
// InspectDataType returns the data type of a given value.
func InspectDataType(v interface{}) DataType {
switch v.(type) {
case float64:
return Number
case bool:
return Boolean
case string:
return String
case time.Time:
return Time
case time.Duration:
return Duration
default:
return Unknown
}
}
2014-11-15 19:04:30 +00:00
// Node represents a node in the InfluxDB abstract syntax tree.
type Node interface {
node()
String() string
2014-11-15 19:04:30 +00:00
}
func (_ *Query) node() {}
func (_ Statements) node() {}
func (_ *SelectStatement) node() {}
func (_ *DeleteStatement) node() {}
func (_ *ListSeriesStatement) node() {}
func (_ *DropSeriesStatement) node() {}
func (_ *ListContinuousQueriesStatement) node() {}
2014-11-25 04:49:09 +00:00
func (_ *CreateContinuousQueryStatement) node() {}
func (_ *DropContinuousQueryStatement) node() {}
2014-11-15 19:04:30 +00:00
func (_ Fields) node() {}
func (_ *Field) node() {}
func (_ Dimensions) node() {}
func (_ *Dimension) node() {}
2014-11-22 04:12:48 +00:00
func (_ *Series) node() {}
func (_ *Join) node() {}
2014-11-22 04:12:48 +00:00
func (_ *Merge) node() {}
2014-11-15 19:04:30 +00:00
func (_ *VarRef) node() {}
func (_ *Call) node() {}
2014-11-22 04:12:48 +00:00
func (_ *NumberLiteral) node() {}
2014-11-15 19:04:30 +00:00
func (_ *StringLiteral) node() {}
func (_ *BooleanLiteral) node() {}
func (_ *TimeLiteral) node() {}
func (_ *DurationLiteral) node() {}
func (_ *BinaryExpr) node() {}
2014-11-25 06:12:32 +00:00
func (_ *ParenExpr) node() {}
2014-11-25 23:23:10 +00:00
func (_ *Wildcard) node() {}
2014-11-15 19:04:30 +00:00
2014-11-22 04:12:48 +00:00
// Query represents a collection of order statements.
type Query struct {
Statements Statements
2014-11-15 19:04:30 +00:00
}
// String returns a string representation of the query.
func (q *Query) String() string { return q.Statements.String() }
2014-11-22 04:12:48 +00:00
// Statements represents a list of statements.
type Statements []Statement
// String returns a string representation of the statements
func (a Statements) String() string {
var str []string
for _, stmt := range a {
str = append(str, stmt.String())
}
return strings.Join(str, ";\n")
}
2014-11-22 04:12:48 +00:00
// Statement represents a single command in InfluxQL.
type Statement interface {
Node
stmt()
}
func (_ *SelectStatement) stmt() {}
func (_ *DeleteStatement) stmt() {}
func (_ *ListSeriesStatement) stmt() {}
func (_ *DropSeriesStatement) stmt() {}
func (_ *ListContinuousQueriesStatement) stmt() {}
2014-11-25 04:49:09 +00:00
func (_ *CreateContinuousQueryStatement) stmt() {}
func (_ *DropContinuousQueryStatement) stmt() {}
2014-11-15 19:04:30 +00:00
// Expr represents an expression that can be evaluated to a value.
type Expr interface {
Node
expr()
}
func (_ *VarRef) expr() {}
func (_ *Call) expr() {}
2014-11-22 04:12:48 +00:00
func (_ *NumberLiteral) expr() {}
2014-11-15 19:04:30 +00:00
func (_ *StringLiteral) expr() {}
func (_ *BooleanLiteral) expr() {}
func (_ *TimeLiteral) expr() {}
func (_ *DurationLiteral) expr() {}
func (_ *BinaryExpr) expr() {}
2014-11-25 06:12:32 +00:00
func (_ *ParenExpr) expr() {}
2014-11-25 23:23:10 +00:00
func (_ *Wildcard) expr() {}
2014-11-15 19:04:30 +00:00
2014-11-22 04:12:48 +00:00
// Source represents a source of data for a statement.
type Source interface {
2014-11-15 19:04:30 +00:00
Node
2014-11-22 04:12:48 +00:00
source()
2014-11-15 19:04:30 +00:00
}
func (_ *Series) source() {}
func (_ *Join) source() {}
func (_ *Merge) source() {}
2014-11-15 19:04:30 +00:00
2014-11-22 04:12:48 +00:00
// SelectStatement represents a command for extracting data from the database.
type SelectStatement struct {
2014-11-15 19:04:30 +00:00
// Expressions returned from the selection.
Fields Fields
// Expressions used for grouping the selection.
Dimensions Dimensions
// Data source that fields are extracted from.
2014-11-22 04:12:48 +00:00
Source Source
2014-11-15 19:04:30 +00:00
// An expression evaluated on data point.
Condition Expr
// Maximum number of rows to be returned.
// Unlimited if zero.
Limit int
// Sort order.
Ascending bool
}
// String returns a string representation of the select statement.
func (s *SelectStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("SELECT ")
_, _ = buf.WriteString(s.Fields.String())
_, _ = buf.WriteString(" FROM ")
_, _ = buf.WriteString(s.Source.String())
if s.Condition != nil {
_, _ = buf.WriteString(" WHERE ")
_, _ = buf.WriteString(s.Condition.String())
}
if len(s.Dimensions) > 0 {
_, _ = buf.WriteString(" GROUP BY ")
_, _ = buf.WriteString(s.Dimensions.String())
}
if s.Limit > 0 {
_, _ = fmt.Fprintf(&buf, " LIMIT %d", s.Limit)
}
if s.Ascending {
_, _ = buf.WriteString(" ORDER BY ASC")
}
return buf.String()
}
2014-11-30 22:52:00 +00:00
// Aggregated returns true if the statement uses aggregate functions.
func (s *SelectStatement) Aggregated() bool {
var v bool
WalkFunc(s.Fields, func(n Node) {
if _, ok := n.(*Call); ok {
v = true
}
})
return v
}
2014-12-10 14:44:52 +00:00
/*
BinaryExpr
SELECT mean(xxx.value) + avg(yyy.value) FROM xxx JOIN yyy WHERE xxx.host = 123
from xxx where host = 123
select avg(value) from yyy where host = 123
SELECT xxx.value FROM xxx WHERE xxx.host = 123
SELECT yyy.value FROM yyy
---
SELECT MEAN(xxx.value) + MEAN(cpu.load.value)
FROM xxx JOIN yyy
GROUP BY host
WHERE (xxx.region == "uswest" OR yyy.region == "uswest") AND xxx.otherfield == "XXX"
select * from (
select mean + mean from xxx join yyy
group by time(5m), host
) (xxx.region == "uswest" OR yyy.region == "uswest") AND xxx.otherfield == "XXX"
(seriesIDS for xxx.region = 'uswest' union seriesIDs for yyy.regnion = 'uswest') | seriesIDS xxx.otherfield = 'XXX'
WHERE xxx.region == "uswest" AND xxx.otherfield == "XXX"
WHERE yyy.region == "uswest"
*/
// Substatement returns a single-series statement for a given variable reference.
func (s *SelectStatement) Substatement(ref *VarRef) (*SelectStatement, error) {
// Copy dimensions and properties to new statement.
other := &SelectStatement{
Fields: Fields{{Expr: ref}},
Dimensions: s.Dimensions,
Limit: s.Limit,
Ascending: s.Ascending,
}
// If there is only one series source then return it with the whole condition.
if _, ok := s.Source.(*Series); ok {
other.Source = s.Source
other.Condition = s.Condition
return other, nil
}
// Find the matching source.
name := MatchSource(s.Source, ref.Val)
if name == "" {
return nil, fmt.Errorf("field source not found: %s", ref.Val)
}
other.Source = &Series{Name: name}
// Filter out conditions.
if s.Condition != nil {
other.Condition = filterExprBySource(name, s.Condition)
}
return other, nil
}
// filters an expression to exclude expressions related to a source.
func filterExprBySource(name string, expr Expr) Expr {
switch expr := expr.(type) {
case *VarRef:
if !strings.HasPrefix(expr.Val, name) {
return nil
}
case *BinaryExpr:
lhs := filterExprBySource(name, expr.LHS)
rhs := filterExprBySource(name, expr.RHS)
// If an expr is logical then return either LHS/RHS or both.
// If an expr is arithmetic or comparative then require both sides.
if expr.Op == AND || expr.Op == OR {
if lhs == nil && rhs == nil {
return nil
} else if lhs != nil && rhs == nil {
return lhs
} else if lhs == nil && rhs != nil {
return rhs
}
} else {
if lhs == nil || rhs == nil {
return nil
}
}
case *ParenExpr:
if filterExprBySource(name, expr.Expr) == nil {
return nil
}
}
return expr
}
// MatchSource returns the source name that matches a field name.
// Returns a blank string if no sources match.
func MatchSource(src Source, name string) string {
switch src := src.(type) {
case *Series:
if strings.HasPrefix(name, src.Name) {
return src.Name
}
case *Join:
if str := MatchSource(src.LHS, name); str != "" {
return str
}
if str := MatchSource(src.RHS, name); str != "" {
return str
}
case *Merge:
if str := MatchSource(src.LHS, name); str != "" {
return str
}
if str := MatchSource(src.RHS, name); str != "" {
return str
}
}
return ""
}
2014-11-22 04:12:48 +00:00
// DeleteStatement represents a command for removing data from the database.
type DeleteStatement struct {
2014-11-15 19:04:30 +00:00
// Data source that values are removed from.
2014-11-22 04:12:48 +00:00
Source Source
2014-11-15 19:04:30 +00:00
// An expression evaluated on data point.
Condition Expr
}
// String returns a string representation of the delete statement.
func (s *DeleteStatement) String() string {
var buf bytes.Buffer
_, _ = buf.WriteString("DELETE ")
_, _ = buf.WriteString(s.Source.String())
if s.Condition != nil {
_, _ = buf.WriteString(" WHERE ")
_, _ = buf.WriteString(s.Condition.String())
}
return s.String()
}
// ListSeriesStatement represents a command for listing series in the database.
type ListSeriesStatement struct{}
// String returns a string representation of the list series statement.
func (s *ListSeriesStatement) String() string { return "LIST SERIES" }
// DropSeriesStatement represents a command for removing a series from the database.
type DropSeriesStatement struct {
Name string
}
// String returns a string representation of the drop series statement.
func (s *DropSeriesStatement) String() string { return fmt.Sprintf("DROP SERIES %s", s.Name) }
// ListContinuousQueriesStatement represents a command for listing continuous queries.
type ListContinuousQueriesStatement struct{}
// String returns a string representation of the list continuous queries statement.
func (s *ListContinuousQueriesStatement) String() string { return "LIST CONTINUOUS QUERIES" }
2014-11-25 04:49:09 +00:00
// CreateContinuousQueriesStatement represents a command for creating a continuous query.
type CreateContinuousQueryStatement struct {
Name string
Source *SelectStatement
Target string
}
// String returns a string representation of the statement.
func (s *CreateContinuousQueryStatement) String() string {
return fmt.Sprintf("CREATE CONTINUOUS QUERY %s AS %s INTO %s", s.Name, s.Source.String(), s.Target)
}
// DropContinuousQueriesStatement represents a command for removing a continuous query.
type DropContinuousQueryStatement struct {
Name string
}
// String returns a string representation of the statement.
func (s *DropContinuousQueryStatement) String() string {
return fmt.Sprintf("DROP CONTINUOUS QUERY %s", s.Name)
}
2014-11-15 19:04:30 +00:00
// Fields represents a list of fields.
type Fields []*Field
// String returns a string representation of the fields.
func (a Fields) String() string {
var str []string
for _, f := range a {
str = append(str, f.String())
}
return strings.Join(str, ", ")
}
2014-11-22 04:12:48 +00:00
// Field represents an expression retrieved from a select statement.
2014-11-15 19:04:30 +00:00
type Field struct {
Expr Expr
Alias string
2014-12-08 05:08:39 +00:00
}
// Name returns the name of the field. Returns alias, if set.
// Otherwise uses the function name or variable name.
func (f *Field) Name() string {
// Return alias, if set.
if f.Alias != "" {
return f.Alias
}
// Return the function name or variable name, if available.
switch expr := f.Expr.(type) {
case *Call:
return expr.Name
case *VarRef:
return expr.Val
}
// Otherwise return a blank name.
return ""
2014-11-15 19:04:30 +00:00
}
// String returns a string representation of the field.
func (f *Field) String() string {
if f.Alias == "" {
return f.Expr.String()
}
return fmt.Sprintf("%s AS %s", f.Expr.String(), QuoteIdent(f.Alias))
}
2014-11-15 19:04:30 +00:00
// Dimensions represents a list of dimensions.
type Dimensions []*Dimension
// String returns a string representation of the dimensions.
func (a Dimensions) String() string {
var str []string
for _, d := range a {
str = append(str, d.String())
}
return strings.Join(str, ", ")
}
2014-11-22 04:12:48 +00:00
// Dimension represents an expression that a select statement is grouped by.
2014-11-15 19:04:30 +00:00
type Dimension struct {
2014-11-22 04:12:48 +00:00
Expr Expr
2014-11-15 19:04:30 +00:00
}
// String returns a string representation of the dimension.
func (d *Dimension) String() string { return d.Expr.String() }
2014-11-22 04:12:48 +00:00
// Series represents a single series used as a datasource.
type Series struct {
Name string
2014-11-15 19:04:30 +00:00
}
// String returns a string representation of the series.
func (s *Series) String() string { return s.Name }
// Join represents two datasources joined together.
type Join struct {
LHS Source
RHS Source
}
// String returns a string representation of the join.
func (j *Join) String() string {
return fmt.Sprintf("%s JOIN %s", j.LHS.String(), j.RHS.String())
2014-11-15 19:04:30 +00:00
}
2014-11-22 04:12:48 +00:00
// Merge represents a datasource created by merging two datasources.
type Merge struct {
LHS Source
RHS Source
}
// String returns a string representation of the merge.
func (m *Merge) String() string {
return fmt.Sprintf("%s JOIN %s", m.LHS.String(), m.RHS.String())
2014-11-15 19:04:30 +00:00
}
2014-11-22 04:12:48 +00:00
// VarRef represents a reference to a variable.
2014-11-15 19:04:30 +00:00
type VarRef struct {
Val string
}
// String returns a string representation of the variable reference.
func (r *VarRef) String() string { return QuoteIdent(r.Val) }
2014-11-15 19:04:30 +00:00
// Call represents a function call.
type Call struct {
Name string
2014-11-25 03:43:23 +00:00
Args []Expr
2014-11-15 19:04:30 +00:00
}
// String returns a string representation of the call.
func (c *Call) String() string {
// Join arguments.
var str []string
for _, arg := range c.Args {
str = append(str, arg.String())
}
// Write function name and args.
return fmt.Sprintf("%s(%s)", c.Name, strings.Join(str, ", "))
}
2014-11-22 04:12:48 +00:00
// NumberLiteral represents a numeric literal.
type NumberLiteral struct {
2014-11-15 19:04:30 +00:00
Val float64
}
// String returns a string representation of the literal.
func (l *NumberLiteral) String() string { return strconv.FormatFloat(l.Val, 'f', 3, 64) }
2014-11-22 04:12:48 +00:00
// BooleanLiteral represents a boolean literal.
2014-11-15 19:04:30 +00:00
type BooleanLiteral struct {
Val bool
}
// String returns a string representation of the literal.
func (l *BooleanLiteral) String() string {
if l.Val {
return "true"
}
return "false"
}
2014-11-22 04:12:48 +00:00
// StringLiteral represents a string literal.
2014-11-15 19:04:30 +00:00
type StringLiteral struct {
Val string
}
// String returns a string representation of the literal.
func (l *StringLiteral) String() string { return Quote(l.Val) }
2014-11-22 04:12:48 +00:00
// TimeLiteral represents a point-in-time literal.
2014-11-15 19:04:30 +00:00
type TimeLiteral struct {
Val time.Time
}
// String returns a string representation of the literal.
2014-12-11 06:32:45 +00:00
func (l *TimeLiteral) String() string {
return `"` + l.Val.UTC().Format(TimeFormat) + `"`
2014-12-11 06:32:45 +00:00
}
2014-11-22 04:12:48 +00:00
// DurationLiteral represents a duration literal.
2014-11-15 19:04:30 +00:00
type DurationLiteral struct {
Val time.Duration
}
// String returns a string representation of the literal.
func (l *DurationLiteral) String() string { return FormatDuration(l.Val) }
2014-11-15 19:04:30 +00:00
// BinaryExpr represents an operation between two expressions.
type BinaryExpr struct {
Op Token
LHS Expr
RHS Expr
}
// String returns a string representation of the binary expression.
func (e *BinaryExpr) String() string {
return fmt.Sprintf("%s %s %s", e.LHS.String(), e.Op.String(), e.RHS.String())
}
2014-11-25 06:12:32 +00:00
// ParenExpr represents a parenthesized expression.
type ParenExpr struct {
Expr Expr
}
// String returns a string representation of the parenthesized expression.
func (e *ParenExpr) String() string { return fmt.Sprintf("(%s)", e.Expr.String()) }
2014-11-25 23:23:10 +00:00
// Wildcard represents a wild card expression.
type Wildcard struct{}
// String returns a string representation of the wildcard.
func (e *Wildcard) String() string { return "*" }
2014-11-25 22:43:22 +00:00
2014-12-11 06:32:45 +00:00
// Fold performs constant folding on an expression.
// The function, "now()", is expanded into the current time during folding.
2014-12-11 16:58:33 +00:00
func Fold(expr Expr, now *time.Time) Expr {
2014-12-11 06:32:45 +00:00
switch expr := expr.(type) {
case *Call:
// Replace "now()" with current time.
2014-12-11 16:58:33 +00:00
if strings.ToLower(expr.Name) == "now" && now != nil {
return &TimeLiteral{Val: *now}
2014-12-11 06:32:45 +00:00
}
// Fold call arguments.
for i, arg := range expr.Args {
expr.Args[i] = Fold(arg, now)
}
return expr
case *BinaryExpr:
// Fold and evaluate binary expression.
return foldBinaryExpr(expr, now)
case *ParenExpr:
// Fold inside expression.
// Return inside expression if not a binary expression.
expr.Expr = Fold(expr.Expr, now)
if _, ok := expr.Expr.(*BinaryExpr); !ok {
return expr.Expr
}
return expr
default:
return expr
}
}
// foldBinaryExpr performs constant folding if the binary expression has two literals.
2014-12-11 16:58:33 +00:00
func foldBinaryExpr(expr *BinaryExpr, now *time.Time) Expr {
2014-12-11 06:32:45 +00:00
// Fold both sides of binary expression first.
expr.LHS = Fold(expr.LHS, now)
expr.RHS = Fold(expr.RHS, now)
// Evaluate operations if both sides are the same type.
switch lhs := expr.LHS.(type) {
case *NumberLiteral:
if _, ok := expr.RHS.(*NumberLiteral); ok {
return foldNumberLiterals(expr)
}
case *BooleanLiteral:
if _, ok := expr.RHS.(*BooleanLiteral); ok {
return foldBooleanLiterals(expr)
}
case *TimeLiteral:
switch expr.RHS.(type) {
case *TimeLiteral:
return foldTimeLiterals(expr)
case *DurationLiteral:
return foldTimeDurationLiterals(expr)
}
case *DurationLiteral:
switch rhs := expr.RHS.(type) {
case *DurationLiteral:
return foldDurationLiterals(expr)
case *NumberLiteral:
return foldDurationNumberLiterals(expr)
case *TimeLiteral:
if expr.Op == ADD {
return &TimeLiteral{Val: rhs.Val.Add(lhs.Val)}
}
}
case *StringLiteral:
if rhs, ok := expr.RHS.(*StringLiteral); ok && expr.Op == ADD {
return &StringLiteral{Val: lhs.Val + rhs.Val}
}
}
return expr
}
// foldNumberLiterals performs constant folding on two number literals.
func foldNumberLiterals(expr *BinaryExpr) Expr {
lhs := expr.LHS.(*NumberLiteral)
rhs := expr.RHS.(*NumberLiteral)
switch expr.Op {
case ADD:
return &NumberLiteral{Val: lhs.Val + rhs.Val}
case SUB:
return &NumberLiteral{Val: lhs.Val - rhs.Val}
case MUL:
return &NumberLiteral{Val: lhs.Val * rhs.Val}
case DIV:
if rhs.Val == 0 {
return &NumberLiteral{Val: 0}
}
return &NumberLiteral{Val: lhs.Val / rhs.Val}
case EQ:
return &BooleanLiteral{Val: lhs.Val == rhs.Val}
case NEQ:
return &BooleanLiteral{Val: lhs.Val != rhs.Val}
case GT:
return &BooleanLiteral{Val: lhs.Val > rhs.Val}
case GTE:
return &BooleanLiteral{Val: lhs.Val >= rhs.Val}
case LT:
return &BooleanLiteral{Val: lhs.Val < rhs.Val}
case LTE:
return &BooleanLiteral{Val: lhs.Val <= rhs.Val}
default:
return expr
}
}
// foldBooleanLiterals performs constant folding on two boolean literals.
func foldBooleanLiterals(expr *BinaryExpr) Expr {
lhs := expr.LHS.(*BooleanLiteral)
rhs := expr.RHS.(*BooleanLiteral)
switch expr.Op {
case EQ:
return &BooleanLiteral{Val: lhs.Val == rhs.Val}
case NEQ:
return &BooleanLiteral{Val: lhs.Val != rhs.Val}
case AND:
return &BooleanLiteral{Val: lhs.Val && rhs.Val}
case OR:
return &BooleanLiteral{Val: lhs.Val || rhs.Val}
default:
return expr
}
}
// foldTimeLiterals performs constant folding on two time literals.
func foldTimeLiterals(expr *BinaryExpr) Expr {
lhs := expr.LHS.(*TimeLiteral)
rhs := expr.RHS.(*TimeLiteral)
switch expr.Op {
case SUB:
return &DurationLiteral{Val: lhs.Val.Sub(rhs.Val)}
case EQ:
return &BooleanLiteral{Val: lhs.Val.Equal(rhs.Val)}
case NEQ:
return &BooleanLiteral{Val: !lhs.Val.Equal(rhs.Val)}
case GT:
return &BooleanLiteral{Val: lhs.Val.After(rhs.Val)}
case GTE:
return &BooleanLiteral{Val: lhs.Val.After(rhs.Val) || lhs.Val.Equal(rhs.Val)}
case LT:
return &BooleanLiteral{Val: lhs.Val.Before(rhs.Val)}
case LTE:
return &BooleanLiteral{Val: lhs.Val.Before(rhs.Val) || lhs.Val.Equal(rhs.Val)}
default:
return expr
}
}
// foldTimeDurationLiterals performs constant folding on a time and duration literal.
func foldTimeDurationLiterals(expr *BinaryExpr) Expr {
lhs := expr.LHS.(*TimeLiteral)
rhs := expr.RHS.(*DurationLiteral)
switch expr.Op {
case ADD:
return &TimeLiteral{Val: lhs.Val.Add(rhs.Val)}
case SUB:
return &TimeLiteral{Val: lhs.Val.Add(-rhs.Val)}
default:
return expr
}
}
// foldDurationLiterals performs constant folding on two duration literals.
func foldDurationLiterals(expr *BinaryExpr) Expr {
lhs := expr.LHS.(*DurationLiteral)
rhs := expr.RHS.(*DurationLiteral)
switch expr.Op {
case ADD:
return &DurationLiteral{Val: lhs.Val + rhs.Val}
case SUB:
return &DurationLiteral{Val: lhs.Val - rhs.Val}
case EQ:
return &BooleanLiteral{Val: lhs.Val == rhs.Val}
case NEQ:
return &BooleanLiteral{Val: lhs.Val != rhs.Val}
case GT:
return &BooleanLiteral{Val: lhs.Val > rhs.Val}
case GTE:
return &BooleanLiteral{Val: lhs.Val >= rhs.Val}
case LT:
return &BooleanLiteral{Val: lhs.Val < rhs.Val}
case LTE:
return &BooleanLiteral{Val: lhs.Val <= rhs.Val}
default:
return expr
}
}
// foldDurationNumberLiterals performs constant folding on duration and number literal.
func foldDurationNumberLiterals(expr *BinaryExpr) Expr {
lhs := expr.LHS.(*DurationLiteral)
rhs := expr.RHS.(*NumberLiteral)
switch expr.Op {
case MUL:
return &DurationLiteral{Val: lhs.Val * time.Duration(rhs.Val)}
case DIV:
if rhs.Val == 0 {
return &DurationLiteral{Val: 0}
}
return &DurationLiteral{Val: lhs.Val / time.Duration(rhs.Val)}
default:
return expr
}
}
2014-11-15 19:04:30 +00:00
// Visitor can be called by Walk to traverse an AST hierarchy.
// The Visit() function is called once per node.
type Visitor interface {
Visit(Node) Visitor
}
// TimeRange returns the minimum and maximum times specified by an expression.
// Returns zero times for if there is no bound.
func TimeRange(expr Expr) (min, max time.Time) {
WalkFunc(expr, func(n Node) {
if n, ok := n.(*BinaryExpr); ok {
// Extract literal expression & operator on LHS.
// Check for "time" on the left-hand side first.
// Otherwise check for for the right-hand side and flip the operator.
value, op := timeExprValue(n.LHS, n.RHS), n.Op
if value.IsZero() {
if value = timeExprValue(n.RHS, n.LHS); value.IsZero() {
return
} else if op == LT {
op = GT
} else if op == LTE {
op = GTE
} else if op == GT {
op = LT
} else if op == GTE {
op = LTE
}
}
// Update the min/max depending on the operator.
// The GT & LT update the value by +/- 1µs not make them "not equal".
switch op {
case GT:
if min.IsZero() || value.After(min) {
min = value.Add(time.Microsecond)
}
case GTE:
if min.IsZero() || value.After(min) {
min = value
}
case LT:
if max.IsZero() || value.Before(max) {
max = value.Add(-time.Microsecond)
}
case LTE:
if max.IsZero() || value.Before(max) {
max = value
}
case EQ:
if min.IsZero() || value.After(min) {
min = value
}
if max.IsZero() || value.Before(max) {
max = value
}
}
}
})
return
}
// timeExprValue returns the time literal value of a "time == <TimeLiteral>" expression.
// Returns zero time if the expression is not a time expression.
func timeExprValue(ref Expr, lit Expr) time.Time {
if ref, ok := ref.(*VarRef); ok && strings.ToLower(ref.Val) == "time" {
if lit, ok := lit.(*TimeLiteral); ok {
return lit.Val
}
}
return time.Time{}
}
2014-11-15 19:04:30 +00:00
// Walk traverses a node hierarchy in depth-first order.
func Walk(v Visitor, node Node) {
if v = v.Visit(node); v == nil {
return
}
switch n := node.(type) {
2014-11-22 04:12:48 +00:00
case *Query:
Walk(v, n.Statements)
case Statements:
for _, s := range n {
Walk(v, s)
2014-11-15 19:04:30 +00:00
}
2014-11-22 04:12:48 +00:00
case *SelectStatement:
Walk(v, n.Fields)
Walk(v, n.Dimensions)
Walk(v, n.Source)
Walk(v, n.Condition)
2014-11-15 19:04:30 +00:00
case Fields:
for _, c := range n {
Walk(v, c)
}
case *Field:
Walk(v, n.Expr)
case Dimensions:
for _, c := range n {
Walk(v, c)
}
case *Dimension:
Walk(v, n.Expr)
case *BinaryExpr:
Walk(v, n.LHS)
Walk(v, n.RHS)
2014-11-25 06:12:32 +00:00
case *ParenExpr:
Walk(v, n.Expr)
2014-11-15 19:04:30 +00:00
case *Call:
2014-11-25 03:43:23 +00:00
for _, expr := range n.Args {
Walk(v, expr)
}
2014-11-15 19:04:30 +00:00
}
}
// WalkFunc traverses a node hierarchy in depth-first order.
func WalkFunc(node Node, fn func(Node)) {
Walk(walkFuncVisitor(fn), node)
}
type walkFuncVisitor func(Node)
func (fn walkFuncVisitor) Visit(n Node) Visitor { fn(n); return fn }