// derivativeInterval returns the time interval for the one (and only) derivative func func derivativeInterval(stmt *influxql.SelectStatement) (time.Duration, error) { if len(stmt.FunctionCalls()[0].Args) == 2 { return stmt.FunctionCalls()[0].Args[1].(*influxql.DurationLiteral).Val, nil } interval, err := stmt.GroupByInterval() if err != nil { return 0, err } if interval > 0 { return interval, nil } return time.Second, nil }
// CreateMappers will create a set of mappers that need to be run to execute the map phase of a MapReduceJob. func (tx *tx) CreateMapReduceJobs(stmt *influxql.SelectStatement, tagKeys []string) ([]*influxql.MapReduceJob, error) { jobs := []*influxql.MapReduceJob{} for _, src := range stmt.Sources { mm, ok := src.(*influxql.Measurement) if !ok { return nil, fmt.Errorf("invalid source type: %#v", src) } // get the index and the retention policy rp, err := tx.meta.RetentionPolicy(mm.Database, mm.RetentionPolicy) if err != nil { return nil, err } m := tx.store.Measurement(mm.Database, mm.Name) if m == nil { return nil, ErrMeasurementNotFound(influxql.QuoteIdent([]string{mm.Database, "", mm.Name}...)) } tx.measurement = m // Validate the fields and tags asked for exist and keep track of which are in the select vs the where var selectFields []string var whereFields []string var selectTags []string for _, n := range stmt.NamesInSelect() { if m.HasField(n) { selectFields = append(selectFields, n) continue } if !m.HasTagKey(n) { return nil, fmt.Errorf("unknown field or tag name in select clause: %s", n) } selectTags = append(selectTags, n) tagKeys = append(tagKeys, n) } for _, n := range stmt.NamesInWhere() { if n == "time" { continue } if m.HasField(n) { whereFields = append(whereFields, n) continue } if !m.HasTagKey(n) { return nil, fmt.Errorf("unknown field or tag name in where clause: %s", n) } } if len(selectFields) == 0 && len(stmt.FunctionCalls()) == 0 { return nil, fmt.Errorf("select statement must include at least one field or function call") } // Validate that group by is not a field for _, d := range stmt.Dimensions { switch e := d.Expr.(type) { case *influxql.VarRef: if !m.HasTagKey(e.Val) { return nil, fmt.Errorf("can not use field in group by clause: %s", e.Val) } } } // Grab time range from statement. tmin, tmax := influxql.TimeRange(stmt.Condition) if tmax.IsZero() { tmax = tx.now } if tmin.IsZero() { tmin = time.Unix(0, 0) } // Find shard groups within time range. var shardGroups []*meta.ShardGroupInfo for _, group := range rp.ShardGroups { if group.Overlaps(tmin, tmax) { g := group shardGroups = append(shardGroups, &g) } } if len(shardGroups) == 0 { return nil, nil } // get the group by interval, if there is one var interval int64 if d, err := stmt.GroupByInterval(); err != nil { return nil, err } else { interval = d.Nanoseconds() } // get the sorted unique tag sets for this query. tagSets, err := m.TagSets(stmt, tagKeys) if err != nil { return nil, err } for _, t := range tagSets { // make a job for each tagset job := &influxql.MapReduceJob{ MeasurementName: m.Name, TagSet: t, TMin: tmin.UnixNano(), TMax: tmax.UnixNano(), } // make a mapper for each shard that must be hit. We may need to hit multiple shards within a shard group var mappers []influxql.Mapper // create mappers for each shard we need to hit for _, sg := range shardGroups { // TODO: implement distributed queries if len(sg.Shards) != 1 { return nil, fmt.Errorf("distributed queries aren't supported yet. You have a replication policy with RF < # of servers in cluster") } shard := tx.store.Shard(sg.Shards[0].ID) if shard == nil { // the store returned nil which means we haven't written any data into this shard yet, so ignore it continue } // get the codec for this measuremnt. If this is nil it just means this measurement was // never written into this shard, so we can skip it and continue. codec := shard.FieldCodec(m.Name) if codec == nil { continue } var mapper influxql.Mapper mapper = &LocalMapper{ seriesKeys: t.SeriesKeys, shard: shard, db: shard.DB(), job: job, decoder: codec, filters: t.Filters, whereFields: whereFields, selectFields: selectFields, selectTags: selectTags, tmin: tmin.UnixNano(), tmax: tmax.UnixNano(), interval: interval, // multiple mappers may need to be merged together to get the results // for a raw query. So each mapper will have to read at least the // limit plus the offset in data points to ensure we've hit our mark limit: uint64(stmt.Limit) + uint64(stmt.Offset), } mappers = append(mappers, mapper) } job.Mappers = mappers jobs = append(jobs, job) } } // always return them in sorted order so the results from running the jobs are returned in a deterministic order sort.Sort(influxql.MapReduceJobs(jobs)) return jobs, nil }