203 lines
5.8 KiB
Go
203 lines
5.8 KiB
Go
package influxql
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/influxdata/flux"
|
|
"github.com/influxdata/flux/ast"
|
|
"github.com/influxdata/flux/execute"
|
|
"github.com/influxdata/flux/functions/transformations"
|
|
"github.com/influxdata/flux/semantic"
|
|
"github.com/influxdata/influxql"
|
|
)
|
|
|
|
// mapCursor holds the mapping of expressions to specific fields that happens at the end of
|
|
// the transpilation.
|
|
// TODO(jsternberg): This abstraction might be useful for subqueries, but we only need the id
|
|
// at the moment so just hold that.
|
|
type mapCursor struct {
|
|
id flux.OperationID
|
|
}
|
|
|
|
func (c *mapCursor) ID() flux.OperationID {
|
|
return c.id
|
|
}
|
|
|
|
func (c *mapCursor) Keys() []influxql.Expr {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
func (c *mapCursor) Value(expr influxql.Expr) (string, bool) {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// mapFields will take the list of symbols and maps each of the operations
|
|
// using the column names.
|
|
func (t *transpilerState) mapFields(in cursor) (cursor, error) {
|
|
columns := t.stmt.ColumnNames()
|
|
if len(columns) != len(t.stmt.Fields) {
|
|
// TODO(jsternberg): This scenario should not be possible. Replace the use of ColumnNames with a more
|
|
// statically verifiable list of columns when we process the fields from the select statement instead
|
|
// of doing this in the future.
|
|
panic("number of columns does not match the number of fields")
|
|
}
|
|
|
|
properties := make([]*semantic.Property, 0, len(t.stmt.Fields)+1)
|
|
properties = append(properties, &semantic.Property{
|
|
Key: &semantic.Identifier{Name: execute.DefaultTimeColLabel},
|
|
Value: &semantic.MemberExpression{
|
|
Object: &semantic.IdentifierExpression{
|
|
Name: "r",
|
|
},
|
|
Property: execute.DefaultTimeColLabel,
|
|
},
|
|
})
|
|
for i, f := range t.stmt.Fields {
|
|
if ref, ok := f.Expr.(*influxql.VarRef); ok && ref.Val == "time" {
|
|
// Skip past any time columns.
|
|
continue
|
|
}
|
|
value, err := t.mapField(f.Expr, in)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
properties = append(properties, &semantic.Property{
|
|
Key: &semantic.Identifier{Name: columns[i]},
|
|
Value: value,
|
|
})
|
|
}
|
|
id := t.op("map", &transformations.MapOpSpec{
|
|
Fn: &semantic.FunctionExpression{
|
|
Block: &semantic.FunctionBlock{
|
|
Parameters: &semantic.FunctionParameters{
|
|
List: []*semantic.FunctionParameter{{
|
|
Key: &semantic.Identifier{Name: "r"},
|
|
}},
|
|
},
|
|
Body: &semantic.ObjectExpression{
|
|
Properties: properties,
|
|
},
|
|
},
|
|
},
|
|
MergeKey: true,
|
|
}, in.ID())
|
|
return &mapCursor{id: id}, nil
|
|
}
|
|
|
|
func (t *transpilerState) mapField(expr influxql.Expr, in cursor) (semantic.Expression, error) {
|
|
if sym, ok := in.Value(expr); ok {
|
|
return &semantic.MemberExpression{
|
|
Object: &semantic.IdentifierExpression{
|
|
Name: "r",
|
|
},
|
|
Property: sym,
|
|
}, nil
|
|
}
|
|
|
|
switch expr := expr.(type) {
|
|
case *influxql.Call:
|
|
if isMathFunction(expr) {
|
|
return nil, fmt.Errorf("unimplemented math function: %q", expr.Name)
|
|
}
|
|
return nil, fmt.Errorf("missing symbol for %s", expr)
|
|
case *influxql.VarRef:
|
|
return nil, fmt.Errorf("missing symbol for %s", expr)
|
|
case *influxql.BinaryExpr:
|
|
return t.evalBinaryExpr(expr, in)
|
|
case *influxql.ParenExpr:
|
|
return t.mapField(expr.Expr, in)
|
|
case *influxql.StringLiteral:
|
|
if ts, err := expr.ToTimeLiteral(time.UTC); err == nil {
|
|
return &semantic.DateTimeLiteral{Value: ts.Val}, nil
|
|
}
|
|
return &semantic.StringLiteral{Value: expr.Val}, nil
|
|
case *influxql.NumberLiteral:
|
|
return &semantic.FloatLiteral{Value: expr.Val}, nil
|
|
case *influxql.IntegerLiteral:
|
|
return &semantic.IntegerLiteral{Value: expr.Val}, nil
|
|
case *influxql.BooleanLiteral:
|
|
return &semantic.BooleanLiteral{Value: expr.Val}, nil
|
|
case *influxql.DurationLiteral:
|
|
return &semantic.DurationLiteral{Value: expr.Val}, nil
|
|
case *influxql.TimeLiteral:
|
|
return &semantic.DateTimeLiteral{Value: expr.Val}, nil
|
|
case *influxql.RegexLiteral:
|
|
return &semantic.RegexpLiteral{Value: expr.Val}, nil
|
|
default:
|
|
// TODO(jsternberg): Handle the other expressions by turning them into
|
|
// an equivalent expression.
|
|
return nil, fmt.Errorf("unimplemented: %T", expr)
|
|
}
|
|
}
|
|
|
|
func (t *transpilerState) evalBinaryExpr(expr *influxql.BinaryExpr, in cursor) (semantic.Expression, error) {
|
|
fn := func() func(left, right semantic.Expression) semantic.Expression {
|
|
b := evalBuilder{}
|
|
switch expr.Op {
|
|
case influxql.EQ:
|
|
return b.eval(ast.EqualOperator)
|
|
case influxql.NEQ:
|
|
return b.eval(ast.NotEqualOperator)
|
|
case influxql.GT:
|
|
return b.eval(ast.GreaterThanOperator)
|
|
case influxql.GTE:
|
|
return b.eval(ast.GreaterThanEqualOperator)
|
|
case influxql.LT:
|
|
return b.eval(ast.LessThanOperator)
|
|
case influxql.LTE:
|
|
return b.eval(ast.LessThanEqualOperator)
|
|
case influxql.ADD:
|
|
return b.eval(ast.AdditionOperator)
|
|
case influxql.SUB:
|
|
return b.eval(ast.SubtractionOperator)
|
|
case influxql.AND:
|
|
return b.logical(ast.AndOperator)
|
|
case influxql.OR:
|
|
return b.logical(ast.OrOperator)
|
|
case influxql.EQREGEX:
|
|
return b.eval(ast.RegexpMatchOperator)
|
|
case influxql.NEQREGEX:
|
|
return b.eval(ast.NotRegexpMatchOperator)
|
|
default:
|
|
return nil
|
|
}
|
|
}()
|
|
if fn == nil {
|
|
return nil, fmt.Errorf("unimplemented binary expression: %s", expr.Op)
|
|
}
|
|
|
|
lhs, err := t.mapField(expr.LHS, in)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rhs, err := t.mapField(expr.RHS, in)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return fn(lhs, rhs), nil
|
|
}
|
|
|
|
// evalBuilder is used for namespacing the logical and eval wrapping functions.
|
|
type evalBuilder struct{}
|
|
|
|
func (evalBuilder) logical(op ast.LogicalOperatorKind) func(left, right semantic.Expression) semantic.Expression {
|
|
return func(left, right semantic.Expression) semantic.Expression {
|
|
return &semantic.LogicalExpression{
|
|
Operator: op,
|
|
Left: left,
|
|
Right: right,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (evalBuilder) eval(op ast.OperatorKind) func(left, right semantic.Expression) semantic.Expression {
|
|
return func(left, right semantic.Expression) semantic.Expression {
|
|
return &semantic.BinaryExpression{
|
|
Operator: op,
|
|
Left: left,
|
|
Right: right,
|
|
}
|
|
}
|
|
}
|