github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/enginetest/dolt_branch_queries.go (about) 1 // Copyright 2022 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 enginetest 16 17 import ( 18 "fmt" 19 "testing" 20 21 "github.com/dolthub/go-mysql-server/enginetest" 22 "github.com/dolthub/go-mysql-server/enginetest/queries" 23 "github.com/dolthub/go-mysql-server/sql" 24 "github.com/dolthub/go-mysql-server/sql/plan" 25 "github.com/dolthub/go-mysql-server/sql/planbuilder" 26 "github.com/dolthub/go-mysql-server/sql/transform" 27 "github.com/dolthub/go-mysql-server/sql/types" 28 "github.com/stretchr/testify/require" 29 ) 30 31 var ForeignKeyBranchTests = []queries.ScriptTest{ 32 { 33 Name: "create fk on branch", 34 SetUpScript: []string{ 35 "call dolt_branch('b1')", 36 "use mydb/b1", 37 "ALTER TABLE child ADD CONSTRAINT fk_named FOREIGN KEY (v1) REFERENCES parent(v1);", 38 }, 39 Assertions: []queries.ScriptTestAssertion{ 40 { 41 Query: "use mydb/b1", 42 SkipResultsCheck: true, 43 }, 44 { 45 Query: "SHOW CREATE TABLE child;", 46 Expected: []sql.Row{{"child", "CREATE TABLE `child` (\n" + 47 " `id` int NOT NULL,\n" + 48 " `v1` int,\n" + 49 " `v2` int,\n" + 50 " PRIMARY KEY (`id`),\n" + 51 " KEY `v1` (`v1`),\n" + 52 " CONSTRAINT `fk_named` FOREIGN KEY (`v1`) REFERENCES `parent` (`v1`)\n" + 53 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, 54 }, 55 { 56 Query: "insert into child values (1, 1, 1)", 57 ExpectedErr: sql.ErrForeignKeyChildViolation, 58 }, 59 { 60 Query: "use mydb/main", 61 SkipResultsCheck: true, 62 }, 63 { 64 Query: "SHOW CREATE TABLE child;", 65 Expected: []sql.Row{{"child", "CREATE TABLE `child` (\n" + 66 " `id` int NOT NULL,\n" + 67 " `v1` int,\n" + 68 " `v2` int,\n" + 69 " PRIMARY KEY (`id`)\n" + 70 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, 71 }, 72 { 73 Query: "insert into child values (1, 1, 1)", 74 Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}}, 75 }, 76 { 77 Query: "insert into `mydb/b1`.child values (1, 1, 1)", 78 ExpectedErr: sql.ErrForeignKeyChildViolation, 79 }, 80 }, 81 }, 82 { 83 Name: "create fk with branch checkout", 84 SetUpScript: []string{ 85 "call dolt_branch('b1')", 86 "call dolt_checkout('b1')", 87 "ALTER TABLE child ADD CONSTRAINT fk_named FOREIGN KEY (v1) REFERENCES parent(v1);", 88 }, 89 Assertions: []queries.ScriptTestAssertion{ 90 { 91 Query: "call dolt_checkout('b1')", 92 SkipResultsCheck: true, 93 }, 94 { 95 Query: "SHOW CREATE TABLE child;", 96 Expected: []sql.Row{{"child", "CREATE TABLE `child` (\n" + 97 " `id` int NOT NULL,\n" + 98 " `v1` int,\n" + 99 " `v2` int,\n" + 100 " PRIMARY KEY (`id`),\n" + 101 " KEY `v1` (`v1`),\n" + 102 " CONSTRAINT `fk_named` FOREIGN KEY (`v1`) REFERENCES `parent` (`v1`)\n" + 103 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, 104 }, 105 { 106 Query: "insert into child values (1, 1, 1)", 107 ExpectedErr: sql.ErrForeignKeyChildViolation, 108 }, 109 { 110 Query: "call dolt_checkout('main')", 111 SkipResultsCheck: true, 112 }, 113 { 114 Query: "SHOW CREATE TABLE child;", 115 Expected: []sql.Row{{"child", "CREATE TABLE `child` (\n" + 116 " `id` int NOT NULL,\n" + 117 " `v1` int,\n" + 118 " `v2` int,\n" + 119 " PRIMARY KEY (`id`)\n" + 120 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, 121 }, 122 { 123 Query: "insert into child values (1, 1, 1)", 124 Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}}, 125 }, 126 }, 127 }, 128 { 129 Name: "create fk on branch not being used", 130 SetUpScript: []string{ 131 "call dolt_branch('b1')", 132 }, 133 Assertions: []queries.ScriptTestAssertion{ 134 { 135 Query: "ALTER TABLE `mydb/b1`.child ADD CONSTRAINT fk_named FOREIGN KEY (v1) REFERENCES parent(v1);", 136 Skip: true, // Incorrectly flagged as a cross-DB foreign key relation 137 }, 138 { 139 Query: "SHOW CREATE TABLE `mydb/b1`.child;", 140 Skip: true, 141 Expected: []sql.Row{{"child", "CREATE TABLE `child` (\n" + 142 " `id` int NOT NULL,\n" + 143 " `v1` int,\n" + 144 " `v2` int,\n" + 145 " PRIMARY KEY (`id`),\n" + 146 " KEY `v1` (`v1`),\n" + 147 " CONSTRAINT `fk_named` FOREIGN KEY (`v1`) REFERENCES `parent` (`v1`)\n" + 148 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, 149 }, 150 { 151 Query: "insert into `mydb/b1`.child values (1, 1, 1)", 152 Skip: true, 153 ExpectedErr: sql.ErrForeignKeyChildViolation, 154 }, 155 { 156 Query: "SHOW CREATE TABLE child;", 157 Skip: true, 158 Expected: []sql.Row{{"child", "CREATE TABLE `child` (\n" + 159 " `id` int NOT NULL,\n" + 160 " `v1` int,\n" + 161 " `v2` int,\n" + 162 " PRIMARY KEY (`id`)\n" + 163 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, 164 }, 165 { 166 Query: "insert into child values (1, 1, 1)", 167 Skip: true, 168 Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}}, 169 }, 170 }, 171 }, 172 } 173 174 var ViewBranchTests = []queries.ScriptTest{ 175 { 176 Name: "create view on branch", 177 SetUpScript: []string{ 178 "create table t1 (a int primary key, b int)", 179 "insert into t1 values (1, 1), (2, 2), (3, 3)", 180 "call dolt_commit('-Am', 'first commit')", 181 "call dolt_branch('b1')", 182 "use mydb/b1", 183 "create view v1 as select * from t1 where a > 2", 184 }, 185 Assertions: []queries.ScriptTestAssertion{ 186 { 187 Query: "use mydb/b1", 188 SkipResultsCheck: true, 189 }, 190 { 191 Query: "select * from v1", 192 Expected: []sql.Row{{3, 3}}, 193 }, 194 { 195 Query: "use mydb/main", 196 SkipResultsCheck: true, 197 }, 198 { 199 Query: "select * from v1", 200 ExpectedErr: sql.ErrTableNotFound, 201 }, 202 { 203 Query: "select * from `mydb/b1`.v1", 204 Expected: []sql.Row{{3, 3}}, 205 }, 206 }, 207 }, 208 { 209 Name: "create view on different branch", 210 SetUpScript: []string{ 211 "create table t1 (a int primary key, b int)", 212 "insert into t1 values (1, 1), (2, 2), (3, 3)", 213 "call dolt_commit('-Am', 'first commit')", 214 "call dolt_branch('b1')", 215 "create view `mydb/b1`.v1 as select * from t1 where a > 2", 216 }, 217 Assertions: []queries.ScriptTestAssertion{ 218 { 219 Query: "use mydb/b1", 220 SkipResultsCheck: true, 221 }, 222 { 223 Query: "select * from v1", 224 Expected: []sql.Row{{3, 3}}, 225 Skip: true, // https://github.com/dolthub/dolt/issues/6078 226 }, 227 { 228 Query: "use mydb/main", 229 SkipResultsCheck: true, 230 }, 231 { 232 Query: "select * from v1", 233 ExpectedErr: sql.ErrTableNotFound, 234 Skip: true, // https://github.com/dolthub/dolt/issues/6078 235 }, 236 { 237 Query: "select * from `mydb/b1`.v1", 238 Expected: []sql.Row{{3, 3}}, 239 Skip: true, // https://github.com/dolthub/dolt/issues/6078 240 }, 241 }, 242 }, 243 } 244 245 var DdlBranchTests = []queries.ScriptTest{ 246 { 247 Name: "create table on branch", 248 SetUpScript: []string{ 249 "create table t1 (a int primary key, b int)", 250 "insert into t1 values (1, 1), (2, 2), (3, 3)", 251 "call dolt_commit('-Am', 'first commit')", 252 "call dolt_branch('b1')", 253 "use mydb/b1", 254 "create table t2 (a int primary key, b int)", 255 "insert into t2 values (4, 4)", 256 }, 257 Assertions: []queries.ScriptTestAssertion{ 258 { 259 Query: "use mydb/b1", 260 SkipResultsCheck: true, 261 }, 262 { 263 Query: "select * from t2", 264 Expected: []sql.Row{{4, 4}}, 265 }, 266 { 267 Query: "use mydb/main", 268 SkipResultsCheck: true, 269 }, 270 { 271 Query: "select * from t2", 272 ExpectedErr: sql.ErrTableNotFound, 273 }, 274 { 275 Query: "select * from `mydb/b1`.t2", 276 Expected: []sql.Row{{4, 4}}, 277 }, 278 }, 279 }, 280 { 281 Name: "create table on different branch", 282 SetUpScript: []string{ 283 "create table t1 (a int primary key, b int)", 284 "insert into t1 values (1, 1), (2, 2), (3, 3)", 285 "call dolt_commit('-Am', 'first commit')", 286 "call dolt_branch('b1')", 287 "create table `mydb/b1`.t2 (a int primary key, b int)", 288 "insert into `mydb/b1`.t2 values (4,4)", 289 }, 290 Assertions: []queries.ScriptTestAssertion{ 291 { 292 Query: "use mydb/b1", 293 SkipResultsCheck: true, 294 }, 295 { 296 Query: "select * from t2", 297 Expected: []sql.Row{{4, 4}}, 298 }, 299 { 300 Query: "use mydb/main", 301 SkipResultsCheck: true, 302 }, 303 { 304 Query: "select * from t2", 305 ExpectedErr: sql.ErrTableNotFound, 306 }, 307 { 308 Query: "select * from `mydb/b1`.t2", 309 Expected: []sql.Row{{4, 4}}, 310 }, 311 }, 312 }, 313 { 314 Name: "create table on different branch, autocommit off", 315 SetUpScript: []string{ 316 "create table t1 (a int primary key, b int)", 317 "insert into t1 values (1, 1), (2, 2), (3, 3)", 318 "call dolt_commit('-Am', 'first commit')", 319 "call dolt_branch('b1')", 320 "set autocommit = off", 321 "create table `mydb/b1`.t2 (a int primary key, b int)", 322 "insert into `mydb/b1`.t2 values (4,4)", 323 "commit", 324 }, 325 Assertions: []queries.ScriptTestAssertion{ 326 { 327 Query: "use mydb/b1", 328 SkipResultsCheck: true, 329 }, 330 { 331 Query: "select * from t2", 332 Expected: []sql.Row{{4, 4}}, 333 }, 334 { 335 Query: "use mydb/main", 336 SkipResultsCheck: true, 337 }, 338 { 339 Query: "select * from t2", 340 ExpectedErr: sql.ErrTableNotFound, 341 }, 342 { 343 Query: "select * from `mydb/b1`.t2", 344 Expected: []sql.Row{{4, 4}}, 345 }, 346 }, 347 }, 348 { 349 Name: "alter table on different branch, add column", 350 SetUpScript: []string{ 351 "create table t1 (a int primary key, b int)", 352 "insert into t1 values (1, 1), (2, 2), (3, 3)", 353 "call dolt_commit('-Am', 'first commit')", 354 "call dolt_branch('b1')", 355 }, 356 Assertions: []queries.ScriptTestAssertion{ 357 { 358 Query: "alter table `mydb/b1`.t1 add column c int", 359 Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}}, 360 }, 361 { 362 Query: "select * from `mydb/b1`.t1", 363 Expected: []sql.Row{{1, 1, nil}, {2, 2, nil}, {3, 3, nil}}, 364 }, 365 { 366 Query: "select * from t1", 367 Expected: []sql.Row{{1, 1}, {2, 2}, {3, 3}}, 368 }, 369 }, 370 }, 371 { 372 Name: "alter table on different branch, drop column", 373 SetUpScript: []string{ 374 "create table t1 (a int primary key, b int)", 375 "insert into t1 values (1, 1), (2, 2), (3, 3)", 376 "call dolt_commit('-Am', 'first commit')", 377 "call dolt_branch('b1')", 378 }, 379 Assertions: []queries.ScriptTestAssertion{ 380 { 381 Query: "alter table `mydb/b1`.t1 drop column b", 382 Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}}, 383 }, 384 { 385 Query: "select * from `mydb/b1`.t1", 386 Expected: []sql.Row{{1}, {2}, {3}}, 387 }, 388 { 389 Query: "select * from t1", 390 Expected: []sql.Row{{1, 1}, {2, 2}, {3, 3}}, 391 }, 392 }, 393 }, 394 { 395 Name: "alter table on different branch, modify column", 396 SetUpScript: []string{ 397 "create table t1 (a int primary key, b int)", 398 "insert into t1 values (1, 1), (2, 2), (3, 3)", 399 "call dolt_commit('-Am', 'first commit')", 400 "call dolt_branch('b1')", 401 }, 402 Assertions: []queries.ScriptTestAssertion{ 403 { 404 Query: "alter table `mydb/b1`.t1 modify column b varchar(1) first", 405 Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}}, 406 }, 407 { 408 Query: "select * from `mydb/b1`.t1", 409 Expected: []sql.Row{{"1", 1}, {"2", 2}, {"3", 3}}, 410 }, 411 { 412 Query: "select * from t1", 413 Expected: []sql.Row{{1, 1}, {2, 2}, {3, 3}}, 414 }, 415 }, 416 }, 417 { 418 Name: "alter table on different branch, create and drop index", 419 SetUpScript: []string{ 420 "create table t1 (a int primary key, b int)", 421 "insert into t1 values (1, 1), (2, 2), (3, 3)", 422 "call dolt_commit('-Am', 'first commit')", 423 "call dolt_branch('b1')", 424 }, 425 Assertions: []queries.ScriptTestAssertion{ 426 { 427 Query: "create index idx on `mydb/b1`.t1 (b)", 428 Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}}, 429 }, 430 { 431 Query: "show create table `mydb/b1`.t1", 432 Expected: []sql.Row{{"t1", "CREATE TABLE `t1` (\n" + 433 " `a` int NOT NULL,\n" + 434 " `b` int,\n" + 435 " PRIMARY KEY (`a`),\n" + 436 " KEY `idx` (`b`)\n" + 437 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, 438 }, 439 { 440 Query: "show create table t1", 441 Expected: []sql.Row{{"t1", "CREATE TABLE `t1` (\n" + 442 " `a` int NOT NULL,\n" + 443 " `b` int,\n" + 444 " PRIMARY KEY (`a`)\n" + 445 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, 446 }, 447 { 448 Query: "alter table `mydb/b1`.t1 drop index idx", 449 Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}}, 450 }, 451 { 452 Query: "show create table `mydb/b1`.t1", 453 Expected: []sql.Row{{"t1", "CREATE TABLE `t1` (\n" + 454 " `a` int NOT NULL,\n" + 455 " `b` int,\n" + 456 " PRIMARY KEY (`a`)\n" + 457 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, 458 }, 459 }, 460 }, 461 { 462 Name: "alter table on different branch, add and drop constraint", 463 SetUpScript: []string{ 464 "create table t1 (a int primary key, b int)", 465 "insert into t1 values (1, 1), (2, 2), (3, 3)", 466 "call dolt_commit('-Am', 'first commit')", 467 "call dolt_branch('b1')", 468 }, 469 Assertions: []queries.ScriptTestAssertion{ 470 { 471 Query: "alter table `mydb/b1`.t1 add constraint chk1 check (b < 4)", 472 Expected: []sql.Row{{types.NewOkResult(0)}}, 473 }, 474 { 475 Query: "show create table `mydb/b1`.t1", 476 Expected: []sql.Row{{"t1", "CREATE TABLE `t1` (\n" + 477 " `a` int NOT NULL,\n" + 478 " `b` int,\n" + 479 " PRIMARY KEY (`a`),\n" + 480 " CONSTRAINT `chk1` CHECK ((`b` < 4))\n" + 481 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, 482 }, 483 { 484 Query: "insert into `mydb/b1`.t1 values (4, 4)", 485 ExpectedErr: sql.ErrCheckConstraintViolated, 486 }, 487 { 488 Query: "show create table t1", 489 Expected: []sql.Row{{"t1", "CREATE TABLE `t1` (\n" + 490 " `a` int NOT NULL,\n" + 491 " `b` int,\n" + 492 " PRIMARY KEY (`a`)\n" + 493 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, 494 }, 495 { 496 Query: "insert into t1 values (4, 4)", 497 Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}}, 498 }, 499 { 500 Query: "alter table `mydb/b1`.t1 drop constraint chk1", 501 Expected: []sql.Row{}, 502 }, 503 { 504 Query: "show create table `mydb/b1`.t1", 505 Expected: []sql.Row{{"t1", "CREATE TABLE `t1` (\n" + 506 " `a` int NOT NULL,\n" + 507 " `b` int,\n" + 508 " PRIMARY KEY (`a`)\n" + 509 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, 510 }, 511 }, 512 }, 513 } 514 515 type indexQuery struct { 516 Query string 517 Index bool 518 } 519 520 var BranchPlanTests = []struct { 521 Name string 522 SetUpScript []string 523 Queries []indexQuery 524 }{ 525 { 526 Name: "use index on branch database", 527 SetUpScript: []string{ 528 "create table t1 (a int primary key, b int)", 529 "insert into t1 values (1, 1), (2, 2), (3, 3)", 530 "call dolt_commit('-Am', 'first commit')", 531 "call dolt_branch('b1')", 532 "use mydb/b1", 533 "create index idx on t1 (b)", 534 }, 535 Queries: []indexQuery{ 536 { 537 Query: "select * from t1 where b = 1", 538 Index: true, 539 }, 540 { 541 Query: "use mydb/main", 542 }, 543 { 544 Query: "select * from `mydb/b1`.t1 where b = 1", 545 Index: true, 546 }, 547 }, 548 }, 549 { 550 Name: "use index on branch database join", 551 SetUpScript: []string{ 552 "create table t1 (a int primary key, b int)", 553 "insert into t1 values (1, 1), (2, 2), (3, 3)", 554 "call dolt_commit('-Am', 'first commit')", 555 "call dolt_branch('b1')", 556 "use mydb/b1", 557 "create index idx on t1 (b)", 558 }, 559 Queries: []indexQuery{ 560 { 561 Query: "select /*+ LOOKUP_JOIN(t1a,t1b) */ * from t1 t1a join t1 t1b on t1a.b = t1b.b order by 1", 562 Index: true, 563 }, 564 { 565 Query: "select * from `mydb/main`.t1 t1a join `mydb/main`.t1 t1b on t1a.b = t1b.b order by 1", 566 Index: false, 567 }, 568 { 569 Query: "use mydb/main", 570 }, 571 { 572 Query: "select /*+ LOOKUP_JOIN(t1a,t1b) */ * from t1 t1a join t1 t1b on t1a.b = t1b.b order by 1", 573 Index: true, 574 }, 575 { 576 Query: "select /*+ LOOKUP_JOIN(t1a,t1b) */ * from `mydb/b1`.t1 t1a join `mydb/b1`.t1 t1b on t1a.b = t1b.b order by 1", 577 Index: true, 578 }, 579 }, 580 }, 581 } 582 583 func TestIndexedAccess(t *testing.T, e enginetest.QueryEngine, harness enginetest.Harness, query string, index bool) { 584 ctx := enginetest.NewContext(harness) 585 ctx = ctx.WithQuery(query) 586 a, err := analyzeQuery(ctx, e, query) 587 require.NoError(t, err) 588 var hasIndex bool 589 transform.Inspect(a, func(n sql.Node) bool { 590 if n == nil { 591 return false 592 } 593 if _, ok := n.(*plan.IndexedTableAccess); ok { 594 hasIndex = true 595 } 596 return true 597 }) 598 599 if index != hasIndex { 600 fmt.Println(a.String()) 601 } 602 require.Equal(t, index, hasIndex) 603 } 604 605 func analyzeQuery(ctx *sql.Context, e enginetest.QueryEngine, query string) (sql.Node, error) { 606 binder := planbuilder.New(ctx, e.EngineAnalyzer().Catalog, sql.NewMysqlParser()) 607 parsed, _, _, err := binder.Parse(query, false) 608 if err != nil { 609 return nil, err 610 } 611 612 return e.EngineAnalyzer().Analyze(ctx, parsed, nil) 613 }