github.com/influxdata/influxql@v1.1.0/ast_test.go (about) 1 package influxql_test 2 3 import ( 4 "fmt" 5 "go/importer" 6 "math" 7 "reflect" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/influxdata/influxql" 13 ) 14 15 func BenchmarkQuery_String(b *testing.B) { 16 p := influxql.NewParser(strings.NewReader(`SELECT foo AS zoo, a AS b FROM bar WHERE value > 10 AND q = 'hello'`)) 17 q, _ := p.ParseStatement() 18 for i := 0; i < b.N; i++ { 19 _ = q.String() 20 } 21 } 22 23 // Ensure a value's data type can be retrieved. 24 func TestInspectDataType(t *testing.T) { 25 for i, tt := range []struct { 26 v interface{} 27 typ influxql.DataType 28 }{ 29 {float64(100), influxql.Float}, 30 {int64(100), influxql.Integer}, 31 {int32(100), influxql.Integer}, 32 {100, influxql.Integer}, 33 {true, influxql.Boolean}, 34 {"string", influxql.String}, 35 {time.Now(), influxql.Time}, 36 {time.Second, influxql.Duration}, 37 {nil, influxql.Unknown}, 38 } { 39 if typ := influxql.InspectDataType(tt.v); tt.typ != typ { 40 t.Errorf("%d. %v (%s): unexpected type: %s", i, tt.v, tt.typ, typ) 41 continue 42 } 43 } 44 } 45 46 func TestDataTypeFromString(t *testing.T) { 47 for i, tt := range []struct { 48 s string 49 typ influxql.DataType 50 }{ 51 {s: "float", typ: influxql.Float}, 52 {s: "integer", typ: influxql.Integer}, 53 {s: "unsigned", typ: influxql.Unsigned}, 54 {s: "string", typ: influxql.String}, 55 {s: "boolean", typ: influxql.Boolean}, 56 {s: "time", typ: influxql.Time}, 57 {s: "duration", typ: influxql.Duration}, 58 {s: "tag", typ: influxql.Tag}, 59 {s: "field", typ: influxql.AnyField}, 60 {s: "foobar", typ: influxql.Unknown}, 61 } { 62 if typ := influxql.DataTypeFromString(tt.s); tt.typ != typ { 63 t.Errorf("%d. %s: unexpected type: %s != %s", i, tt.s, tt.typ, typ) 64 } 65 } 66 } 67 68 func TestDataType_String(t *testing.T) { 69 for i, tt := range []struct { 70 typ influxql.DataType 71 v string 72 }{ 73 {influxql.Float, "float"}, 74 {influxql.Integer, "integer"}, 75 {influxql.Boolean, "boolean"}, 76 {influxql.String, "string"}, 77 {influxql.Time, "time"}, 78 {influxql.Duration, "duration"}, 79 {influxql.Tag, "tag"}, 80 {influxql.Unknown, "unknown"}, 81 } { 82 if v := tt.typ.String(); tt.v != v { 83 t.Errorf("%d. %v (%s): unexpected string: %s", i, tt.typ, tt.v, v) 84 } 85 } 86 } 87 88 func TestDataType_LessThan(t *testing.T) { 89 for i, tt := range []struct { 90 typ influxql.DataType 91 other influxql.DataType 92 exp bool 93 }{ 94 {typ: influxql.Unknown, other: influxql.Unknown, exp: true}, 95 {typ: influxql.Unknown, other: influxql.Float, exp: true}, 96 {typ: influxql.Unknown, other: influxql.Integer, exp: true}, 97 {typ: influxql.Unknown, other: influxql.Unsigned, exp: true}, 98 {typ: influxql.Unknown, other: influxql.String, exp: true}, 99 {typ: influxql.Unknown, other: influxql.Boolean, exp: true}, 100 {typ: influxql.Unknown, other: influxql.Tag, exp: true}, 101 {typ: influxql.Float, other: influxql.Unknown, exp: false}, 102 {typ: influxql.Integer, other: influxql.Unknown, exp: false}, 103 {typ: influxql.Unsigned, other: influxql.Unknown, exp: false}, 104 {typ: influxql.String, other: influxql.Unknown, exp: false}, 105 {typ: influxql.Boolean, other: influxql.Unknown, exp: false}, 106 {typ: influxql.Tag, other: influxql.Unknown, exp: false}, 107 {typ: influxql.Float, other: influxql.Float, exp: false}, 108 {typ: influxql.Float, other: influxql.Integer, exp: false}, 109 {typ: influxql.Float, other: influxql.Unsigned, exp: false}, 110 {typ: influxql.Float, other: influxql.String, exp: false}, 111 {typ: influxql.Float, other: influxql.Boolean, exp: false}, 112 {typ: influxql.Float, other: influxql.Tag, exp: false}, 113 {typ: influxql.Integer, other: influxql.Float, exp: true}, 114 {typ: influxql.Integer, other: influxql.Integer, exp: false}, 115 {typ: influxql.Integer, other: influxql.Unsigned, exp: false}, 116 {typ: influxql.Integer, other: influxql.String, exp: false}, 117 {typ: influxql.Integer, other: influxql.Boolean, exp: false}, 118 {typ: influxql.Integer, other: influxql.Tag, exp: false}, 119 {typ: influxql.Unsigned, other: influxql.Float, exp: true}, 120 {typ: influxql.Unsigned, other: influxql.Integer, exp: true}, 121 {typ: influxql.Unsigned, other: influxql.Unsigned, exp: false}, 122 {typ: influxql.Unsigned, other: influxql.String, exp: false}, 123 {typ: influxql.Unsigned, other: influxql.Boolean, exp: false}, 124 {typ: influxql.Unsigned, other: influxql.Tag, exp: false}, 125 {typ: influxql.String, other: influxql.Float, exp: true}, 126 {typ: influxql.String, other: influxql.Integer, exp: true}, 127 {typ: influxql.String, other: influxql.Unsigned, exp: true}, 128 {typ: influxql.String, other: influxql.String, exp: false}, 129 {typ: influxql.String, other: influxql.Boolean, exp: false}, 130 {typ: influxql.String, other: influxql.Tag, exp: false}, 131 {typ: influxql.Boolean, other: influxql.Float, exp: true}, 132 {typ: influxql.Boolean, other: influxql.Integer, exp: true}, 133 {typ: influxql.Boolean, other: influxql.Unsigned, exp: true}, 134 {typ: influxql.Boolean, other: influxql.String, exp: true}, 135 {typ: influxql.Boolean, other: influxql.Boolean, exp: false}, 136 {typ: influxql.Boolean, other: influxql.Tag, exp: false}, 137 {typ: influxql.Tag, other: influxql.Float, exp: true}, 138 {typ: influxql.Tag, other: influxql.Integer, exp: true}, 139 {typ: influxql.Tag, other: influxql.Unsigned, exp: true}, 140 {typ: influxql.Tag, other: influxql.String, exp: true}, 141 {typ: influxql.Tag, other: influxql.Boolean, exp: true}, 142 {typ: influxql.Tag, other: influxql.Tag, exp: false}, 143 } { 144 if got, exp := tt.typ.LessThan(tt.other), tt.exp; got != exp { 145 t.Errorf("%d. %q.LessThan(%q) = %v; exp = %v", i, tt.typ, tt.other, got, exp) 146 } 147 } 148 } 149 150 // Ensure the SELECT statement can extract GROUP BY interval. 151 func TestSelectStatement_GroupByInterval(t *testing.T) { 152 q := "SELECT sum(value) from foo where time < now() GROUP BY time(10m)" 153 stmt, err := influxql.NewParser(strings.NewReader(q)).ParseStatement() 154 if err != nil { 155 t.Fatalf("invalid statement: %q: %s", stmt, err) 156 } 157 158 s := stmt.(*influxql.SelectStatement) 159 d, err := s.GroupByInterval() 160 if d != 10*time.Minute { 161 t.Fatalf("group by interval not equal:\nexp=%s\ngot=%s", 10*time.Minute, d) 162 } 163 if err != nil { 164 t.Fatalf("error parsing group by interval: %s", err.Error()) 165 } 166 } 167 168 // Ensure the SELECT statement can have its start and end time set 169 func TestSelectStatement_SetTimeRange(t *testing.T) { 170 q := "SELECT sum(value) from foo where time < now() GROUP BY time(10m)" 171 stmt, err := influxql.NewParser(strings.NewReader(q)).ParseStatement() 172 if err != nil { 173 t.Fatalf("invalid statement: %q: %s", stmt, err) 174 } 175 176 s := stmt.(*influxql.SelectStatement) 177 start := time.Now().Add(-20 * time.Hour).Round(time.Second).UTC() 178 end := time.Now().Add(10 * time.Hour).Round(time.Second).UTC() 179 s.SetTimeRange(start, end) 180 min, max := MustTimeRange(s.Condition) 181 182 if min != start { 183 t.Fatalf("start time wasn't set properly.\n exp: %s\n got: %s", start, min) 184 } 185 // the end range is actually one nanosecond before the given one since end is exclusive 186 end = end.Add(-time.Nanosecond) 187 if max != end { 188 t.Fatalf("end time wasn't set properly.\n exp: %s\n got: %s", end, max) 189 } 190 191 // ensure we can set a time on a select that already has one set 192 start = time.Now().Add(-20 * time.Hour).Round(time.Second).UTC() 193 end = time.Now().Add(10 * time.Hour).Round(time.Second).UTC() 194 q = fmt.Sprintf("SELECT sum(value) from foo WHERE time >= %ds and time <= %ds GROUP BY time(10m)", start.Unix(), end.Unix()) 195 stmt, err = influxql.NewParser(strings.NewReader(q)).ParseStatement() 196 if err != nil { 197 t.Fatalf("invalid statement: %q: %s", stmt, err) 198 } 199 200 s = stmt.(*influxql.SelectStatement) 201 min, max = MustTimeRange(s.Condition) 202 if start != min || end != max { 203 t.Fatalf("start and end times weren't equal:\n exp: %s\n got: %s\n exp: %s\n got:%s\n", start, min, end, max) 204 } 205 206 // update and ensure it saves it 207 start = time.Now().Add(-40 * time.Hour).Round(time.Second).UTC() 208 end = time.Now().Add(20 * time.Hour).Round(time.Second).UTC() 209 s.SetTimeRange(start, end) 210 min, max = MustTimeRange(s.Condition) 211 212 // TODO: right now the SetTimeRange can't override the start time if it's more recent than what they're trying to set it to. 213 // shouldn't matter for our purposes with continuous queries, but fix this later 214 215 if min != start { 216 t.Fatalf("start time wasn't set properly.\n exp: %s\n got: %s", start, min) 217 } 218 // the end range is actually one nanosecond before the given one since end is exclusive 219 end = end.Add(-time.Nanosecond) 220 if max != end { 221 t.Fatalf("end time wasn't set properly.\n exp: %s\n got: %s", end, max) 222 } 223 224 // ensure that when we set a time range other where clause conditions are still there 225 q = "SELECT sum(value) from foo WHERE foo = 'bar' and time < now() GROUP BY time(10m)" 226 stmt, err = influxql.NewParser(strings.NewReader(q)).ParseStatement() 227 if err != nil { 228 t.Fatalf("invalid statement: %q: %s", stmt, err) 229 } 230 231 s = stmt.(*influxql.SelectStatement) 232 233 // update and ensure it saves it 234 start = time.Now().Add(-40 * time.Hour).Round(time.Second).UTC() 235 end = time.Now().Add(20 * time.Hour).Round(time.Second).UTC() 236 s.SetTimeRange(start, end) 237 min, max = MustTimeRange(s.Condition) 238 239 if min != start { 240 t.Fatalf("start time wasn't set properly.\n exp: %s\n got: %s", start, min) 241 } 242 // the end range is actually one nanosecond before the given one since end is exclusive 243 end = end.Add(-time.Nanosecond) 244 if max != end { 245 t.Fatalf("end time wasn't set properly.\n exp: %s\n got: %s", end, max) 246 } 247 248 // ensure the where clause is there 249 hasWhere := false 250 influxql.WalkFunc(s.Condition, func(n influxql.Node) { 251 if ex, ok := n.(*influxql.BinaryExpr); ok { 252 if lhs, ok := ex.LHS.(*influxql.VarRef); ok { 253 if lhs.Val == "foo" { 254 if rhs, ok := ex.RHS.(*influxql.StringLiteral); ok { 255 if rhs.Val == "bar" { 256 hasWhere = true 257 } 258 } 259 } 260 } 261 } 262 }) 263 if !hasWhere { 264 t.Fatal("set time range cleared out the where clause") 265 } 266 } 267 268 func TestSelectStatement_HasWildcard(t *testing.T) { 269 var tests = []struct { 270 stmt string 271 wildcard bool 272 }{ 273 // No wildcards 274 { 275 stmt: `SELECT value FROM cpu`, 276 wildcard: false, 277 }, 278 279 // Query wildcard 280 { 281 stmt: `SELECT * FROM cpu`, 282 wildcard: true, 283 }, 284 285 // No GROUP BY wildcards 286 { 287 stmt: `SELECT value FROM cpu GROUP BY host`, 288 wildcard: false, 289 }, 290 291 // No GROUP BY wildcards, time only 292 { 293 stmt: `SELECT mean(value) FROM cpu where time < now() GROUP BY time(5ms)`, 294 wildcard: false, 295 }, 296 297 // GROUP BY wildcard 298 { 299 stmt: `SELECT value FROM cpu GROUP BY *`, 300 wildcard: true, 301 }, 302 303 // GROUP BY wildcard with time 304 { 305 stmt: `SELECT mean(value) FROM cpu where time < now() GROUP BY *,time(1m)`, 306 wildcard: true, 307 }, 308 309 // GROUP BY wildcard with explicit 310 { 311 stmt: `SELECT value FROM cpu GROUP BY *,host`, 312 wildcard: true, 313 }, 314 315 // GROUP BY multiple wildcards 316 { 317 stmt: `SELECT value FROM cpu GROUP BY *,*`, 318 wildcard: true, 319 }, 320 321 // Combo 322 { 323 stmt: `SELECT * FROM cpu GROUP BY *`, 324 wildcard: true, 325 }, 326 } 327 328 for i, tt := range tests { 329 // Parse statement. 330 stmt, err := influxql.NewParser(strings.NewReader(tt.stmt)).ParseStatement() 331 if err != nil { 332 t.Fatalf("invalid statement: %q: %s", tt.stmt, err) 333 } 334 335 // Test wildcard detection. 336 if w := stmt.(*influxql.SelectStatement).HasWildcard(); tt.wildcard != w { 337 t.Errorf("%d. %q: unexpected wildcard detection:\n\nexp=%v\n\ngot=%v\n\n", i, tt.stmt, tt.wildcard, w) 338 continue 339 } 340 } 341 } 342 343 // Test SELECT statement field rewrite. 344 func TestSelectStatement_RewriteFields(t *testing.T) { 345 var tests = []struct { 346 stmt string 347 rewrite string 348 err string 349 }{ 350 // No wildcards 351 { 352 stmt: `SELECT value FROM cpu`, 353 rewrite: `SELECT value FROM cpu`, 354 }, 355 356 // Query wildcard 357 { 358 stmt: `SELECT * FROM cpu`, 359 rewrite: `SELECT host::tag, region::tag, value1::float, value2::integer FROM cpu`, 360 }, 361 362 // Parser fundamentally prohibits multiple query sources 363 364 // Query wildcard with explicit 365 { 366 stmt: `SELECT *,value1 FROM cpu`, 367 rewrite: `SELECT host::tag, region::tag, value1::float, value2::integer, value1::float FROM cpu`, 368 }, 369 370 // Query multiple wildcards 371 { 372 stmt: `SELECT *,* FROM cpu`, 373 rewrite: `SELECT host::tag, region::tag, value1::float, value2::integer, host::tag, region::tag, value1::float, value2::integer FROM cpu`, 374 }, 375 376 // Query wildcards with group by 377 { 378 stmt: `SELECT * FROM cpu GROUP BY host`, 379 rewrite: `SELECT region::tag, value1::float, value2::integer FROM cpu GROUP BY host`, 380 }, 381 382 // No GROUP BY wildcards 383 { 384 stmt: `SELECT value FROM cpu GROUP BY host`, 385 rewrite: `SELECT value FROM cpu GROUP BY host`, 386 }, 387 388 // No GROUP BY wildcards, time only 389 { 390 stmt: `SELECT mean(value) FROM cpu where time < now() GROUP BY time(5ms)`, 391 rewrite: `SELECT mean(value) FROM cpu WHERE time < now() GROUP BY time(5ms)`, 392 }, 393 394 // GROUP BY wildcard 395 { 396 stmt: `SELECT value FROM cpu GROUP BY *`, 397 rewrite: `SELECT value FROM cpu GROUP BY host, region`, 398 }, 399 400 // GROUP BY wildcard with time 401 { 402 stmt: `SELECT mean(value) FROM cpu where time < now() GROUP BY *,time(1m)`, 403 rewrite: `SELECT mean(value) FROM cpu WHERE time < now() GROUP BY host, region, time(1m)`, 404 }, 405 406 // GROUP BY wildcard with fill 407 { 408 stmt: `SELECT mean(value) FROM cpu where time < now() GROUP BY *,time(1m) fill(0)`, 409 rewrite: `SELECT mean(value) FROM cpu WHERE time < now() GROUP BY host, region, time(1m) fill(0)`, 410 }, 411 412 // GROUP BY wildcard with explicit 413 { 414 stmt: `SELECT value FROM cpu GROUP BY *,host`, 415 rewrite: `SELECT value FROM cpu GROUP BY host, region, host`, 416 }, 417 418 // GROUP BY multiple wildcards 419 { 420 stmt: `SELECT value FROM cpu GROUP BY *,*`, 421 rewrite: `SELECT value FROM cpu GROUP BY host, region, host, region`, 422 }, 423 424 // Combo 425 { 426 stmt: `SELECT * FROM cpu GROUP BY *`, 427 rewrite: `SELECT value1::float, value2::integer FROM cpu GROUP BY host, region`, 428 }, 429 430 // Wildcard function with all fields. 431 { 432 stmt: `SELECT mean(*) FROM cpu`, 433 rewrite: `SELECT mean(value1::float) AS mean_value1, mean(value2::integer) AS mean_value2 FROM cpu`, 434 }, 435 436 { 437 stmt: `SELECT distinct(*) FROM strings`, 438 rewrite: `SELECT distinct(string::string) AS distinct_string, distinct(value::float) AS distinct_value FROM strings`, 439 }, 440 441 { 442 stmt: `SELECT distinct(*) FROM bools`, 443 rewrite: `SELECT distinct(bool::boolean) AS distinct_bool, distinct(value::float) AS distinct_value FROM bools`, 444 }, 445 446 // Wildcard function with some fields excluded. 447 { 448 stmt: `SELECT mean(*) FROM strings`, 449 rewrite: `SELECT mean(value::float) AS mean_value FROM strings`, 450 }, 451 452 { 453 stmt: `SELECT mean(*) FROM bools`, 454 rewrite: `SELECT mean(value::float) AS mean_value FROM bools`, 455 }, 456 457 // Wildcard function with an alias. 458 { 459 stmt: `SELECT mean(*) AS alias FROM cpu`, 460 rewrite: `SELECT mean(value1::float) AS alias_value1, mean(value2::integer) AS alias_value2 FROM cpu`, 461 }, 462 463 // Query regex 464 { 465 stmt: `SELECT /1/ FROM cpu`, 466 rewrite: `SELECT value1::float FROM cpu`, 467 }, 468 469 { 470 stmt: `SELECT value1 FROM cpu GROUP BY /h/`, 471 rewrite: `SELECT value1::float FROM cpu GROUP BY host`, 472 }, 473 474 // Query regex 475 { 476 stmt: `SELECT mean(/1/) FROM cpu`, 477 rewrite: `SELECT mean(value1::float) AS mean_value1 FROM cpu`, 478 }, 479 // Rewrite subquery 480 { 481 stmt: `SELECT * FROM (SELECT mean(value1) FROM cpu GROUP BY host) GROUP BY *`, 482 rewrite: `SELECT mean::float FROM (SELECT mean(value1::float) FROM cpu GROUP BY host) GROUP BY host`, 483 }, 484 485 // Invalid queries that can't be rewritten should return an error (to 486 // avoid a panic in the query engine) 487 { 488 stmt: `SELECT count(*) / 2 FROM cpu`, 489 err: `unsupported expression with wildcard: count(*) / 2`, 490 }, 491 492 { 493 stmt: `SELECT * / 2 FROM (SELECT count(*) FROM cpu)`, 494 err: `unsupported expression with wildcard: * / 2`, 495 }, 496 497 { 498 stmt: `SELECT count(/value/) / 2 FROM cpu`, 499 err: `unsupported expression with regex field: count(/value/) / 2`, 500 }, 501 502 // This one should be possible though since there's no wildcard in the 503 // binary expression. 504 { 505 stmt: `SELECT value1 + value2, * FROM cpu`, 506 rewrite: `SELECT value1::float + value2::integer, host::tag, region::tag, value1::float, value2::integer FROM cpu`, 507 }, 508 509 { 510 stmt: `SELECT value1 + value2, /value/ FROM cpu`, 511 rewrite: `SELECT value1::float + value2::integer, value1::float, value2::integer FROM cpu`, 512 }, 513 } 514 515 for i, tt := range tests { 516 // Parse statement. 517 stmt, err := influxql.NewParser(strings.NewReader(tt.stmt)).ParseStatement() 518 if err != nil { 519 t.Fatalf("invalid statement: %q: %s", tt.stmt, err) 520 } 521 522 var mapper FieldMapper 523 mapper.FieldDimensionsFn = func(m *influxql.Measurement) (fields map[string]influxql.DataType, dimensions map[string]struct{}, err error) { 524 switch m.Name { 525 case "cpu": 526 fields = map[string]influxql.DataType{ 527 "value1": influxql.Float, 528 "value2": influxql.Integer, 529 } 530 case "strings": 531 fields = map[string]influxql.DataType{ 532 "value": influxql.Float, 533 "string": influxql.String, 534 } 535 case "bools": 536 fields = map[string]influxql.DataType{ 537 "value": influxql.Float, 538 "bool": influxql.Boolean, 539 } 540 } 541 dimensions = map[string]struct{}{"host": struct{}{}, "region": struct{}{}} 542 return 543 } 544 545 // Rewrite statement. 546 rw, err := stmt.(*influxql.SelectStatement).RewriteFields(&mapper) 547 if tt.err != "" { 548 if err != nil && err.Error() != tt.err { 549 t.Errorf("%d. %q: unexpected error: %s != %s", i, tt.stmt, err.Error(), tt.err) 550 } else if err == nil { 551 t.Errorf("%d. %q: expected error", i, tt.stmt) 552 } 553 } else { 554 if err != nil { 555 t.Errorf("%d. %q: error: %s", i, tt.stmt, err) 556 } else if rw == nil && tt.err == "" { 557 t.Errorf("%d. %q: unexpected nil statement", i, tt.stmt) 558 } else if rw := rw.String(); tt.rewrite != rw { 559 t.Errorf("%d. %q: unexpected rewrite:\n\nexp=%s\n\ngot=%s\n\n", i, tt.stmt, tt.rewrite, rw) 560 } 561 } 562 } 563 } 564 565 // Test SELECT statement regex conditions rewrite. 566 func TestSelectStatement_RewriteRegexConditions(t *testing.T) { 567 var tests = []struct { 568 in string 569 out string 570 }{ 571 {in: `SELECT value FROM cpu`, out: `SELECT value FROM cpu`}, 572 {in: `SELECT value FROM cpu WHERE host = 'server-1'`, out: `SELECT value FROM cpu WHERE host = 'server-1'`}, 573 {in: `SELECT value FROM cpu WHERE host = 'server-1'`, out: `SELECT value FROM cpu WHERE host = 'server-1'`}, 574 {in: `SELECT value FROM cpu WHERE host != 'server-1'`, out: `SELECT value FROM cpu WHERE host != 'server-1'`}, 575 576 // Non matching regex 577 {in: `SELECT value FROM cpu WHERE host =~ /server-1|server-2|server-3/`, out: `SELECT value FROM cpu WHERE host =~ /server-1|server-2|server-3/`}, 578 {in: `SELECT value FROM cpu WHERE host =~ /server-1/`, out: `SELECT value FROM cpu WHERE host =~ /server-1/`}, 579 {in: `SELECT value FROM cpu WHERE host !~ /server-1/`, out: `SELECT value FROM cpu WHERE host !~ /server-1/`}, 580 {in: `SELECT value FROM cpu WHERE host =~ /^server-1/`, out: `SELECT value FROM cpu WHERE host =~ /^server-1/`}, 581 {in: `SELECT value FROM cpu WHERE host =~ /server-1$/`, out: `SELECT value FROM cpu WHERE host =~ /server-1$/`}, 582 {in: `SELECT value FROM cpu WHERE host !~ /\^server-1$/`, out: `SELECT value FROM cpu WHERE host !~ /\^server-1$/`}, 583 {in: `SELECT value FROM cpu WHERE host !~ /\^$/`, out: `SELECT value FROM cpu WHERE host !~ /\^$/`}, 584 {in: `SELECT value FROM cpu WHERE host !~ /^server-1\$/`, out: `SELECT value FROM cpu WHERE host !~ /^server-1\$/`}, 585 {in: `SELECT value FROM cpu WHERE host =~ /^\$/`, out: `SELECT value FROM cpu WHERE host =~ /^\$/`}, 586 {in: `SELECT value FROM cpu WHERE host !~ /^a/`, out: `SELECT value FROM cpu WHERE host !~ /^a/`}, 587 588 // These regexes are not supported due to the presence of escaped or meta characters. 589 {in: `SELECT value FROM cpu WHERE host !~ /^?a$/`, out: `SELECT value FROM cpu WHERE host !~ /^?a$/`}, 590 {in: `SELECT value FROM cpu WHERE host !~ /^a*$/`, out: `SELECT value FROM cpu WHERE host !~ /^a*$/`}, 591 {in: `SELECT value FROM cpu WHERE host !~ /^a.b$/`, out: `SELECT value FROM cpu WHERE host !~ /^a.b$/`}, 592 {in: `SELECT value FROM cpu WHERE host !~ /^ab+$/`, out: `SELECT value FROM cpu WHERE host !~ /^ab+$/`}, 593 594 // These regexes are not supported due to the presence of unsupported regex flags. 595 {in: `SELECT value FROM cpu WHERE host =~ /(?i)^SeRvEr01$/`, out: `SELECT value FROM cpu WHERE host =~ /(?i)^SeRvEr01$/`}, 596 597 // These regexes are not supported due to large character class(es). 598 {in: `SELECT value FROM cpu WHERE host =~ /^[^abcd]$/`, out: `SELECT value FROM cpu WHERE host =~ /^[^abcd]$/`}, 599 600 // These regexes all match and will be rewritten. 601 {in: `SELECT value FROM cpu WHERE host !~ /^a[2]$/`, out: `SELECT value FROM cpu WHERE host != 'a2'`}, 602 {in: `SELECT value FROM cpu WHERE host =~ /^server-1$/`, out: `SELECT value FROM cpu WHERE host = 'server-1'`}, 603 {in: `SELECT value FROM cpu WHERE host !~ /^server-1$/`, out: `SELECT value FROM cpu WHERE host != 'server-1'`}, 604 {in: `SELECT value FROM cpu WHERE host =~ /^server 1$/`, out: `SELECT value FROM cpu WHERE host = 'server 1'`}, 605 {in: `SELECT value FROM cpu WHERE host =~ /^$/`, out: `SELECT value FROM cpu WHERE host = ''`}, 606 {in: `SELECT value FROM cpu WHERE host !~ /^$/`, out: `SELECT value FROM cpu WHERE host != ''`}, 607 {in: `SELECT value FROM cpu WHERE host =~ /^server-1$/ OR host =~ /^server-2$/`, out: `SELECT value FROM cpu WHERE host = 'server-1' OR host = 'server-2'`}, 608 {in: `SELECT value FROM cpu WHERE host =~ /^server-1$/ OR host =~ /^server]a$/`, out: `SELECT value FROM cpu WHERE host = 'server-1' OR host = 'server]a'`}, 609 {in: `SELECT value FROM cpu WHERE host =~ /^hello\?$/`, out: `SELECT value FROM cpu WHERE host = 'hello?'`}, 610 {in: `SELECT value FROM cpu WHERE host !~ /^\\$/`, out: `SELECT value FROM cpu WHERE host != '\\'`}, 611 {in: `SELECT value FROM cpu WHERE host !~ /^\\\$$/`, out: `SELECT value FROM cpu WHERE host != '\\$'`}, 612 // This is supported, but annoying to write and the below queries satisfy this condition. 613 //{in: `SELECT value FROM cpu WHERE host =~ /^hello\world$/`, out: `SELECT value FROM cpu WHERE host =~ /^hello\world$/`}, 614 {in: `SELECT value FROM cpu WHERE host =~ /^(server-1|server-2|server-3)$/`, out: `SELECT value FROM cpu WHERE host = 'server-1' OR host = 'server-2' OR host = 'server-3'`}, 615 {in: `SELECT value FROM cpu WHERE host !~ /^(foo|bar)$/`, out: `SELECT value FROM cpu WHERE host != 'foo' AND host != 'bar'`}, 616 {in: `SELECT value FROM cpu WHERE host !~ /^\d$/`, out: `SELECT value FROM cpu WHERE host != '0' AND host != '1' AND host != '2' AND host != '3' AND host != '4' AND host != '5' AND host != '6' AND host != '7' AND host != '8' AND host != '9'`}, 617 {in: `SELECT value FROM cpu WHERE host !~ /^[a-z]$/`, out: `SELECT value FROM cpu WHERE host != 'a' AND host != 'b' AND host != 'c' AND host != 'd' AND host != 'e' AND host != 'f' AND host != 'g' AND host != 'h' AND host != 'i' AND host != 'j' AND host != 'k' AND host != 'l' AND host != 'm' AND host != 'n' AND host != 'o' AND host != 'p' AND host != 'q' AND host != 'r' AND host != 's' AND host != 't' AND host != 'u' AND host != 'v' AND host != 'w' AND host != 'x' AND host != 'y' AND host != 'z'`}, 618 619 {in: `SELECT value FROM cpu WHERE host =~ /^[ab]{3}$/`, out: `SELECT value FROM cpu WHERE host = 'aaa' OR host = 'aab' OR host = 'aba' OR host = 'abb' OR host = 'baa' OR host = 'bab' OR host = 'bba' OR host = 'bbb'`}, 620 } 621 622 for i, test := range tests { 623 stmt, err := influxql.NewParser(strings.NewReader(test.in)).ParseStatement() 624 if err != nil { 625 t.Fatalf("[Example %d], %v", i, err) 626 } 627 628 // Rewrite any supported regex conditions. 629 stmt.(*influxql.SelectStatement).RewriteRegexConditions() 630 631 // Get the expected rewritten statement. 632 expStmt, err := influxql.NewParser(strings.NewReader(test.out)).ParseStatement() 633 if err != nil { 634 t.Fatalf("[Example %d], %v", i, err) 635 } 636 637 // Compare the (potentially) rewritten AST to the expected AST. 638 if got, exp := stmt, expStmt; !reflect.DeepEqual(got, exp) { 639 t.Errorf("[Example %d]\nattempting %v\ngot %v\n%s\n\nexpected %v\n%s\n", i+1, test.in, got, mustMarshalJSON(got), exp, mustMarshalJSON(exp)) 640 } 641 } 642 } 643 644 // Test SELECT statement time field rewrite. 645 func TestSelectStatement_RewriteTimeFields(t *testing.T) { 646 var tests = []struct { 647 s string 648 stmt influxql.Statement 649 }{ 650 { 651 s: `SELECT time, field1 FROM cpu`, 652 stmt: &influxql.SelectStatement{ 653 IsRawQuery: true, 654 Fields: []*influxql.Field{ 655 {Expr: &influxql.VarRef{Val: "field1"}}, 656 }, 657 Sources: []influxql.Source{ 658 &influxql.Measurement{Name: "cpu"}, 659 }, 660 }, 661 }, 662 { 663 s: `SELECT time AS timestamp, field1 FROM cpu`, 664 stmt: &influxql.SelectStatement{ 665 IsRawQuery: true, 666 Fields: []*influxql.Field{ 667 {Expr: &influxql.VarRef{Val: "field1"}}, 668 }, 669 Sources: []influxql.Source{ 670 &influxql.Measurement{Name: "cpu"}, 671 }, 672 TimeAlias: "timestamp", 673 }, 674 }, 675 } 676 677 for i, tt := range tests { 678 // Parse statement. 679 stmt, err := influxql.NewParser(strings.NewReader(tt.s)).ParseStatement() 680 if err != nil { 681 t.Fatalf("invalid statement: %q: %s", tt.s, err) 682 } 683 684 // Rewrite statement. 685 stmt.(*influxql.SelectStatement).RewriteTimeFields() 686 if !reflect.DeepEqual(tt.stmt, stmt) { 687 t.Logf("\n# %s\nexp=%s\ngot=%s\n", tt.s, mustMarshalJSON(tt.stmt), mustMarshalJSON(stmt)) 688 t.Logf("\nSQL exp=%s\nSQL got=%s\n", tt.stmt.String(), stmt.String()) 689 t.Errorf("%d. %q\n\nstmt mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.s, tt.stmt, stmt) 690 } 691 } 692 } 693 694 // Ensure that the IsRawQuery flag gets set properly 695 func TestSelectStatement_IsRawQuerySet(t *testing.T) { 696 var tests = []struct { 697 stmt string 698 isRaw bool 699 }{ 700 { 701 stmt: "select * from foo", 702 isRaw: true, 703 }, 704 { 705 stmt: "select value1,value2 from foo", 706 isRaw: true, 707 }, 708 { 709 stmt: "select value1,value2 from foo, time(10m)", 710 isRaw: true, 711 }, 712 { 713 stmt: "select mean(value) from foo where time < now() group by time(5m)", 714 isRaw: false, 715 }, 716 { 717 stmt: "select mean(value) from foo group by bar", 718 isRaw: false, 719 }, 720 { 721 stmt: "select mean(value) from foo group by *", 722 isRaw: false, 723 }, 724 { 725 stmt: "select mean(value) from foo group by *", 726 isRaw: false, 727 }, 728 } 729 730 for _, tt := range tests { 731 s := MustParseSelectStatement(tt.stmt) 732 if s.IsRawQuery != tt.isRaw { 733 t.Errorf("'%s', IsRawQuery should be %v", tt.stmt, tt.isRaw) 734 } 735 } 736 } 737 738 // Ensure binary expression names can be evaluated. 739 func TestBinaryExprName(t *testing.T) { 740 for i, tt := range []struct { 741 expr string 742 name string 743 }{ 744 {expr: `value + 1`, name: `value`}, 745 {expr: `"user" / total`, name: `user_total`}, 746 {expr: `("user" + total) / total`, name: `user_total_total`}, 747 } { 748 expr := influxql.MustParseExpr(tt.expr) 749 switch expr := expr.(type) { 750 case *influxql.BinaryExpr: 751 name := influxql.BinaryExprName(expr) 752 if name != tt.name { 753 t.Errorf("%d. unexpected name %s, got %s", i, name, tt.name) 754 } 755 default: 756 t.Errorf("%d. unexpected expr type: %T", i, expr) 757 } 758 } 759 } 760 761 func TestConditionExpr(t *testing.T) { 762 mustParseTime := func(value string) time.Time { 763 ts, err := time.Parse(time.RFC3339, value) 764 if err != nil { 765 t.Fatalf("unable to parse time: %s", err) 766 } 767 return ts 768 } 769 now := mustParseTime("2000-01-01T00:00:00Z") 770 valuer := influxql.NowValuer{Now: now} 771 772 for _, tt := range []struct { 773 s string 774 cond string 775 min, max time.Time 776 err string 777 }{ 778 {s: `host = 'server01'`, cond: `host = 'server01'`}, 779 {s: `time >= '2000-01-01T00:00:00Z' AND time < '2000-01-01T01:00:00Z'`, 780 min: mustParseTime("2000-01-01T00:00:00Z"), 781 max: mustParseTime("2000-01-01T01:00:00Z").Add(-1)}, 782 {s: `host = 'server01' AND (region = 'uswest' AND time >= now() - 10m)`, 783 cond: `host = 'server01' AND (region = 'uswest')`, 784 min: mustParseTime("1999-12-31T23:50:00Z")}, 785 {s: `(host = 'server01' AND region = 'uswest') AND time >= now() - 10m`, 786 cond: `host = 'server01' AND region = 'uswest'`, 787 min: mustParseTime("1999-12-31T23:50:00Z")}, 788 {s: `host = 'server01' AND (time >= '2000-01-01T00:00:00Z' AND time < '2000-01-01T01:00:00Z')`, 789 cond: `host = 'server01'`, 790 min: mustParseTime("2000-01-01T00:00:00Z"), 791 max: mustParseTime("2000-01-01T01:00:00Z").Add(-1)}, 792 {s: `(time >= '2000-01-01T00:00:00Z' AND time < '2000-01-01T01:00:00Z') AND host = 'server01'`, 793 cond: `host = 'server01'`, 794 min: mustParseTime("2000-01-01T00:00:00Z"), 795 max: mustParseTime("2000-01-01T01:00:00Z").Add(-1)}, 796 {s: `'2000-01-01T00:00:00Z' <= time AND '2000-01-01T01:00:00Z' > time`, 797 min: mustParseTime("2000-01-01T00:00:00Z"), 798 max: mustParseTime("2000-01-01T01:00:00Z").Add(-1)}, 799 {s: `'2000-01-01T00:00:00Z' < time AND '2000-01-01T01:00:00Z' >= time`, 800 min: mustParseTime("2000-01-01T00:00:00Z").Add(1), 801 max: mustParseTime("2000-01-01T01:00:00Z")}, 802 {s: `time = '2000-01-01T00:00:00Z'`, 803 min: mustParseTime("2000-01-01T00:00:00Z"), 804 max: mustParseTime("2000-01-01T00:00:00Z")}, 805 {s: `time >= 10s`, min: mustParseTime("1970-01-01T00:00:10Z")}, 806 {s: `time >= 10000000000`, min: mustParseTime("1970-01-01T00:00:10Z")}, 807 {s: `time >= 10000000000.0`, min: mustParseTime("1970-01-01T00:00:10Z")}, 808 {s: `time > now()`, min: now.Add(1)}, 809 {s: `value`, err: `invalid condition expression: value`}, 810 {s: `4`, err: `invalid condition expression: 4`}, 811 {s: `time >= 'today'`, err: `invalid operation: time and *influxql.StringLiteral are not compatible`}, 812 {s: `time != '2000-01-01T00:00:00Z'`, err: `invalid time comparison operator: !=`}, 813 // This query makes no logical sense, but it's common enough that we pretend 814 // it does. Technically, this should be illegal because the AND has higher precedence 815 // than the OR so the AND only applies to the server02 tag, but a person's intention 816 // is to have it apply to both and previous versions worked that way. 817 {s: `host = 'server01' OR host = 'server02' AND time >= now() - 10m`, 818 cond: `host = 'server01' OR host = 'server02'`, 819 min: mustParseTime("1999-12-31T23:50:00Z")}, 820 // TODO(jsternberg): This should be an error, but we can't because the above query 821 // needs to work. Until we can work a way for the above to work or at least get 822 // a warning message for people to transition to a correct syntax, the bad behavior 823 // stays. 824 //{s: `host = 'server01' OR (time >= now() - 10m AND host = 'server02')`, err: `cannot use OR with time conditions`}, 825 {s: `value AND host = 'server01'`, err: `invalid condition expression: value`}, 826 {s: `host = 'server01' OR (value)`, err: `invalid condition expression: value`}, 827 {s: `time > '2262-04-11 23:47:17'`, err: `time 2262-04-11T23:47:17Z overflows time literal`}, 828 {s: `time > '1677-09-20 19:12:43'`, err: `time 1677-09-20T19:12:43Z underflows time literal`}, 829 {s: `true AND (false OR product = 'xyz')`, 830 cond: `product = 'xyz'`, 831 }, 832 {s: `'a' = 'a'`, cond: ``}, 833 {s: `value > 0 OR true`, cond: ``}, 834 {s: `host = 'server01' AND false`, cond: `false`}, 835 {s: `TIME >= '2000-01-01T00:00:00Z'`, min: mustParseTime("2000-01-01T00:00:00Z")}, 836 {s: `'2000-01-01T00:00:00Z' <= TIME`, min: mustParseTime("2000-01-01T00:00:00Z")}, 837 // Remove enclosing parentheses 838 {s: `(host = 'server01')`, cond: `host = 'server01'`}, 839 // Preserve nested parentheses 840 {s: `host = 'server01' AND (region = 'region01' OR region = 'region02')`, 841 cond: `host = 'server01' AND (region = 'region01' OR region = 'region02')`, 842 }, 843 } { 844 t.Run(tt.s, func(t *testing.T) { 845 expr, err := influxql.ParseExpr(tt.s) 846 if err != nil { 847 t.Fatalf("unexpected error: %s", err) 848 } 849 850 cond, timeRange, err := influxql.ConditionExpr(expr, &valuer) 851 if err != nil { 852 if tt.err == "" { 853 t.Fatalf("unexpected error: %s", err) 854 } else if have, want := err.Error(), tt.err; have != want { 855 t.Fatalf("unexpected error: %s != %s", have, want) 856 } 857 } 858 if cond != nil { 859 if have, want := cond.String(), tt.cond; have != want { 860 t.Errorf("unexpected condition:\nhave=%s\nwant=%s", have, want) 861 } 862 } else { 863 if have, want := "", tt.cond; have != want { 864 t.Errorf("unexpected condition:\nhave=%s\nwant=%s", have, want) 865 } 866 } 867 if have, want := timeRange.Min, tt.min; !have.Equal(want) { 868 t.Errorf("unexpected min time:\nhave=%s\nwant=%s", have, want) 869 } 870 if have, want := timeRange.Max, tt.max; !have.Equal(want) { 871 t.Errorf("unexpected max time:\nhave=%s\nwant=%s", have, want) 872 } 873 }) 874 } 875 } 876 877 // Ensure an AST node can be rewritten. 878 func TestRewrite(t *testing.T) { 879 expr := MustParseExpr(`time > 1 OR foo = 2`) 880 881 // Flip LHS & RHS in all binary expressions. 882 act := influxql.RewriteFunc(expr, func(n influxql.Node) influxql.Node { 883 switch n := n.(type) { 884 case *influxql.BinaryExpr: 885 return &influxql.BinaryExpr{Op: n.Op, LHS: n.RHS, RHS: n.LHS} 886 default: 887 return n 888 } 889 }) 890 891 // Verify that everything is flipped. 892 if act := act.String(); act != `2 = foo OR 1 > time` { 893 t.Fatalf("unexpected result: %s", act) 894 } 895 } 896 897 // Ensure an Expr can be rewritten handling nils. 898 func TestRewriteExpr(t *testing.T) { 899 expr := MustParseExpr(`(time > 1 AND time < 10) OR foo = 2`) 900 901 // Remove all time expressions. 902 act := influxql.RewriteExpr(expr, func(e influxql.Expr) influxql.Expr { 903 switch e := e.(type) { 904 case *influxql.BinaryExpr: 905 if lhs, ok := e.LHS.(*influxql.VarRef); ok && lhs.Val == "time" { 906 return nil 907 } 908 } 909 return e 910 }) 911 912 // Verify that everything is flipped. 913 if act := act.String(); act != `foo = 2` { 914 t.Fatalf("unexpected result: %s", act) 915 } 916 } 917 918 // Ensure that the String() value of a statement is parseable 919 func TestParseString(t *testing.T) { 920 var tests = []struct { 921 stmt string 922 }{ 923 { 924 stmt: `SELECT "cpu load" FROM myseries`, 925 }, 926 { 927 stmt: `SELECT "cpu load" FROM "my series"`, 928 }, 929 { 930 stmt: `SELECT "cpu\"load" FROM myseries`, 931 }, 932 { 933 stmt: `SELECT "cpu'load" FROM myseries`, 934 }, 935 { 936 stmt: `SELECT "cpu load" FROM "my\"series"`, 937 }, 938 { 939 stmt: `SELECT "field with spaces" FROM "\"ugly\" db"."\"ugly\" rp"."\"ugly\" measurement"`, 940 }, 941 { 942 stmt: `SELECT * FROM myseries`, 943 }, 944 { 945 stmt: `DROP DATABASE "!"`, 946 }, 947 { 948 stmt: `DROP RETENTION POLICY "my rp" ON "a database"`, 949 }, 950 { 951 stmt: `CREATE RETENTION POLICY "my rp" ON "a database" DURATION 1d REPLICATION 1`, 952 }, 953 { 954 stmt: `ALTER RETENTION POLICY "my rp" ON "a database" DEFAULT`, 955 }, 956 { 957 stmt: `SHOW RETENTION POLICIES ON "a database"`, 958 }, 959 { 960 stmt: `SHOW TAG VALUES WITH KEY IN ("a long name", short)`, 961 }, 962 { 963 stmt: `DROP CONTINUOUS QUERY "my query" ON "my database"`, 964 }, 965 // See issues https://github.com/influxdata/influxdb/issues/1647 966 // and https://github.com/influxdata/influxdb/issues/4404 967 //{ 968 // stmt: `DELETE FROM "my db"."my rp"."my measurement"`, 969 //}, 970 { 971 stmt: `DROP SUBSCRIPTION "ugly \"subscription\" name" ON "\"my\" db"."\"my\" rp"`, 972 }, 973 { 974 stmt: `CREATE SUBSCRIPTION "ugly \"subscription\" name" ON "\"my\" db"."\"my\" rp" DESTINATIONS ALL 'my host', 'my other host'`, 975 }, 976 { 977 stmt: `SHOW MEASUREMENTS WITH MEASUREMENT =~ /foo/`, 978 }, 979 { 980 stmt: `SHOW MEASUREMENTS WITH MEASUREMENT = "and/or"`, 981 }, 982 { 983 stmt: `DROP USER "user with spaces"`, 984 }, 985 { 986 stmt: `GRANT ALL PRIVILEGES ON "db with spaces" TO "user with spaces"`, 987 }, 988 { 989 stmt: `GRANT ALL PRIVILEGES TO "user with spaces"`, 990 }, 991 { 992 stmt: `SHOW GRANTS FOR "user with spaces"`, 993 }, 994 { 995 stmt: `REVOKE ALL PRIVILEGES ON "db with spaces" FROM "user with spaces"`, 996 }, 997 { 998 stmt: `REVOKE ALL PRIVILEGES FROM "user with spaces"`, 999 }, 1000 { 1001 stmt: `CREATE DATABASE "db with spaces"`, 1002 }, 1003 } 1004 1005 for _, tt := range tests { 1006 // Parse statement. 1007 stmt, err := influxql.NewParser(strings.NewReader(tt.stmt)).ParseStatement() 1008 if err != nil { 1009 t.Fatalf("invalid statement: %q: %s", tt.stmt, err) 1010 } 1011 1012 stmtCopy, err := influxql.NewParser(strings.NewReader(stmt.String())).ParseStatement() 1013 if err != nil { 1014 t.Fatalf("failed to parse string: %v\norig: %v\ngot: %v", err, tt.stmt, stmt.String()) 1015 } 1016 1017 if !reflect.DeepEqual(stmt, stmtCopy) { 1018 t.Fatalf("statement changed after stringifying and re-parsing:\noriginal : %v\nre-parsed: %v\n", tt.stmt, stmtCopy.String()) 1019 } 1020 } 1021 } 1022 1023 // Ensure an expression can be reduced. 1024 func TestEval(t *testing.T) { 1025 for i, tt := range []struct { 1026 in string 1027 out interface{} 1028 data map[string]interface{} 1029 }{ 1030 // Number literals. 1031 {in: `1 + 2`, out: int64(3)}, 1032 {in: `(foo*2) + ( (4/2) + (3 * 5) - 0.5 )`, out: float64(26.5), data: map[string]interface{}{"foo": float64(5)}}, 1033 {in: `foo / 2`, out: float64(2), data: map[string]interface{}{"foo": float64(4)}}, 1034 {in: `4 = 4`, out: true}, 1035 {in: `4 <> 4`, out: false}, 1036 {in: `6 > 4`, out: true}, 1037 {in: `4 >= 4`, out: true}, 1038 {in: `4 < 6`, out: true}, 1039 {in: `4 <= 4`, out: true}, 1040 {in: `4 AND 5`, out: nil}, 1041 {in: `0 = 'test'`, out: false}, 1042 {in: `1.0 = 1`, out: true}, 1043 {in: `1.2 = 1`, out: false}, 1044 {in: `-1 = 9223372036854775808`, out: false}, 1045 {in: `-1 != 9223372036854775808`, out: true}, 1046 {in: `-1 < 9223372036854775808`, out: true}, 1047 {in: `-1 <= 9223372036854775808`, out: true}, 1048 {in: `-1 > 9223372036854775808`, out: false}, 1049 {in: `-1 >= 9223372036854775808`, out: false}, 1050 {in: `9223372036854775808 = -1`, out: false}, 1051 {in: `9223372036854775808 != -1`, out: true}, 1052 {in: `9223372036854775808 < -1`, out: false}, 1053 {in: `9223372036854775808 <= -1`, out: false}, 1054 {in: `9223372036854775808 > -1`, out: true}, 1055 {in: `9223372036854775808 >= -1`, out: true}, 1056 {in: `9223372036854775808 = 9223372036854775808`, out: true}, 1057 {in: `9223372036854775808 != 9223372036854775808`, out: false}, 1058 {in: `9223372036854775808 < 9223372036854775808`, out: false}, 1059 {in: `9223372036854775808 <= 9223372036854775808`, out: true}, 1060 {in: `9223372036854775808 > 9223372036854775808`, out: false}, 1061 {in: `9223372036854775808 >= 9223372036854775808`, out: true}, 1062 {in: `9223372036854775809 = 9223372036854775808`, out: false}, 1063 {in: `9223372036854775809 != 9223372036854775808`, out: true}, 1064 {in: `9223372036854775809 < 9223372036854775808`, out: false}, 1065 {in: `9223372036854775809 <= 9223372036854775808`, out: false}, 1066 {in: `9223372036854775809 > 9223372036854775808`, out: true}, 1067 {in: `9223372036854775809 >= 9223372036854775808`, out: true}, 1068 {in: `9223372036854775808 / 0`, out: uint64(0)}, 1069 {in: `9223372036854775808 + 1`, out: uint64(9223372036854775809)}, 1070 {in: `9223372036854775808 - 1`, out: uint64(9223372036854775807)}, 1071 {in: `9223372036854775809 - 9223372036854775808`, out: uint64(1)}, 1072 1073 // Boolean literals. 1074 {in: `true AND false`, out: false}, 1075 {in: `true OR false`, out: true}, 1076 {in: `false = 4`, out: false}, 1077 1078 // String literals. 1079 {in: `'foo' = 'bar'`, out: false}, 1080 {in: `'foo' = 'foo'`, out: true}, 1081 {in: `'' = 4`, out: false}, 1082 1083 // Regex literals. 1084 {in: `'foo' =~ /f.*/`, out: true}, 1085 {in: `'foo' =~ /b.*/`, out: false}, 1086 {in: `'foo' !~ /f.*/`, out: false}, 1087 {in: `'foo' !~ /b.*/`, out: true}, 1088 1089 // Variable references. 1090 {in: `foo`, out: "bar", data: map[string]interface{}{"foo": "bar"}}, 1091 {in: `foo = 'bar'`, out: true, data: map[string]interface{}{"foo": "bar"}}, 1092 {in: `foo = 'bar'`, out: false, data: map[string]interface{}{"foo": nil}}, 1093 {in: `'bar' = foo`, out: false, data: map[string]interface{}{"foo": nil}}, 1094 {in: `foo <> 'bar'`, out: true, data: map[string]interface{}{"foo": "xxx"}}, 1095 {in: `foo =~ /b.*/`, out: true, data: map[string]interface{}{"foo": "bar"}}, 1096 {in: `foo !~ /b.*/`, out: false, data: map[string]interface{}{"foo": "bar"}}, 1097 {in: `foo > 2 OR bar > 3`, out: true, data: map[string]interface{}{"foo": float64(4)}}, 1098 {in: `foo > 2 OR bar > 3`, out: true, data: map[string]interface{}{"bar": float64(4)}}, 1099 } { 1100 // Evaluate expression. 1101 out := influxql.Eval(MustParseExpr(tt.in), tt.data) 1102 1103 // Compare with expected output. 1104 if !reflect.DeepEqual(tt.out, out) { 1105 t.Errorf("%d. %s: unexpected output:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.in, tt.out, out) 1106 continue 1107 } 1108 } 1109 } 1110 1111 type EvalFixture map[string]map[string]influxql.DataType 1112 1113 func (e EvalFixture) MapType(measurement *influxql.Measurement, field string) influxql.DataType { 1114 m := e[measurement.Name] 1115 if m == nil { 1116 return influxql.Unknown 1117 } 1118 return m[field] 1119 } 1120 1121 func (e EvalFixture) CallType(name string, args []influxql.DataType) (influxql.DataType, error) { 1122 switch name { 1123 case "mean", "median", "integral", "stddev": 1124 return influxql.Float, nil 1125 case "count": 1126 return influxql.Integer, nil 1127 case "elapsed": 1128 return influxql.Integer, nil 1129 default: 1130 return args[0], nil 1131 } 1132 } 1133 1134 func TestEvalType(t *testing.T) { 1135 for i, tt := range []struct { 1136 name string 1137 in string 1138 typ influxql.DataType 1139 err string 1140 data EvalFixture 1141 }{ 1142 { 1143 name: `a single data type`, 1144 in: `min(value)`, 1145 typ: influxql.Integer, 1146 data: EvalFixture{ 1147 "cpu": map[string]influxql.DataType{ 1148 "value": influxql.Integer, 1149 }, 1150 }, 1151 }, 1152 { 1153 name: `multiple data types`, 1154 in: `min(value)`, 1155 typ: influxql.Integer, 1156 data: EvalFixture{ 1157 "cpu": map[string]influxql.DataType{ 1158 "value": influxql.Integer, 1159 }, 1160 "mem": map[string]influxql.DataType{ 1161 "value": influxql.String, 1162 }, 1163 }, 1164 }, 1165 { 1166 name: `count() with a float`, 1167 in: `count(value)`, 1168 typ: influxql.Integer, 1169 data: EvalFixture{ 1170 "cpu": map[string]influxql.DataType{ 1171 "value": influxql.Float, 1172 }, 1173 }, 1174 }, 1175 { 1176 name: `mean() with an integer`, 1177 in: `mean(value)`, 1178 typ: influxql.Float, 1179 data: EvalFixture{ 1180 "cpu": map[string]influxql.DataType{ 1181 "value": influxql.Integer, 1182 }, 1183 }, 1184 }, 1185 { 1186 name: `stddev() with an integer`, 1187 in: `stddev(value)`, 1188 typ: influxql.Float, 1189 data: EvalFixture{ 1190 "cpu": map[string]influxql.DataType{ 1191 "value": influxql.Integer, 1192 }, 1193 }, 1194 }, 1195 { 1196 name: `value inside a parenthesis`, 1197 in: `(value)`, 1198 typ: influxql.Float, 1199 data: EvalFixture{ 1200 "cpu": map[string]influxql.DataType{ 1201 "value": influxql.Float, 1202 }, 1203 }, 1204 }, 1205 { 1206 name: `binary expression with a float and integer`, 1207 in: `v1 + v2`, 1208 typ: influxql.Float, 1209 data: EvalFixture{ 1210 "cpu": map[string]influxql.DataType{ 1211 "v1": influxql.Float, 1212 "v2": influxql.Integer, 1213 }, 1214 }, 1215 }, 1216 { 1217 name: `integer and unsigned literal`, 1218 in: `value + 9223372036854775808`, 1219 err: `type error: value + 9223372036854775808: cannot use + with an integer and unsigned literal`, 1220 data: EvalFixture{ 1221 "cpu": map[string]influxql.DataType{ 1222 "value": influxql.Integer, 1223 }, 1224 }, 1225 }, 1226 { 1227 name: `unsigned and integer literal`, 1228 in: `value + 1`, 1229 typ: influxql.Unsigned, 1230 data: EvalFixture{ 1231 "cpu": map[string]influxql.DataType{ 1232 "value": influxql.Unsigned, 1233 }, 1234 }, 1235 }, 1236 { 1237 name: `incompatible types`, 1238 in: `v1 + v2`, 1239 err: `type error: v1 + v2: incompatible types: string and integer`, 1240 data: EvalFixture{ 1241 "cpu": map[string]influxql.DataType{ 1242 "v1": influxql.String, 1243 "v2": influxql.Integer, 1244 }, 1245 }, 1246 }, 1247 } { 1248 sources := make([]influxql.Source, 0, len(tt.data)) 1249 for src := range tt.data { 1250 sources = append(sources, &influxql.Measurement{Name: src}) 1251 } 1252 1253 expr := influxql.MustParseExpr(tt.in) 1254 valuer := influxql.TypeValuerEval{ 1255 TypeMapper: tt.data, 1256 Sources: sources, 1257 } 1258 typ, err := valuer.EvalType(expr) 1259 if err != nil { 1260 if exp, got := tt.err, err.Error(); exp != got { 1261 t.Errorf("%d. %s: unexpected error:\n\nexp=%#v\n\ngot=%v\n\n", i, tt.name, exp, got) 1262 } 1263 } else if typ != tt.typ { 1264 t.Errorf("%d. %s: unexpected type:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.name, tt.typ, typ) 1265 } 1266 } 1267 } 1268 1269 // Ensure an expression can be reduced. 1270 func TestReduce(t *testing.T) { 1271 now := mustParseTime("2000-01-01T00:00:00Z") 1272 1273 for i, tt := range []struct { 1274 in string 1275 out string 1276 data influxql.MapValuer 1277 }{ 1278 // Number literals. 1279 {in: `1 + 2`, out: `3`}, 1280 {in: `(foo*2) + ( (4/2) + (3 * 5) - 0.5 )`, out: `(foo * 2) + 16.500`}, 1281 {in: `foo(bar(2 + 3), 4)`, out: `foo(bar(5), 4)`}, 1282 {in: `4 / 0`, out: `0.000`}, 1283 {in: `1 / 2`, out: `0.500`}, 1284 {in: `2 % 3`, out: `2`}, 1285 {in: `5 % 2`, out: `1`}, 1286 {in: `2 % 0`, out: `0`}, 1287 {in: `2.5 % 0`, out: `NaN`}, 1288 {in: `254 & 3`, out: `2`}, 1289 {in: `254 | 3`, out: `255`}, 1290 {in: `254 ^ 3`, out: `253`}, 1291 {in: `-3 & 3`, out: `1`}, 1292 {in: `8 & -3`, out: `8`}, 1293 {in: `8.5 & -3`, out: `8.500 & -3`}, 1294 {in: `4 = 4`, out: `true`}, 1295 {in: `4 <> 4`, out: `false`}, 1296 {in: `6 > 4`, out: `true`}, 1297 {in: `4 >= 4`, out: `true`}, 1298 {in: `4 < 6`, out: `true`}, 1299 {in: `4 <= 4`, out: `true`}, 1300 {in: `4 AND 5`, out: `4 AND 5`}, 1301 {in: `-1 = 9223372036854775808`, out: `false`}, 1302 {in: `-1 != 9223372036854775808`, out: `true`}, 1303 {in: `-1 < 9223372036854775808`, out: `true`}, 1304 {in: `-1 <= 9223372036854775808`, out: `true`}, 1305 {in: `-1 > 9223372036854775808`, out: `false`}, 1306 {in: `-1 >= 9223372036854775808`, out: `false`}, 1307 {in: `9223372036854775808 = -1`, out: `false`}, 1308 {in: `9223372036854775808 != -1`, out: `true`}, 1309 {in: `9223372036854775808 < -1`, out: `false`}, 1310 {in: `9223372036854775808 <= -1`, out: `false`}, 1311 {in: `9223372036854775808 > -1`, out: `true`}, 1312 {in: `9223372036854775808 >= -1`, out: `true`}, 1313 {in: `9223372036854775808 = 9223372036854775808`, out: `true`}, 1314 {in: `9223372036854775808 != 9223372036854775808`, out: `false`}, 1315 {in: `9223372036854775808 < 9223372036854775808`, out: `false`}, 1316 {in: `9223372036854775808 <= 9223372036854775808`, out: `true`}, 1317 {in: `9223372036854775808 > 9223372036854775808`, out: `false`}, 1318 {in: `9223372036854775808 >= 9223372036854775808`, out: `true`}, 1319 {in: `9223372036854775809 = 9223372036854775808`, out: `false`}, 1320 {in: `9223372036854775809 != 9223372036854775808`, out: `true`}, 1321 {in: `9223372036854775809 < 9223372036854775808`, out: `false`}, 1322 {in: `9223372036854775809 <= 9223372036854775808`, out: `false`}, 1323 {in: `9223372036854775809 > 9223372036854775808`, out: `true`}, 1324 {in: `9223372036854775809 >= 9223372036854775808`, out: `true`}, 1325 {in: `9223372036854775808 / 0`, out: `0`}, 1326 {in: `9223372036854775808 + 1`, out: `9223372036854775809`}, 1327 {in: `9223372036854775808 - 1`, out: `9223372036854775807`}, 1328 {in: `9223372036854775809 - 9223372036854775808`, out: `1`}, 1329 1330 // Boolean literals. 1331 {in: `true AND false`, out: `false`}, 1332 {in: `true OR false`, out: `true`}, 1333 {in: `true OR (foo = bar AND 1 > 2)`, out: `true`}, 1334 {in: `(foo = bar AND 1 > 2) OR true`, out: `true`}, 1335 {in: `false OR (foo = bar AND 1 > 2)`, out: `false`}, 1336 {in: `(foo = bar AND 1 > 2) OR false`, out: `false`}, 1337 {in: `true = false`, out: `false`}, 1338 {in: `true <> false`, out: `true`}, 1339 {in: `true + false`, out: `true + false`}, 1340 1341 // Time literals with now(). 1342 {in: `now() + 2h`, out: `'2000-01-01T02:00:00Z'`}, 1343 {in: `now() / 2h`, out: `'2000-01-01T00:00:00Z' / 2h`}, 1344 {in: `4µ + now()`, out: `'2000-01-01T00:00:00.000004Z'`}, 1345 {in: `now() + 2000000000`, out: `'2000-01-01T00:00:02Z'`}, 1346 {in: `2000000000 + now()`, out: `'2000-01-01T00:00:02Z'`}, 1347 {in: `now() - 2000000000`, out: `'1999-12-31T23:59:58Z'`}, 1348 {in: `now() = now()`, out: `true`}, 1349 {in: `now() <> now()`, out: `false`}, 1350 {in: `now() < now() + 1h`, out: `true`}, 1351 {in: `now() <= now() + 1h`, out: `true`}, 1352 {in: `now() >= now() - 1h`, out: `true`}, 1353 {in: `now() > now() - 1h`, out: `true`}, 1354 {in: `now() - (now() - 60s)`, out: `1m`}, 1355 {in: `now() AND now()`, out: `'2000-01-01T00:00:00Z' AND '2000-01-01T00:00:00Z'`}, 1356 {in: `946684800000000000 + 2h`, out: `'2000-01-01T02:00:00Z'`}, 1357 1358 // Time literals. 1359 {in: `'2000-01-01T00:00:00Z' + 2h`, out: `'2000-01-01T02:00:00Z'`}, 1360 {in: `'2000-01-01T00:00:00Z' / 2h`, out: `'2000-01-01T00:00:00Z' / 2h`}, 1361 {in: `4µ + '2000-01-01T00:00:00Z'`, out: `'2000-01-01T00:00:00.000004Z'`}, 1362 {in: `'2000-01-01T00:00:00Z' + 2000000000`, out: `'2000-01-01T00:00:02Z'`}, 1363 {in: `2000000000 + '2000-01-01T00:00:00Z'`, out: `'2000-01-01T00:00:02Z'`}, 1364 {in: `'2000-01-01T00:00:00Z' - 2000000000`, out: `'1999-12-31T23:59:58Z'`}, 1365 {in: `'2000-01-01T00:00:00Z' = '2000-01-01T00:00:00Z'`, out: `true`}, 1366 {in: `'2000-01-01T00:00:00.000000000Z' = '2000-01-01T00:00:00Z'`, out: `true`}, 1367 {in: `'2000-01-01T00:00:00Z' <> '2000-01-01T00:00:00Z'`, out: `false`}, 1368 {in: `'2000-01-01T00:00:00.000000000Z' <> '2000-01-01T00:00:00Z'`, out: `false`}, 1369 {in: `'2000-01-01T00:00:00Z' < '2000-01-01T00:00:00Z' + 1h`, out: `true`}, 1370 {in: `'2000-01-01T00:00:00.000000000Z' < '2000-01-01T00:00:00Z' + 1h`, out: `true`}, 1371 {in: `'2000-01-01T00:00:00Z' <= '2000-01-01T00:00:00Z' + 1h`, out: `true`}, 1372 {in: `'2000-01-01T00:00:00.000000000Z' <= '2000-01-01T00:00:00Z' + 1h`, out: `true`}, 1373 {in: `'2000-01-01T00:00:00Z' > '2000-01-01T00:00:00Z' - 1h`, out: `true`}, 1374 {in: `'2000-01-01T00:00:00.000000000Z' > '2000-01-01T00:00:00Z' - 1h`, out: `true`}, 1375 {in: `'2000-01-01T00:00:00Z' >= '2000-01-01T00:00:00Z' - 1h`, out: `true`}, 1376 {in: `'2000-01-01T00:00:00.000000000Z' >= '2000-01-01T00:00:00Z' - 1h`, out: `true`}, 1377 {in: `'2000-01-01T00:00:00Z' - ('2000-01-01T00:00:00Z' - 60s)`, out: `1m`}, 1378 {in: `'2000-01-01T00:00:00Z' AND '2000-01-01T00:00:00Z'`, out: `'2000-01-01T00:00:00Z' AND '2000-01-01T00:00:00Z'`}, 1379 1380 // Duration literals. 1381 {in: `10m + 1h - 60s`, out: `69m`}, 1382 {in: `(10m / 2) * 5`, out: `25m`}, 1383 {in: `60s = 1m`, out: `true`}, 1384 {in: `60s <> 1m`, out: `false`}, 1385 {in: `60s < 1h`, out: `true`}, 1386 {in: `60s <= 1h`, out: `true`}, 1387 {in: `60s > 12s`, out: `true`}, 1388 {in: `60s >= 1m`, out: `true`}, 1389 {in: `60s AND 1m`, out: `1m AND 1m`}, 1390 {in: `60m / 0`, out: `0s`}, 1391 {in: `60m + 50`, out: `1h + 50`}, 1392 1393 // String literals. 1394 {in: `'foo' + 'bar'`, out: `'foobar'`}, 1395 1396 // Variable references. 1397 {in: `foo`, out: `'bar'`, data: map[string]interface{}{"foo": "bar"}}, 1398 {in: `foo = 'bar'`, out: `true`, data: map[string]interface{}{"foo": "bar"}}, 1399 {in: `foo = 'bar'`, out: `false`, data: map[string]interface{}{"foo": nil}}, 1400 {in: `foo <> 'bar'`, out: `false`, data: map[string]interface{}{"foo": nil}}, 1401 } { 1402 // Fold expression. 1403 expr := influxql.Reduce(MustParseExpr(tt.in), influxql.MultiValuer( 1404 tt.data, 1405 &influxql.NowValuer{Now: now}, 1406 )) 1407 1408 // Compare with expected output. 1409 if out := expr.String(); tt.out != out { 1410 t.Errorf("%d. %s: unexpected expr:\n\nexp=%s\n\ngot=%s\n\n", i, tt.in, tt.out, out) 1411 continue 1412 } 1413 } 1414 } 1415 1416 func Test_fieldsNames(t *testing.T) { 1417 for _, test := range []struct { 1418 in []string 1419 out []string 1420 alias []string 1421 }{ 1422 { //case: binary expr(valRef) 1423 in: []string{"value+value"}, 1424 out: []string{"value", "value"}, 1425 alias: []string{"value_value"}, 1426 }, 1427 { //case: binary expr + valRef 1428 in: []string{"value+value", "temperature"}, 1429 out: []string{"value", "value", "temperature"}, 1430 alias: []string{"value_value", "temperature"}, 1431 }, 1432 { //case: aggregate expr 1433 in: []string{"mean(value)"}, 1434 out: []string{"mean"}, 1435 alias: []string{"mean"}, 1436 }, 1437 { //case: binary expr(aggregate expr) 1438 in: []string{"mean(value) + max(value)"}, 1439 out: []string{"value", "value"}, 1440 alias: []string{"mean_max"}, 1441 }, 1442 { //case: binary expr(aggregate expr) + valRef 1443 in: []string{"mean(value) + max(value)", "temperature"}, 1444 out: []string{"value", "value", "temperature"}, 1445 alias: []string{"mean_max", "temperature"}, 1446 }, 1447 { //case: mixed aggregate and varRef 1448 in: []string{"mean(value) + temperature"}, 1449 out: []string{"value", "temperature"}, 1450 alias: []string{"mean_temperature"}, 1451 }, 1452 { //case: ParenExpr(varRef) 1453 in: []string{"(value)"}, 1454 out: []string{"value"}, 1455 alias: []string{"value"}, 1456 }, 1457 { //case: ParenExpr(varRef + varRef) 1458 in: []string{"(value + value)"}, 1459 out: []string{"value", "value"}, 1460 alias: []string{"value_value"}, 1461 }, 1462 { //case: ParenExpr(aggregate) 1463 in: []string{"(mean(value))"}, 1464 out: []string{"value"}, 1465 alias: []string{"mean"}, 1466 }, 1467 { //case: ParenExpr(aggregate + aggregate) 1468 in: []string{"(mean(value) + max(value))"}, 1469 out: []string{"value", "value"}, 1470 alias: []string{"mean_max"}, 1471 }, 1472 } { 1473 fields := influxql.Fields{} 1474 for _, s := range test.in { 1475 expr := MustParseExpr(s) 1476 fields = append(fields, &influxql.Field{Expr: expr}) 1477 } 1478 got := fields.Names() 1479 if !reflect.DeepEqual(got, test.out) { 1480 t.Errorf("get fields name:\nexp=%v\ngot=%v\n", test.out, got) 1481 } 1482 alias := fields.AliasNames() 1483 if !reflect.DeepEqual(alias, test.alias) { 1484 t.Errorf("get fields alias name:\nexp=%v\ngot=%v\n", test.alias, alias) 1485 } 1486 } 1487 1488 } 1489 1490 func TestSelect_ColumnNames(t *testing.T) { 1491 for i, tt := range []struct { 1492 stmt *influxql.SelectStatement 1493 columns []string 1494 }{ 1495 { 1496 stmt: &influxql.SelectStatement{ 1497 Fields: influxql.Fields([]*influxql.Field{ 1498 {Expr: &influxql.VarRef{Val: "value"}}, 1499 }), 1500 }, 1501 columns: []string{"time", "value"}, 1502 }, 1503 { 1504 stmt: &influxql.SelectStatement{ 1505 Fields: influxql.Fields([]*influxql.Field{ 1506 {Expr: &influxql.VarRef{Val: "value"}}, 1507 {Expr: &influxql.VarRef{Val: "value"}}, 1508 {Expr: &influxql.VarRef{Val: "value_1"}}, 1509 }), 1510 }, 1511 columns: []string{"time", "value", "value_1", "value_1_1"}, 1512 }, 1513 { 1514 stmt: &influxql.SelectStatement{ 1515 Fields: influxql.Fields([]*influxql.Field{ 1516 {Expr: &influxql.VarRef{Val: "value"}}, 1517 {Expr: &influxql.VarRef{Val: "value_1"}}, 1518 {Expr: &influxql.VarRef{Val: "value"}}, 1519 }), 1520 }, 1521 columns: []string{"time", "value", "value_1", "value_2"}, 1522 }, 1523 { 1524 stmt: &influxql.SelectStatement{ 1525 Fields: influxql.Fields([]*influxql.Field{ 1526 {Expr: &influxql.VarRef{Val: "value"}}, 1527 {Expr: &influxql.VarRef{Val: "total"}, Alias: "value"}, 1528 {Expr: &influxql.VarRef{Val: "value"}}, 1529 }), 1530 }, 1531 columns: []string{"time", "value_1", "value", "value_2"}, 1532 }, 1533 { 1534 stmt: &influxql.SelectStatement{ 1535 Fields: influxql.Fields([]*influxql.Field{ 1536 {Expr: &influxql.VarRef{Val: "value"}}, 1537 }), 1538 TimeAlias: "timestamp", 1539 }, 1540 columns: []string{"timestamp", "value"}, 1541 }, 1542 } { 1543 columns := tt.stmt.ColumnNames() 1544 if !reflect.DeepEqual(columns, tt.columns) { 1545 t.Errorf("%d. expected %s, got %s", i, tt.columns, columns) 1546 } 1547 } 1548 } 1549 1550 func TestSelect_Privileges(t *testing.T) { 1551 stmt := &influxql.SelectStatement{ 1552 Target: &influxql.Target{ 1553 Measurement: &influxql.Measurement{Database: "db2"}, 1554 }, 1555 Sources: []influxql.Source{ 1556 &influxql.Measurement{Database: "db0"}, 1557 &influxql.Measurement{Database: "db1"}, 1558 }, 1559 } 1560 1561 exp := influxql.ExecutionPrivileges{ 1562 influxql.ExecutionPrivilege{Name: "db0", Privilege: influxql.ReadPrivilege}, 1563 influxql.ExecutionPrivilege{Name: "db1", Privilege: influxql.ReadPrivilege}, 1564 influxql.ExecutionPrivilege{Name: "db2", Privilege: influxql.WritePrivilege}, 1565 } 1566 1567 got, err := stmt.RequiredPrivileges() 1568 if err != nil { 1569 t.Fatal(err) 1570 } 1571 1572 if !reflect.DeepEqual(exp, got) { 1573 t.Errorf("exp: %v, got: %v", exp, got) 1574 } 1575 } 1576 1577 func TestSelect_SubqueryPrivileges(t *testing.T) { 1578 stmt := &influxql.SelectStatement{ 1579 Target: &influxql.Target{ 1580 Measurement: &influxql.Measurement{Database: "db2"}, 1581 }, 1582 Sources: []influxql.Source{ 1583 &influxql.Measurement{Database: "db0"}, 1584 &influxql.SubQuery{ 1585 Statement: &influxql.SelectStatement{ 1586 Sources: []influxql.Source{ 1587 &influxql.Measurement{Database: "db1"}, 1588 }, 1589 }, 1590 }, 1591 }, 1592 } 1593 1594 exp := influxql.ExecutionPrivileges{ 1595 influxql.ExecutionPrivilege{Name: "db0", Privilege: influxql.ReadPrivilege}, 1596 influxql.ExecutionPrivilege{Name: "db1", Privilege: influxql.ReadPrivilege}, 1597 influxql.ExecutionPrivilege{Name: "db2", Privilege: influxql.WritePrivilege}, 1598 } 1599 1600 got, err := stmt.RequiredPrivileges() 1601 if err != nil { 1602 t.Fatal(err) 1603 } 1604 1605 if !reflect.DeepEqual(exp, got) { 1606 t.Errorf("exp: %v, got: %v", exp, got) 1607 } 1608 } 1609 1610 func TestShow_Privileges(t *testing.T) { 1611 for _, c := range []struct { 1612 stmt influxql.Statement 1613 exp influxql.ExecutionPrivileges 1614 }{ 1615 { 1616 stmt: &influxql.ShowDatabasesStatement{}, 1617 exp: influxql.ExecutionPrivileges{{Admin: false, Privilege: influxql.NoPrivileges}}, 1618 }, 1619 { 1620 stmt: &influxql.ShowFieldKeysStatement{}, 1621 exp: influxql.ExecutionPrivileges{{Admin: false, Privilege: influxql.ReadPrivilege}}, 1622 }, 1623 { 1624 stmt: &influxql.ShowMeasurementsStatement{}, 1625 exp: influxql.ExecutionPrivileges{{Admin: false, Privilege: influxql.ReadPrivilege}}, 1626 }, 1627 { 1628 stmt: &influxql.ShowQueriesStatement{}, 1629 exp: influxql.ExecutionPrivileges{{Admin: false, Privilege: influxql.ReadPrivilege}}, 1630 }, 1631 { 1632 stmt: &influxql.ShowRetentionPoliciesStatement{}, 1633 exp: influxql.ExecutionPrivileges{{Admin: false, Privilege: influxql.ReadPrivilege}}, 1634 }, 1635 { 1636 stmt: &influxql.ShowSeriesStatement{}, 1637 exp: influxql.ExecutionPrivileges{{Admin: false, Privilege: influxql.ReadPrivilege}}, 1638 }, 1639 { 1640 stmt: &influxql.ShowShardGroupsStatement{}, 1641 exp: influxql.ExecutionPrivileges{{Admin: true, Privilege: influxql.AllPrivileges}}, 1642 }, 1643 { 1644 stmt: &influxql.ShowShardsStatement{}, 1645 exp: influxql.ExecutionPrivileges{{Admin: true, Privilege: influxql.AllPrivileges}}, 1646 }, 1647 { 1648 stmt: &influxql.ShowStatsStatement{}, 1649 exp: influxql.ExecutionPrivileges{{Admin: true, Privilege: influxql.AllPrivileges}}, 1650 }, 1651 { 1652 stmt: &influxql.ShowSubscriptionsStatement{}, 1653 exp: influxql.ExecutionPrivileges{{Admin: true, Privilege: influxql.AllPrivileges}}, 1654 }, 1655 { 1656 stmt: &influxql.ShowDiagnosticsStatement{}, 1657 exp: influxql.ExecutionPrivileges{{Admin: true, Privilege: influxql.AllPrivileges}}, 1658 }, 1659 { 1660 stmt: &influxql.ShowTagKeysStatement{}, 1661 exp: influxql.ExecutionPrivileges{{Admin: false, Privilege: influxql.ReadPrivilege}}, 1662 }, 1663 { 1664 stmt: &influxql.ShowTagValuesStatement{}, 1665 exp: influxql.ExecutionPrivileges{{Admin: false, Privilege: influxql.ReadPrivilege}}, 1666 }, 1667 { 1668 stmt: &influxql.ShowUsersStatement{}, 1669 exp: influxql.ExecutionPrivileges{{Admin: true, Privilege: influxql.AllPrivileges}}, 1670 }, 1671 } { 1672 got, err := c.stmt.RequiredPrivileges() 1673 if err != nil { 1674 t.Fatal(err) 1675 } 1676 1677 if !reflect.DeepEqual(c.exp, got) { 1678 t.Errorf("exp: %v, got: %v", c.exp, got) 1679 } 1680 } 1681 } 1682 1683 func TestBoundParameter_String(t *testing.T) { 1684 stmt := &influxql.SelectStatement{ 1685 IsRawQuery: true, 1686 Fields: []*influxql.Field{{ 1687 Expr: &influxql.VarRef{Val: "value"}}}, 1688 Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}}, 1689 Condition: &influxql.BinaryExpr{ 1690 Op: influxql.GT, 1691 LHS: &influxql.VarRef{Val: "value"}, 1692 RHS: &influxql.BoundParameter{Name: "value"}, 1693 }, 1694 } 1695 1696 if got, exp := stmt.String(), `SELECT value FROM cpu WHERE value > $value`; got != exp { 1697 t.Fatalf("stmt mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", exp, got) 1698 } 1699 1700 stmt = &influxql.SelectStatement{ 1701 IsRawQuery: true, 1702 Fields: []*influxql.Field{{ 1703 Expr: &influxql.VarRef{Val: "value"}}}, 1704 Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}}, 1705 Condition: &influxql.BinaryExpr{ 1706 Op: influxql.GT, 1707 LHS: &influxql.VarRef{Val: "value"}, 1708 RHS: &influxql.BoundParameter{Name: "multi-word value"}, 1709 }, 1710 } 1711 1712 if got, exp := stmt.String(), `SELECT value FROM cpu WHERE value > $"multi-word value"`; got != exp { 1713 t.Fatalf("stmt mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", exp, got) 1714 } 1715 } 1716 1717 // This test checks to ensure that we have given thought to the database 1718 // context required for security checks. If a new statement is added, this 1719 // test will fail until it is categorized into the correct bucket below. 1720 func Test_EnforceHasDefaultDatabase(t *testing.T) { 1721 pkg, err := importer.Default().Import("github.com/influxdata/influxql") 1722 if err != nil { 1723 fmt.Printf("error: %s\n", err.Error()) 1724 return 1725 } 1726 statements := []string{} 1727 1728 // this is a list of statements that do not have a database context 1729 exemptStatements := []string{ 1730 "CreateDatabaseStatement", 1731 "CreateUserStatement", 1732 "DeleteSeriesStatement", 1733 "DropDatabaseStatement", 1734 "DropMeasurementStatement", 1735 "DropSeriesStatement", 1736 "DropShardStatement", 1737 "DropUserStatement", 1738 "ExplainStatement", 1739 "GrantAdminStatement", 1740 "KillQueryStatement", 1741 "RevokeAdminStatement", 1742 "SelectStatement", 1743 "SetPasswordUserStatement", 1744 "ShowContinuousQueriesStatement", 1745 "ShowDatabasesStatement", 1746 "ShowDiagnosticsStatement", 1747 "ShowGrantsForUserStatement", 1748 "ShowQueriesStatement", 1749 "ShowShardGroupsStatement", 1750 "ShowShardsStatement", 1751 "ShowStatsStatement", 1752 "ShowSubscriptionsStatement", 1753 "ShowUsersStatement", 1754 } 1755 1756 exists := func(stmt string) bool { 1757 switch stmt { 1758 // These are functions with the word statement in them, and can be ignored 1759 case "Statement", "MustParseStatement", "ParseStatement", "RewriteStatement": 1760 return true 1761 default: 1762 // check the exempt statements 1763 for _, s := range exemptStatements { 1764 if s == stmt { 1765 return true 1766 } 1767 } 1768 // check the statements that passed the interface test for HasDefaultDatabase 1769 for _, s := range statements { 1770 if s == stmt { 1771 return true 1772 } 1773 } 1774 return false 1775 } 1776 } 1777 1778 needsHasDefault := []interface{}{ 1779 &influxql.AlterRetentionPolicyStatement{}, 1780 &influxql.CreateContinuousQueryStatement{}, 1781 &influxql.CreateRetentionPolicyStatement{}, 1782 &influxql.CreateSubscriptionStatement{}, 1783 &influxql.DeleteStatement{}, 1784 &influxql.DropContinuousQueryStatement{}, 1785 &influxql.DropRetentionPolicyStatement{}, 1786 &influxql.DropSubscriptionStatement{}, 1787 &influxql.GrantStatement{}, 1788 &influxql.RevokeStatement{}, 1789 &influxql.ShowFieldKeysStatement{}, 1790 &influxql.ShowFieldKeyCardinalityStatement{}, 1791 &influxql.ShowMeasurementCardinalityStatement{}, 1792 &influxql.ShowMeasurementsStatement{}, 1793 &influxql.ShowRetentionPoliciesStatement{}, 1794 &influxql.ShowSeriesStatement{}, 1795 &influxql.ShowSeriesCardinalityStatement{}, 1796 &influxql.ShowTagKeysStatement{}, 1797 &influxql.ShowTagKeyCardinalityStatement{}, 1798 &influxql.ShowTagValuesStatement{}, 1799 &influxql.ShowTagValuesCardinalityStatement{}, 1800 } 1801 1802 for _, stmt := range needsHasDefault { 1803 statements = append(statements, strings.TrimPrefix(fmt.Sprintf("%T", stmt), "*influxql.")) 1804 if _, ok := stmt.(influxql.HasDefaultDatabase); !ok { 1805 t.Errorf("%T was expected to declare DefaultDatabase method", stmt) 1806 } 1807 1808 } 1809 1810 for _, declName := range pkg.Scope().Names() { 1811 if strings.HasSuffix(declName, "Statement") { 1812 if !exists(declName) { 1813 t.Errorf("unchecked statement %s. please update this test to determine if this statement needs to declare 'DefaultDatabase'", declName) 1814 } 1815 } 1816 } 1817 } 1818 1819 // MustTimeRange will parse a time range. Panic on error. 1820 func MustTimeRange(expr influxql.Expr) (min, max time.Time) { 1821 _, timeRange, err := influxql.ConditionExpr(expr, nil) 1822 if err != nil { 1823 panic(err) 1824 } 1825 return timeRange.Min, timeRange.Max 1826 } 1827 1828 // mustParseTime parses an IS0-8601 string. Panic on error. 1829 func mustParseTime(s string) time.Time { 1830 t, err := time.Parse(time.RFC3339, s) 1831 if err != nil { 1832 panic(err.Error()) 1833 } 1834 return t 1835 } 1836 1837 // FieldMapper is a mockable implementation of influxql.FieldMapper. 1838 type FieldMapper struct { 1839 FieldDimensionsFn func(m *influxql.Measurement) (fields map[string]influxql.DataType, dimensions map[string]struct{}, err error) 1840 } 1841 1842 func (fm *FieldMapper) FieldDimensions(m *influxql.Measurement) (fields map[string]influxql.DataType, dimensions map[string]struct{}, err error) { 1843 return fm.FieldDimensionsFn(m) 1844 } 1845 1846 func (fm *FieldMapper) MapType(m *influxql.Measurement, field string) influxql.DataType { 1847 f, d, err := fm.FieldDimensions(m) 1848 if err != nil { 1849 return influxql.Unknown 1850 } 1851 1852 if typ, ok := f[field]; ok { 1853 return typ 1854 } 1855 if _, ok := d[field]; ok { 1856 return influxql.Tag 1857 } 1858 return influxql.Unknown 1859 } 1860 1861 func (fm *FieldMapper) CallType(name string, args []influxql.DataType) (influxql.DataType, error) { 1862 switch name { 1863 case "mean", "median", "integral", "stddev": 1864 return influxql.Float, nil 1865 case "count": 1866 return influxql.Integer, nil 1867 case "elapsed": 1868 return influxql.Integer, nil 1869 default: 1870 return args[0], nil 1871 } 1872 } 1873 1874 // BenchmarkExprNames benchmarks how long it takes to run ExprNames. 1875 func BenchmarkExprNames(b *testing.B) { 1876 exprs := make([]string, 100) 1877 for i := range exprs { 1878 exprs[i] = fmt.Sprintf("host = 'server%02d'", i) 1879 } 1880 condition := MustParseExpr(strings.Join(exprs, " OR ")) 1881 1882 b.ResetTimer() 1883 b.ReportAllocs() 1884 1885 for i := 0; i < b.N; i++ { 1886 refs := influxql.ExprNames(condition) 1887 if have, want := refs, []influxql.VarRef{{Val: "host"}}; !reflect.DeepEqual(have, want) { 1888 b.Fatalf("unexpected expression names: have=%s want=%s", have, want) 1889 } 1890 } 1891 } 1892 1893 type FunctionValuer struct{} 1894 1895 var _ influxql.CallValuer = FunctionValuer{} 1896 1897 func (FunctionValuer) Value(key string) (interface{}, bool) { 1898 return nil, false 1899 } 1900 1901 func (FunctionValuer) Call(name string, args []interface{}) (interface{}, bool) { 1902 switch name { 1903 case "abs": 1904 arg0 := args[0].(float64) 1905 return math.Abs(arg0), true 1906 case "pow": 1907 arg0, arg1 := args[0].(float64), args[1].(int64) 1908 return math.Pow(arg0, float64(arg1)), true 1909 default: 1910 return nil, false 1911 } 1912 } 1913 1914 // BenchmarkEval benchmarks how long it takes to run Eval. 1915 func BenchmarkEval(b *testing.B) { 1916 expr := MustParseExpr(`f1 + abs(f2) / pow(f3, 3)`) 1917 valuer := influxql.ValuerEval{ 1918 Valuer: influxql.MultiValuer( 1919 influxql.MapValuer(map[string]interface{}{ 1920 "f1": float64(15), 1921 "f2": float64(-3), 1922 "f3": float64(2), 1923 }), 1924 FunctionValuer{}, 1925 ), 1926 } 1927 1928 b.ReportAllocs() 1929 for i := 0; i < b.N; i++ { 1930 valuer.Eval(expr) 1931 } 1932 }