2017-08-15 19:24:22 +00:00
|
|
|
package query
|
|
|
|
|
2017-10-11 14:08:31 +00:00
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
|
2017-10-30 21:40:26 +00:00
|
|
|
"github.com/influxdata/influxql"
|
2017-10-11 14:08:31 +00:00
|
|
|
)
|
2017-02-28 21:40:43 +00:00
|
|
|
|
|
|
|
type subqueryBuilder struct {
|
|
|
|
ic IteratorCreator
|
2017-08-15 19:24:22 +00:00
|
|
|
stmt *influxql.SelectStatement
|
2017-02-28 21:40:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// buildAuxIterator constructs an auxiliary Iterator from a subquery.
|
2017-10-11 14:08:31 +00:00
|
|
|
func (b *subqueryBuilder) buildAuxIterator(ctx context.Context, opt IteratorOptions) (Iterator, error) {
|
2017-02-28 21:40:43 +00:00
|
|
|
// Map the desired auxiliary fields from the substatement.
|
2018-03-30 21:58:37 +00:00
|
|
|
indexes := b.mapAuxFields(opt.Aux)
|
2018-03-27 19:56:27 +00:00
|
|
|
|
2018-02-27 23:10:10 +00:00
|
|
|
subOpt, err := newIteratorOptionsSubstatement(ctx, b.stmt, opt)
|
2017-02-28 21:40:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
Refactor the math engine to compile the query and use eval
This change makes it so that we simplify the math engine so it doesn't
use a complicated set of nested iterators. That way, we have to change
math in one fewer place.
It also greatly simplifies the query engine as now we can create the
necessary iterators, join them by time, name, and tags, and then use the
cursor interface to read them and use eval to compute the result. It
makes it so the auxiliary iterators and all of their complexity can be
removed.
This also makes use of the new eval functionality that was recently
added to the influxql package.
No math functions have been added, but the scaffolding has been included
so things like trigonometry functions are just a single commit away.
This also introduces a small breaking change. Because of the call
optimization, it is now possible to use the same selector multiple times
as a selector. So if you do this:
SELECT max(value) * 2, max(value) / 2 FROM cpu
This will now return the timestamp of the max value rather than zero
since this query is considered to have only a single selector rather
than multiple separate selectors. If any aspect of the selector is
different, such as different selector functions or different arguments,
it will consider the selectors to be aggregates like the old behavior.
2018-03-19 17:05:55 +00:00
|
|
|
cur, err := buildCursor(ctx, b.stmt, b.ic, subOpt)
|
2017-02-28 21:40:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-03-28 18:13:46 +00:00
|
|
|
// Filter the cursor by a condition if one was given.
|
2017-02-28 21:40:43 +00:00
|
|
|
if opt.Condition != nil {
|
2018-03-28 18:13:46 +00:00
|
|
|
cur = newFilterCursor(cur, opt.Condition)
|
2017-02-28 21:40:43 +00:00
|
|
|
}
|
2018-03-28 18:13:46 +00:00
|
|
|
|
|
|
|
// Construct the iterators for the subquery.
|
|
|
|
return NewIteratorMapper(cur, nil, indexes, subOpt), nil
|
2017-02-28 21:40:43 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 21:58:37 +00:00
|
|
|
func (b *subqueryBuilder) mapAuxFields(auxFields []influxql.VarRef) []IteratorMap {
|
|
|
|
indexes := make([]IteratorMap, len(auxFields))
|
2017-02-28 21:40:43 +00:00
|
|
|
for i, name := range auxFields {
|
|
|
|
m := b.mapAuxField(&name)
|
|
|
|
if m == nil {
|
|
|
|
// If this field doesn't map to anything, use the NullMap so it
|
|
|
|
// shows up as null.
|
2018-03-30 21:58:37 +00:00
|
|
|
m = NullMap{}
|
2017-02-28 21:40:43 +00:00
|
|
|
}
|
2018-03-30 21:58:37 +00:00
|
|
|
indexes[i] = m
|
2017-02-28 21:40:43 +00:00
|
|
|
}
|
2018-03-30 21:58:37 +00:00
|
|
|
return indexes
|
2017-02-28 21:40:43 +00:00
|
|
|
}
|
|
|
|
|
2017-08-15 19:24:22 +00:00
|
|
|
func (b *subqueryBuilder) mapAuxField(name *influxql.VarRef) IteratorMap {
|
2017-02-28 21:40:43 +00:00
|
|
|
offset := 0
|
|
|
|
for i, f := range b.stmt.Fields {
|
|
|
|
if f.Name() == name.Val {
|
2018-03-30 21:58:37 +00:00
|
|
|
return FieldMap{
|
|
|
|
Index: i + offset,
|
|
|
|
// Cast the result of the field into the desired type.
|
|
|
|
Type: name.Type,
|
|
|
|
}
|
2017-08-15 19:24:22 +00:00
|
|
|
} else if call, ok := f.Expr.(*influxql.Call); ok && (call.Name == "top" || call.Name == "bottom") {
|
2017-02-28 21:40:43 +00:00
|
|
|
// We may match one of the arguments in "top" or "bottom".
|
|
|
|
if len(call.Args) > 2 {
|
|
|
|
for j, arg := range call.Args[1 : len(call.Args)-1] {
|
2017-08-15 19:24:22 +00:00
|
|
|
if arg, ok := arg.(*influxql.VarRef); ok && arg.Val == name.Val {
|
2018-03-30 21:58:37 +00:00
|
|
|
return FieldMap{
|
|
|
|
Index: i + j + 1,
|
|
|
|
Type: influxql.String,
|
|
|
|
}
|
2017-02-28 21:40:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Increment the offset so we have the correct index for later fields.
|
|
|
|
offset += len(call.Args) - 2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unable to find this in the list of fields.
|
|
|
|
// Look within the dimensions and create a field if we find it.
|
|
|
|
for _, d := range b.stmt.Dimensions {
|
2017-08-15 19:24:22 +00:00
|
|
|
if d, ok := d.Expr.(*influxql.VarRef); ok && name.Val == d.Val {
|
2017-02-28 21:40:43 +00:00
|
|
|
return TagMap(d.Val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unable to find any matches.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-10-11 14:08:31 +00:00
|
|
|
func (b *subqueryBuilder) buildVarRefIterator(ctx context.Context, expr *influxql.VarRef, opt IteratorOptions) (Iterator, error) {
|
2017-02-28 21:40:43 +00:00
|
|
|
// Look for the field or tag that is driving this query.
|
|
|
|
driver := b.mapAuxField(expr)
|
|
|
|
if driver == nil {
|
|
|
|
// Exit immediately if there is no driver. If there is no driver, there
|
|
|
|
// are no results. Period.
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Map the auxiliary fields to their index in the subquery.
|
2018-03-30 21:58:37 +00:00
|
|
|
indexes := b.mapAuxFields(opt.Aux)
|
2018-02-27 23:10:10 +00:00
|
|
|
subOpt, err := newIteratorOptionsSubstatement(ctx, b.stmt, opt)
|
2017-02-28 21:40:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
Refactor the math engine to compile the query and use eval
This change makes it so that we simplify the math engine so it doesn't
use a complicated set of nested iterators. That way, we have to change
math in one fewer place.
It also greatly simplifies the query engine as now we can create the
necessary iterators, join them by time, name, and tags, and then use the
cursor interface to read them and use eval to compute the result. It
makes it so the auxiliary iterators and all of their complexity can be
removed.
This also makes use of the new eval functionality that was recently
added to the influxql package.
No math functions have been added, but the scaffolding has been included
so things like trigonometry functions are just a single commit away.
This also introduces a small breaking change. Because of the call
optimization, it is now possible to use the same selector multiple times
as a selector. So if you do this:
SELECT max(value) * 2, max(value) / 2 FROM cpu
This will now return the timestamp of the max value rather than zero
since this query is considered to have only a single selector rather
than multiple separate selectors. If any aspect of the selector is
different, such as different selector functions or different arguments,
it will consider the selectors to be aggregates like the old behavior.
2018-03-19 17:05:55 +00:00
|
|
|
cur, err := buildCursor(ctx, b.stmt, b.ic, subOpt)
|
2017-02-28 21:40:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-03-28 18:13:46 +00:00
|
|
|
// Filter the cursor by a condition if one was given.
|
2017-02-28 21:40:43 +00:00
|
|
|
if opt.Condition != nil {
|
2018-03-28 18:13:46 +00:00
|
|
|
cur = newFilterCursor(cur, opt.Condition)
|
2017-02-28 21:40:43 +00:00
|
|
|
}
|
2018-03-28 18:13:46 +00:00
|
|
|
|
|
|
|
// Construct the iterators for the subquery.
|
|
|
|
return NewIteratorMapper(cur, driver, indexes, subOpt), nil
|
2017-02-28 21:40:43 +00:00
|
|
|
}
|