github.com/influxdata/influxdb/v2@v2.7.6/influxql/query/cursor.go (about) 1 package query 2 3 import ( 4 "math" 5 "time" 6 7 "github.com/influxdata/influxql" 8 ) 9 10 var NullFloat interface{} = (*float64)(nil) 11 12 // Series represents the metadata about a series. 13 type Series struct { 14 // Name is the measurement name. 15 Name string 16 17 // Tags for the series. 18 Tags Tags 19 20 // This is an internal id used to easily compare if a series is the 21 // same as another series. Whenever the internal cursor changes 22 // to a new series, this id gets incremented. It is not exposed to 23 // the user so we can implement this in whatever way we want. 24 // If a series is not generated by a cursor, this id is zero and 25 // it will instead attempt to compare the name and tags. 26 id uint64 27 } 28 29 // SameSeries checks if this is the same series as another one. 30 // It does not necessarily check for equality so this is different from 31 // checking to see if the name and tags are the same. It checks whether 32 // the two are part of the same series in the response. 33 func (s Series) SameSeries(other Series) bool { 34 if s.id != 0 && other.id != 0 { 35 return s.id == other.id 36 } 37 return s.Name == other.Name && s.Tags.ID() == other.Tags.ID() 38 } 39 40 // Equal checks to see if the Series are identical. 41 func (s Series) Equal(other Series) bool { 42 if s.id != 0 && other.id != 0 { 43 // If the ids are the same, then we can short-circuit and assume they 44 // are the same. If they are not the same, do the long check since 45 // they may still be identical, but not necessarily generated from 46 // the same cursor. 47 if s.id == other.id { 48 return true 49 } 50 } 51 return s.Name == other.Name && s.Tags.ID() == other.Tags.ID() 52 } 53 54 // Row represents a single row returned by the query engine. 55 type Row struct { 56 // Time returns the time for this row. If the cursor was created to 57 // return time as one of the values, the time will also be included as 58 // a time.Time in the appropriate column within Values. 59 // This ensures that time is always present in the Row structure 60 // even if it hasn't been requested in the output. 61 Time int64 62 63 // Series contains the series metadata for this row. 64 Series Series 65 66 // Values contains the values within the current row. 67 Values []interface{} 68 } 69 70 type Cursor interface { 71 // Scan will retrieve the next row and assign the result to 72 // the passed in Row. If the Row has not been initialized, the Cursor 73 // will initialize the Row. 74 // To increase speed and memory usage, the same Row can be used and 75 // the previous values will be overwritten while using the same memory. 76 Scan(row *Row) bool 77 78 // Stats returns the IteratorStats from the underlying iterators. 79 Stats() IteratorStats 80 81 // Err returns any errors that were encountered from scanning the rows. 82 Err() error 83 84 // Columns returns the column names and types. 85 Columns() []influxql.VarRef 86 87 // Close closes the underlying resources that the cursor is using. 88 Close() error 89 } 90 91 // RowCursor returns a Cursor that iterates over Rows. 92 func RowCursor(rows []Row, columns []influxql.VarRef) Cursor { 93 return &rowCursor{ 94 rows: rows, 95 columns: columns, 96 } 97 } 98 99 type rowCursor struct { 100 rows []Row 101 columns []influxql.VarRef 102 103 series Series 104 } 105 106 func (cur *rowCursor) Scan(row *Row) bool { 107 if len(cur.rows) == 0 { 108 return false 109 } 110 111 *row = cur.rows[0] 112 if row.Series.Name != cur.series.Name || !row.Series.Tags.Equals(&cur.series.Tags) { 113 cur.series.Name = row.Series.Name 114 cur.series.Tags = row.Series.Tags 115 cur.series.id++ 116 } 117 cur.rows = cur.rows[1:] 118 return true 119 } 120 121 func (cur *rowCursor) Stats() IteratorStats { 122 return IteratorStats{} 123 } 124 125 func (cur *rowCursor) Err() error { 126 return nil 127 } 128 129 func (cur *rowCursor) Columns() []influxql.VarRef { 130 return cur.columns 131 } 132 133 func (cur *rowCursor) Close() error { 134 return nil 135 } 136 137 type scannerFunc func(m map[string]interface{}) (int64, string, Tags) 138 139 type scannerCursorBase struct { 140 fields []influxql.Expr 141 m map[string]interface{} 142 143 series Series 144 columns []influxql.VarRef 145 loc *time.Location 146 147 scan scannerFunc 148 valuer influxql.ValuerEval 149 } 150 151 func newScannerCursorBase(scan scannerFunc, fields []*influxql.Field, loc *time.Location) scannerCursorBase { 152 typmap := FunctionTypeMapper{} 153 exprs := make([]influxql.Expr, len(fields)) 154 columns := make([]influxql.VarRef, len(fields)) 155 for i, f := range fields { 156 exprs[i] = f.Expr 157 columns[i] = influxql.VarRef{ 158 Val: f.Name(), 159 Type: influxql.EvalType(f.Expr, nil, typmap), 160 } 161 } 162 if loc == nil { 163 loc = time.UTC 164 } 165 166 m := make(map[string]interface{}) 167 return scannerCursorBase{ 168 fields: exprs, 169 m: m, 170 columns: columns, 171 loc: loc, 172 scan: scan, 173 valuer: influxql.ValuerEval{ 174 Valuer: influxql.MultiValuer( 175 MathValuer{}, 176 influxql.MapValuer(m), 177 ), 178 IntegerFloatDivision: true, 179 }, 180 } 181 } 182 183 func (cur *scannerCursorBase) Scan(row *Row) bool { 184 ts, name, tags := cur.scan(cur.m) 185 if ts == ZeroTime { 186 return false 187 } 188 189 row.Time = ts 190 if name != cur.series.Name || tags.ID() != cur.series.Tags.ID() { 191 cur.series.Name = name 192 cur.series.Tags = tags 193 cur.series.id++ 194 } 195 row.Series = cur.series 196 197 if len(cur.columns) > len(row.Values) { 198 row.Values = make([]interface{}, len(cur.columns)) 199 } 200 201 for i, expr := range cur.fields { 202 // A special case if the field is time to reduce memory allocations. 203 if ref, ok := expr.(*influxql.VarRef); ok && ref.Val == "time" { 204 row.Values[i] = time.Unix(0, row.Time).In(cur.loc) 205 continue 206 } 207 v := cur.valuer.Eval(expr) 208 if fv, ok := v.(float64); ok && math.IsNaN(fv) { 209 // If the float value is NaN, convert it to a null float 210 // so this can be serialized correctly, but not mistaken for 211 // a null value that needs to be filled. 212 v = NullFloat 213 } 214 row.Values[i] = v 215 } 216 return true 217 } 218 219 func (cur *scannerCursorBase) Columns() []influxql.VarRef { 220 return cur.columns 221 } 222 223 func (cur *scannerCursorBase) clear(m map[string]interface{}) { 224 for k := range m { 225 delete(m, k) 226 } 227 } 228 229 var _ Cursor = (*scannerCursor)(nil) 230 231 type scannerCursor struct { 232 scanner IteratorScanner 233 scannerCursorBase 234 } 235 236 func newScannerCursor(s IteratorScanner, fields []*influxql.Field, opt IteratorOptions) *scannerCursor { 237 cur := &scannerCursor{scanner: s} 238 cur.scannerCursorBase = newScannerCursorBase(cur.scan, fields, opt.Location) 239 return cur 240 } 241 242 func (s *scannerCursor) scan(m map[string]interface{}) (int64, string, Tags) { 243 ts, name, tags := s.scanner.Peek() 244 // if a new series, clear the map of previous values 245 if name != s.series.Name || tags.ID() != s.series.Tags.ID() { 246 s.clear(m) 247 } 248 if ts == ZeroTime { 249 return ts, name, tags 250 } 251 s.scanner.ScanAt(ts, name, tags, m) 252 return ts, name, tags 253 } 254 255 func (cur *scannerCursor) Stats() IteratorStats { 256 return cur.scanner.Stats() 257 } 258 259 func (cur *scannerCursor) Err() error { 260 return cur.scanner.Err() 261 } 262 263 func (cur *scannerCursor) Close() error { 264 return cur.scanner.Close() 265 } 266 267 var _ Cursor = (*multiScannerCursor)(nil) 268 269 type multiScannerCursor struct { 270 scanners []IteratorScanner 271 err error 272 ascending bool 273 scannerCursorBase 274 } 275 276 func newMultiScannerCursor(scanners []IteratorScanner, fields []*influxql.Field, opt IteratorOptions) *multiScannerCursor { 277 cur := &multiScannerCursor{ 278 scanners: scanners, 279 ascending: opt.Ascending, 280 } 281 cur.scannerCursorBase = newScannerCursorBase(cur.scan, fields, opt.Location) 282 return cur 283 } 284 285 func (cur *multiScannerCursor) scan(m map[string]interface{}) (ts int64, name string, tags Tags) { 286 ts = ZeroTime 287 for _, s := range cur.scanners { 288 curTime, curName, curTags := s.Peek() 289 if curTime == ZeroTime { 290 if err := s.Err(); err != nil { 291 cur.err = err 292 return ZeroTime, "", Tags{} 293 } 294 continue 295 } 296 297 if ts == ZeroTime { 298 ts, name, tags = curTime, curName, curTags 299 continue 300 } 301 302 if cur.ascending { 303 if (curName < name) || (curName == name && curTags.ID() < tags.ID()) || (curName == name && curTags.ID() == tags.ID() && curTime < ts) { 304 ts, name, tags = curTime, curName, curTags 305 } 306 continue 307 } 308 309 if (curName > name) || (curName == name && curTags.ID() > tags.ID()) || (curName == name && curTags.ID() == tags.ID() && curTime > ts) { 310 ts, name, tags = curTime, curName, curTags 311 } 312 } 313 314 if ts == ZeroTime { 315 return ts, name, tags 316 } 317 // if a new series, clear the map of previous values 318 if name != cur.series.Name || tags.ID() != cur.series.Tags.ID() { 319 cur.clear(m) 320 } 321 for _, s := range cur.scanners { 322 s.ScanAt(ts, name, tags, m) 323 } 324 return ts, name, tags 325 } 326 327 func (cur *multiScannerCursor) Stats() IteratorStats { 328 var stats IteratorStats 329 for _, s := range cur.scanners { 330 stats.Add(s.Stats()) 331 } 332 return stats 333 } 334 335 func (cur *multiScannerCursor) Err() error { 336 return cur.err 337 } 338 339 func (cur *multiScannerCursor) Close() error { 340 var err error 341 for _, s := range cur.scanners { 342 if e := s.Close(); e != nil && err == nil { 343 err = e 344 } 345 } 346 return err 347 } 348 349 type filterCursor struct { 350 Cursor 351 // fields holds the mapping of field names to the index in the row 352 // based off of the column metadata. This only contains the fields 353 // we need and will exclude the ones we do not. 354 fields map[string]IteratorMap 355 filter influxql.Expr 356 m map[string]interface{} 357 valuer influxql.ValuerEval 358 } 359 360 func newFilterCursor(cur Cursor, filter influxql.Expr) *filterCursor { 361 fields := make(map[string]IteratorMap) 362 for _, name := range influxql.ExprNames(filter) { 363 for i, col := range cur.Columns() { 364 if name.Val == col.Val { 365 fields[name.Val] = FieldMap{ 366 Index: i, 367 Type: name.Type, 368 } 369 break 370 } 371 } 372 373 // If the field is not a column, assume it is a tag value. 374 // We do not know what the tag values will be, but there really 375 // isn't any different between NullMap and a TagMap that's pointed 376 // at the wrong location for the purposes described here. 377 if _, ok := fields[name.Val]; !ok { 378 fields[name.Val] = TagMap(name.Val) 379 } 380 } 381 m := make(map[string]interface{}) 382 return &filterCursor{ 383 Cursor: cur, 384 fields: fields, 385 filter: filter, 386 m: m, 387 valuer: influxql.ValuerEval{Valuer: influxql.MapValuer(m)}, 388 } 389 } 390 391 func (cur *filterCursor) Scan(row *Row) bool { 392 for cur.Cursor.Scan(row) { 393 // Use the field mappings to prepare the map for the valuer. 394 for name, f := range cur.fields { 395 cur.m[name] = f.Value(row) 396 } 397 398 if cur.valuer.EvalBool(cur.filter) { 399 // Passes the filter! Return true. We no longer need to 400 // search for a suitable value. 401 return true 402 } 403 } 404 return false 405 } 406 407 type nullCursor struct { 408 columns []influxql.VarRef 409 } 410 411 func newNullCursor(fields []*influxql.Field) *nullCursor { 412 columns := make([]influxql.VarRef, len(fields)) 413 for i, f := range fields { 414 columns[i].Val = f.Name() 415 } 416 return &nullCursor{columns: columns} 417 } 418 419 func (cur *nullCursor) Scan(row *Row) bool { 420 return false 421 } 422 423 func (cur *nullCursor) Stats() IteratorStats { 424 return IteratorStats{} 425 } 426 427 func (cur *nullCursor) Err() error { 428 return nil 429 } 430 431 func (cur *nullCursor) Columns() []influxql.VarRef { 432 return cur.columns 433 } 434 435 func (cur *nullCursor) Close() error { 436 return nil 437 } 438 439 // DrainCursor will read and discard all values from a Cursor and return the error 440 // if one happens. 441 func DrainCursor(cur Cursor) error { 442 var row Row 443 for cur.Scan(&row) { 444 // Do nothing with the result. 445 } 446 return cur.Err() 447 }