github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/sqlselect_test.go (about) 1 // Copyright 2020 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sqle 16 17 import ( 18 "context" 19 "testing" 20 "time" 21 22 "github.com/dolthub/go-mysql-server/sql" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 26 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 27 "github.com/dolthub/dolt/go/libraries/doltcore/doltdocs" 28 "github.com/dolthub/dolt/go/libraries/doltcore/dtestutils" 29 "github.com/dolthub/dolt/go/libraries/doltcore/envtestutils" 30 "github.com/dolthub/dolt/go/libraries/doltcore/row" 31 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 32 . "github.com/dolthub/dolt/go/libraries/doltcore/sql/sqltestutil" 33 "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables" 34 "github.com/dolthub/dolt/go/store/types" 35 ) 36 37 // Set to the name of a single test to run just that test, useful for debugging 38 const singleSelectQueryTest = "" //"Natural join with join clause" 39 40 // Set to false to run tests known to be broken 41 const skipBrokenSelect = true 42 43 // Structure for a test of a select query 44 type SelectTest struct { 45 // The name of this test. Names should be unique and descriptive. 46 Name string 47 // The query to run, excluding an ending semicolon 48 Query string 49 // The schema of the result of the query, nil if an error is expected 50 ExpectedSchema schema.Schema 51 // The schema of the result of the query, nil if an error is expected. Mutually exclusive with ExpectedSchema. Use if 52 // the schema is difficult to specify with dolt schemas. 53 ExpectedSqlSchema sql.Schema 54 // The rows this query should return, nil if an error is expected 55 ExpectedRows []sql.Row 56 // An expected error string 57 ExpectedErr string 58 // Setup logic to run before executing this test, after initial tables have been created and populated 59 AdditionalSetup SetupFn 60 // Whether to skip this test on SqlEngine (go-mysql-server) execution. 61 // Over time, this should become false for every query. 62 SkipOnSqlEngine bool 63 } 64 65 // BasicSelectTests cover basic select statement features and error handling 66 var BasicSelectTests = []SelectTest{ 67 { 68 Name: "select * on primary key", 69 Query: "select * from people where id = 2", 70 ExpectedRows: ToSqlRows(PeopleTestSchema, Bart), 71 ExpectedSchema: CompressSchema(PeopleTestSchema), 72 }, 73 { 74 Name: "select * ", 75 Query: "select * from people", 76 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa, Moe, Barney), 77 ExpectedSchema: CompressSchema(PeopleTestSchema), 78 }, 79 { 80 Name: "select *, limit 1", 81 Query: "select * from people limit 1", 82 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer), 83 ExpectedSchema: CompressSchema(PeopleTestSchema), 84 }, 85 { 86 Name: "select *, limit 1 offset 0", 87 Query: "select * from people limit 0,1", 88 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer), 89 ExpectedSchema: CompressSchema(PeopleTestSchema), 90 }, 91 { 92 Name: "select *, limit 1 offset 1", 93 Query: "select * from people limit 1 offset 1;", 94 ExpectedRows: ToSqlRows(PeopleTestSchema, Marge), 95 ExpectedSchema: CompressSchema(PeopleTestSchema), 96 }, 97 { 98 Name: "select *, limit 1 offset 5", 99 Query: "select * from people limit 5,1", 100 ExpectedRows: ToSqlRows(PeopleTestSchema, Barney), 101 ExpectedSchema: CompressSchema(PeopleTestSchema), 102 }, 103 { 104 Name: "select *, limit 1 offset 6", 105 Query: "select * from people limit 6,1", 106 ExpectedRows: ToSqlRows(PeopleTestSchema), 107 ExpectedSchema: CompressSchema(PeopleTestSchema), 108 }, 109 { 110 Name: "select *, limit 0", 111 Query: "select * from people limit 0", 112 ExpectedRows: ToSqlRows(PeopleTestSchema), 113 ExpectedSchema: CompressSchema(PeopleTestSchema), 114 }, 115 { 116 Name: "select *, limit 0 offset 0", 117 Query: "select * from people limit 0,0", 118 ExpectedRows: ToSqlRows(PeopleTestSchema), 119 ExpectedSchema: CompressSchema(PeopleTestSchema), 120 }, 121 { 122 Name: "select *, limit -1", 123 Query: "select * from people limit -1", 124 ExpectedErr: "Limit must be >= 0 if supplied", 125 }, 126 { 127 Name: "select *, offset -1", 128 Query: "select * from people limit -1,1", 129 ExpectedErr: "Offset must be >= 0 if supplied", 130 }, 131 { 132 Name: "select *, limit 100", 133 Query: "select * from people limit 100", 134 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa, Moe, Barney), 135 ExpectedSchema: CompressSchema(PeopleTestSchema), 136 }, 137 { 138 Name: "select *, where < int", 139 Query: "select * from people where age < 40", 140 ExpectedRows: ToSqlRows(PeopleTestSchema, Marge, Bart, Lisa), 141 ExpectedSchema: CompressSchema(PeopleTestSchema), 142 }, 143 { 144 Name: "select *, where < int, limit 1", 145 Query: "select * from people where age < 40 limit 1", 146 ExpectedRows: ToSqlRows(PeopleTestSchema, Marge), 147 ExpectedSchema: CompressSchema(PeopleTestSchema), 148 }, 149 { 150 Name: "select *, where < int, limit 2", 151 Query: "select * from people where age < 40 limit 2", 152 ExpectedRows: ToSqlRows(PeopleTestSchema, Marge, Bart), 153 ExpectedSchema: CompressSchema(PeopleTestSchema), 154 }, 155 { 156 Name: "select *, where < int, limit 100", 157 Query: "select * from people where age < 40 limit 100", 158 ExpectedRows: ToSqlRows(PeopleTestSchema, Marge, Bart, Lisa), 159 ExpectedSchema: CompressSchema(PeopleTestSchema), 160 }, 161 { 162 Name: "select *, order by int", 163 Query: "select * from people order by id", 164 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa, Moe, Barney), 165 ExpectedSchema: CompressSchema(PeopleTestSchema), 166 }, 167 { 168 Name: "select *, order by int desc", 169 Query: "select * from people order by id desc", 170 ExpectedRows: ToSqlRows(PeopleTestSchema, Barney, Moe, Lisa, Bart, Marge, Homer), 171 ExpectedSchema: CompressSchema(PeopleTestSchema), 172 }, 173 { 174 Name: "select *, order by float", 175 Query: "select * from people order by rating", 176 ExpectedRows: ToSqlRows(PeopleTestSchema, Barney, Moe, Marge, Homer, Bart, Lisa), 177 ExpectedSchema: CompressSchema(PeopleTestSchema), 178 }, 179 { 180 Name: "select *, order by string", 181 Query: "select * from people order by first_name", 182 ExpectedRows: ToSqlRows(PeopleTestSchema, Barney, Bart, Homer, Lisa, Marge, Moe), 183 ExpectedSchema: CompressSchema(PeopleTestSchema), 184 }, 185 { 186 Name: "select *, order by string,string", 187 Query: "select * from people order by last_name desc, first_name asc", 188 ExpectedRows: ToSqlRows(PeopleTestSchema, Moe, Bart, Homer, Lisa, Marge, Barney), 189 ExpectedSchema: CompressSchema(PeopleTestSchema), 190 }, 191 { 192 Name: "select *, order by with limit", 193 Query: "select * from people order by first_name limit 2", 194 ExpectedRows: ToSqlRows(PeopleTestSchema, Barney, Bart), 195 ExpectedSchema: CompressSchema(PeopleTestSchema), 196 }, 197 { 198 Name: "select *, order by string,string with limit", 199 Query: "select * from people order by last_name desc, first_name asc limit 2", 200 ExpectedRows: ToSqlRows(PeopleTestSchema, Moe, Bart), 201 ExpectedSchema: CompressSchema(PeopleTestSchema), 202 }, 203 { 204 Name: "select *, where > int reversed", 205 Query: "select * from people where 40 > age", 206 ExpectedRows: ToSqlRows(PeopleTestSchema, Marge, Bart, Lisa), 207 ExpectedSchema: CompressSchema(PeopleTestSchema), 208 }, 209 { 210 Name: "select *, where <= int", 211 Query: "select * from people where age <= 40", 212 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa, Barney), 213 ExpectedSchema: CompressSchema(PeopleTestSchema), 214 }, 215 { 216 Name: "select *, where >= int reversed", 217 Query: "select * from people where 40 >= age", 218 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa, Barney), 219 ExpectedSchema: CompressSchema(PeopleTestSchema), 220 }, 221 { 222 Name: "select *, where > int", 223 Query: "select * from people where age > 40", 224 ExpectedRows: ToSqlRows(PeopleTestSchema, Moe), 225 ExpectedSchema: CompressSchema(PeopleTestSchema), 226 }, 227 { 228 Name: "select *, where < int reversed", 229 Query: "select * from people where 40 < age", 230 ExpectedRows: ToSqlRows(PeopleTestSchema, Moe), 231 ExpectedSchema: CompressSchema(PeopleTestSchema), 232 }, 233 { 234 Name: "select *, where >= int", 235 Query: "select * from people where age >= 40", 236 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Moe, Barney), 237 ExpectedSchema: CompressSchema(PeopleTestSchema), 238 }, 239 { 240 Name: "select *, where <= int reversed", 241 Query: "select * from people where 40 <= age", 242 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Moe, Barney), 243 ExpectedSchema: CompressSchema(PeopleTestSchema), 244 }, 245 { 246 Name: "select *, where > string", 247 Query: "select * from people where last_name > 'Simpson'", 248 ExpectedRows: ToSqlRows(PeopleTestSchema, Moe), 249 ExpectedSchema: CompressSchema(PeopleTestSchema), 250 }, 251 { 252 Name: "select *, where < string", 253 Query: "select * from people where last_name < 'Simpson'", 254 ExpectedRows: ToSqlRows(PeopleTestSchema, Barney), 255 ExpectedSchema: CompressSchema(PeopleTestSchema), 256 }, 257 { 258 Name: "select *, where = string", 259 Query: "select * from people where last_name = 'Simpson'", 260 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa), 261 ExpectedSchema: CompressSchema(PeopleTestSchema), 262 }, 263 { 264 Name: "select *, where > float", 265 Query: "select * from people where rating > 8.0 order by id", 266 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Bart, Lisa), 267 ExpectedSchema: CompressSchema(PeopleTestSchema), 268 }, 269 { 270 Name: "select *, where < float", 271 Query: "select * from people where rating < 8.0", 272 ExpectedRows: ToSqlRows(PeopleTestSchema, Moe, Barney), 273 ExpectedSchema: CompressSchema(PeopleTestSchema), 274 }, 275 { 276 Name: "select *, where = float", 277 Query: "select * from people where rating = 8.0", 278 ExpectedRows: ToSqlRows(PeopleTestSchema, Marge), 279 ExpectedSchema: CompressSchema(PeopleTestSchema), 280 }, 281 { 282 Name: "select *, where < float reversed", 283 Query: "select * from people where 8.0 < rating", 284 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Bart, Lisa), 285 ExpectedSchema: CompressSchema(PeopleTestSchema), 286 }, 287 { 288 Name: "select *, where > float reversed", 289 Query: "select * from people where 8.0 > rating", 290 ExpectedRows: ToSqlRows(PeopleTestSchema, Moe, Barney), 291 ExpectedSchema: CompressSchema(PeopleTestSchema), 292 }, 293 { 294 Name: "select *, where = float reversed", 295 Query: "select * from people where 8.0 = rating", 296 ExpectedRows: ToSqlRows(PeopleTestSchema, Marge), 297 ExpectedSchema: CompressSchema(PeopleTestSchema), 298 }, 299 { 300 Name: "select *, where bool = ", 301 Query: "select * from people where is_married = true", 302 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge), 303 ExpectedSchema: CompressSchema(PeopleTestSchema), 304 }, 305 { 306 Name: "select *, where bool = false ", 307 Query: "select * from people where is_married = false", 308 ExpectedRows: ToSqlRows(PeopleTestSchema, Bart, Lisa, Moe, Barney), 309 ExpectedSchema: CompressSchema(PeopleTestSchema), 310 }, 311 { 312 Name: "select *, where bool <> ", 313 Query: "select * from people where is_married <> false", 314 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge), 315 ExpectedSchema: CompressSchema(PeopleTestSchema), 316 }, 317 { 318 Name: "select *, where bool", 319 Query: "select * from people where is_married", 320 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge), 321 ExpectedSchema: CompressSchema(PeopleTestSchema), 322 }, 323 { 324 Name: "select *, and clause", 325 Query: "select * from people where is_married and age > 38", 326 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer), 327 ExpectedSchema: CompressSchema(PeopleTestSchema), 328 }, 329 { 330 Name: "select *, or clause", 331 Query: "select * from people where is_married or age < 20", 332 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa), 333 ExpectedSchema: CompressSchema(PeopleTestSchema), 334 }, 335 { 336 Name: "select *, in clause string", 337 Query: "select * from people where first_name in ('Homer', 'Marge')", 338 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge), 339 ExpectedSchema: CompressSchema(PeopleTestSchema), 340 }, 341 { 342 Name: "select *, in clause integer", 343 Query: "select * from people where age in (-10, 40)", 344 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Barney), 345 ExpectedSchema: CompressSchema(PeopleTestSchema), 346 }, 347 { 348 Name: "select *, in clause float", 349 Query: "select * from people where rating in (-10.0, 8.5)", 350 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer), 351 ExpectedSchema: CompressSchema(PeopleTestSchema), 352 }, 353 { 354 Name: "select *, in clause, mixed types", 355 Query: "select * from people where first_name in ('Homer', 40)", 356 ExpectedSchema: CompressSchema(PeopleTestSchema), 357 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer), 358 }, 359 { 360 Name: "select *, in clause, mixed numeric types", 361 Query: "select * from people where age in (-10.0, 40)", 362 ExpectedSchema: CompressSchema(PeopleTestSchema), 363 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Barney), 364 }, 365 { 366 Name: "select *, not in clause", 367 Query: "select * from people where first_name not in ('Homer', 'Marge')", 368 ExpectedRows: ToSqlRows(PeopleTestSchema, Bart, Lisa, Moe, Barney), 369 ExpectedSchema: CompressSchema(PeopleTestSchema), 370 }, 371 { 372 Name: "select *, in clause single element", 373 Query: "select * from people where first_name in ('Homer')", 374 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer), 375 ExpectedSchema: CompressSchema(PeopleTestSchema), 376 }, 377 { 378 Name: "select *, in clause single type mismatch", 379 Query: "select * from people where first_name in (1.0)", 380 ExpectedRows: ToSqlRows(PeopleTestSchema), 381 ExpectedSchema: CompressSchema(PeopleTestSchema), 382 }, 383 { 384 Name: "select *, is null clause ", 385 Query: "select * from people where uuid is null", 386 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer), 387 ExpectedSchema: CompressSchema(PeopleTestSchema), 388 }, 389 { 390 Name: "select *, is not null clause ", 391 Query: "select * from people where uuid is not null", 392 ExpectedRows: ToSqlRows(PeopleTestSchema, Marge, Bart, Lisa, Moe, Barney), 393 ExpectedSchema: CompressSchema(PeopleTestSchema), 394 }, 395 { 396 Name: "select *, is true clause ", 397 Query: "select * from people where is_married is true", 398 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge), 399 ExpectedSchema: CompressSchema(PeopleTestSchema), 400 }, 401 { 402 Name: "select *, is not true clause ", 403 Query: "select * from people where is_married is not true", 404 ExpectedRows: ToSqlRows(PeopleTestSchema, Bart, Lisa, Moe, Barney), 405 ExpectedSchema: CompressSchema(PeopleTestSchema), 406 }, 407 { 408 Name: "select *, is false clause ", 409 Query: "select * from people where is_married is false", 410 ExpectedRows: ToSqlRows(PeopleTestSchema, Bart, Lisa, Moe, Barney), 411 ExpectedSchema: CompressSchema(PeopleTestSchema), 412 }, 413 { 414 Name: "select *, is not false clause ", 415 Query: "select * from people where is_married is not false", 416 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Marge), 417 ExpectedSchema: CompressSchema(PeopleTestSchema), 418 }, 419 { 420 Name: "select *, is true clause on non-bool column", 421 Query: "select * from people where age is true", 422 ExpectedRows: ToSqlRows(PeopleTestSchema, AllPeopleRows...), 423 ExpectedSchema: CompressSchema(PeopleTestSchema), 424 }, 425 { 426 Name: "binary expression in select", 427 Query: "select age + 1 as a from people where is_married order by a", 428 ExpectedRows: ToSqlRows(NewResultSetSchema("a", types.IntKind), NewResultSetRow(types.Int(39)), NewResultSetRow(types.Int(41))), 429 ExpectedSchema: NewResultSetSchema("a", types.IntKind), 430 }, 431 { 432 Name: "and expression in select", 433 Query: "select is_married and age >= 40 from people where last_name = 'Simpson' order by id limit 2", 434 ExpectedRows: []sql.Row{{true}, {false}}, 435 ExpectedSqlSchema: sql.Schema{ 436 &sql.Column{Name: "is_married and age >= 40", Type: sql.Int8}, 437 }, 438 }, 439 { 440 Name: "or expression in select", 441 Query: "select first_name, age <= 10 or age >= 40 as not_marge from people where last_name = 'Simpson' order by id desc", 442 ExpectedRows: []sql.Row{ 443 {"Lisa", true}, 444 {"Bart", true}, 445 {"Marge", false}, 446 {"Homer", true}, 447 }, 448 ExpectedSqlSchema: sql.Schema{ 449 &sql.Column{Name: "first_name", Type: sql.LongText}, 450 &sql.Column{Name: "not_marge", Type: sql.Int8}, 451 }, 452 }, 453 { 454 Name: "unary expression in select", 455 Query: "select -age as age from people where is_married order by age", 456 ExpectedRows: ToSqlRows(NewResultSetSchema("age", types.IntKind), NewResultSetRow(types.Int(-40)), NewResultSetRow(types.Int(-38))), 457 ExpectedSchema: NewResultSetSchema("age", types.IntKind), 458 }, 459 { 460 Name: "unary expression in select, alias named after column", 461 Query: "select -age as age from people where is_married order by people.age", 462 ExpectedRows: ToSqlRows(NewResultSetSchema("age", types.IntKind), NewResultSetRow(types.Int(-38)), NewResultSetRow(types.Int(-40))), 463 ExpectedSchema: NewResultSetSchema("age", types.IntKind), 464 SkipOnSqlEngine: true, // this seems to be a bug in the engine 465 }, 466 { 467 Name: "select *, -column", 468 Query: "select * from people where -rating = -8.5", 469 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer), 470 ExpectedSchema: CompressSchema(PeopleTestSchema), 471 }, 472 { 473 Name: "select *, -column, string type", 474 Query: "select * from people where -first_name = 'Homer'", 475 ExpectedSchema: CompressSchema(PeopleTestSchema), 476 ExpectedRows: ToSqlRows(PeopleTestSchema, AllPeopleRows...), // A little weird, but correct due to mysql type conversion rules (both expression evaluate to 0 after conversion) 477 }, 478 { 479 Name: "select *, binary + in where", 480 Query: "select * from people where age + 1 = 41", 481 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Barney), 482 ExpectedSchema: CompressSchema(PeopleTestSchema), 483 }, 484 { 485 Name: "select *, binary - in where", 486 Query: "select * from people where age - 1 = 39", 487 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Barney), 488 ExpectedSchema: CompressSchema(PeopleTestSchema), 489 }, 490 { 491 Name: "select *, binary / in where", 492 Query: "select * from people where age / 2 = 20", 493 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Barney), 494 ExpectedSchema: CompressSchema(PeopleTestSchema), 495 }, 496 { 497 Name: "select *, binary * in where", 498 Query: "select * from people where age * 2 = 80", 499 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Barney), 500 ExpectedSchema: CompressSchema(PeopleTestSchema), 501 }, 502 { 503 Name: "select *, binary % in where", 504 Query: "select * from people where age % 4 = 0", 505 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Lisa, Moe, Barney), 506 ExpectedSchema: CompressSchema(PeopleTestSchema), 507 }, 508 { 509 Name: "select *, complex binary expr in where", 510 Query: "select * from people where age / 4 + 2 * 2 = 14", 511 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Barney), 512 ExpectedSchema: CompressSchema(PeopleTestSchema), 513 }, 514 { 515 Name: "select *, binary + in where type mismatch", 516 Query: "select * from people where first_name + 1 = 41", 517 ExpectedErr: "Type mismatch evaluating expression 'first_name + 1'", 518 }, 519 { 520 Name: "select *, binary - in where type mismatch", 521 Query: "select * from people where first_name - 1 = 39", 522 ExpectedErr: "Type mismatch evaluating expression 'first_name - 1'", 523 }, 524 { 525 Name: "select *, binary / in where type mismatch", 526 Query: "select * from people where first_name / 2 = 20", 527 ExpectedErr: "Type mismatch evaluating expression 'first_name / 2'", 528 }, 529 { 530 Name: "select *, binary * in where type mismatch", 531 Query: "select * from people where first_name * 2 = 80", 532 ExpectedErr: "Type mismatch evaluating expression 'first_name * 2'", 533 }, 534 { 535 Name: "select *, binary % in where type mismatch", 536 Query: "select * from people where first_name % 4 = 0", 537 ExpectedErr: "Type mismatch evaluating expression 'first_name % 4'", 538 }, 539 { 540 Name: "select * with where, order by", 541 Query: "select * from people where `uuid` is not null and first_name <> 'Marge' order by last_name desc, age", 542 ExpectedRows: ToSqlRows(PeopleTestSchema, Moe, Lisa, Bart, Barney), 543 ExpectedSchema: CompressSchema(PeopleTestSchema), 544 }, 545 { 546 Name: "select subset of cols", 547 Query: "select first_name, last_name from people where age >= 40", 548 ExpectedRows: ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name", "last_name"), Homer, Moe, Barney), 549 ExpectedSchema: CompressSchema(PeopleTestSchema, "first_name", "last_name"), 550 }, 551 { 552 Name: "column aliases", 553 Query: "select first_name as f, last_name as l from people where age >= 40", 554 ExpectedRows: ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name", "last_name"), Homer, Moe, Barney), 555 ExpectedSchema: NewResultSetSchema("f", types.StringKind, "l", types.StringKind), 556 }, 557 { 558 Name: "duplicate column aliases", 559 Query: "select first_name as f, last_name as f from people where age >= 40", 560 ExpectedRows: ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name", "last_name"), Homer, Moe, Barney), 561 ExpectedSchema: NewResultSetSchema("f", types.StringKind, "f", types.StringKind), 562 }, 563 { 564 Name: "column selected more than once", 565 Query: "select first_name, first_name from people where age >= 40 order by id", 566 ExpectedRows: []sql.Row{ 567 {"Homer", "Homer"}, 568 {"Moe", "Moe"}, 569 {"Barney", "Barney"}, 570 }, 571 ExpectedSchema: NewResultSetSchema("first_name", types.StringKind, "first_name", types.StringKind), 572 }, 573 { 574 Name: "duplicate table selection", 575 Query: "select first_name as f, last_name as f from people, people where age >= 40", 576 ExpectedErr: "Non-unique table name / alias: people", 577 }, 578 { 579 Name: "duplicate table alias", 580 Query: "select * from people p, people p where age >= 40", 581 ExpectedErr: "Non-unique table name / alias: 'p'", 582 }, 583 { 584 Name: "column aliases in where clause", 585 Query: `select first_name as f, last_name as l from people where f = "Homer"`, 586 ExpectedErr: "Unknown column: 'f'", 587 SkipOnSqlEngine: true, // this is actually a bug (aliases aren't usable in filters) 588 }, 589 { 590 Name: "select subset of columns with order by", 591 Query: "select first_name from people order by age, first_name", 592 ExpectedRows: ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name"), Lisa, Bart, Marge, Barney, Homer, Moe), 593 ExpectedSchema: CompressSchema(PeopleTestSchema, "first_name"), 594 }, 595 { 596 Name: "column aliases with order by", 597 Query: "select first_name as f from people order by age, f", 598 ExpectedRows: ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name"), Lisa, Bart, Marge, Barney, Homer, Moe), 599 ExpectedSchema: NewResultSetSchema("f", types.StringKind), 600 }, 601 { 602 Name: "ambiguous column in order by", 603 Query: "select first_name as f, last_name as f from people order by f", 604 ExpectedErr: "Ambiguous column: 'f'", 605 SkipOnSqlEngine: true, // this is a bug in go-mysql-server 606 }, 607 { 608 Name: "table aliases", 609 Query: "select p.first_name as f, p.last_name as l from people p where p.first_name = 'Homer'", 610 ExpectedRows: ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name", "last_name"), Homer), 611 ExpectedSchema: NewResultSetSchema("f", types.StringKind, "l", types.StringKind), 612 }, 613 { 614 Name: "table aliases without column aliases", 615 Query: "select p.first_name, p.last_name from people p where p.first_name = 'Homer'", 616 ExpectedRows: ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name", "last_name"), Homer), 617 ExpectedSchema: NewResultSetSchema("first_name", types.StringKind, "last_name", types.StringKind), 618 }, 619 { 620 Name: "table aliases with bad alias", 621 Query: "select m.first_name as f, p.last_name as l from people p where p.f = 'Homer'", 622 ExpectedErr: "Unknown table: 'm'", 623 }, 624 { 625 Name: "column aliases, all columns", 626 Query: `select id as i, first_name as f, last_name as l, is_married as m, age as a, 627 rating as r, uuid as u, num_episodes as n from people 628 where age >= 40`, 629 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Moe, Barney), 630 ExpectedSchema: NewResultSetSchema("i", types.IntKind, "f", types.StringKind, 631 "l", types.StringKind, "m", types.BoolKind, "a", types.IntKind, "r", types.FloatKind, 632 "u", types.UUIDKind, "n", types.UintKind), 633 }, 634 { 635 Name: "select *, not equals", 636 Query: "select * from people where age <> 40", 637 ExpectedRows: ToSqlRows(PeopleTestSchema, Marge, Bart, Lisa, Moe), 638 ExpectedSchema: CompressSchema(PeopleTestSchema), 639 }, 640 { 641 Name: "empty result set", 642 Query: "select * from people where age > 80", 643 ExpectedRows: ToSqlRows(PeopleTestSchema), 644 ExpectedSchema: CompressSchema(PeopleTestSchema), 645 }, 646 { 647 Name: "empty result set with columns", 648 Query: "select id, age from people where age > 80", 649 ExpectedRows: ToSqlRows(PeopleTestSchema), 650 ExpectedSchema: CompressSchema(PeopleTestSchema, "id", "age"), 651 }, 652 { 653 Name: "unknown table", 654 Query: "select * from dne", 655 ExpectedErr: `Unknown table: 'dne'`, 656 }, 657 { 658 Name: "unknown diff table", 659 Query: "select * from dolt_diff_dne", 660 ExpectedErr: `Unknown table: 'dolt_diff_dne'`, 661 }, 662 { 663 Name: "unknown diff table", 664 Query: "select * from dolt_commit_diff_dne", 665 ExpectedErr: `Unknown table: 'dolt_commit_diff_dne'`, 666 }, 667 { 668 Name: "unknown history table", 669 Query: "select * from dolt_history_dne", 670 ExpectedErr: `Unknown table: 'dolt_history_dne'`, 671 }, 672 { 673 Name: "unknown table in join", 674 Query: "select * from people join dne", 675 ExpectedErr: `Unknown table: 'dne'`, 676 }, 677 { 678 Name: "no table", 679 Query: "select 1", 680 ExpectedSqlSchema: sql.Schema{ 681 &sql.Column{ 682 Name: "1", 683 Type: sql.Int8, 684 }, 685 }, 686 ExpectedRows: []sql.Row{{int8(1)}}, 687 }, 688 { 689 Name: "unknown column in where", 690 Query: "select * from people where dne > 8.0", 691 ExpectedErr: `Unknown column: 'dne'`, 692 }, 693 { 694 Name: "unknown column in order by", 695 Query: "select * from people where rating > 8.0 order by dne", 696 ExpectedErr: `Unknown column: 'dne'`, 697 }, 698 { 699 Name: "unsupported comparison", 700 Query: "select * from people where function(first_name)", 701 ExpectedErr: "not supported", 702 }, 703 { 704 Name: "type mismatch in where clause", 705 Query: `select * from people where id = "0"`, 706 ExpectedSchema: CompressSchema(PeopleTestSchema), 707 ExpectedRows: ToSqlRows(PeopleTestSchema, Homer), 708 }, 709 { 710 Name: "select * from log system table", 711 Query: "select * from dolt_log", 712 ExpectedRows: []sql.Row{ 713 { 714 "m8lrhp8bmfesmknc6d5iatmjbcjf17al", 715 "billy bob", 716 "bigbillieb@fake.horse", 717 time.Date(1970, 1, 1, 0, 0, 0, 0, &time.Location{}), 718 "Initialize data repository", 719 }, 720 }, 721 ExpectedSqlSchema: sql.Schema{ 722 &sql.Column{Name: "commit_hash", Type: sql.Text}, 723 &sql.Column{Name: "committer", Type: sql.Text}, 724 &sql.Column{Name: "email", Type: sql.Text}, 725 &sql.Column{Name: "date", Type: sql.Datetime}, 726 &sql.Column{Name: "message", Type: sql.Text}, 727 }, 728 }, 729 { 730 Name: "select * from conflicts system table", 731 Query: "select * from dolt_conflicts", 732 ExpectedRows: []sql.Row{}, 733 ExpectedSqlSchema: sql.Schema{ 734 &sql.Column{Name: "table", Type: sql.Text}, 735 &sql.Column{Name: "num_conflicts", Type: sql.Uint64}, 736 }, 737 }, 738 { 739 Name: "select * from branches system table", 740 Query: "select * from dolt_branches", 741 ExpectedRows: []sql.Row{ 742 { 743 "master", 744 "m8lrhp8bmfesmknc6d5iatmjbcjf17al", 745 "billy bob", "bigbillieb@fake.horse", 746 time.Date(1970, 1, 1, 0, 0, 0, 0, &time.Location{}), 747 "Initialize data repository", 748 }, 749 }, 750 ExpectedSqlSchema: sql.Schema{ 751 &sql.Column{Name: "name", Type: sql.Text}, 752 &sql.Column{Name: "hash", Type: sql.Text}, 753 &sql.Column{Name: "latest_committer", Type: sql.Text}, 754 &sql.Column{Name: "latest_committer_email", Type: sql.Text}, 755 &sql.Column{Name: "latest_commit_date", Type: sql.Datetime}, 756 &sql.Column{Name: "latest_commit_message", Type: sql.Text}, 757 }, 758 }, 759 } 760 761 var sqlDiffSchema = sql.Schema{ 762 &sql.Column{Name: "to_id", Type: sql.Int64}, 763 &sql.Column{Name: "to_first_name", Type: sql.LongText}, 764 &sql.Column{Name: "to_last_name", Type: sql.LongText}, 765 &sql.Column{Name: "to_addr", Type: sql.LongText}, 766 &sql.Column{Name: "from_id", Type: sql.Int64}, 767 &sql.Column{Name: "from_first_name", Type: sql.LongText}, 768 &sql.Column{Name: "from_last_name", Type: sql.LongText}, 769 &sql.Column{Name: "from_addr", Type: sql.LongText}, 770 &sql.Column{Name: "diff_type", Type: sql.Text}, 771 } 772 773 var SelectDiffTests = []SelectTest{ 774 { 775 Name: "select from diff system table", 776 Query: "select to_id, to_first_name, to_last_name, to_addr, from_id, from_first_name, from_last_name, from_addr, diff_type from dolt_diff_test_table", 777 ExpectedRows: ToSqlRows(DiffSchema, 778 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(6), 1: types.String("Katie"), 2: types.String("McCulloch"), 14: types.String("added")})), 779 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 7: types.Int(0), 8: types.String("Aaron"), 9: types.String("Son"), 10: types.String("123 Fake St"), 14: types.String("modified")})), 780 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 7: types.Int(1), 8: types.String("Brian"), 9: types.String("Hendriks"), 10: types.String("456 Bull Ln"), 14: types.String("modified")})), 781 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 7: types.Int(2), 8: types.String("Tim"), 9: types.String("Sehn"), 10: types.String("789 Not Real Ct"), 14: types.String("modified")})), 782 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 3: types.String("-1 Imaginary Wy"), 7: types.Int(3), 8: types.String("Zach"), 9: types.String("Musgrave"), 14: types.String("modified")})), 783 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(5), 1: types.String("Daylon"), 2: types.String("Wilkins"), 14: types.String("added")})), 784 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 14: types.String("added")})), 785 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 14: types.String("added")})), 786 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 14: types.String("added")})), 787 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 14: types.String("added")})), 788 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele"), 14: types.String("added")})), 789 ), 790 ExpectedSqlSchema: sqlDiffSchema, 791 }, 792 { 793 Name: "select from diff system table with to commit", 794 Query: "select to_id, to_first_name, to_last_name, to_addr, from_id, from_first_name, from_last_name, from_addr, diff_type from dolt_diff_test_table where to_commit = 'WORKING'", 795 ExpectedRows: ToSqlRows(DiffSchema, 796 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(6), 1: types.String("Katie"), 2: types.String("McCulloch"), 14: types.String("added")})), 797 ), 798 ExpectedSqlSchema: sqlDiffSchema, 799 }, 800 // TODO: fix dependencies to hashof function can be registered and used here, also create branches when generating the history so that different from and to commits can be tested. 801 /*{ 802 Name: "select from diff system table with from and to commit and test insensitive name", 803 Query: "select to_id, to_first_name, to_last_name, to_addr, to_age_4, to_age_5, from_id, from_first_name, from_last_name, from_addr, from_age_4, from_age_5, diff_type from dolt_diff_TeSt_TaBlE where from_commit = 'add-age' and to_commit = 'master'", 804 ExpectedRows: ToSqlRows(DiffSchema, 805 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{7: types.Int(0), 8: types.String("Aaron"), 9: types.String("Son"), 11: types.Int(35), 0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 5: types.Uint(35), 13: types.String("add-age"), 6: types.String("master"), 14: types.String("modified")})), 806 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{7: types.Int(1), 8: types.String("Brian"), 9: types.String("Hendriks"), 11: types.Int(38), 0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 5: types.Uint(38), 13: types.String("add-age"), 6: types.String("master"), 14: types.String("modified")})), 807 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{7: types.Int(2), 8: types.String("Tim"), 9: types.String("Sehn"), 11: types.Int(37), 0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 5: types.Uint(37), 13: types.String("add-age"), 6: types.String("master"), 14: types.String("modified")})), 808 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{7: types.Int(3), 8: types.String("Zach"), 9: types.String("Musgrave"), 11: types.Int(37), 0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 3: types.String("-1 Imaginary Wy"), 5: types.Uint(37), 13: types.String("add-age"), 6: types.String("master"), 14: types.String("modified")})), 809 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele"), 3: types.NullValue, 13: types.String("add-age"), 6: types.String("master"), 14: types.String("added")})), 810 mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(5), 1: types.String("Daylon"), 2: types.String("Wilkins"), 3: types.NullValue, 13: types.String("add-age"), 6: types.String("master"), 14: types.String("added")})), 811 ), 812 ExpectedSqlSchema: sqlDiffSchema, 813 },*/ 814 } 815 816 var AsOfTests = []SelectTest{ 817 { 818 Name: "select * from seed branch", 819 Query: "select * from test_table as of 'seed'", 820 ExpectedRows: ToSqlRows(InitialHistSch, 821 mustRow(row.New(types.Format_Default, InitialHistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son")})), 822 mustRow(row.New(types.Format_Default, InitialHistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks")})), 823 mustRow(row.New(types.Format_Default, InitialHistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn")})), 824 ), 825 ExpectedSchema: InitialHistSch, 826 }, 827 { 828 Name: "select * from add-age branch", 829 Query: "select * from test_table as of 'add-age'", 830 ExpectedRows: ToSqlRows(AddAgeAt4HistSch, 831 mustRow(row.New(types.Format_Default, AddAgeAt4HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 4: types.Int(35)})), 832 mustRow(row.New(types.Format_Default, AddAgeAt4HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 4: types.Int(38)})), 833 mustRow(row.New(types.Format_Default, AddAgeAt4HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 4: types.Int(37)})), 834 mustRow(row.New(types.Format_Default, AddAgeAt4HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 4: types.Int(37)})), 835 ), 836 ExpectedSchema: AddAgeAt4HistSch, 837 }, 838 { 839 Name: "select * from master branch", 840 Query: "select * from test_table as of 'master'", 841 ExpectedRows: ToSqlRows(ReaddAgeAt5HistSch, 842 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 5: types.Uint(35)})), 843 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 5: types.Uint(38)})), 844 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 5: types.Uint(37)})), 845 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 3: types.String("-1 Imaginary Wy"), 5: types.Uint(37)})), 846 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})), 847 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(5), 1: types.String("Daylon"), 2: types.String("Wilkins")})), 848 ), 849 ExpectedSchema: ReaddAgeAt5HistSch, 850 }, 851 { 852 Name: "select * from HEAD~", 853 Query: "select * from test_table as of 'HEAD~'", 854 ExpectedRows: ToSqlRows(AddAddrAt3HistSch, 855 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St")})), 856 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln")})), 857 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct")})), 858 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave")})), 859 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})), 860 ), 861 ExpectedSchema: AddAddrAt3HistSch, 862 }, 863 { 864 Name: "select * from HEAD^", 865 Query: "select * from test_table as of 'HEAD^'", 866 ExpectedRows: ToSqlRows(AddAddrAt3HistSch, 867 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St")})), 868 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln")})), 869 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct")})), 870 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave")})), 871 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})), 872 ), 873 ExpectedSchema: AddAddrAt3HistSch, 874 }, 875 { 876 Name: "select * from master^", 877 Query: "select * from test_table as of 'master^'", 878 ExpectedRows: ToSqlRows(AddAddrAt3HistSch, 879 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St")})), 880 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln")})), 881 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct")})), 882 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave")})), 883 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})), 884 ), 885 ExpectedSchema: AddAddrAt3HistSch, 886 }, 887 // Because of an implementation detail in the way we process history for test setup, each commit is 2 hours apart. 888 { 889 Name: "select * from timestamp after HEAD", 890 Query: "select * from test_table as of CONVERT('1970-01-01 10:00:00', DATETIME)", 891 ExpectedRows: ToSqlRows(ReaddAgeAt5HistSch, 892 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 5: types.Uint(35)})), 893 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 5: types.Uint(38)})), 894 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 5: types.Uint(37)})), 895 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 3: types.String("-1 Imaginary Wy"), 5: types.Uint(37)})), 896 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})), 897 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(5), 1: types.String("Daylon"), 2: types.String("Wilkins")})), 898 ), 899 ExpectedSchema: ReaddAgeAt5HistSch, 900 }, 901 { 902 Name: "select * from timestamp, HEAD exact", 903 Query: "select * from test_table as of CONVERT('1970-01-01 08:00:00', DATETIME)", 904 ExpectedRows: ToSqlRows(ReaddAgeAt5HistSch, 905 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 5: types.Uint(35)})), 906 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 5: types.Uint(38)})), 907 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 5: types.Uint(37)})), 908 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 3: types.String("-1 Imaginary Wy"), 5: types.Uint(37)})), 909 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})), 910 mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(5), 1: types.String("Daylon"), 2: types.String("Wilkins")})), 911 ), 912 ExpectedSchema: ReaddAgeAt5HistSch, 913 }, 914 { 915 Name: "select * from timestamp, HEAD~ + 1", 916 Query: "select * from test_table as of CONVERT('1970-01-01 07:00:00', DATETIME)", 917 ExpectedRows: ToSqlRows(AddAddrAt3HistSch, 918 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St")})), 919 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln")})), 920 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct")})), 921 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave")})), 922 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})), 923 ), 924 ExpectedSchema: AddAddrAt3HistSch, 925 }, 926 { 927 Name: "select * from timestamp, HEAD~", 928 Query: "select * from test_table as of CONVERT('1970-01-01 06:00:00', DATETIME)", 929 ExpectedRows: ToSqlRows(AddAddrAt3HistSch, 930 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St")})), 931 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln")})), 932 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct")})), 933 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave")})), 934 mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})), 935 ), 936 ExpectedSchema: AddAddrAt3HistSch, 937 }, 938 { 939 Name: "select * from timestamp, before table creation", 940 Query: "select * from test_table as of CONVERT('1970-01-01 02:00:00', DATETIME)", 941 ExpectedErr: "not found", 942 }, 943 } 944 945 // SQL is supposed to be case insensitive. These are tests of that promise. 946 // Many of these are currently broken in go-myqsl-server. The queries return the correct results in all cases, but the 947 // column names in the result schemas often have the wrong case. They sometimes use the case from the table, rather 948 // than the case of the expression in the query (the correct behavior). This is a minor issue, but we should fix it 949 // eventually. 950 var CaseSensitivityTests = []SelectTest{ 951 { 952 Name: "table name has mixed case, select lower case", 953 AdditionalSetup: CreateTableWithRowsFn("MiXeDcAsE", 954 NewSchema("test", types.StringKind), 955 []types.Value{types.String("1")}), 956 Query: "select test from mixedcase", 957 ExpectedSchema: NewResultSetSchema("test", types.StringKind), 958 ExpectedRows: []sql.Row{{"1"}}, 959 }, 960 { 961 Name: "table name has mixed case, select upper case", 962 AdditionalSetup: CreateTableWithRowsFn("MiXeDcAsE", 963 NewSchema("test", types.StringKind), 964 []types.Value{types.String("1")}), 965 Query: "select test from MIXEDCASE", 966 ExpectedSchema: NewResultSetSchema("test", types.StringKind), 967 ExpectedRows: []sql.Row{{"1"}}, 968 }, 969 { 970 Name: "qualified select *", 971 AdditionalSetup: CreateTableWithRowsFn("MiXeDcAsE", 972 NewSchema("test", types.StringKind), 973 []types.Value{types.String("1")}), 974 Query: "select mixedcAse.* from MIXEDCASE", 975 ExpectedSchema: NewResultSetSchema("test", types.StringKind), 976 ExpectedRows: []sql.Row{{"1"}}, 977 }, 978 { 979 Name: "qualified select column", 980 AdditionalSetup: CreateTableWithRowsFn("MiXeDcAsE", 981 NewSchema("test", types.StringKind), 982 []types.Value{types.String("1")}), 983 Query: "select mixedcAse.TeSt from MIXEDCASE", 984 ExpectedSchema: NewResultSetSchema("TeSt", types.StringKind), 985 ExpectedRows: []sql.Row{{"1"}}, 986 SkipOnSqlEngine: true, 987 }, 988 { 989 Name: "table alias select *", 990 AdditionalSetup: CreateTableWithRowsFn("MiXeDcAsE", 991 NewSchema("test", types.StringKind), 992 []types.Value{types.String("1")}), 993 Query: "select Mc.* from MIXEDCASE as mc", 994 ExpectedSchema: NewResultSetSchema("test", types.StringKind), 995 ExpectedRows: []sql.Row{{"1"}}, 996 }, 997 { 998 Name: "table alias select column", 999 AdditionalSetup: CreateTableWithRowsFn("MiXeDcAsE", 1000 NewSchema("test", types.StringKind), 1001 []types.Value{types.String("1")}), 1002 Query: "select mC.TeSt from MIXEDCASE as MC", 1003 ExpectedSchema: NewResultSetSchema("TeSt", types.StringKind), 1004 ExpectedRows: []sql.Row{{"1"}}, 1005 SkipOnSqlEngine: true, 1006 }, 1007 { 1008 Name: "multiple tables with the same case-insensitive name, exact match", 1009 AdditionalSetup: Compose( 1010 // the table name passed to NewSchemaForTable isn't important, except to get unique tags 1011 CreateTableWithRowsFn("tableName", NewSchemaForTable("tableName1", "test", types.StringKind), []types.Value{types.String("1")}), 1012 CreateTableWithRowsFn("TABLENAME", NewSchemaForTable("TABLENAME2", "test", types.StringKind)), 1013 CreateTableWithRowsFn("tablename", NewSchemaForTable("tablename3", "test", types.StringKind)), 1014 ), 1015 Query: "select test from tableName", 1016 ExpectedSchema: NewResultSetSchema("test", types.StringKind), 1017 ExpectedRows: []sql.Row{{"1"}}, 1018 }, 1019 { 1020 Name: "alias with same name as table", 1021 AdditionalSetup: Compose( 1022 CreateTableWithRowsFn("tableName", NewSchema("test", types.StringKind)), 1023 CreateTableWithRowsFn("other", NewSchema("othercol", types.StringKind)), 1024 ), 1025 Query: "select other.test from tablename as other, other", 1026 ExpectedErr: "Non-unique table name / alias: 'other'", 1027 }, 1028 { 1029 Name: "two table aliases with same name", 1030 AdditionalSetup: Compose( 1031 CreateTableWithRowsFn("tableName", NewSchema("test", types.StringKind)), 1032 CreateTableWithRowsFn("other", NewSchema("othercol", types.StringKind)), 1033 ), 1034 Query: "select bad.test from tablename as bad, other as bad", 1035 ExpectedErr: "Non-unique table name / alias: 'bad'", 1036 }, 1037 { 1038 Name: "column name has mixed case, select lower case", 1039 AdditionalSetup: CreateTableWithRowsFn("test", 1040 NewSchema("MiXeDcAsE", types.StringKind), 1041 []types.Value{types.String("1")}), 1042 Query: "select mixedcase from test", 1043 ExpectedSchema: NewResultSetSchema("mixedcase", types.StringKind), 1044 ExpectedRows: []sql.Row{{"1"}}, 1045 SkipOnSqlEngine: true, 1046 }, 1047 { 1048 Name: "column name has mixed case, select upper case", 1049 AdditionalSetup: CreateTableWithRowsFn("test", 1050 NewSchema("MiXeDcAsE", types.StringKind), 1051 []types.Value{types.String("1")}), 1052 Query: "select MIXEDCASE from test", 1053 ExpectedSchema: NewResultSetSchema("MIXEDCASE", types.StringKind), 1054 ExpectedRows: []sql.Row{{"1"}}, 1055 SkipOnSqlEngine: true, 1056 }, 1057 { 1058 Name: "select with multiple matching columns, exact match", 1059 AdditionalSetup: CreateTableWithRowsFn("test", 1060 NewSchema("MiXeDcAsE", types.StringKind, "mixedcase", types.StringKind), 1061 []types.Value{types.String("1"), types.String("2")}), 1062 Query: "select mixedcase from test", 1063 ExpectedSchema: NewResultSetSchema("mixedcase", types.StringKind), 1064 ExpectedRows: []sql.Row{{"1"}}, 1065 SkipOnSqlEngine: true, // TODO: table should be illegal. field names cannot be the same case-insensitive 1066 }, 1067 { 1068 Name: "column is reserved word, select not backticked", 1069 AdditionalSetup: CreateTableWithRowsFn("test", 1070 NewSchema( 1071 "Timestamp", types.StringKind, 1072 "and", types.StringKind, 1073 "or", types.StringKind, 1074 "select", types.StringKind), 1075 []types.Value{types.String("1"), types.String("1.1"), types.String("aaa"), types.String("create")}), 1076 Query: "select Timestamp from test", 1077 ExpectedRows: []sql.Row{{"1"}}, 1078 ExpectedSchema: NewResultSetSchema("Timestamp", types.StringKind), 1079 }, 1080 { 1081 Name: "column is reserved word, qualified with table alias", 1082 AdditionalSetup: CreateTableWithRowsFn("test", 1083 NewSchema( 1084 "Timestamp", types.StringKind, 1085 "and", types.StringKind, 1086 "or", types.StringKind, 1087 "select", types.StringKind), 1088 []types.Value{types.String("1"), types.String("1.1"), types.String("aaa"), types.String("create")}), 1089 Query: "select t.Timestamp from test as t", 1090 ExpectedRows: []sql.Row{{"1"}}, 1091 ExpectedSchema: NewResultSetSchema("Timestamp", types.StringKind), 1092 }, 1093 { 1094 Name: "column is reserved word, select not backticked #2", 1095 AdditionalSetup: CreateTableWithRowsFn("test", 1096 NewSchema("YeAr", types.StringKind), 1097 []types.Value{types.String("1")}), 1098 Query: "select Year from test", 1099 ExpectedSchema: NewResultSetSchema("Year", types.StringKind), 1100 ExpectedRows: []sql.Row{{"1"}}, 1101 SkipOnSqlEngine: true, 1102 }, 1103 { 1104 Name: "column is reserved word, select backticked", 1105 AdditionalSetup: CreateTableWithRowsFn("test", 1106 NewSchema( 1107 "Timestamp", types.StringKind, 1108 "and", types.StringKind, 1109 "or", types.StringKind, 1110 "select", types.StringKind), 1111 []types.Value{types.String("1"), types.String("1.1"), types.String("aaa"), types.String("create")}), 1112 Query: "select `Timestamp` from test", 1113 ExpectedRows: []sql.Row{{"1"}}, 1114 ExpectedSchema: NewResultSetSchema("Timestamp", types.StringKind), 1115 SkipOnSqlEngine: true, 1116 }, 1117 { 1118 Name: "column is reserved word, select backticked #2", 1119 AdditionalSetup: CreateTableWithRowsFn("test", 1120 NewSchema( 1121 "Year", types.StringKind, 1122 "and", types.StringKind, 1123 "or", types.StringKind, 1124 "select", types.StringKind), 1125 []types.Value{types.String("1"), types.String("1.1"), types.String("aaa"), types.String("create")}), 1126 Query: "select `Year`, `OR`, `SELect`, `anD` from test", 1127 ExpectedSchema: NewResultSetSchema( 1128 "Year", types.StringKind, 1129 "OR", types.StringKind, 1130 "SELect", types.StringKind, 1131 "anD", types.StringKind), 1132 ExpectedRows: []sql.Row{{"1", "aaa", "create", "1.1"}}, 1133 SkipOnSqlEngine: true, 1134 }, 1135 { 1136 Name: "column is reserved word, table qualified", 1137 AdditionalSetup: CreateTableWithRowsFn("test", 1138 NewSchema( 1139 "Year", types.StringKind, 1140 "and", types.StringKind, 1141 "or", types.StringKind, 1142 "select", types.StringKind), 1143 []types.Value{types.String("1"), types.String("1.1"), types.String("aaa"), types.String("create")}), 1144 Query: "select Year, t.OR, t.SELect, t.anD from test t", 1145 ExpectedSchema: NewResultSetSchema( 1146 "Year", types.StringKind, 1147 "OR", types.StringKind, 1148 "SELect", types.StringKind, 1149 "anD", types.StringKind), 1150 ExpectedRows: []sql.Row{{"1", "aaa", "create", "1.1"}}, 1151 SkipOnSqlEngine: true, 1152 }, 1153 } 1154 1155 // Tests of join functionality, basically any query involving more than one table should go here for now. 1156 var JoinTests = []SelectTest{ 1157 { 1158 Name: "Full cross product", 1159 Query: `select * from people, episodes`, 1160 ExpectedRows: ToSqlRows(CompressSchemas(PeopleTestSchema, EpisodesTestSchema), 1161 ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep1), 1162 ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep2), 1163 ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep3), 1164 ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep4), 1165 ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep1), 1166 ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep2), 1167 ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep3), 1168 ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep4), 1169 ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep1), 1170 ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep2), 1171 ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep3), 1172 ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep4), 1173 ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep1), 1174 ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep2), 1175 ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep3), 1176 ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep4), 1177 ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep1), 1178 ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep2), 1179 ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep3), 1180 ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep4), 1181 ConcatRows(PeopleTestSchema, Barney, EpisodesTestSchema, Ep1), 1182 ConcatRows(PeopleTestSchema, Barney, EpisodesTestSchema, Ep2), 1183 ConcatRows(PeopleTestSchema, Barney, EpisodesTestSchema, Ep3), 1184 ConcatRows(PeopleTestSchema, Barney, EpisodesTestSchema, Ep4), 1185 ), 1186 ExpectedSchema: CompressSchemas(PeopleTestSchema, EpisodesTestSchema), 1187 }, 1188 { 1189 Name: "Natural join with where clause", 1190 Query: `select * from people p, episodes e where e.id = p.id`, 1191 ExpectedRows: ToSqlRows(CompressSchemas(PeopleTestSchema, EpisodesTestSchema), 1192 ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep1), 1193 ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep2), 1194 ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep3), 1195 ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep4), 1196 ), 1197 ExpectedSchema: CompressSchemas(PeopleTestSchema, EpisodesTestSchema), 1198 }, 1199 { 1200 Name: "Three table natural join with where clause", 1201 Query: `select p.*, e.* from people p, episodes e, appearances a where a.episode_id = e.id and a.character_id = p.id`, 1202 ExpectedRows: ToSqlRows(CompressSchemas(PeopleTestSchema, EpisodesTestSchema), 1203 ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep1), 1204 ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep2), 1205 ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep3), 1206 ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep1), 1207 ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep3), 1208 ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep2), 1209 ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep2), 1210 ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep3), 1211 ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep2), 1212 ConcatRows(PeopleTestSchema, Barney, EpisodesTestSchema, Ep3), 1213 ), 1214 ExpectedSchema: CompressSchemas(PeopleTestSchema, EpisodesTestSchema), 1215 }, 1216 // TODO: error messages are different in SQL engine 1217 { 1218 Name: "ambiguous column in select", 1219 Query: `select id from people p, episodes e, appearances a where a.episode_id = e.id and a.character_id = p.id`, 1220 ExpectedErr: "Ambiguous column: 'id'", 1221 }, 1222 { 1223 Name: "ambiguous column in where", 1224 Query: `select p.*, e.* from people p, episodes e, appearances a where a.episode_id = id and a.character_id = id`, 1225 ExpectedErr: "Ambiguous column: 'id'", 1226 }, 1227 { 1228 Name: "Natural join with where clause, select subset of columns", 1229 Query: `select e.id, p.id, e.name, p.first_name, p.last_name from people p, episodes e where e.id = p.id`, 1230 ExpectedRows: ToSqlRows( 1231 NewResultSetSchema("id", types.IntKind, "id", types.IntKind, 1232 "name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind), 1233 NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")), 1234 NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")), 1235 NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")), 1236 NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")), 1237 ), 1238 ExpectedSchema: NewResultSetSchema("id", types.IntKind, "id", types.IntKind, 1239 "name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind), 1240 }, 1241 { 1242 Name: "Natural join with where clause and column aliases", 1243 Query: "select e.id as eid, p.id as pid, e.name as ename, p.first_name as pfirst_name, p.last_name last_name from people p, episodes e where e.id = p.id", 1244 ExpectedRows: ToSqlRows( 1245 NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind, 1246 "ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind), 1247 NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")), 1248 NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")), 1249 NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")), 1250 NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")), 1251 ), 1252 ExpectedSchema: NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind, 1253 "ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind), 1254 }, 1255 { 1256 Name: "Natural join with where clause and quoted column alias", 1257 Query: "select e.id as eid, p.id as `p.id`, e.name as ename, p.first_name as pfirst_name, p.last_name last_name from people p, episodes e where e.id = p.id", 1258 ExpectedRows: ToSqlRows( 1259 NewResultSetSchema("eid", types.IntKind, "p.id", types.IntKind, 1260 "ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind), 1261 NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")), 1262 NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")), 1263 NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")), 1264 NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")), 1265 ), 1266 ExpectedSchema: NewResultSetSchema("eid", types.IntKind, "p.id", types.IntKind, 1267 "ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind), 1268 }, 1269 { 1270 Name: "Natural join with join clause", 1271 Query: `select * from people p join episodes e on e.id = p.id`, 1272 ExpectedRows: ToSqlRows(CompressSchemas(PeopleTestSchema, EpisodesTestSchema), 1273 ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep1), 1274 ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep2), 1275 ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep3), 1276 ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep4), 1277 ), 1278 ExpectedSchema: CompressSchemas(PeopleTestSchema, EpisodesTestSchema), 1279 }, 1280 { 1281 Name: "Three table natural join with join clause", 1282 Query: `select p.*, e.* from people p join appearances a on a.character_id = p.id join episodes e on a.episode_id = e.id`, 1283 ExpectedRows: ToSqlRows(CompressSchemas(PeopleTestSchema, EpisodesTestSchema), 1284 ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep1), 1285 ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep2), 1286 ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep3), 1287 ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep1), 1288 ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep3), 1289 ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep2), 1290 ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep2), 1291 ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep3), 1292 ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep2), 1293 ConcatRows(PeopleTestSchema, Barney, EpisodesTestSchema, Ep3), 1294 ), 1295 ExpectedSchema: CompressSchemas(PeopleTestSchema, EpisodesTestSchema), 1296 }, 1297 { 1298 Name: "Natural join with join clause, select subset of columns", 1299 Query: `select e.id, p.id, e.name, p.first_name, p.last_name from people p join episodes e on e.id = p.id`, 1300 ExpectedRows: ToSqlRows( 1301 NewResultSetSchema("id", types.IntKind, "id", types.IntKind, 1302 "name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind), 1303 NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")), 1304 NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")), 1305 NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")), 1306 NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")), 1307 ), 1308 ExpectedSchema: NewResultSetSchema("id", types.IntKind, "id", types.IntKind, 1309 "name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind), 1310 }, 1311 { 1312 Name: "Natural join with join clause, select subset of columns, join columns not selected", 1313 Query: `select e.name, p.first_name, p.last_name from people p join episodes e on e.id = p.id`, 1314 ExpectedRows: ToSqlRows(NewResultSetSchema("name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind), 1315 NewResultSetRow(types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")), 1316 NewResultSetRow(types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")), 1317 NewResultSetRow(types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")), 1318 NewResultSetRow(types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")), 1319 ), 1320 ExpectedSchema: NewResultSetSchema("name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind), 1321 }, 1322 { 1323 Name: "Natural join with join clause, select subset of columns, order by clause", 1324 Query: `select e.id, p.id, e.name, p.first_name, p.last_name from people p 1325 join episodes e on e.id = p.id 1326 order by e.name`, 1327 ExpectedRows: ToSqlRows( 1328 NewResultSetSchema("id", types.IntKind, "id", types.IntKind, 1329 "name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind), 1330 NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")), 1331 NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")), 1332 NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")), 1333 NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")), 1334 ), 1335 ExpectedSchema: NewResultSetSchema("id", types.IntKind, "id", types.IntKind, 1336 "name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind), 1337 }, 1338 { 1339 Name: "Natural join with join clause, select subset of columns, order by clause on non-selected column", 1340 Query: `select e.id, p.id, e.name, p.first_name, p.last_name from people p 1341 join episodes e on e.id = p.id 1342 order by age`, 1343 ExpectedRows: ToSqlRows( 1344 NewResultSetSchema("id", types.IntKind, "id", types.IntKind, 1345 "name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind), 1346 NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")), 1347 NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")), 1348 NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")), 1349 NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")), 1350 ), 1351 ExpectedSchema: NewResultSetSchema("id", types.IntKind, "id", types.IntKind, 1352 "name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind), 1353 }, 1354 { 1355 Name: "Natural join with join clause and column aliases", 1356 Query: "select e.id as eid, p.id as pid, e.name as ename, p.first_name as pfirst_name, p.last_name last_name from people p join episodes e on e.id = p.id", 1357 ExpectedRows: ToSqlRows( 1358 NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind, 1359 "ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind), 1360 NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")), 1361 NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")), 1362 NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")), 1363 NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")), 1364 ), 1365 ExpectedSchema: NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind, 1366 "ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind), 1367 }, 1368 { 1369 Name: "Natural join with join clause and column aliases, order by", 1370 Query: "select e.id as eid, p.id as pid, e.name as ename, p.first_name as pfirst_name, p.last_name last_name from people p join episodes e on e.id = p.id order by ename", 1371 ExpectedRows: ToSqlRows( 1372 NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind, 1373 "ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind), 1374 NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")), 1375 NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")), 1376 NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")), 1377 NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")), 1378 ), 1379 ExpectedSchema: NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind, 1380 "ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind), 1381 }, 1382 { 1383 Name: "Natural join with join clause and quoted column alias", 1384 Query: "select e.id as eid, p.id as `p.id`, e.name as ename, p.first_name as pfirst_name, p.last_name last_name from people p join episodes e on e.id = p.id", 1385 ExpectedRows: ToSqlRows( 1386 NewResultSetSchema("eid", types.IntKind, "p.id", types.IntKind, 1387 "ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind), 1388 NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")), 1389 NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")), 1390 NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")), 1391 NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")), 1392 ), 1393 ExpectedSchema: NewResultSetSchema("eid", types.IntKind, "p.id", types.IntKind, 1394 "ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind), 1395 }, 1396 { 1397 Name: "Join from table with two key columns to table with one key column", 1398 Query: `select a.episode_id as eid, p.id as pid, p.first_name 1399 from appearances a join people p on a.character_id = p.id order by eid, pid`, 1400 ExpectedRows: ToSqlRows( 1401 NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind, 1402 "first_name", types.StringKind), 1403 NewResultSetRow(types.Int(1), types.Int(0), types.String("Homer")), 1404 NewResultSetRow(types.Int(1), types.Int(1), types.String("Marge")), 1405 NewResultSetRow(types.Int(2), types.Int(0), types.String("Homer")), 1406 NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart")), 1407 NewResultSetRow(types.Int(2), types.Int(3), types.String("Lisa")), 1408 NewResultSetRow(types.Int(2), types.Int(4), types.String("Moe")), 1409 NewResultSetRow(types.Int(3), types.Int(0), types.String("Homer")), 1410 NewResultSetRow(types.Int(3), types.Int(1), types.String("Marge")), 1411 NewResultSetRow(types.Int(3), types.Int(3), types.String("Lisa")), 1412 NewResultSetRow(types.Int(3), types.Int(5), types.String("Barney")), 1413 ), 1414 ExpectedSchema: NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind, 1415 "first_name", types.StringKind), 1416 }, 1417 } 1418 1419 func TestSelect(t *testing.T) { 1420 for _, test := range BasicSelectTests { 1421 t.Run(test.Name, func(t *testing.T) { 1422 testSelectQuery(t, test) 1423 }) 1424 } 1425 } 1426 1427 func TestDiffQueries(t *testing.T) { 1428 for _, test := range SelectDiffTests { 1429 t.Run(test.Name, func(t *testing.T) { 1430 testSelectDiffQuery(t, test) 1431 }) 1432 } 1433 } 1434 1435 func TestAsOfQueries(t *testing.T) { 1436 for _, test := range AsOfTests { 1437 t.Run(test.Name, func(t *testing.T) { 1438 // AS OF queries use the same history as the diff tests, so exercise the same test setup 1439 testSelectDiffQuery(t, test) 1440 }) 1441 } 1442 } 1443 1444 func TestJoins(t *testing.T) { 1445 for _, tt := range JoinTests { 1446 if tt.Name == "Join from table with two key columns to table with one key column" { 1447 t.Run(tt.Name, func(t *testing.T) { 1448 testSelectQuery(t, tt) 1449 }) 1450 } 1451 } 1452 } 1453 1454 // Tests of case sensitivity handling 1455 func TestCaseSensitivity(t *testing.T) { 1456 for _, tt := range CaseSensitivityTests { 1457 t.Run(tt.Name, func(t *testing.T) { 1458 testSelectQuery(t, tt) 1459 }) 1460 } 1461 } 1462 1463 var systemTableSelectTests = []SelectTest{ 1464 { 1465 Name: "select from dolt_docs", 1466 AdditionalSetup: CreateTableFn("dolt_docs", 1467 doltdocs.Schema, 1468 NewRowWithSchema(doltdocs.Schema, 1469 types.String("LICENSE.md"), 1470 types.String("A license")), 1471 ), 1472 Query: "select * from dolt_docs", 1473 ExpectedRows: ToSqlRows(CompressSchema(doltdocs.Schema), 1474 NewRow(types.String("LICENSE.md"), types.String("A license"))), 1475 ExpectedSchema: CompressSchema(doltdocs.Schema), 1476 }, 1477 { 1478 Name: "select from dolt_query_catalog", 1479 AdditionalSetup: CreateTableFn(doltdb.DoltQueryCatalogTableName, 1480 dtables.DoltQueryCatalogSchema, 1481 NewRowWithSchema(dtables.DoltQueryCatalogSchema, 1482 types.String("existingEntry"), 1483 types.Uint(2), 1484 types.String("example"), 1485 types.String("select 2+2 from dual"), 1486 types.String("description")), 1487 ), 1488 Query: "select * from dolt_query_catalog", 1489 ExpectedRows: ToSqlRows(CompressSchema(dtables.DoltQueryCatalogSchema), 1490 NewRow(types.String("existingEntry"), types.Uint(2), types.String("example"), types.String("select 2+2 from dual"), types.String("description")), 1491 ), 1492 ExpectedSchema: CompressSchema(dtables.DoltQueryCatalogSchema), 1493 }, 1494 { 1495 Name: "select from dolt_schemas", 1496 AdditionalSetup: CreateTableFn(doltdb.SchemasTableName, 1497 schemasTableDoltSchema(), 1498 NewRowWithSchema(schemasTableDoltSchema(), 1499 types.String("view"), 1500 types.String("name"), 1501 types.String("select 2+2 from dual"), 1502 types.Int(1), 1503 )), 1504 Query: "select * from dolt_schemas", 1505 ExpectedRows: ToSqlRows(CompressSchema(schemasTableDoltSchema()), 1506 NewRow(types.String("view"), types.String("name"), types.String("select 2+2 from dual"), types.Int(1)), 1507 ), 1508 ExpectedSchema: CompressSchema(schemasTableDoltSchema()), 1509 }, 1510 } 1511 1512 func TestSelectSystemTables(t *testing.T) { 1513 for _, test := range systemTableSelectTests { 1514 t.Run(test.Name, func(t *testing.T) { 1515 testSelectQuery(t, test) 1516 }) 1517 } 1518 } 1519 1520 type testCommitClock struct { 1521 unixNano int64 1522 } 1523 1524 func (tcc *testCommitClock) Now() time.Time { 1525 now := time.Unix(0, tcc.unixNano) 1526 tcc.unixNano += int64(time.Hour) 1527 return now 1528 } 1529 1530 // Tests the given query on a freshly created dataset, asserting that the result has the given schema and rows. If 1531 // expectedErr is set, asserts instead that the execution returns an error that matches. 1532 func testSelectQuery(t *testing.T, test SelectTest) { 1533 validateTest(t, test) 1534 1535 tcc := &testCommitClock{} 1536 doltdb.CommitNowFunc = tcc.Now 1537 doltdb.CommitLoc = time.UTC 1538 1539 dEnv := dtestutils.CreateTestEnv() 1540 CreateTestDatabase(dEnv, t) 1541 1542 if test.AdditionalSetup != nil { 1543 test.AdditionalSetup(t, dEnv) 1544 } 1545 1546 root, _ := dEnv.WorkingRoot(context.Background()) 1547 actualRows, sch, err := executeSelect(context.Background(), dEnv, root, test.Query) 1548 if len(test.ExpectedErr) > 0 { 1549 require.Error(t, err) 1550 // Too much work to synchronize error messages between the two implementations, so for now we'll just assert that an error occurred. 1551 // require.Contains(t, err.Error(), test.ExpectedErr) 1552 return 1553 } else { 1554 require.NoError(t, err) 1555 } 1556 1557 assert.Equal(t, test.ExpectedRows, actualRows) 1558 var sqlSchema sql.Schema 1559 if test.ExpectedSqlSchema != nil { 1560 sqlSchema = test.ExpectedSqlSchema 1561 } else { 1562 sqlSchema = mustSqlSchema(test.ExpectedSchema) 1563 } 1564 1565 assertSchemasEqual(t, sqlSchema, sch) 1566 } 1567 1568 func testSelectDiffQuery(t *testing.T, test SelectTest) { 1569 validateTest(t, test) 1570 1571 ctx := context.Background() 1572 tcc := &testCommitClock{} 1573 doltdb.CommitNowFunc = tcc.Now 1574 doltdb.CommitLoc = time.UTC 1575 1576 dEnv := dtestutils.CreateTestEnv() 1577 envtestutils.InitializeWithHistory(t, ctx, dEnv, CreateHistory(ctx, dEnv, t)...) 1578 if test.AdditionalSetup != nil { 1579 test.AdditionalSetup(t, dEnv) 1580 } 1581 1582 cs, err := doltdb.NewCommitSpec("master") 1583 require.NoError(t, err) 1584 1585 cm, err := dEnv.DoltDB.Resolve(ctx, cs, nil) 1586 require.NoError(t, err) 1587 1588 root, err := cm.GetRootValue() 1589 require.NoError(t, err) 1590 1591 _, err = dEnv.UpdateStagedRoot(ctx, root) 1592 require.NoError(t, err) 1593 1594 err = dEnv.UpdateWorkingRoot(ctx, root) 1595 require.NoError(t, err) 1596 1597 root, err = dEnv.WorkingRoot(context.Background()) 1598 require.NoError(t, err) 1599 1600 root = envtestutils.UpdateTables(t, ctx, root, CreateWorkingRootUpdate()) 1601 1602 err = dEnv.UpdateWorkingRoot(ctx, root) 1603 require.NoError(t, err) 1604 1605 actualRows, sch, err := executeSelect(ctx, dEnv, root, test.Query) 1606 if len(test.ExpectedErr) > 0 { 1607 require.Error(t, err) 1608 return 1609 } else { 1610 require.NoError(t, err) 1611 } 1612 1613 assert.Equal(t, test.ExpectedRows, actualRows) 1614 1615 var sqlSchema sql.Schema 1616 if test.ExpectedSqlSchema != nil { 1617 sqlSchema = test.ExpectedSqlSchema 1618 } else { 1619 sqlSchema = mustSqlSchema(test.ExpectedSchema) 1620 } 1621 1622 assertSchemasEqual(t, sqlSchema, sch) 1623 } 1624 1625 func validateTest(t *testing.T, test SelectTest) { 1626 if (test.ExpectedRows == nil) != (test.ExpectedSchema == nil && test.ExpectedSqlSchema == nil) { 1627 require.Fail(t, "Incorrect test setup: schema and rows must both be provided if one is") 1628 } 1629 1630 if len(test.ExpectedErr) == 0 && (test.ExpectedSchema == nil) == (test.ExpectedSqlSchema == nil) { 1631 require.Fail(t, "Incorrect test setup: must set at most one of ExpectedSchema, ExpectedSqlSchema") 1632 } 1633 1634 if len(singleSelectQueryTest) > 0 && test.Name != singleSelectQueryTest { 1635 t.Skip("Skipping tests until " + singleSelectQueryTest) 1636 } 1637 1638 if len(singleSelectQueryTest) == 0 && test.SkipOnSqlEngine && skipBrokenSelect { 1639 t.Skip("Skipping test broken on SQL engine") 1640 } 1641 }