github.com/dolthub/go-mysql-server@v0.18.0/enginetest/queries/procedure_queries.go (about) 1 // Copyright 2020-2021 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 queries 16 17 import ( 18 "time" 19 20 "github.com/dolthub/go-mysql-server/sql" 21 "github.com/dolthub/go-mysql-server/sql/types" 22 ) 23 24 var ProcedureLogicTests = []ScriptTest{ 25 { 26 // When a loop is executed once before the first evaluation of the loop condition, we expect the stored 27 // procedure to return the last result set from that first loop execution. 28 Name: "REPEAT with OnceBefore returns first loop evaluation result set", 29 SetUpScript: []string{ 30 `CREATE PROCEDURE p1() 31 BEGIN 32 SET @counter = 0; 33 REPEAT 34 SELECT 42 from dual; 35 SET @counter = @counter + 1; 36 UNTIL @counter >= 0 37 END REPEAT; 38 END`, 39 }, 40 Assertions: []ScriptTestAssertion{ 41 { 42 Query: "CALL p1;", 43 Expected: []sql.Row{{42}}, 44 }, 45 }, 46 }, 47 { 48 // When a loop condition evals to false, we expect the stored procedure to return the last 49 // result set from the previous loop execution. 50 Name: "WHILE returns previous loop evaluation result set", 51 SetUpScript: []string{ 52 `CREATE PROCEDURE p1() 53 BEGIN 54 SET @counter = 0; 55 WHILE @counter <= 0 DO 56 SET @counter = @counter + 1; 57 SELECT CAST(@counter + 41 as SIGNED) from dual; 58 END WHILE; 59 END`, 60 }, 61 Assertions: []ScriptTestAssertion{ 62 { 63 Query: "CALL p1;", 64 Expected: []sql.Row{{42}}, 65 }, 66 }, 67 }, 68 69 { 70 Name: "Simple SELECT", 71 SetUpScript: []string{ 72 "CREATE PROCEDURE testabc(x DOUBLE, y DOUBLE) SELECT x*y", 73 }, 74 Assertions: []ScriptTestAssertion{ 75 { 76 Query: "CALL testabc(2, 3)", 77 Expected: []sql.Row{ 78 { 79 6.0, 80 }, 81 }, 82 }, 83 { 84 Query: "CALL testabc(9, 9.5)", 85 Expected: []sql.Row{ 86 { 87 85.5, 88 }, 89 }, 90 }, 91 }, 92 }, 93 { 94 Name: "Multiple SELECTs", 95 SetUpScript: []string{ 96 "CREATE TABLE t1(pk VARCHAR(20) PRIMARY KEY)", 97 "INSERT INTO t1 VALUES (3), (4), (50)", 98 `CREATE PROCEDURE p1() 99 BEGIN 100 SELECT * FROM t1; 101 UPDATE t1 SET pk = CONCAT(pk, '0'); 102 SELECT * FROM t1; 103 INSERT INTO t1 VALUES (1), (2); 104 SELECT * FROM t1; 105 REPLACE INTO t1 VALUES (1), (30); 106 DELETE FROM t1 WHERE pk LIKE '%00'; 107 END;`, 108 }, 109 Assertions: []ScriptTestAssertion{ 110 { 111 Query: "CALL p1()", 112 Expected: []sql.Row{ 113 {"1"}, 114 {"2"}, 115 {"30"}, 116 {"40"}, 117 {"500"}, 118 }, 119 }, 120 { 121 Query: "SELECT * FROM t1 ORDER BY 1", 122 Expected: []sql.Row{ 123 {"1"}, 124 {"2"}, 125 {"30"}, 126 {"40"}, 127 }, 128 }, 129 }, 130 }, 131 { 132 Name: "IF/ELSE with 1 SELECT at end", 133 SetUpScript: []string{ 134 "SET @outparam = ''", 135 `CREATE PROCEDURE p1(OUT s VARCHAR(200), N DOUBLE, m DOUBLE) 136 BEGIN 137 SET s = ''; 138 IF n = m THEN SET s = 'equals'; 139 ELSE 140 IF n > m THEN SET s = 'greater'; 141 ELSE SET s = 'less'; 142 END IF; 143 SET s = CONCAT('is ', s, ' than'); 144 END IF; 145 SET s = CONCAT(n, ' ', s, ' ', m, '.'); 146 SELECT s; 147 END;`, 148 `CREATE PROCEDURE p2(s VARCHAR(200), N DOUBLE, m DOUBLE) 149 BEGIN 150 SET s = ''; 151 IF n = m THEN SET s = 'equals'; 152 ELSE 153 IF n > m THEN SET s = 'greater'; 154 ELSE SET s = 'less'; 155 END IF; 156 SET s = CONCAT('is ', s, ' than'); 157 END IF; 158 SET s = CONCAT(n, ' ', s, ' ', m, '.'); 159 SELECT s; 160 END;`, 161 }, 162 Assertions: []ScriptTestAssertion{ 163 { 164 Query: "CALL p1(@outparam, 1, 2)", 165 Expected: []sql.Row{ 166 { 167 "1 is less than 2.", 168 }, 169 }, 170 }, 171 { 172 Query: "SELECT @outparam", 173 Expected: []sql.Row{ 174 { 175 "1 is less than 2.", 176 }, 177 }, 178 }, 179 { 180 Query: "CALL p1(@outparam, null, 2)", 181 Expected: []sql.Row{ 182 { 183 nil, 184 }, 185 }, 186 }, 187 { 188 Query: "CALL p1(@outparam, 7, 4)", 189 Expected: []sql.Row{ 190 { 191 "7 is greater than 4.", 192 }, 193 }, 194 }, 195 { 196 Query: "SELECT @outparam", 197 Expected: []sql.Row{ 198 { 199 "7 is greater than 4.", 200 }, 201 }, 202 }, 203 { 204 Query: "CALL p1(@outparam, 5, 5)", 205 Expected: []sql.Row{ 206 { 207 "5 equals 5.", 208 }, 209 }, 210 }, 211 { 212 Query: "SELECT @outparam", 213 Expected: []sql.Row{ 214 { 215 "5 equals 5.", 216 }, 217 }, 218 }, 219 { 220 Query: "CALL p2(@outparam, 9, 3)", 221 Expected: []sql.Row{ 222 { 223 "9 is greater than 3.", 224 }, 225 }, 226 }, 227 { // Not affected as p2 has an IN param rather than OUT 228 Query: "SELECT @outparam", 229 Expected: []sql.Row{ 230 { 231 "5 equals 5.", 232 }, 233 }, 234 }, 235 }, 236 }, 237 { 238 Name: "IF/ELSE with nested SELECT in branches", 239 SetUpScript: []string{ 240 "CREATE TABLE t1(pk BIGINT PRIMARY KEY)", 241 `CREATE PROCEDURE p1(x BIGINT) 242 BEGIN 243 DELETE FROM t1; 244 IF x < 10 THEN 245 IF x = 0 THEN 246 SELECT 1000; 247 ELSEIF x = 1 THEN 248 SELECT 1001; 249 ELSE 250 INSERT INTO t1 VALUES (3), (4), (5); 251 END IF; 252 ELSEIF x < 20 THEN 253 IF x = 10 THEN 254 INSERT INTO t1 VALUES (1), (2), (6), (7); 255 ELSEIF x = 11 THEN 256 INSERT INTO t1 VALUES (8), (9), (10), (11), (12); 257 SELECT * FROM t1; 258 ELSE 259 SELECT 2002; 260 SELECT 2003; 261 END IF; 262 END IF; 263 INSERT INTO t1 VALUES (1), (2); 264 END;`, 265 }, 266 Assertions: []ScriptTestAssertion{ 267 { 268 Query: "CALL p1(0)", 269 Expected: []sql.Row{ 270 { 271 int64(1000), 272 }, 273 }, 274 }, 275 { 276 Query: "CALL p1(1)", 277 Expected: []sql.Row{ 278 { 279 int64(1001), 280 }, 281 }, 282 }, 283 { 284 SkipResultCheckOnServerEngine: true, // tracking issue: https://github.com/dolthub/dolt/issues/6918 285 Query: "CALL p1(2)", 286 Expected: []sql.Row{ 287 { 288 types.NewOkResult(2), 289 }, 290 }, 291 }, 292 { 293 Query: "CALL p1(10)", 294 ExpectedErr: sql.ErrPrimaryKeyViolation, 295 }, 296 { 297 Query: "CALL p1(11)", 298 Expected: []sql.Row{ 299 {int64(8)}, 300 {int64(9)}, 301 {int64(10)}, 302 {int64(11)}, 303 {int64(12)}, 304 }, 305 }, 306 { 307 Query: "CALL p1(12)", 308 Expected: []sql.Row{ 309 { 310 int64(2003), 311 }, 312 }, 313 }, 314 }, 315 }, 316 { 317 Name: "REPEAT loop over user variable", 318 SetUpScript: []string{ 319 `CREATE PROCEDURE p1(p1 INT) 320 BEGIN 321 SET @x = 0; 322 REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT; 323 END`, 324 }, 325 Assertions: []ScriptTestAssertion{ 326 // TODO: MySQL won't actually return *any* result set for these stored procedures. We have done work 327 // to filter out all but the last result set generated by the stored procedure, but we still 328 // need to filter out Result Sets that should be completely omitted. 329 { 330 Query: "CALL p1(0)", 331 Expected: []sql.Row{{}}, 332 }, 333 { 334 Query: "CALL p1(1)", 335 Expected: []sql.Row{{}}, 336 }, 337 { 338 Query: "CALL p1(2)", 339 Expected: []sql.Row{{}}, 340 }, 341 { 342 // https://github.com/dolthub/dolt/issues/6230 343 Query: "CALL p1(200)", 344 Expected: []sql.Row{{}}, 345 }, 346 }, 347 }, 348 { 349 Name: "WHILE loop over user variable", 350 SetUpScript: []string{ 351 `CREATE PROCEDURE p1(p1 INT) 352 BEGIN 353 SET @x = 0; 354 WHILE @x <= p1 DO 355 SET @x = @x + 1; 356 END WHILE; 357 END`, 358 }, 359 Assertions: []ScriptTestAssertion{ 360 // TODO: MySQL won't actually return *any* result set for these stored procedures. We have done work 361 // to filter out all but the last result set generated by the stored procedure, but we still 362 // need to filter out Result Sets that should be completely omitted. 363 { 364 Query: "CALL p1(0)", 365 Expected: []sql.Row{{}}, 366 }, 367 { 368 Query: "CALL p1(1)", 369 Expected: []sql.Row{{}}, 370 }, 371 { 372 Query: "CALL p1(2)", 373 Expected: []sql.Row{{}}, 374 }, 375 }, 376 }, 377 { 378 Name: "CASE statements", 379 SetUpScript: []string{ 380 `CREATE PROCEDURE p1(IN a BIGINT) 381 BEGIN 382 DECLARE b VARCHAR(200) DEFAULT ""; 383 tloop: LOOP 384 CASE 385 WHEN a < 4 THEN 386 SET b = CONCAT(b, "a"); 387 SET a = a + 1; 388 WHEN a < 8 THEN 389 SET b = CONCAT(b, "b"); 390 SET a = a + 1; 391 ELSE 392 LEAVE tloop; 393 END CASE; 394 END LOOP; 395 SELECT b; 396 END;`, 397 `CREATE PROCEDURE p2(IN a BIGINT) 398 BEGIN 399 DECLARE b VARCHAR(200) DEFAULT ""; 400 tloop: LOOP 401 CASE a 402 WHEN 1 THEN 403 SET b = CONCAT(b, "a"); 404 SET a = a + 1; 405 WHEN 2 THEN 406 SET b = CONCAT(b, "b"); 407 SET a = a + 1; 408 WHEN 3 THEN 409 SET b = CONCAT(b, "c"); 410 SET a = a + 1; 411 ELSE 412 LEAVE tloop; 413 END CASE; 414 END LOOP; 415 SELECT b; 416 END;`, 417 `CREATE PROCEDURE p3(IN a BIGINT) 418 BEGIN 419 DECLARE b VARCHAR(200) DEFAULT ""; 420 tloop: LOOP 421 CASE a 422 WHEN 1 THEN 423 SET b = CONCAT(b, "a"); 424 SET a = a + 1; 425 END CASE; 426 END LOOP; 427 SELECT b; 428 END;`, 429 `CREATE PROCEDURE p4(IN a BIGINT) 430 BEGIN 431 DECLARE b VARCHAR(200) DEFAULT ""; 432 tloop: LOOP 433 CASE 434 WHEN a = 1 THEN 435 SET b = CONCAT(b, "a"); 436 SET a = a + 1; 437 END CASE; 438 END LOOP; 439 SELECT b; 440 END;`, 441 `CREATE PROCEDURE p5(IN a BIGINT) 442 BEGIN 443 DECLARE b VARCHAR(200) DEFAULT ""; 444 REPEAT 445 CASE 446 WHEN a <= 1 THEN 447 SET b = CONCAT(b, "a"); 448 SET a = a + 1; 449 END CASE; 450 UNTIL a > 1 451 END REPEAT; 452 SELECT b; 453 END;`, 454 }, 455 Assertions: []ScriptTestAssertion{ 456 { 457 Query: "CALL p1(0)", 458 Expected: []sql.Row{ 459 {"aaaabbbb"}, 460 }, 461 }, 462 { 463 Query: "CALL p1(3)", 464 Expected: []sql.Row{ 465 {"abbbb"}, 466 }, 467 }, 468 { 469 Query: "CALL p1(6)", 470 Expected: []sql.Row{ 471 {"bb"}, 472 }, 473 }, 474 { 475 Query: "CALL p1(9)", 476 Expected: []sql.Row{ 477 {""}, 478 }, 479 }, 480 { 481 Query: "CALL p2(1)", 482 Expected: []sql.Row{ 483 {"abc"}, 484 }, 485 }, 486 { 487 Query: "CALL p2(2)", 488 Expected: []sql.Row{ 489 {"bc"}, 490 }, 491 }, 492 { 493 Query: "CALL p2(3)", 494 Expected: []sql.Row{ 495 {"c"}, 496 }, 497 }, 498 { 499 Query: "CALL p2(4)", 500 Expected: []sql.Row{ 501 {""}, 502 }, 503 }, 504 { 505 Query: "CALL p3(1)", 506 ExpectedErrStr: "Case not found for CASE statement (errno 1339) (sqlstate 20000)", 507 }, 508 { 509 Query: "CALL p3(2)", 510 ExpectedErrStr: "Case not found for CASE statement (errno 1339) (sqlstate 20000)", 511 }, 512 { 513 Query: "CALL p4(1)", 514 ExpectedErrStr: "Case not found for CASE statement (errno 1339) (sqlstate 20000)", 515 }, 516 { 517 Query: "CALL p4(-1)", 518 ExpectedErrStr: "Case not found for CASE statement (errno 1339) (sqlstate 20000)", 519 }, 520 { 521 Query: "CALL p5(0)", 522 Expected: []sql.Row{ 523 {"aa"}, 524 }, 525 }, 526 { 527 Query: "CALL p5(1)", 528 Expected: []sql.Row{ 529 {"a"}, 530 }, 531 }, 532 }, 533 }, 534 { 535 Name: "SELECT with JOIN and table aliases", 536 SetUpScript: []string{ 537 "CREATE TABLE foo(a BIGINT PRIMARY KEY, b VARCHAR(20))", 538 "INSERT INTO foo VALUES (1, 'd'), (2, 'e'), (3, 'f')", 539 "CREATE TABLE bar(b VARCHAR(30) PRIMARY KEY, c BIGINT)", 540 "INSERT INTO bar VALUES ('x', 3), ('y', 2), ('z', 1)", 541 // Direct child is SELECT 542 "CREATE PROCEDURE p1() SELECT f.a, bar.b, f.b FROM foo f INNER JOIN bar ON f.a = bar.c ORDER BY 1", 543 // Direct child is BEGIN/END 544 "CREATE PROCEDURE p2() BEGIN SELECT f.a, bar.b, f.b FROM foo f INNER JOIN bar ON f.a = bar.c ORDER BY 1; END;", 545 // Direct child is IF 546 "CREATE PROCEDURE p3() IF 0 = 0 THEN SELECT f.a, bar.b, f.b FROM foo f INNER JOIN bar ON f.a = bar.c ORDER BY 1; END IF;", 547 // Direct child is BEGIN/END with preceding SELECT 548 "CREATE PROCEDURE p4() BEGIN SELECT 7; SELECT f.a, bar.b, f.b FROM foo f INNER JOIN bar ON f.a = bar.c ORDER BY 1; END;", 549 // Direct child is IF with preceding SELECT 550 "CREATE PROCEDURE p5() IF 0 = 0 THEN SELECT 7; SELECT f.a, bar.b, f.b FROM foo f INNER JOIN bar ON f.a = bar.c ORDER BY 1; END IF;", 551 }, 552 Assertions: []ScriptTestAssertion{ 553 { // Enforces that this is the expected output from the query normally 554 Query: "SELECT f.a, bar.b, f.b FROM foo f INNER JOIN bar ON f.a = bar.c ORDER BY 1", 555 Expected: []sql.Row{ 556 {int64(1), "z", "d"}, 557 {int64(2), "y", "e"}, 558 {int64(3), "x", "f"}, 559 }, 560 }, 561 { 562 Query: "CALL p1()", 563 Expected: []sql.Row{ 564 {int64(1), "z", "d"}, 565 {int64(2), "y", "e"}, 566 {int64(3), "x", "f"}, 567 }, 568 }, 569 { 570 Query: "CALL p2()", 571 Expected: []sql.Row{ 572 {int64(1), "z", "d"}, 573 {int64(2), "y", "e"}, 574 {int64(3), "x", "f"}, 575 }, 576 }, 577 { 578 SkipResultCheckOnServerEngine: true, // tracking issue: https://github.com/dolthub/dolt/issues/6918 579 Query: "CALL p3()", 580 Expected: []sql.Row{ 581 {int64(1), "z", "d"}, 582 {int64(2), "y", "e"}, 583 {int64(3), "x", "f"}, 584 }, 585 }, 586 { 587 Query: "CALL p4()", 588 Expected: []sql.Row{ 589 {int64(1), "z", "d"}, 590 {int64(2), "y", "e"}, 591 {int64(3), "x", "f"}, 592 }, 593 }, 594 { 595 SkipResultCheckOnServerEngine: true, // tracking issue: https://github.com/dolthub/dolt/issues/6918 596 Query: "CALL p5()", 597 Expected: []sql.Row{ 598 {int64(1), "z", "d"}, 599 {int64(2), "y", "e"}, 600 {int64(3), "x", "f"}, 601 }, 602 }, 603 }, 604 }, 605 { 606 Name: "Nested CALL in IF/ELSE branch", 607 SetUpScript: []string{ 608 "CREATE TABLE t1(pk BIGINT PRIMARY KEY)", 609 "INSERT INTO t1 VALUES (2), (3)", 610 "CREATE PROCEDURE p1(INOUT x BIGINT) BEGIN IF X = 1 THEN CALL p2(10); ELSEIF x = 2 THEN CALL p2(100); ELSE CALL p2(X); END IF; END;", 611 "CREATE PROCEDURE p2(INOUT y BIGINT) BEGIN SELECT pk * y FROM t1 ORDER BY 1; END;", 612 }, 613 Assertions: []ScriptTestAssertion{ 614 { 615 Query: "CALL p1(1)", 616 Expected: []sql.Row{ 617 {int64(20)}, 618 {int64(30)}, 619 }, 620 }, 621 { 622 Query: "CALL p1(2)", 623 Expected: []sql.Row{ 624 {int64(200)}, 625 {int64(300)}, 626 }, 627 }, 628 { 629 Query: "CALL p1(5)", 630 Expected: []sql.Row{ 631 {int64(10)}, 632 {int64(15)}, 633 }, 634 }, 635 }, 636 }, 637 { 638 Name: "INSERT INTO SELECT doesn't override SELECT", 639 SetUpScript: []string{ 640 "CREATE TABLE t1(pk BIGINT PRIMARY KEY)", 641 "CREATE TABLE t2(pk BIGINT PRIMARY KEY)", 642 "INSERT INTO t1 VALUES (2), (3)", 643 "INSERT INTO t2 VALUES (1)", 644 `CREATE PROCEDURE p1(x BIGINT) 645 BEGIN 646 DELETE FROM t2 WHERE pk > 1; 647 INSERT INTO t2 SELECT pk FROM t1; 648 SELECT * FROM t2; 649 INSERT INTO t2 SELECT pk + 10 FROM t1; 650 IF x = 1 THEN 651 SELECT * FROM t2; 652 END IF; 653 END;`, 654 }, 655 Assertions: []ScriptTestAssertion{ 656 { 657 Query: "CALL p1(0)", 658 Expected: []sql.Row{ 659 {int64(1)}, 660 {int64(2)}, 661 {int64(3)}, 662 }, 663 }, 664 { 665 Query: "CALL p1(1)", 666 Expected: []sql.Row{ 667 {int64(1)}, 668 {int64(2)}, 669 {int64(3)}, 670 {int64(12)}, 671 {int64(13)}, 672 }, 673 }, 674 }, 675 }, 676 { 677 Name: "Parameters resolve inside of INSERT", 678 SetUpScript: []string{ 679 `CREATE TABLE items ( 680 id INT PRIMARY KEY AUTO_INCREMENT, 681 item TEXT NOT NULL 682 );`, 683 `CREATE PROCEDURE add_item (IN txt TEXT) MODIFIES SQL DATA 684 INSERT INTO items (item) VALUES (txt)`, 685 }, 686 Assertions: []ScriptTestAssertion{ 687 { 688 SkipResultCheckOnServerEngine: true, // call depends on stored procedure stmt for whether to use 'query' or 'exec' from go sql driver. 689 Query: "CALL add_item('A test item');", 690 Expected: []sql.Row{ 691 {types.OkResult{RowsAffected: 1, InsertID: 1}}, 692 }, 693 }, 694 { 695 Query: "SELECT * FROM items;", 696 Expected: []sql.Row{ 697 {1, "A test item"}, 698 }, 699 }, 700 }, 701 }, 702 { 703 Name: "Parameters resolve inside of SELECT UNION", 704 SetUpScript: []string{ 705 "CREATE TABLE t1(pk BIGINT PRIMARY KEY, v1 BIGINT)", 706 "INSERT INTO t1 VALUES (1, 2)", 707 "SELECT pk, v1 FROM t1 UNION SELECT 1, 2;", 708 `CREATE PROCEDURE p1(x BIGINT, y BIGINT) 709 BEGIN 710 SELECT pk+x, v1+y FROM t1 UNION SELECT x, y; 711 END;`, 712 `CREATE PROCEDURE p2(u BIGINT, v BIGINT) SELECT pk+u, v1+v FROM t1 UNION SELECT u, v;`, 713 }, 714 Assertions: []ScriptTestAssertion{ 715 { 716 Query: "CALL p1(3, 4)", 717 Expected: []sql.Row{ 718 {4, 6}, 719 {3, 4}, 720 }, 721 }, 722 { 723 Query: "CALL p2(5, 6)", 724 Expected: []sql.Row{ 725 {6, 8}, 726 {5, 6}, 727 }, 728 }, 729 }, 730 }, 731 { 732 Name: "Parameters resolve inside of REPLACE", 733 SetUpScript: []string{ 734 `CREATE TABLE items ( 735 id INT PRIMARY KEY AUTO_INCREMENT, 736 item INT NOT NULL 737 );`, 738 `CREATE PROCEDURE add_item (IN num INT) MODIFIES SQL DATA 739 BEGIN 740 REPLACE INTO items (item) VALUES (5), (num), (num+1); 741 END`, 742 }, 743 Assertions: []ScriptTestAssertion{ 744 { 745 SkipResultCheckOnServerEngine: true, // call depends on stored procedure stmt for whether to use 'query' or 'exec' from go sql driver. 746 Query: "CALL add_item(6);", 747 Expected: []sql.Row{ 748 {types.NewOkResult(3)}, 749 }, 750 }, 751 { 752 Query: "SELECT * FROM items ORDER BY 1;", 753 Expected: []sql.Row{ 754 {1, 5}, 755 {2, 6}, 756 {3, 7}, 757 }, 758 }, 759 }, 760 }, 761 { 762 Name: "Parameters resolve inside of INSERT INTO SELECT", 763 SetUpScript: []string{ 764 "CREATE TABLE t1(pk BIGINT PRIMARY KEY)", 765 "CREATE TABLE t2(pk BIGINT PRIMARY KEY)", 766 "INSERT INTO t1 VALUES (1), (2)", 767 `CREATE PROCEDURE p1(x BIGINT) 768 BEGIN 769 TRUNCATE t2; 770 INSERT INTO t2 SELECT pk+x FROM t1; 771 SELECT * FROM t2; 772 END;`, 773 }, 774 Assertions: []ScriptTestAssertion{ 775 { 776 Query: "CALL p1(0)", 777 Expected: []sql.Row{ 778 {int64(1)}, 779 {int64(2)}, 780 }, 781 }, 782 { 783 Query: "CALL p1(5)", 784 Expected: []sql.Row{ 785 {int64(6)}, 786 {int64(7)}, 787 }, 788 }, 789 }, 790 }, 791 { 792 Name: "Subquery on SET user variable captures parameter", 793 SetUpScript: []string{ 794 `CREATE PROCEDURE p1(x VARCHAR(20)) BEGIN SET @randomvar = (SELECT LENGTH(x)); SELECT @randomvar; END;`, 795 }, 796 Assertions: []ScriptTestAssertion{ 797 { 798 SkipResultCheckOnServerEngine: true, // the user var has null type, which returns nil value over the wire. 799 Query: "CALL p1('hi')", 800 Expected: []sql.Row{ 801 {int64(2)}, 802 }, 803 }, 804 { 805 Query: "CALL p1('hello')", 806 Expected: []sql.Row{ 807 {int64(5)}, 808 }, 809 }, 810 }, 811 }, 812 { 813 Name: "Simple SELECT INTO", 814 SetUpScript: []string{ 815 "CREATE PROCEDURE testabc(IN x DOUBLE, IN y DOUBLE, OUT abc DOUBLE) SELECT x*y INTO abc", 816 "CALL testabc(2, 3, @res1)", 817 "CALL testabc(9, 9.5, @res2)", 818 }, 819 Assertions: []ScriptTestAssertion{ 820 { 821 Query: "SELECT @res1", 822 Expected: []sql.Row{{float64(6)}}, 823 }, 824 { 825 Query: "SELECT @res2", 826 Expected: []sql.Row{{float64(85.5)}}, 827 }, 828 }, 829 }, 830 { 831 Name: "Multiple variables in SELECT INTO", 832 SetUpScript: []string{ 833 "CREATE PROCEDURE new_proc(IN x DOUBLE, IN y DOUBLE, OUT abc DOUBLE, OUT def DOUBLE) SELECT x*y, x+y INTO abc, def", 834 "CALL new_proc(2, 3, @res1, @res2)", 835 "CALL new_proc(9, 9.5, @res3, @res4)", 836 }, 837 Assertions: []ScriptTestAssertion{ 838 { 839 Query: "SELECT @res1, @res2", 840 Expected: []sql.Row{{float64(6), float64(5)}}, 841 }, 842 { 843 Query: "SELECT @res3, @res4", 844 Expected: []sql.Row{{float64(85.5), float64(18.5)}}, 845 }, 846 }, 847 }, 848 { 849 Name: "SELECT INTO with condition", 850 SetUpScript: []string{ 851 "CREATE TABLE inventory (item_id int primary key, shelf_id int, items varchar(100))", 852 "INSERT INTO inventory VALUES (1, 1, 'a'), (2, 1, 'b'), (3, 2, 'c'), (4, 1, 'd'), (5, 4, 'e')", 853 "CREATE PROCEDURE in_stock (IN p_id INT, OUT p_count INT) SELECT COUNT(*) FROM inventory WHERE shelf_id = p_id INTO p_count", 854 "CALL in_stock(1, @shelf1)", 855 "CALL in_stock(2, @shelf2)", 856 "CALL in_stock(3, @shelf3)", 857 }, 858 Assertions: []ScriptTestAssertion{ 859 { 860 Query: "SELECT @shelf1, @shelf2, @shelf3", 861 Expected: []sql.Row{{3, 1, 0}}, 862 }, 863 }, 864 }, 865 { 866 Name: "SELECT INTO with group by, order by and limit", 867 SetUpScript: []string{ 868 "CREATE TABLE inventory (item_id int primary key, shelf_id int, item varchar(10))", 869 "INSERT INTO inventory VALUES (1, 1, 'a'), (2, 1, 'b'), (3, 2, 'c'), (4, 1, 'd'), (5, 4, 'e')", 870 "CREATE PROCEDURE first_shelf (OUT p_count INT) SELECT COUNT(*) FROM inventory GROUP BY shelf_id ORDER BY shelf_id ASC LIMIT 1 INTO p_count", 871 "CREATE PROCEDURE last_shelf (OUT p_count INT) SELECT COUNT(*) FROM inventory GROUP BY shelf_id ORDER BY shelf_id DESC LIMIT 1 INTO p_count", 872 "CALL first_shelf(@result1)", 873 "CALL last_shelf(@result2)", 874 }, 875 Assertions: []ScriptTestAssertion{ 876 { 877 Query: "SELECT @result1", 878 Expected: []sql.Row{{3}}, 879 }, 880 { 881 Query: "SELECT @result2", 882 Expected: []sql.Row{{1}}, 883 }, 884 }, 885 }, 886 { 887 Name: "multiple SELECT INTO in begin end block", 888 SetUpScript: []string{ 889 "CREATE TABLE inventory (item_id int primary key, shelf_id int, item varchar(10))", 890 "INSERT INTO inventory VALUES (1, 1, 'a'), (2, 1, 'b'), (3, 2, 'c'), (4, 1, 'd'), (5, 4, 'e')", 891 "CREATE PROCEDURE random_info(OUT p_count1 INT, OUT p_count2 VARCHAR(10)) BEGIN " + 892 "SELECT COUNT(*) FROM inventory GROUP BY shelf_id ORDER BY shelf_id ASC LIMIT 1 INTO p_count1;" + 893 "SELECT item INTO p_count2 FROM inventory WHERE shelf_id = 1 ORDER BY item DESC LIMIT 1; " + 894 "END", 895 "CALL random_info(@s1, @s2)", 896 }, 897 Assertions: []ScriptTestAssertion{ 898 { 899 Query: "SELECT @s1, @s2", 900 Expected: []sql.Row{{3, "d"}}, 901 }, 902 }, 903 }, 904 { 905 Name: "multiple statement with single SELECT INTO in begin end block", 906 SetUpScript: []string{ 907 "CREATE TABLE inventory (item_id int primary key, shelf_id int, item varchar(10))", 908 "INSERT INTO inventory VALUES (1, 1, 'a'), (2, 1, 'b'), (3, 2, 'c'), (4, 1, 'd'), (5, 4, 'e')", 909 `CREATE PROCEDURE count_and_print(IN p_shelf_id INT, OUT p_count INT) BEGIN 910 SELECT item FROM inventory WHERE shelf_id = p_shelf_id ORDER BY item ASC; 911 SELECT COUNT(*) INTO p_count FROM inventory WHERE shelf_id = p_shelf_id; 912 END`, 913 }, 914 Assertions: []ScriptTestAssertion{ 915 { 916 Query: "CALL count_and_print(1, @total)", 917 Expected: []sql.Row{{"a"}, {"b"}, {"d"}}, 918 }, 919 { 920 Query: "SELECT @total", 921 Expected: []sql.Row{{3}}, 922 }, 923 }, 924 }, 925 { 926 Name: "DECLARE variables, proper nesting support", 927 SetUpScript: []string{ 928 `CREATE PROCEDURE p1(OUT x BIGINT) 929 BEGIN 930 DECLARE a INT; 931 DECLARE b MEDIUMINT; 932 DECLARE c VARCHAR(20); 933 SELECT 1, 2, 'a' INTO a, b, c; 934 BEGIN 935 DECLARE b MEDIUMINT; 936 SET a = 4; 937 SET b = 5; 938 END; 939 SET x = a + b; 940 SELECT a, b, c; 941 END;`, 942 }, 943 Assertions: []ScriptTestAssertion{ 944 { 945 Query: "CALL p1(@x);", 946 Expected: []sql.Row{ 947 {4, 2, "a"}, 948 }, 949 }, 950 { 951 Query: "SELECT @x;", 952 Expected: []sql.Row{ 953 {6}, 954 }, 955 }, 956 }, 957 }, 958 { 959 Name: "DECLARE multiple variables, same statement", 960 SetUpScript: []string{ 961 `CREATE PROCEDURE p1() 962 BEGIN 963 DECLARE a, b, c INT; 964 SELECT 2, 3, 4 INTO a, b, c; 965 SELECT a + b + c; 966 END;`, 967 }, 968 Assertions: []ScriptTestAssertion{ 969 { 970 Query: "CALL p1();", 971 Expected: []sql.Row{ 972 {9}, 973 }, 974 }, 975 }, 976 }, 977 { 978 Name: "DECLARE variable shadows parameter", 979 SetUpScript: []string{ 980 `CREATE PROCEDURE p1(INOUT x INT) 981 BEGIN 982 DECLARE x INT; 983 SET x = 5; 984 END;`, 985 }, 986 Assertions: []ScriptTestAssertion{ 987 { 988 Query: "SET @x = 2;", 989 Expected: []sql.Row{{}}, 990 }, 991 { 992 Query: "CALL p1(@x);", 993 Expected: []sql.Row{{}}, 994 }, 995 { 996 Query: "SELECT @x;", 997 Expected: []sql.Row{ 998 {2}, 999 }, 1000 }, 1001 }, 1002 }, 1003 { 1004 Name: "DECLARE CONDITION", 1005 SetUpScript: []string{ 1006 `CREATE PROCEDURE p1(x INT) 1007 BEGIN 1008 DECLARE specialty CONDITION FOR SQLSTATE '45000'; 1009 DECLARE specialty2 CONDITION FOR SQLSTATE '02000'; 1010 IF x = 0 THEN 1011 SIGNAL SQLSTATE '01000'; 1012 ELSEIF x = 1 THEN 1013 SIGNAL SQLSTATE '45000' 1014 SET MESSAGE_TEXT = 'A custom error occurred 1'; 1015 ELSEIF x = 2 THEN 1016 SIGNAL specialty 1017 SET MESSAGE_TEXT = 'A custom error occurred 2', MYSQL_ERRNO = 1002; 1018 ELSEIF x = 3 THEN 1019 SIGNAL specialty; 1020 ELSEIF x = 4 THEN 1021 SIGNAL specialty2; 1022 ELSE 1023 SIGNAL SQLSTATE '01000' 1024 SET MESSAGE_TEXT = 'A warning occurred', MYSQL_ERRNO = 1000; 1025 SIGNAL SQLSTATE '45000' 1026 SET MESSAGE_TEXT = 'An error occurred', MYSQL_ERRNO = 1001; 1027 END IF; 1028 BEGIN 1029 DECLARE specialty3 CONDITION FOR SQLSTATE '45000'; 1030 END; 1031 END;`, 1032 }, 1033 Assertions: []ScriptTestAssertion{ 1034 { 1035 Query: "CALL p1(0)", 1036 ExpectedErrStr: "warnings not yet implemented", 1037 }, 1038 { 1039 Query: "CALL p1(1)", 1040 ExpectedErrStr: "A custom error occurred 1 (errno 1644) (sqlstate 45000)", 1041 }, 1042 { 1043 Query: "CALL p1(2)", 1044 ExpectedErrStr: "A custom error occurred 2 (errno 1002) (sqlstate 45000)", 1045 }, 1046 { 1047 Query: "CALL p1(3)", 1048 ExpectedErrStr: "Unhandled user-defined exception condition (errno 1644) (sqlstate 45000)", 1049 }, 1050 { 1051 Query: "CALL p1(4)", 1052 ExpectedErrStr: "Unhandled user-defined not found condition (errno 1643) (sqlstate 02000)", 1053 }, 1054 }, 1055 }, 1056 { 1057 Name: "DECLARE CONDITION nesting priority", 1058 SetUpScript: []string{ 1059 `CREATE PROCEDURE p1(x INT) 1060 BEGIN 1061 DECLARE cond_name CONDITION FOR SQLSTATE '02000'; 1062 BEGIN 1063 DECLARE cond_name CONDITION FOR SQLSTATE '45000'; 1064 IF x = 0 THEN 1065 SIGNAL cond_name; 1066 END IF; 1067 END; 1068 SIGNAL cond_name; 1069 END;`, 1070 `CREATE PROCEDURE p2(x INT) 1071 BEGIN 1072 DECLARE cond_name CONDITION FOR SQLSTATE '45000'; 1073 BEGIN 1074 DECLARE cond_name CONDITION FOR SQLSTATE '02000'; 1075 IF x = 0 THEN 1076 SIGNAL cond_name; 1077 END IF; 1078 END; 1079 SIGNAL cond_name; 1080 END;`, 1081 }, 1082 Assertions: []ScriptTestAssertion{ 1083 { 1084 Query: "CALL p1(0)", 1085 ExpectedErrStr: "Unhandled user-defined exception condition (errno 1644) (sqlstate 45000)", 1086 }, 1087 { 1088 Query: "CALL p1(1)", 1089 ExpectedErrStr: "Unhandled user-defined not found condition (errno 1643) (sqlstate 02000)", 1090 }, 1091 { 1092 Query: "CALL p2(0)", 1093 ExpectedErrStr: "Unhandled user-defined not found condition (errno 1643) (sqlstate 02000)", 1094 }, 1095 { 1096 Query: "CALL p2(1)", 1097 ExpectedErrStr: "Unhandled user-defined exception condition (errno 1644) (sqlstate 45000)", 1098 }, 1099 }, 1100 }, 1101 { 1102 Name: "FETCH multiple rows", 1103 SetUpScript: []string{ 1104 `CREATE TABLE t1 (pk BIGINT PRIMARY KEY);`, 1105 `CREATE PROCEDURE p1() 1106 BEGIN 1107 DECLARE a, b INT; 1108 DECLARE cur1 CURSOR FOR SELECT pk FROM t1; 1109 DELETE FROM t1; 1110 INSERT INTO t1 VALUES (1), (2); 1111 OPEN cur1; 1112 FETCH cur1 INTO a; 1113 FETCH cur1 INTO b; 1114 CLOSE cur1; 1115 SELECT a, b; 1116 END;`, 1117 }, 1118 Assertions: []ScriptTestAssertion{ 1119 { 1120 Query: "CALL p1();", 1121 Expected: []sql.Row{ 1122 {1, 2}, 1123 }, 1124 }, 1125 }, 1126 }, 1127 { 1128 Name: "FETCH with multiple opens and closes", 1129 SetUpScript: []string{ 1130 `CREATE TABLE t1 (pk BIGINT PRIMARY KEY);`, 1131 `CREATE PROCEDURE p1() 1132 BEGIN 1133 DECLARE a, b INT; 1134 DECLARE cur1 CURSOR FOR SELECT pk FROM t1; 1135 DELETE FROM t1; 1136 INSERT INTO t1 VALUES (1); 1137 OPEN cur1; 1138 FETCH cur1 INTO a; 1139 CLOSE cur1; 1140 UPDATE t1 SET pk = 2; 1141 OPEN cur1; 1142 FETCH cur1 INTO b; 1143 CLOSE cur1; 1144 SELECT a, b; 1145 END;`, 1146 }, 1147 Assertions: []ScriptTestAssertion{ 1148 { 1149 Query: "CALL p1();", 1150 Expected: []sql.Row{ 1151 {1, 2}, 1152 }, 1153 }, 1154 }, 1155 }, 1156 { 1157 Name: "issue 7458: proc params as limit values", 1158 SetUpScript: []string{ 1159 "create table t (i int primary key);", 1160 "insert into t values (0), (1), (2), (3)", 1161 "CREATE PROCEDURE limited(the_limit int, the_offset bigint) SELECT * FROM t LIMIT the_limit OFFSET the_offset", 1162 "CREATE PROCEDURE limited_uns(the_limit int unsigned, the_offset bigint unsigned) SELECT * FROM t LIMIT the_limit OFFSET the_offset", 1163 }, 1164 Assertions: []ScriptTestAssertion{ 1165 { 1166 Query: "call limited(1,0)", 1167 Expected: []sql.Row{{0}}, 1168 }, 1169 { 1170 Query: "call limited(2,0)", 1171 Expected: []sql.Row{{0}, {1}}, 1172 }, 1173 { 1174 Query: "call limited(2,2)", 1175 Expected: []sql.Row{{2}, {3}}, 1176 }, 1177 { 1178 Query: "call limited_uns(2,2)", 1179 Expected: []sql.Row{{2}, {3}}, 1180 }, 1181 { 1182 Query: "CREATE PROCEDURE limited_inv(the_limit CHAR(3), the_offset INT) SELECT * FROM t LIMIT the_limit OFFSET the_offset", 1183 ExpectedErrStr: "the variable 'the_limit' has a non-integer based type: char(3) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin", 1184 }, 1185 { 1186 Query: "CREATE PROCEDURE limited_inv(the_limit float, the_offset INT) SELECT * FROM t LIMIT the_limit OFFSET the_offset", 1187 ExpectedErrStr: "the variable 'the_limit' has a non-integer based type: float", 1188 }, 1189 }, 1190 }, 1191 { 1192 Name: "FETCH captures state at OPEN", 1193 SetUpScript: []string{ 1194 `CREATE TABLE t1 (pk BIGINT PRIMARY KEY);`, 1195 `CREATE PROCEDURE p1() 1196 BEGIN 1197 DECLARE a, b INT; 1198 DECLARE cur1 CURSOR FOR SELECT pk FROM t1; 1199 DELETE FROM t1; 1200 INSERT INTO t1 VALUES (1); 1201 OPEN cur1; 1202 UPDATE t1 SET pk = 2; 1203 FETCH cur1 INTO a; 1204 CLOSE cur1; 1205 OPEN cur1; 1206 FETCH cur1 INTO b; 1207 CLOSE cur1; 1208 SELECT a, b; 1209 END;`, 1210 }, 1211 Assertions: []ScriptTestAssertion{ 1212 { 1213 Query: "CALL p1();", 1214 Expected: []sql.Row{ 1215 {1, 2}, 1216 }, 1217 }, 1218 }, 1219 }, 1220 { 1221 Name: "FETCH implicitly closes", 1222 SetUpScript: []string{ 1223 `CREATE TABLE t1 (pk BIGINT PRIMARY KEY);`, 1224 `CREATE PROCEDURE p1() 1225 BEGIN 1226 DECLARE a INT; 1227 DECLARE cur1 CURSOR FOR SELECT pk FROM t1; 1228 DELETE FROM t1; 1229 INSERT INTO t1 VALUES (4); 1230 OPEN cur1; 1231 FETCH cur1 INTO a; 1232 SELECT a; 1233 END;`, 1234 }, 1235 Assertions: []ScriptTestAssertion{ 1236 { 1237 Query: "CALL p1();", 1238 Expected: []sql.Row{ 1239 {4}, 1240 }, 1241 }, 1242 }, 1243 }, 1244 { 1245 Name: "SQLEXCEPTION declare handler", 1246 SetUpScript: []string{ 1247 `DROP TABLE IF EXISTS t1;`, 1248 `CREATE TABLE t1 (pk BIGINT PRIMARY KEY);`, 1249 `CREATE PROCEDURE eof() 1250 BEGIN 1251 DECLARE a, b INT DEFAULT 1; 1252 DECLARE cur1 CURSOR FOR SELECT * FROM t1; 1253 OPEN cur1; 1254 BEGIN 1255 DECLARE EXIT HANDLER FOR SQLEXCEPTION SET a = 7; 1256 tloop: LOOP 1257 FETCH cur1 INTO b; 1258 IF a > 1000 THEN 1259 LEAVE tloop; 1260 END IF; 1261 END LOOP; 1262 END; 1263 CLOSE cur1; 1264 SELECT a; 1265 END;`, 1266 `CREATE PROCEDURE duplicate_key() 1267 BEGIN 1268 DECLARE a, b INT DEFAULT 1; 1269 BEGIN 1270 DECLARE EXIT HANDLER FOR SQLEXCEPTION SET a = 7; 1271 INSERT INTO t1 values (0); 1272 END; 1273 SELECT a; 1274 END;`, 1275 }, 1276 Assertions: []ScriptTestAssertion{ 1277 { 1278 Query: "CALL eof();", 1279 Expected: []sql.Row{}, 1280 }, 1281 { 1282 Query: "CALL duplicate_key();", 1283 Expected: []sql.Row{{1}}, 1284 }, 1285 { 1286 Query: "CALL duplicate_key();", 1287 Expected: []sql.Row{{7}}, 1288 }, 1289 }, 1290 }, 1291 { 1292 Name: "DECLARE HANDLERs exit according to the block they were declared in", 1293 SetUpScript: []string{ 1294 `DROP TABLE IF EXISTS t1;`, 1295 `CREATE TABLE t1 (pk BIGINT PRIMARY KEY);`, 1296 `CREATE PROCEDURE outer_declare() 1297 BEGIN 1298 DECLARE a, b INT DEFAULT 1; 1299 DECLARE cur1 CURSOR FOR SELECT * FROM t1; 1300 DECLARE EXIT HANDLER FOR NOT FOUND SET a = 1001; 1301 OPEN cur1; 1302 BEGIN 1303 tloop: LOOP 1304 FETCH cur1 INTO b; 1305 IF a > 1000 THEN 1306 LEAVE tloop; 1307 END IF; 1308 END LOOP; 1309 END; 1310 CLOSE cur1; 1311 SELECT a; 1312 END;`, 1313 `CREATE PROCEDURE inner_declare() 1314 BEGIN 1315 DECLARE a, b INT DEFAULT 1; 1316 DECLARE cur1 CURSOR FOR SELECT * FROM t1; 1317 DECLARE EXIT HANDLER FOR NOT FOUND SET a = a + 1; 1318 OPEN cur1; 1319 BEGIN 1320 DECLARE EXIT HANDLER FOR NOT FOUND SET a = 1001; 1321 tloop: LOOP 1322 FETCH cur1 INTO b; 1323 IF a > 1000 THEN 1324 LEAVE tloop; 1325 END IF; 1326 END LOOP; 1327 END; 1328 CLOSE cur1; 1329 SELECT a; 1330 END;`, 1331 }, 1332 Assertions: []ScriptTestAssertion{ 1333 { 1334 Query: "CALL outer_declare();", 1335 Expected: []sql.Row{}, 1336 }, 1337 { 1338 Query: "CALL inner_declare();", 1339 Expected: []sql.Row{ 1340 {1001}, 1341 }, 1342 }, 1343 }, 1344 }, 1345 { 1346 Name: "Labeled BEGIN...END", 1347 SetUpScript: []string{ 1348 `CREATE PROCEDURE p1() 1349 BEGIN 1350 DECLARE a INT DEFAULT 1; 1351 tblock: BEGIN 1352 LOOP 1353 SET a = a + 3; 1354 LEAVE tblock; 1355 END LOOP; 1356 END; 1357 SELECT a; 1358 END;`, 1359 }, 1360 Assertions: []ScriptTestAssertion{ 1361 { 1362 Query: "CALL p1();", 1363 Expected: []sql.Row{ 1364 {4}, 1365 }, 1366 }, 1367 { 1368 Query: `CREATE PROCEDURE p2() BEGIN tblock: BEGIN ITERATE tblock; END; END;`, 1369 ExpectedErr: sql.ErrLoopLabelNotFound, 1370 }, 1371 }, 1372 }, 1373 { 1374 Name: "REPEAT runs loop before first evaluation", 1375 SetUpScript: []string{ 1376 `CREATE PROCEDURE p1() 1377 BEGIN 1378 DECLARE a INT DEFAULT 10; 1379 REPEAT 1380 SET a = a * 5; 1381 UNTIL a > 0 1382 END REPEAT; 1383 SELECT a; 1384 END;`, 1385 }, 1386 Assertions: []ScriptTestAssertion{ 1387 { 1388 Query: "CALL p1();", 1389 Expected: []sql.Row{ 1390 {50}, 1391 }, 1392 }, 1393 }, 1394 }, 1395 { 1396 Name: "WHILE runs evaluation before first loop", 1397 SetUpScript: []string{ 1398 `CREATE PROCEDURE p1() 1399 BEGIN 1400 DECLARE a INT DEFAULT 10; 1401 WHILE a < 10 DO 1402 SET a = a * 10; 1403 END WHILE; 1404 SELECT a; 1405 END;`, 1406 }, 1407 Assertions: []ScriptTestAssertion{ 1408 { 1409 Query: "CALL p1();", 1410 Expected: []sql.Row{ 1411 {10}, 1412 }, 1413 }, 1414 }, 1415 }, 1416 { 1417 Name: "ITERATE and LEAVE LOOP", 1418 SetUpScript: []string{ 1419 `CREATE TABLE t1 (pk BIGINT PRIMARY KEY);`, 1420 `INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9)`, 1421 `CREATE PROCEDURE p1() 1422 BEGIN 1423 DECLARE a, b INT DEFAULT 1; 1424 DECLARE cur1 CURSOR FOR SELECT * FROM t1; 1425 DECLARE EXIT HANDLER FOR NOT FOUND BEGIN END; 1426 OPEN cur1; 1427 BEGIN 1428 tloop: LOOP 1429 FETCH cur1 INTO b; 1430 SET a = (a + b) * 10; 1431 IF a < 1000 THEN 1432 ITERATE tloop; 1433 ELSE 1434 LEAVE tloop; 1435 END IF; 1436 END LOOP; 1437 END; 1438 CLOSE cur1; 1439 SELECT a; 1440 END;`, 1441 }, 1442 Assertions: []ScriptTestAssertion{ 1443 { 1444 Query: "CALL p1();", 1445 Expected: []sql.Row{ 1446 {2230}, 1447 }, 1448 }, 1449 }, 1450 }, 1451 { 1452 Name: "ITERATE and LEAVE REPEAT", 1453 SetUpScript: []string{ 1454 `CREATE TABLE t1 (pk BIGINT PRIMARY KEY);`, 1455 `INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9)`, 1456 `CREATE PROCEDURE p1() 1457 BEGIN 1458 DECLARE a, b INT DEFAULT 1; 1459 DECLARE cur1 CURSOR FOR SELECT * FROM t1; 1460 DECLARE EXIT HANDLER FOR NOT FOUND BEGIN END; 1461 OPEN cur1; 1462 BEGIN 1463 tloop: REPEAT 1464 FETCH cur1 INTO b; 1465 SET a = (a + b) * 10; 1466 IF a < 1000 THEN 1467 ITERATE tloop; 1468 ELSE 1469 LEAVE tloop; 1470 END IF; 1471 UNTIL false 1472 END REPEAT; 1473 END; 1474 CLOSE cur1; 1475 SELECT a; 1476 END;`, 1477 }, 1478 Assertions: []ScriptTestAssertion{ 1479 { 1480 Query: "CALL p1();", 1481 Expected: []sql.Row{ 1482 {2230}, 1483 }, 1484 }, 1485 }, 1486 }, 1487 { 1488 Name: "ITERATE and LEAVE WHILE", 1489 SetUpScript: []string{ 1490 `CREATE TABLE t1 (pk BIGINT PRIMARY KEY);`, 1491 `INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9)`, 1492 `CREATE PROCEDURE p1() 1493 BEGIN 1494 DECLARE a, b INT DEFAULT 1; 1495 DECLARE cur1 CURSOR FOR SELECT * FROM t1; 1496 DECLARE EXIT HANDLER FOR NOT FOUND BEGIN END; 1497 OPEN cur1; 1498 BEGIN 1499 tloop: WHILE true DO 1500 FETCH cur1 INTO b; 1501 SET a = (a + b) * 10; 1502 IF a < 1000 THEN 1503 ITERATE tloop; 1504 ELSE 1505 LEAVE tloop; 1506 END IF; 1507 END WHILE; 1508 END; 1509 CLOSE cur1; 1510 SELECT a; 1511 END;`, 1512 }, 1513 Assertions: []ScriptTestAssertion{ 1514 { 1515 Query: "CALL p1();", 1516 Expected: []sql.Row{ 1517 {2230}, 1518 }, 1519 }, 1520 }, 1521 }, 1522 { 1523 Name: "Handle setting an uninitialized user variable", 1524 SetUpScript: []string{ 1525 `CREATE PROCEDURE p1(INOUT param VARCHAR(10)) 1526 BEGIN 1527 SELECT param; 1528 SET param = '5'; 1529 END`, 1530 }, 1531 Assertions: []ScriptTestAssertion{ 1532 { 1533 Query: "CALL p1(@uservar4);", 1534 Expected: []sql.Row{ 1535 {nil}, 1536 }, 1537 }, 1538 { 1539 Query: "SELECT @uservar4;", 1540 Expected: []sql.Row{ 1541 {"5"}, 1542 }, 1543 }, 1544 }, 1545 }, 1546 { 1547 Name: "Dolt Issue #4980", 1548 SetUpScript: []string{ 1549 `CREATE TABLE person_cal_entries (id VARCHAR(36) PRIMARY KEY, cal_entry_id_fk VARCHAR(36), person_id_fk VARCHAR(36));`, 1550 `CREATE TABLE personnel (id VARCHAR(36) PRIMARY KEY, event_id VARCHAR(36));`, 1551 `CREATE TABLE season_participants (person_id_fk VARCHAR(36), season_id_fk VARCHAR(36));`, 1552 `CREATE TABLE cal_entries (id VARCHAR(36) PRIMARY KEY, season_id_fk VARCHAR(36));`, 1553 `INSERT INTO personnel VALUES ('6140e23e-7b9b-11ed-a1eb-0242ac120002', 'c546abc4-7b9b-11ed-a1eb-0242ac120002');`, 1554 `INSERT INTO season_participants VALUES ('6140e23e-7b9b-11ed-a1eb-0242ac120002', '46d7041e-7b9b-11ed-a1eb-0242ac120002');`, 1555 `INSERT INTO cal_entries VALUES ('cb8ba301-6c27-4bf8-b99b-617082d72621', '46d7041e-7b9b-11ed-a1eb-0242ac120002');`, 1556 `CREATE PROCEDURE create_cal_entries_for_event(IN event_id VARCHAR(36)) 1557 BEGIN 1558 INSERT INTO person_cal_entries (id, cal_entry_id_fk, person_id_fk) 1559 SELECT 'd17cb898-7b9b-11ed-a1eb-0242ac120002' as id, event_id as cal_entry_id_fk, id as person_id_fk 1560 FROM personnel 1561 WHERE id IN ( 1562 SELECT person_id_fk 1563 FROM season_participants 1564 WHERE season_id_fk = ( 1565 SELECT season_id_fk 1566 FROM cal_entries 1567 WHERE id = event_id 1568 ) 1569 ); 1570 END`, 1571 }, 1572 Assertions: []ScriptTestAssertion{ 1573 { 1574 SkipResultCheckOnServerEngine: true, // call depends on stored procedure stmt for whether to use 'query' or 'exec' from go sql driver. 1575 Query: "call create_cal_entries_for_event('cb8ba301-6c27-4bf8-b99b-617082d72621');", 1576 Expected: []sql.Row{ 1577 {types.NewOkResult(1)}, 1578 }, 1579 }, 1580 { 1581 Query: "SELECT * FROM person_cal_entries;", 1582 Expected: []sql.Row{ 1583 {"d17cb898-7b9b-11ed-a1eb-0242ac120002", "cb8ba301-6c27-4bf8-b99b-617082d72621", "6140e23e-7b9b-11ed-a1eb-0242ac120002"}, 1584 }, 1585 }, 1586 }, 1587 }, 1588 { 1589 Name: "HANDLERs ignore variables declared after them", 1590 SetUpScript: []string{ 1591 `CREATE TABLE t1 (pk BIGINT PRIMARY KEY);`, 1592 `CREATE PROCEDURE p1() 1593 BEGIN 1594 DECLARE dvar BIGINT DEFAULT 1; 1595 DECLARE cur1 CURSOR FOR SELECT * FROM t1; 1596 OPEN cur1; 1597 BEGIN 1598 DECLARE EXIT HANDLER FOR NOT FOUND SET dvar = 10; 1599 BEGIN 1600 DECLARE dvar BIGINT DEFAULT 2; 1601 BEGIN 1602 DECLARE dvar BIGINT DEFAULT 3; 1603 LOOP 1604 FETCH cur1 INTO dvar; # Handler is triggered here, but should only set the first "dvar" 1605 END LOOP; 1606 END; 1607 END; 1608 END; 1609 SELECT dvar; 1610 END`, 1611 }, 1612 Assertions: []ScriptTestAssertion{ 1613 { 1614 Query: "CALL p1();", 1615 Expected: []sql.Row{ 1616 {10}, 1617 }, 1618 }, 1619 }, 1620 }, 1621 { 1622 Name: "Duplicate parameter names", 1623 Query: "CREATE PROCEDURE p1(abc DATETIME, abc DOUBLE) SELECT abc", 1624 ExpectedErr: sql.ErrDeclareVariableDuplicate, 1625 }, 1626 { 1627 Name: "Duplicate parameter names mixed casing", 1628 Query: "CREATE PROCEDURE p1(abc DATETIME, ABC DOUBLE) SELECT abc", 1629 ExpectedErr: sql.ErrDeclareVariableDuplicate, 1630 }, 1631 { 1632 Name: "Invalid parameter type", 1633 Query: "CREATE PROCEDURE p1(x FAKETYPE) SELECT x", 1634 ExpectedErr: sql.ErrSyntaxError, 1635 }, 1636 { // This statement is not allowed in stored procedures, and is caught by the vitess parser. 1637 Name: "Invalid USE statement", 1638 Query: `CREATE PROCEDURE p1() USE mydb`, 1639 ExpectedErr: sql.ErrSyntaxError, 1640 }, 1641 { // These statements are not allowed in stored procedures, and are caught by the vitess parser. 1642 Name: "Invalid LOCK/UNLOCK statements", 1643 SetUpScript: []string{ 1644 "CREATE TABLE t1(pk BIGINT PRIMARY KEY)", 1645 }, 1646 Assertions: []ScriptTestAssertion{ 1647 { 1648 Query: "CREATE PROCEDURE p1(x BIGINT) LOCK TABLES t1 READ", 1649 ExpectedErr: sql.ErrSyntaxError, 1650 }, 1651 { 1652 Query: "CREATE PROCEDURE p1(x BIGINT) UNLOCK TABLES", 1653 ExpectedErr: sql.ErrSyntaxError, 1654 }, 1655 }, 1656 }, 1657 { 1658 Name: "DECLARE CONDITION wrong positions", 1659 Assertions: []ScriptTestAssertion{ 1660 { 1661 Query: `CREATE PROCEDURE p1(x INT) 1662 BEGIN 1663 SELECT x; 1664 DECLARE cond_name CONDITION FOR SQLSTATE '45000'; 1665 END;`, 1666 ExpectedErr: sql.ErrDeclareConditionOrderInvalid, 1667 }, 1668 { 1669 Query: `CREATE PROCEDURE p1(x INT) 1670 BEGIN 1671 BEGIN 1672 SELECT x; 1673 DECLARE cond_name CONDITION FOR SQLSTATE '45000'; 1674 END; 1675 END;`, 1676 ExpectedErr: sql.ErrDeclareConditionOrderInvalid, 1677 }, 1678 { 1679 Query: `CREATE PROCEDURE p1(x INT) 1680 BEGIN 1681 IF x = 0 THEN 1682 DECLARE cond_name CONDITION FOR SQLSTATE '45000'; 1683 END IF; 1684 END;`, 1685 ExpectedErr: sql.ErrDeclareConditionOrderInvalid, 1686 }, 1687 { 1688 Query: `CREATE PROCEDURE p1(x INT) 1689 BEGIN 1690 IF x = 0 THEN 1691 SELECT x; 1692 ELSE 1693 DECLARE cond_name CONDITION FOR SQLSTATE '45000'; 1694 END IF; 1695 END;`, 1696 ExpectedErr: sql.ErrDeclareConditionOrderInvalid, 1697 }, 1698 }, 1699 }, 1700 { 1701 Name: "DECLARE CONDITION duplicate name", 1702 Query: `CREATE PROCEDURE p1() 1703 BEGIN 1704 DECLARE cond_name CONDITION FOR SQLSTATE '45000'; 1705 DECLARE cond_name CONDITION FOR SQLSTATE '45000'; 1706 END;`, 1707 ExpectedErr: sql.ErrDeclareConditionDuplicate, 1708 }, 1709 { //TODO: change this test when we implement DECLARE CONDITION for MySQL error codes 1710 Name: "SIGNAL references condition name for MySQL error code", 1711 Query: `CREATE PROCEDURE p1(x INT) 1712 BEGIN 1713 DECLARE mysql_err_code CONDITION FOR 1000; 1714 SIGNAL mysql_err_code; 1715 END;`, 1716 ExpectedErr: sql.ErrUnsupportedSyntax, 1717 }, 1718 { 1719 Name: "SIGNAL non-existent condition name", 1720 Query: `CREATE PROCEDURE p1(x INT) 1721 BEGIN 1722 DECLARE abcdefg CONDITION FOR SQLSTATE '45000'; 1723 SIGNAL abcdef; 1724 END;`, 1725 ExpectedErr: sql.ErrDeclareConditionNotFound, 1726 }, 1727 { 1728 Name: "Duplicate procedure name", 1729 SetUpScript: []string{ 1730 "CREATE PROCEDURE test_proc(x DOUBLE, y DOUBLE) SELECT x*y", 1731 }, 1732 Query: "CREATE PROCEDURE test_proc(z VARCHAR(20)) SELECT z", 1733 ExpectedErr: sql.ErrStoredProcedureAlreadyExists, 1734 }, 1735 { 1736 Name: "Broken procedure shouldn't break other procedures", 1737 SetUpScript: []string{ 1738 "CREATE TABLE t (pk INT PRIMARY KEY, other INT);", 1739 "INSERT INTO t VALUES (1, 1), (2, 2), (3, 3);", 1740 "CREATE PROCEDURE fragile() select other from t;", 1741 "CREATE PROCEDURE stable() select pk from t;", 1742 }, 1743 Assertions: []ScriptTestAssertion{ 1744 { 1745 Query: "CALL stable();", 1746 Expected: []sql.Row{{1}, {2}, {3}}, 1747 }, 1748 { 1749 Query: "CALL fragile();", 1750 Expected: []sql.Row{{1}, {2}, {3}}, 1751 }, 1752 { 1753 Query: "SHOW PROCEDURE STATUS LIKE 'stable'", 1754 SkipResultsCheck: true, // ensure that there's no error 1755 }, 1756 { 1757 Query: "SHOW PROCEDURE STATUS LIKE 'fragile'", 1758 SkipResultsCheck: true, // ensure that there's no error 1759 }, 1760 { 1761 Query: "alter table t drop other;", 1762 Expected: []sql.Row{{types.NewOkResult(0)}}, 1763 }, 1764 { 1765 Query: "CALL stable();", 1766 Expected: []sql.Row{{1}, {2}, {3}}, 1767 }, 1768 { 1769 Query: "CALL fragile();", 1770 ExpectedErrStr: "column \"other\" could not be found in any table in scope", 1771 }, 1772 { 1773 Query: "SHOW PROCEDURE STATUS LIKE 'stable'", 1774 SkipResultsCheck: true, // ensure that there's no error 1775 }, 1776 { 1777 Query: "SHOW PROCEDURE STATUS LIKE 'fragile'", 1778 SkipResultsCheck: true, // ensure that there's no error 1779 }, 1780 { 1781 Query: "ALTER TABLE t ADD COLUMN other INT", 1782 Expected: []sql.Row{{types.NewOkResult(0)}}, 1783 }, 1784 { 1785 Query: "CALL stable();", 1786 Expected: []sql.Row{{1}, {2}, {3}}, 1787 }, 1788 { 1789 Query: "CALL fragile();", 1790 Expected: []sql.Row{{nil}, {nil}, {nil}}, 1791 }, 1792 { 1793 Query: "INSERT INTO t VALUES (4, 4), (5, 5), (6, 6);", 1794 Expected: []sql.Row{{types.NewOkResult(3)}}, 1795 }, 1796 { 1797 Query: "CALL stable();", 1798 Expected: []sql.Row{{1}, {2}, {3}, {4}, {5}, {6}}, 1799 }, 1800 { 1801 Query: "CALL fragile();", 1802 Expected: []sql.Row{{nil}, {nil}, {nil}, {4}, {5}, {6}}, 1803 }, 1804 }, 1805 }, 1806 { 1807 Name: "DECLARE name duplicate same type", 1808 Assertions: []ScriptTestAssertion{ 1809 { 1810 Query: `CREATE PROCEDURE p1() 1811 BEGIN 1812 DECLARE x INT; 1813 DECLARE x INT; 1814 SELECT 1; 1815 END;`, 1816 ExpectedErr: sql.ErrDeclareVariableDuplicate, 1817 }, 1818 }, 1819 }, 1820 { 1821 Name: "DECLARE name duplicate different type", 1822 Assertions: []ScriptTestAssertion{ 1823 { 1824 Query: `CREATE PROCEDURE p1() 1825 BEGIN 1826 DECLARE x INT; 1827 DECLARE x VARCHAR(20); 1828 SELECT 1; 1829 END;`, 1830 ExpectedErr: sql.ErrDeclareVariableDuplicate, 1831 }, 1832 }, 1833 }, 1834 { 1835 Name: "Variable, condition, and cursor in invalid order", 1836 Assertions: []ScriptTestAssertion{ 1837 { 1838 Query: `CREATE PROCEDURE p1() 1839 BEGIN 1840 DECLARE var_name INT; 1841 DECLARE cur_name CURSOR FOR SELECT 1; 1842 DECLARE cond_name CONDITION FOR SQLSTATE '45000'; 1843 SELECT 1; 1844 END;`, 1845 ExpectedErr: sql.ErrDeclareConditionOrderInvalid, 1846 }, 1847 { 1848 Query: `CREATE PROCEDURE p2() 1849 BEGIN 1850 DECLARE cond_name CONDITION FOR SQLSTATE '45000'; 1851 DECLARE cur_name CURSOR FOR SELECT 1; 1852 DECLARE var_name INT; 1853 SELECT 1; 1854 END;`, 1855 ExpectedErr: sql.ErrDeclareVariableOrderInvalid, 1856 }, 1857 { 1858 Query: `CREATE PROCEDURE p3() 1859 BEGIN 1860 DECLARE cond_name CONDITION FOR SQLSTATE '45000'; 1861 DECLARE var_name INT; 1862 SELECT 1; 1863 DECLARE cur_name CURSOR FOR SELECT 1; 1864 END;`, 1865 ExpectedErr: sql.ErrDeclareCursorOrderInvalid, 1866 }, 1867 }, 1868 }, 1869 { 1870 Name: "FETCH non-existent cursor", 1871 Assertions: []ScriptTestAssertion{ 1872 { 1873 Query: `CREATE PROCEDURE p1() 1874 BEGIN 1875 DECLARE a INT; 1876 FETCH no_cursor INTO a; 1877 END;`, 1878 ExpectedErr: sql.ErrCursorNotFound, 1879 }, 1880 }, 1881 }, 1882 { 1883 Name: "OPEN non-existent cursor", 1884 Assertions: []ScriptTestAssertion{ 1885 { 1886 Query: `CREATE PROCEDURE p1() 1887 BEGIN 1888 OPEN no_cursor; 1889 END;`, 1890 ExpectedErr: sql.ErrCursorNotFound, 1891 }, 1892 }, 1893 }, 1894 { 1895 Name: "CLOSE non-existent cursor", 1896 Assertions: []ScriptTestAssertion{ 1897 { 1898 Query: `CREATE PROCEDURE p1() 1899 BEGIN 1900 CLOSE no_cursor; 1901 END;`, 1902 ExpectedErr: sql.ErrCursorNotFound, 1903 }, 1904 }, 1905 }, 1906 { 1907 Name: "CLOSE without OPEN", 1908 SetUpScript: []string{ 1909 `CREATE PROCEDURE p1() 1910 BEGIN 1911 DECLARE cur1 CURSOR FOR SELECT 1; 1912 CLOSE cur1; 1913 END;`, 1914 }, 1915 Assertions: []ScriptTestAssertion{ 1916 { 1917 Query: "CALL p1();", 1918 ExpectedErr: sql.ErrCursorNotOpen, 1919 }, 1920 }, 1921 }, 1922 { 1923 Name: "OPEN repeatedly", 1924 SetUpScript: []string{ 1925 `CREATE PROCEDURE p1() 1926 BEGIN 1927 DECLARE cur1 CURSOR FOR SELECT 1; 1928 OPEN cur1; 1929 OPEN cur1; 1930 END;`, 1931 }, 1932 Assertions: []ScriptTestAssertion{ 1933 { 1934 Query: "CALL p1();", 1935 ExpectedErr: sql.ErrCursorAlreadyOpen, 1936 }, 1937 }, 1938 }, 1939 { 1940 Name: "CLOSE repeatedly", 1941 SetUpScript: []string{ 1942 `CREATE PROCEDURE p1() 1943 BEGIN 1944 DECLARE cur1 CURSOR FOR SELECT 1; 1945 OPEN cur1; 1946 CLOSE cur1; 1947 CLOSE cur1; 1948 END;`, 1949 }, 1950 Assertions: []ScriptTestAssertion{ 1951 { 1952 Query: "CALL p1();", 1953 ExpectedErr: sql.ErrCursorNotOpen, 1954 }, 1955 }, 1956 }, 1957 { 1958 Name: "With CTE using variable", 1959 SetUpScript: []string{ 1960 `CREATE PROCEDURE p1() 1961 BEGIN 1962 DECLARE v1 INT DEFAULT 1234; 1963 WITH cte as (SELECT v1) 1964 SELECT * FROM cte; 1965 END;`, 1966 }, 1967 Assertions: []ScriptTestAssertion{ 1968 { 1969 Query: "CALL p1();", 1970 Expected: []sql.Row{ 1971 {1234}, 1972 }, 1973 }, 1974 }, 1975 }, 1976 { 1977 Name: "With CTE using parameter", 1978 SetUpScript: []string{ 1979 `CREATE PROCEDURE p1(v1 int) 1980 BEGIN 1981 WITH cte as (SELECT v1) 1982 SELECT * FROM cte; 1983 END;`, 1984 }, 1985 Assertions: []ScriptTestAssertion{ 1986 { 1987 Query: "CALL p1(1234);", 1988 Expected: []sql.Row{ 1989 {1234}, 1990 }, 1991 }, 1992 }, 1993 }, 1994 { 1995 Name: "Dolt Issue #4480", 1996 SetUpScript: []string{ 1997 "create table p1 (row_id int primary key, pred int, actual int)", 1998 "create table p2 (row_id int primary key, pred int, actual int)", 1999 "insert into p1 values (0, 0, 0), (1, 0, 1), (2, 1, 0), (3, 1, 1)", 2000 "insert into p2 values (0, 0, 0), (1, 0, 1), (2, 1, 0), (3, 1, 1)", 2001 `CREATE PROCEDURE computeSummary(c VARCHAR(200)) 2002 BEGIN 2003 with t as ( 2004 select 2005 case 2006 when p1.pred = p2.actual then 1 2007 else 0 2008 end as correct, 2009 p1.actual 2010 from p1 2011 join p2 2012 on p1.row_id = p2.row_id 2013 ) 2014 select 2015 sum(correct)/count(*), 2016 count(*) as row_num 2017 from t; 2018 END;`, 2019 }, 2020 Assertions: []ScriptTestAssertion{ 2021 { 2022 Query: "CALL computeSummary('i am not used');", 2023 Expected: []sql.Row{ 2024 {float64(0.5), 4}, 2025 }, 2026 }, 2027 }, 2028 }, 2029 } 2030 2031 var ProcedureCallTests = []ScriptTest{ 2032 { 2033 Name: "OUT param with SET", 2034 SetUpScript: []string{ 2035 "SET @outparam = 5", 2036 "CREATE PROCEDURE testabc(OUT x BIGINT) SET x = 9", 2037 "CALL testabc(@outparam)", 2038 }, 2039 Assertions: []ScriptTestAssertion{ 2040 { 2041 Query: "SELECT @outparam", 2042 Expected: []sql.Row{ 2043 { 2044 int64(9), 2045 }, 2046 }, 2047 }, 2048 }, 2049 }, 2050 { 2051 Name: "OUT param without SET", 2052 SetUpScript: []string{ 2053 "SET @outparam = 5", 2054 "CREATE PROCEDURE testabc(OUT x BIGINT) SELECT x", 2055 "CALL testabc(@outparam)", 2056 }, 2057 Assertions: []ScriptTestAssertion{ 2058 { 2059 Query: "SELECT @outparam", 2060 Expected: []sql.Row{ 2061 { 2062 nil, 2063 }, 2064 }, 2065 }, 2066 }, 2067 }, 2068 { 2069 Name: "INOUT param with SET", 2070 SetUpScript: []string{ 2071 "SET @outparam = 5", 2072 "CREATE PROCEDURE testabc(INOUT x BIGINT) BEGIN SET x = x + 1; SET x = x + 3; END;", 2073 "CALL testabc(@outparam)", 2074 }, 2075 Assertions: []ScriptTestAssertion{ 2076 { 2077 Query: "SELECT @outparam", 2078 Expected: []sql.Row{ 2079 { 2080 int64(9), 2081 }, 2082 }, 2083 }, 2084 }, 2085 }, 2086 { 2087 Name: "INOUT param without SET", 2088 SetUpScript: []string{ 2089 "SET @outparam = 5", 2090 "CREATE PROCEDURE testabc(INOUT x BIGINT) SELECT x", 2091 "CALL testabc(@outparam)", 2092 }, 2093 Assertions: []ScriptTestAssertion{ 2094 { 2095 Query: "SELECT @outparam", 2096 Expected: []sql.Row{ 2097 { 2098 int64(5), 2099 }, 2100 }, 2101 }, 2102 }, 2103 }, 2104 { 2105 Name: "Nested CALL with INOUT param", 2106 SetUpScript: []string{ 2107 "SET @outparam = 5", 2108 "CREATE PROCEDURE p3(INOUT z INT) BEGIN SET z = z * 111; END;", 2109 "CREATE PROCEDURE p2(INOUT y DOUBLE) BEGIN SET y = y + 4; CALL p3(y); END;", 2110 "CREATE PROCEDURE p1(INOUT x BIGINT) BEGIN SET x = 3; CALL p2(x); END;", 2111 "CALL p1(@outparam)", 2112 }, 2113 Assertions: []ScriptTestAssertion{ 2114 { 2115 Query: "SELECT @outparam", 2116 Expected: []sql.Row{ 2117 { 2118 int64(777), 2119 }, 2120 }, 2121 }, 2122 }, 2123 }, 2124 { 2125 Name: "OUT param without SET", 2126 SetUpScript: []string{ 2127 "SET @outparam = 5", 2128 "CREATE PROCEDURE testabc(OUT x BIGINT) SELECT x", 2129 "CALL testabc(@outparam)", 2130 }, 2131 Assertions: []ScriptTestAssertion{ 2132 { 2133 Query: "SELECT @outparam", 2134 Expected: []sql.Row{ 2135 { 2136 nil, 2137 }, 2138 }, 2139 }, 2140 }, 2141 }, 2142 { 2143 Name: "Incompatible type for parameter", 2144 SetUpScript: []string{ 2145 "CREATE PROCEDURE p1(x DATETIME) SELECT x", 2146 }, 2147 Assertions: []ScriptTestAssertion{ 2148 { 2149 Query: "CALL p1('hi')", 2150 ExpectedErr: types.ErrConvertingToTime, 2151 }, 2152 }, 2153 }, 2154 { 2155 Name: "Incorrect parameter count", 2156 SetUpScript: []string{ 2157 "CREATE PROCEDURE p1(x BIGINT, y BIGINT) SELECT x + y", 2158 }, 2159 Assertions: []ScriptTestAssertion{ 2160 { 2161 Query: "CALL p1(1)", 2162 ExpectedErr: sql.ErrCallIncorrectParameterCount, 2163 }, 2164 { 2165 Query: "CALL p1(1, 2, 3)", 2166 ExpectedErr: sql.ErrCallIncorrectParameterCount, 2167 }, 2168 }, 2169 }, 2170 { 2171 Name: "use procedure parameter in filter expressions and multiple statements", 2172 SetUpScript: []string{ 2173 "CREATE TABLE inventory (store_id int, product varchar(5))", 2174 "INSERT INTO inventory VALUES (1, 'a'), (1, 'b'), (1, 'c'), (1, 'd'), (2, 'e'), (2, 'f'), (1, 'g'), (1, 'h'), (3, 'i')", 2175 "CREATE PROCEDURE proc1 (IN p_store_id INT) SELECT COUNT(*) FROM inventory WHERE store_id = p_store_id;", 2176 "CREATE PROCEDURE proc2 (IN p_store_id INT, OUT p_film_count INT) READS SQL DATA BEGIN SELECT COUNT(*) as counted FROM inventory WHERE store_id = p_store_id; SET p_film_count = 44; END ;", 2177 }, 2178 Assertions: []ScriptTestAssertion{ 2179 { 2180 Query: "CALL proc1(1)", 2181 Expected: []sql.Row{ 2182 { 2183 int64(6), 2184 }, 2185 }, 2186 }, 2187 { 2188 Query: "CALL proc1(2)", 2189 Expected: []sql.Row{ 2190 { 2191 int64(2), 2192 }, 2193 }, 2194 }, { 2195 Query: "CALL proc1(4)", 2196 Expected: []sql.Row{ 2197 { 2198 int64(0), 2199 }, 2200 }, 2201 }, { 2202 Query: "CALL proc2(3, @foo)", 2203 Expected: []sql.Row{ 2204 { 2205 int64(1), 2206 }, 2207 }, 2208 }, { 2209 Query: "SELECT @foo", 2210 Expected: []sql.Row{ 2211 { 2212 int64(44), 2213 }, 2214 }, 2215 }, 2216 }, 2217 }, 2218 { 2219 Name: "Call procedures by their qualified name", 2220 SetUpScript: []string{ 2221 "CREATE DATABASE otherdb", 2222 "CREATE PROCEDURE mydb.p1() SELECT 42", 2223 "CREATE PROCEDURE otherdb.p1() SELECT 43", 2224 }, 2225 Assertions: []ScriptTestAssertion{ 2226 { 2227 Query: "CALL p1()", 2228 Expected: []sql.Row{{42}}, 2229 }, 2230 { 2231 Query: "CALL mydb.p1()", 2232 Expected: []sql.Row{{42}}, 2233 }, 2234 { 2235 Query: "CALL otherdb.p1()", 2236 Expected: []sql.Row{{43}}, 2237 }, 2238 { 2239 Query: "USE otherdb", 2240 Expected: []sql.Row{}, 2241 }, 2242 { 2243 Query: "CALL p1()", 2244 Expected: []sql.Row{{43}}, 2245 }, 2246 }, 2247 }, 2248 2249 { 2250 Name: "String literals with escaped chars", 2251 SetUpScript: []string{ 2252 `CREATE PROCEDURE joe(IN str VARCHAR(15)) SELECT CONCAT('joe''s bar:', str);`, 2253 `CREATE PROCEDURE jill(IN str VARCHAR(15)) SELECT CONCAT('jill\'s bar:', str);`, 2254 `CREATE PROCEDURE stan(IN str VARCHAR(15)) SELECT CONCAT("stan\'s bar:", str);`, 2255 }, 2256 Assertions: []ScriptTestAssertion{ 2257 { 2258 Query: "CALL joe('open')", 2259 Expected: []sql.Row{{"joe's bar:open"}}, 2260 }, 2261 { 2262 Query: "CALL jill('closed')", 2263 Expected: []sql.Row{{"jill's bar:closed"}}, 2264 }, 2265 { 2266 Query: "CALL stan('quarantined')", 2267 Expected: []sql.Row{{"stan's bar:quarantined"}}, 2268 }, 2269 }, 2270 }, 2271 } 2272 2273 var ProcedureDropTests = []ScriptTest{ 2274 { 2275 Name: "DROP procedures", 2276 SetUpScript: []string{ 2277 "CREATE PROCEDURE p1() SELECT 5", 2278 "CREATE PROCEDURE p2() SELECT 6", 2279 }, 2280 Assertions: []ScriptTestAssertion{ 2281 { 2282 Query: "CALL p1", 2283 Expected: []sql.Row{ 2284 { 2285 int64(5), 2286 }, 2287 }, 2288 }, 2289 { 2290 Query: "CALL p2", 2291 Expected: []sql.Row{ 2292 { 2293 int64(6), 2294 }, 2295 }, 2296 }, 2297 { 2298 Query: "DROP PROCEDURE p1", 2299 Expected: []sql.Row{{types.OkResult{}}}, 2300 }, 2301 { 2302 Query: "CALL p1", 2303 ExpectedErr: sql.ErrStoredProcedureDoesNotExist, 2304 }, 2305 { 2306 Query: "DROP PROCEDURE IF EXISTS p2", 2307 Expected: []sql.Row{{types.OkResult{}}}, 2308 }, 2309 { 2310 Query: "CALL p2", 2311 ExpectedErr: sql.ErrStoredProcedureDoesNotExist, 2312 }, 2313 { 2314 Query: "DROP PROCEDURE p3", 2315 ExpectedErr: sql.ErrStoredProcedureDoesNotExist, 2316 }, 2317 { 2318 Query: "DROP PROCEDURE IF EXISTS p4", 2319 Expected: []sql.Row{{types.OkResult{}}}, 2320 }, 2321 }, 2322 }, 2323 } 2324 2325 var ProcedureShowStatus = []ScriptTest{ 2326 { 2327 Name: "SHOW procedures", 2328 SetUpScript: []string{ 2329 "CREATE PROCEDURE p1() COMMENT 'hi' DETERMINISTIC SELECT 6", 2330 "CREATE definer=`user` PROCEDURE p2() SQL SECURITY INVOKER SELECT 7", 2331 "CREATE PROCEDURE p21() SQL SECURITY DEFINER SELECT 8", 2332 }, 2333 Assertions: []ScriptTestAssertion{ 2334 { 2335 Query: "SHOW PROCEDURE STATUS", 2336 Expected: []sql.Row{ 2337 { 2338 "mydb", // Db 2339 "p1", // Name 2340 "PROCEDURE", // Type 2341 "", // Definer 2342 time.Unix(0, 0).UTC(), // Modified 2343 time.Unix(0, 0).UTC(), // Created 2344 "DEFINER", // Security_type 2345 "hi", // Comment 2346 "utf8mb4", // character_set_client 2347 "utf8mb4_0900_bin", // collation_connection 2348 "utf8mb4_0900_bin", // Database Collation 2349 }, 2350 { 2351 "mydb", // Db 2352 "p2", // Name 2353 "PROCEDURE", // Type 2354 "user@%", // Definer 2355 time.Unix(0, 0).UTC(), // Modified 2356 time.Unix(0, 0).UTC(), // Created 2357 "INVOKER", // Security_type 2358 "", // Comment 2359 "utf8mb4", // character_set_client 2360 "utf8mb4_0900_bin", // collation_connection 2361 "utf8mb4_0900_bin", // Database Collation 2362 }, 2363 { 2364 "mydb", // Db 2365 "p21", // Name 2366 "PROCEDURE", // Type 2367 "", // Definer 2368 time.Unix(0, 0).UTC(), // Modified 2369 time.Unix(0, 0).UTC(), // Created 2370 "DEFINER", // Security_type 2371 "", // Comment 2372 "utf8mb4", // character_set_client 2373 "utf8mb4_0900_bin", // collation_connection 2374 "utf8mb4_0900_bin", // Database Collation 2375 }, 2376 }, 2377 }, 2378 { 2379 Query: "SHOW PROCEDURE STATUS LIKE 'p2%'", 2380 Expected: []sql.Row{ 2381 { 2382 "mydb", // Db 2383 "p2", // Name 2384 "PROCEDURE", // Type 2385 "user@%", // Definer 2386 time.Unix(0, 0).UTC(), // Modified 2387 time.Unix(0, 0).UTC(), // Created 2388 "INVOKER", // Security_type 2389 "", // Comment 2390 "utf8mb4", // character_set_client 2391 "utf8mb4_0900_bin", // collation_connection 2392 "utf8mb4_0900_bin", // Database Collation 2393 }, 2394 { 2395 "mydb", // Db 2396 "p21", // Name 2397 "PROCEDURE", // Type 2398 "", // Definer 2399 time.Unix(0, 0).UTC(), // Modified 2400 time.Unix(0, 0).UTC(), // Created 2401 "DEFINER", // Security_type 2402 "", // Comment 2403 "utf8mb4", // character_set_client 2404 "utf8mb4_0900_bin", // collation_connection 2405 "utf8mb4_0900_bin", // Database Collation 2406 }, 2407 }, 2408 }, 2409 { 2410 Query: "SHOW PROCEDURE STATUS LIKE 'p4'", 2411 Expected: []sql.Row{}, 2412 }, 2413 { 2414 Query: "SHOW PROCEDURE STATUS WHERE Db = 'mydb'", 2415 Expected: []sql.Row{ 2416 { 2417 "mydb", // Db 2418 "p1", // Name 2419 "PROCEDURE", // Type 2420 "", // Definer 2421 time.Unix(0, 0).UTC(), // Modified 2422 time.Unix(0, 0).UTC(), // Created 2423 "DEFINER", // Security_type 2424 "hi", // Comment 2425 "utf8mb4", // character_set_client 2426 "utf8mb4_0900_bin", // collation_connection 2427 "utf8mb4_0900_bin", // Database Collation 2428 }, 2429 { 2430 "mydb", // Db 2431 "p2", // Name 2432 "PROCEDURE", // Type 2433 "user@%", // Definer 2434 time.Unix(0, 0).UTC(), // Modified 2435 time.Unix(0, 0).UTC(), // Created 2436 "INVOKER", // Security_type 2437 "", // Comment 2438 "utf8mb4", // character_set_client 2439 "utf8mb4_0900_bin", // collation_connection 2440 "utf8mb4_0900_bin", // Database Collation 2441 }, 2442 { 2443 "mydb", // Db 2444 "p21", // Name 2445 "PROCEDURE", // Type 2446 "", // Definer 2447 time.Unix(0, 0).UTC(), // Modified 2448 time.Unix(0, 0).UTC(), // Created 2449 "DEFINER", // Security_type 2450 "", // Comment 2451 "utf8mb4", // character_set_client 2452 "utf8mb4_0900_bin", // collation_connection 2453 "utf8mb4_0900_bin", // Database Collation 2454 }, 2455 }, 2456 }, 2457 { 2458 Query: "SHOW PROCEDURE STATUS WHERE Name LIKE '%1'", 2459 Expected: []sql.Row{ 2460 { 2461 "mydb", // Db 2462 "p1", // Name 2463 "PROCEDURE", // Type 2464 "", // Definer 2465 time.Unix(0, 0).UTC(), // Modified 2466 time.Unix(0, 0).UTC(), // Created 2467 "DEFINER", // Security_type 2468 "hi", // Comment 2469 "utf8mb4", // character_set_client 2470 "utf8mb4_0900_bin", // collation_connection 2471 "utf8mb4_0900_bin", // Database Collation 2472 }, 2473 { 2474 "mydb", // Db 2475 "p21", // Name 2476 "PROCEDURE", // Type 2477 "", // Definer 2478 time.Unix(0, 0).UTC(), // Modified 2479 time.Unix(0, 0).UTC(), // Created 2480 "DEFINER", // Security_type 2481 "", // Comment 2482 "utf8mb4", // character_set_client 2483 "utf8mb4_0900_bin", // collation_connection 2484 "utf8mb4_0900_bin", // Database Collation 2485 }, 2486 }, 2487 }, 2488 { 2489 Query: "SHOW PROCEDURE STATUS WHERE Security_type = 'INVOKER'", 2490 Expected: []sql.Row{ 2491 { 2492 "mydb", // Db 2493 "p2", // Name 2494 "PROCEDURE", // Type 2495 "user@%", // Definer 2496 time.Unix(0, 0).UTC(), // Modified 2497 time.Unix(0, 0).UTC(), // Created 2498 "INVOKER", // Security_type 2499 "", // Comment 2500 "utf8mb4", // character_set_client 2501 "utf8mb4_0900_bin", // collation_connection 2502 "utf8mb4_0900_bin", // Database Collation 2503 }, 2504 }, 2505 }, 2506 { 2507 Query: "SHOW PROCEDURE STATUS", 2508 Expected: []sql.Row{ 2509 { 2510 "mydb", // Db 2511 "p1", // Name 2512 "PROCEDURE", // Type 2513 "", // Definer 2514 time.Unix(0, 0).UTC(), // Modified 2515 time.Unix(0, 0).UTC(), // Created 2516 "DEFINER", // Security_type 2517 "hi", // Comment 2518 "utf8mb4", // character_set_client 2519 "utf8mb4_0900_bin", // collation_connection 2520 "utf8mb4_0900_bin", // Database Collation 2521 }, 2522 { 2523 "mydb", // Db 2524 "p2", // Name 2525 "PROCEDURE", // Type 2526 "user@%", // Definer 2527 time.Unix(0, 0).UTC(), // Modified 2528 time.Unix(0, 0).UTC(), // Created 2529 "INVOKER", // Security_type 2530 "", // Comment 2531 "utf8mb4", // character_set_client 2532 "utf8mb4_0900_bin", // collation_connection 2533 "utf8mb4_0900_bin", // Database Collation 2534 }, 2535 { 2536 "mydb", // Db 2537 "p21", // Name 2538 "PROCEDURE", // Type 2539 "", // Definer 2540 time.Unix(0, 0).UTC(), // Modified 2541 time.Unix(0, 0).UTC(), // Created 2542 "DEFINER", // Security_type 2543 "", // Comment 2544 "utf8mb4", // character_set_client 2545 "utf8mb4_0900_bin", // collation_connection 2546 "utf8mb4_0900_bin", // Database Collation 2547 }, 2548 }, 2549 }, 2550 }, 2551 }, 2552 } 2553 2554 var ProcedureShowCreate = []ScriptTest{ 2555 { 2556 Name: "SHOW procedures", 2557 SetUpScript: []string{ 2558 "CREATE PROCEDURE p1() COMMENT 'hi' DETERMINISTIC SELECT 6", 2559 "CREATE definer=`user` PROCEDURE p2() SQL SECURITY INVOKER SELECT 7", 2560 "CREATE PROCEDURE p21() SQL SECURITY DEFINER SELECT 8", 2561 }, 2562 Assertions: []ScriptTestAssertion{ 2563 { 2564 Query: "SHOW CREATE PROCEDURE p1", 2565 Expected: []sql.Row{ 2566 { 2567 "p1", // Procedure 2568 "", // sql_mode 2569 "CREATE PROCEDURE p1() COMMENT 'hi' DETERMINISTIC SELECT 6", // Create Procedure 2570 "utf8mb4", // character_set_client 2571 "utf8mb4_0900_bin", // collation_connection 2572 "utf8mb4_0900_bin", // Database Collation 2573 }, 2574 }, 2575 }, 2576 { 2577 Query: "SHOW CREATE PROCEDURE p2", 2578 Expected: []sql.Row{ 2579 { 2580 "p2", // Procedure 2581 "", // sql_mode 2582 "CREATE definer=`user` PROCEDURE p2() SQL SECURITY INVOKER SELECT 7", // Create Procedure 2583 "utf8mb4", // character_set_client 2584 "utf8mb4_0900_bin", // collation_connection 2585 "utf8mb4_0900_bin", // Database Collation 2586 }, 2587 }, 2588 }, 2589 { 2590 Query: "SHOW CREATE PROCEDURE p21", 2591 Expected: []sql.Row{ 2592 { 2593 "p21", // Procedure 2594 "", // sql_mode 2595 "CREATE PROCEDURE p21() SQL SECURITY DEFINER SELECT 8", // Create Procedure 2596 "utf8mb4", // character_set_client 2597 "utf8mb4_0900_bin", // collation_connection 2598 "utf8mb4_0900_bin", // Database Collation 2599 }, 2600 }, 2601 }, 2602 }, 2603 }, 2604 { 2605 Name: "SHOW non-existent procedures", 2606 SetUpScript: []string{}, 2607 Assertions: []ScriptTestAssertion{ 2608 { 2609 Query: "SHOW CREATE PROCEDURE p1", 2610 ExpectedErr: sql.ErrStoredProcedureDoesNotExist, 2611 }, 2612 }, 2613 }, 2614 } 2615 2616 var NoDbProcedureTests = []ScriptTestAssertion{ 2617 { 2618 Query: "SHOW databases;", 2619 Expected: []sql.Row{{"information_schema"}, {"mydb"}, {"mysql"}}, 2620 }, 2621 { 2622 Query: "SELECT database();", 2623 Expected: []sql.Row{{nil}}, 2624 }, 2625 { 2626 Query: "CREATE PROCEDURE mydb.p5() SELECT 42;", 2627 Expected: []sql.Row{{types.NewOkResult(0)}}, 2628 }, 2629 { 2630 Query: "SHOW CREATE PROCEDURE mydb.p5;", 2631 SkipResultsCheck: true, 2632 }, 2633 { 2634 Query: "SHOW CREATE PROCEDURE p5;", 2635 ExpectedErr: sql.ErrNoDatabaseSelected, 2636 }, 2637 }