github.com/influxdata/influxdb/v2@v2.7.6/influxql/query/subquery.go (about)

     1  package query
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/influxdata/influxql"
     7  )
     8  
     9  type subqueryBuilder struct {
    10  	ic   IteratorCreator
    11  	stmt *influxql.SelectStatement
    12  }
    13  
    14  // buildAuxIterator constructs an auxiliary Iterator from a subquery.
    15  func (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  
    42  func (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  
    56  func (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  
    94  func (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  }