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