1package query 2 3import ( 4 "context" 5 6 "github.com/influxdata/influxql" 7) 8 9type subqueryBuilder struct { 10 ic IteratorCreator 11 stmt *influxql.SelectStatement 12} 13 14// buildAuxIterator constructs an auxiliary Iterator from a subquery. 15func (b *subqueryBuilder) buildAuxIterator(ctx context.Context, opt IteratorOptions) (Iterator, error) { 16 // Map the desired auxiliary fields from the substatement. 17 indexes := b.mapAuxFields(opt.Aux) 18 19 subOpt, err := newIteratorOptionsSubstatement(ctx, b.stmt, opt) 20 if err != nil { 21 return nil, err 22 } 23 24 cur, err := buildCursor(ctx, b.stmt, b.ic, subOpt) 25 if err != nil { 26 return nil, err 27 } 28 29 // Filter the cursor by a condition if one was given. 30 if opt.Condition != nil { 31 cur = newFilterCursor(cur, opt.Condition) 32 } 33 34 // Construct the iterators for the subquery. 35 return NewIteratorMapper(cur, nil, indexes, subOpt), nil 36} 37 38func (b *subqueryBuilder) mapAuxFields(auxFields []influxql.VarRef) []IteratorMap { 39 indexes := make([]IteratorMap, len(auxFields)) 40 for i, name := range auxFields { 41 m := b.mapAuxField(&name) 42 if m == nil { 43 // If this field doesn't map to anything, use the NullMap so it 44 // shows up as null. 45 m = NullMap{} 46 } 47 indexes[i] = m 48 } 49 return indexes 50} 51 52func (b *subqueryBuilder) mapAuxField(name *influxql.VarRef) IteratorMap { 53 offset := 0 54 for i, f := range b.stmt.Fields { 55 if f.Name() == name.Val { 56 return FieldMap{ 57 Index: i + offset, 58 // Cast the result of the field into the desired type. 59 Type: name.Type, 60 } 61 } else if call, ok := f.Expr.(*influxql.Call); ok && (call.Name == "top" || call.Name == "bottom") { 62 // We may match one of the arguments in "top" or "bottom". 63 if len(call.Args) > 2 { 64 for j, arg := range call.Args[1 : len(call.Args)-1] { 65 if arg, ok := arg.(*influxql.VarRef); ok && arg.Val == name.Val { 66 return FieldMap{ 67 Index: i + j + 1, 68 Type: influxql.String, 69 } 70 } 71 } 72 // Increment the offset so we have the correct index for later fields. 73 offset += len(call.Args) - 2 74 } 75 } 76 } 77 78 // Unable to find this in the list of fields. 79 // Look within the dimensions and create a field if we find it. 80 for _, d := range b.stmt.Dimensions { 81 if d, ok := d.Expr.(*influxql.VarRef); ok && name.Val == d.Val { 82 return TagMap(d.Val) 83 } 84 } 85 86 // Unable to find any matches. 87 return nil 88} 89 90func (b *subqueryBuilder) buildVarRefIterator(ctx context.Context, expr *influxql.VarRef, opt IteratorOptions) (Iterator, error) { 91 // Look for the field or tag that is driving this query. 92 driver := b.mapAuxField(expr) 93 if driver == nil { 94 // Exit immediately if there is no driver. If there is no driver, there 95 // are no results. Period. 96 return nil, nil 97 } 98 99 // Map the auxiliary fields to their index in the subquery. 100 indexes := b.mapAuxFields(opt.Aux) 101 subOpt, err := newIteratorOptionsSubstatement(ctx, b.stmt, opt) 102 if err != nil { 103 return nil, err 104 } 105 106 cur, err := buildCursor(ctx, b.stmt, b.ic, subOpt) 107 if err != nil { 108 return nil, err 109 } 110 111 // Filter the cursor by a condition if one was given. 112 if opt.Condition != nil { 113 cur = newFilterCursor(cur, opt.Condition) 114 } 115 116 // Construct the iterators for the subquery. 117 return NewIteratorMapper(cur, driver, indexes, subOpt), nil 118} 119