github.com/mithrandie/csvq@v1.18.1/lib/query/processor_test.go (about) 1 package query 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "strings" 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/mithrandie/csvq/lib/option" 13 "github.com/mithrandie/csvq/lib/parser" 14 "github.com/mithrandie/csvq/lib/value" 15 16 "github.com/mithrandie/go-text" 17 "github.com/mithrandie/ternary" 18 ) 19 20 func TestContextForStoringResults(t *testing.T) { 21 defer func() { 22 TestTx.SelectedViews = nil 23 TestTx.AffectedRows = 0 24 }() 25 26 tx := TestTx 27 proc := NewProcessor(tx) 28 29 ctx := ContextForStoringResults(context.Background()) 30 _, err := proc.Execute(ctx, []parser.Statement{ 31 parser.SelectQuery{ 32 SelectEntity: parser.SelectEntity{ 33 SelectClause: parser.SelectClause{ 34 Fields: []parser.QueryExpression{ 35 parser.Field{Object: parser.NewIntegerValueFromString("1")}, 36 }, 37 }, 38 }, 39 }, 40 }) 41 if err != nil { 42 t.Fatalf("unexpected error %q", err) 43 } 44 45 if len(proc.Tx.SelectedViews) < 1 { 46 t.Fatalf("result set is not set to the transaction") 47 } 48 } 49 50 var processorExecuteStatementTests = []struct { 51 Input parser.Statement 52 UncommittedViews UncommittedViews 53 Logs string 54 SelectLogs []string 55 Error string 56 ReturnCode int 57 }{ 58 { 59 Input: parser.SetFlag{ 60 Flag: parser.Flag{Name: "invalid"}, 61 Value: parser.NewStringValue("\t"), 62 }, 63 Error: "@@INVALID is an unknown flag", 64 ReturnCode: ReturnCodeApplicationError, 65 }, 66 { 67 Input: parser.SetFlag{ 68 Flag: parser.Flag{Name: "delimiter"}, 69 Value: parser.NewStringValue(","), 70 }, 71 }, 72 { 73 Input: parser.Echo{ 74 Value: parser.Function{ 75 Name: "trunc_time", 76 Args: []parser.QueryExpression{ 77 parser.Function{ 78 Name: "datetime", 79 Args: []parser.QueryExpression{ 80 parser.NewStringValue("2001::01::01"), 81 }, 82 }, 83 }, 84 }, 85 }, 86 Logs: "NULL\n", 87 }, 88 { 89 Input: parser.AddFlagElement{ 90 Flag: parser.Flag{Name: "datetime_format"}, 91 Value: parser.NewStringValue("%Y::%m::%d"), 92 }, 93 }, 94 { 95 Input: parser.Echo{ 96 Value: parser.Function{ 97 Name: "trunc_time", 98 Args: []parser.QueryExpression{ 99 parser.Function{ 100 Name: "datetime", 101 Args: []parser.QueryExpression{ 102 parser.NewStringValue("2001::01::01"), 103 }, 104 }, 105 }, 106 }, 107 }, 108 Logs: "2001-01-01T00:00:00Z\n", 109 }, 110 { 111 Input: parser.RemoveFlagElement{ 112 Flag: parser.Flag{Name: "datetime_format"}, 113 Value: parser.NewStringValue("%Y::%m::%d"), 114 }, 115 }, 116 { 117 Input: parser.Echo{ 118 Value: parser.Function{ 119 Name: "trunc_time", 120 Args: []parser.QueryExpression{ 121 parser.Function{ 122 Name: "datetime", 123 Args: []parser.QueryExpression{ 124 parser.NewStringValue("2001::01::01"), 125 }, 126 }, 127 }, 128 }, 129 }, 130 Logs: "NULL\n", 131 }, 132 { 133 Input: parser.ShowFlag{ 134 Flag: parser.Flag{Name: "repository"}, 135 }, 136 Logs: "@@REPOSITORY: " + TestDir + "\n", 137 }, 138 { 139 Input: parser.SetEnvVar{ 140 EnvVar: parser.EnvironmentVariable{Name: "CSVQ_PROC_TEST"}, 141 Value: parser.NewStringValue("foo"), 142 }, 143 }, 144 { 145 Input: parser.Echo{ 146 Value: parser.EnvironmentVariable{Name: "CSVQ_PROC_TEST"}, 147 }, 148 Logs: "foo\n", 149 }, 150 { 151 Input: parser.Print{ 152 Value: parser.EnvironmentVariable{Name: "CSVQ_PROC_TEST"}, 153 }, 154 Logs: "'foo'\n", 155 }, 156 { 157 Input: parser.UnsetEnvVar{ 158 EnvVar: parser.EnvironmentVariable{Name: "CSVQ_PROC_TEST"}, 159 }, 160 }, 161 { 162 Input: parser.Print{ 163 Value: parser.EnvironmentVariable{Name: "CSVQ_PROC_TEST"}, 164 }, 165 Logs: "''\n", 166 }, 167 { 168 Input: parser.VariableDeclaration{ 169 Assignments: []parser.VariableAssignment{ 170 { 171 Variable: parser.Variable{Name: "var1"}, 172 }, 173 }, 174 }, 175 }, 176 { 177 Input: parser.VariableDeclaration{ 178 Assignments: []parser.VariableAssignment{ 179 { 180 Variable: parser.Variable{Name: "var2"}, 181 }, 182 }, 183 }, 184 }, 185 { 186 Input: parser.VariableDeclaration{ 187 Assignments: []parser.VariableAssignment{ 188 { 189 Variable: parser.Variable{Name: "var3"}, 190 }, 191 }, 192 }, 193 }, 194 { 195 Input: parser.VariableDeclaration{ 196 Assignments: []parser.VariableAssignment{ 197 { 198 Variable: parser.Variable{Name: "var4"}, 199 }, 200 }, 201 }, 202 }, 203 { 204 Input: parser.VariableSubstitution{ 205 Variable: parser.Variable{Name: "var1"}, 206 Value: parser.NewIntegerValueFromString("1"), 207 }, 208 }, 209 { 210 Input: parser.Print{ 211 Value: parser.Variable{Name: "var1"}, 212 }, 213 Logs: "1\n", 214 }, 215 { 216 Input: parser.DisposeVariable{ 217 Variable: parser.Variable{Name: "var4"}, 218 }, 219 }, 220 { 221 Input: parser.VariableDeclaration{ 222 Assignments: []parser.VariableAssignment{ 223 { 224 Variable: parser.Variable{Name: "var4"}, 225 }, 226 }, 227 }, 228 }, 229 { 230 Input: parser.FunctionDeclaration{ 231 Name: parser.Identifier{Literal: "userfunc"}, 232 Parameters: []parser.VariableAssignment{ 233 { 234 Variable: parser.Variable{Name: "arg1"}, 235 }, 236 }, 237 Statements: []parser.Statement{ 238 parser.Print{ 239 Value: parser.Variable{Name: "arg1"}, 240 }, 241 }, 242 }, 243 }, 244 { 245 Input: parser.Function{ 246 Name: "userfunc", 247 Args: []parser.QueryExpression{ 248 parser.NewIntegerValueFromString("1"), 249 }, 250 }, 251 Logs: "1\n", 252 }, 253 { 254 Input: parser.DisposeFunction{ 255 Name: parser.Identifier{Literal: "userfunc"}, 256 }, 257 Logs: "", 258 }, 259 { 260 Input: parser.Function{ 261 Name: "userfunc", 262 Args: []parser.QueryExpression{ 263 parser.NewIntegerValueFromString("1"), 264 }, 265 }, 266 Error: "function userfunc does not exist", 267 ReturnCode: ReturnCodeApplicationError, 268 }, 269 { 270 Input: parser.CursorDeclaration{ 271 Cursor: parser.Identifier{Literal: "cur"}, 272 Query: selectQueryForCursorTest, 273 }, 274 }, 275 { 276 Input: parser.OpenCursor{ 277 Cursor: parser.Identifier{Literal: "cur"}, 278 }, 279 }, 280 { 281 Input: parser.FetchCursor{ 282 Cursor: parser.Identifier{Literal: "cur"}, 283 Position: parser.FetchPosition{ 284 Position: parser.Token{Token: parser.NEXT, Literal: "next"}, 285 }, 286 Variables: []parser.Variable{ 287 {Name: "var2"}, 288 {Name: "var3"}, 289 }, 290 }, 291 }, 292 { 293 Input: parser.Print{ 294 Value: parser.Variable{Name: "var2"}, 295 }, 296 Logs: "'1'\n", 297 }, 298 { 299 Input: parser.Print{ 300 Value: parser.Variable{Name: "var3"}, 301 }, 302 Logs: "'str1'\n", 303 }, 304 { 305 Input: parser.CloseCursor{ 306 Cursor: parser.Identifier{Literal: "cur"}, 307 }, 308 }, 309 { 310 Input: parser.DisposeCursor{ 311 Cursor: parser.Identifier{Literal: "cur"}, 312 }, 313 }, 314 { 315 Input: parser.ViewDeclaration{ 316 View: parser.Identifier{Literal: "tbl"}, 317 Fields: []parser.QueryExpression{ 318 parser.Identifier{Literal: "column1"}, 319 parser.Identifier{Literal: "column2"}, 320 }, 321 Query: parser.SelectQuery{ 322 SelectEntity: parser.SelectEntity{ 323 SelectClause: parser.SelectClause{ 324 Fields: []parser.QueryExpression{ 325 parser.Field{Object: parser.NewIntegerValueFromString("1")}, 326 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 327 }, 328 }, 329 }, 330 }, 331 }, 332 }, 333 { 334 Input: parser.StatementPreparation{ 335 Name: parser.Identifier{Literal: "stmt"}, 336 Statement: value.NewString("select 1"), 337 }, 338 }, 339 { 340 Input: parser.ExecuteStatement{ 341 Name: parser.Identifier{Literal: "invalidstmt"}, 342 }, 343 Error: "statement invalidstmt does not exist", 344 ReturnCode: ReturnCodeApplicationError, 345 }, 346 { 347 Input: parser.ExecuteStatement{ 348 Name: parser.Identifier{Literal: "stmt"}, 349 }, 350 Logs: "1\n1\n", 351 }, 352 { 353 Input: parser.DisposeStatement{ 354 Name: parser.Identifier{Literal: "stmt"}, 355 }, 356 }, 357 { 358 Input: parser.SelectQuery{ 359 SelectEntity: parser.SelectEntity{ 360 SelectClause: parser.SelectClause{ 361 Fields: []parser.QueryExpression{ 362 parser.Field{ 363 Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 364 }, 365 parser.Field{ 366 Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 367 }, 368 }, 369 }, 370 FromClause: parser.FromClause{ 371 Tables: []parser.QueryExpression{ 372 parser.Table{Object: parser.Identifier{Literal: "tbl"}}, 373 }, 374 }, 375 }, 376 }, 377 Logs: "column1,column2\n1,2\n", 378 }, 379 { 380 Input: parser.SetFlag{ 381 Flag: parser.Flag{Name: "format"}, 382 Value: parser.NewStringValue("text"), 383 }, 384 }, 385 { 386 Input: parser.SelectQuery{ 387 SelectEntity: parser.SelectEntity{ 388 SelectClause: parser.SelectClause{ 389 Fields: []parser.QueryExpression{ 390 parser.Field{ 391 Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 392 }, 393 parser.Field{ 394 Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 395 }, 396 }, 397 }, 398 FromClause: parser.FromClause{ 399 Tables: []parser.QueryExpression{ 400 parser.Table{Object: parser.Identifier{Literal: "tbl"}}, 401 }, 402 }, 403 WhereClause: parser.WhereClause{ 404 BaseExpr: nil, 405 Filter: parser.NewTernaryValueFromString("false"), 406 }, 407 }, 408 }, 409 Logs: "Empty RecordSet\n", 410 }, 411 { 412 Input: parser.SetFlag{ 413 Flag: parser.Flag{Name: "without_header"}, 414 Value: parser.NewTernaryValueFromString("true"), 415 }, 416 }, 417 { 418 Input: parser.SetFlag{ 419 Flag: parser.Flag{Name: "format"}, 420 Value: parser.NewStringValue("csv"), 421 }, 422 }, 423 { 424 Input: parser.SelectQuery{ 425 SelectEntity: parser.SelectEntity{ 426 SelectClause: parser.SelectClause{ 427 Fields: []parser.QueryExpression{ 428 parser.Field{ 429 Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 430 }, 431 parser.Field{ 432 Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 433 }, 434 }, 435 }, 436 FromClause: parser.FromClause{ 437 Tables: []parser.QueryExpression{ 438 parser.Table{Object: parser.Identifier{Literal: "tbl"}}, 439 }, 440 }, 441 WhereClause: parser.WhereClause{ 442 BaseExpr: nil, 443 Filter: parser.NewTernaryValueFromString("false"), 444 }, 445 }, 446 }, 447 Logs: "", 448 }, 449 { 450 Input: parser.SetFlag{ 451 Flag: parser.Flag{Name: "without_header"}, 452 Value: parser.NewTernaryValueFromString("false"), 453 }, 454 }, 455 { 456 Input: parser.SelectQuery{ 457 SelectEntity: parser.SelectEntity{ 458 SelectClause: parser.SelectClause{ 459 Fields: []parser.QueryExpression{ 460 parser.Field{ 461 Object: parser.NewIntegerValueFromString("1234"), 462 }, 463 }, 464 }, 465 IntoClause: parser.IntoClause{ 466 Variables: []parser.Variable{ 467 {Name: "var4"}, 468 }, 469 }, 470 }, 471 }, 472 }, 473 { 474 Input: parser.Print{ 475 Value: parser.Variable{Name: "var4"}, 476 }, 477 Logs: "1234\n", 478 }, 479 { 480 Input: parser.DisposeView{ 481 View: parser.Identifier{Literal: "tbl"}, 482 }, 483 }, 484 { 485 Input: parser.AggregateDeclaration{ 486 Name: parser.Identifier{Literal: "useraggfunc"}, 487 Cursor: parser.Identifier{Literal: "list"}, 488 Statements: []parser.Statement{ 489 parser.VariableDeclaration{ 490 Assignments: []parser.VariableAssignment{ 491 { 492 Variable: parser.Variable{Name: "value"}, 493 }, 494 { 495 Variable: parser.Variable{Name: "fetch"}, 496 }, 497 }, 498 }, 499 parser.WhileInCursor{ 500 Variables: []parser.Variable{ 501 {Name: "fetch"}, 502 }, 503 Cursor: parser.Identifier{Literal: "list"}, 504 Statements: []parser.Statement{ 505 parser.If{ 506 Condition: parser.Is{ 507 LHS: parser.Variable{Name: "fetch"}, 508 RHS: parser.NewNullValue(), 509 }, 510 Statements: []parser.Statement{ 511 parser.FlowControl{Token: parser.CONTINUE}, 512 }, 513 }, 514 parser.If{ 515 Condition: parser.Is{ 516 LHS: parser.Variable{Name: "value"}, 517 RHS: parser.NewNullValue(), 518 }, 519 Statements: []parser.Statement{ 520 parser.VariableSubstitution{ 521 Variable: parser.Variable{Name: "value"}, 522 Value: parser.Variable{Name: "fetch"}, 523 }, 524 parser.FlowControl{Token: parser.CONTINUE}, 525 }, 526 }, 527 parser.VariableSubstitution{ 528 Variable: parser.Variable{Name: "value"}, 529 Value: parser.Arithmetic{ 530 LHS: parser.Variable{Name: "value"}, 531 RHS: parser.Variable{Name: "fetch"}, 532 Operator: parser.Token{Token: '*', Literal: "*"}, 533 }, 534 }, 535 }, 536 }, 537 parser.Return{ 538 Value: parser.Variable{Name: "value"}, 539 }, 540 }, 541 }, 542 }, 543 { 544 Input: parser.SelectQuery{ 545 SelectEntity: parser.SelectEntity{ 546 SelectClause: parser.SelectClause{ 547 Fields: []parser.QueryExpression{ 548 parser.Field{ 549 Object: parser.Function{ 550 Name: "useraggfunc", 551 Args: []parser.QueryExpression{ 552 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 553 }, 554 }, 555 Alias: parser.Identifier{Literal: "multiplication"}, 556 }, 557 }, 558 }, 559 FromClause: parser.FromClause{ 560 Tables: []parser.QueryExpression{ 561 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 562 }, 563 }, 564 }, 565 }, 566 Logs: "multiplication\n6\n", 567 }, 568 { 569 Input: parser.SelectQuery{ 570 SelectEntity: parser.SelectEntity{ 571 SelectClause: parser.SelectClause{ 572 Fields: []parser.QueryExpression{ 573 parser.Field{ 574 Object: parser.Variable{Name: "var1"}, 575 Alias: parser.Identifier{Literal: "var1"}, 576 }, 577 }, 578 }, 579 }, 580 }, 581 Logs: "var1\n1\n", 582 }, 583 { 584 Input: parser.SetFlag{ 585 Flag: parser.Flag{Name: "strip_ending_line_break"}, 586 Value: parser.NewTernaryValue(ternary.TRUE), 587 }, 588 }, 589 { 590 Input: parser.SelectQuery{ 591 SelectEntity: parser.SelectEntity{ 592 SelectClause: parser.SelectClause{ 593 Fields: []parser.QueryExpression{ 594 parser.Field{ 595 Object: parser.Variable{Name: "var1"}, 596 Alias: parser.Identifier{Literal: "var1"}, 597 }, 598 }, 599 }, 600 }, 601 }, 602 Logs: "var1\n1", 603 }, 604 { 605 Input: parser.SetFlag{ 606 Flag: parser.Flag{Name: "strip_ending_line_break"}, 607 Value: parser.NewTernaryValue(ternary.FALSE), 608 }, 609 }, 610 { 611 Input: parser.VariableDeclaration{ 612 Assignments: []parser.VariableAssignment{ 613 { 614 Variable: parser.Variable{Name: "var1"}, 615 }, 616 }, 617 }, 618 Error: "variable @var1 is redeclared", 619 ReturnCode: ReturnCodeApplicationError, 620 }, 621 { 622 Input: parser.VariableSubstitution{ 623 Variable: parser.Variable{Name: "var9"}, 624 Value: parser.NewIntegerValueFromString("1"), 625 }, 626 Error: "variable @var9 is undeclared", 627 ReturnCode: ReturnCodeApplicationError, 628 }, 629 { 630 Input: parser.InsertQuery{ 631 Table: parser.Table{Object: parser.Identifier{Literal: "table1"}}, 632 Fields: []parser.QueryExpression{ 633 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 634 parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 635 }, 636 ValuesList: []parser.QueryExpression{ 637 parser.RowValue{ 638 Value: parser.ValueList{ 639 Values: []parser.QueryExpression{ 640 parser.NewIntegerValueFromString("4"), 641 parser.NewStringValue("str4"), 642 }, 643 }, 644 }, 645 parser.RowValue{ 646 Value: parser.ValueList{ 647 Values: []parser.QueryExpression{ 648 parser.NewIntegerValueFromString("5"), 649 parser.NewStringValue("str5"), 650 }, 651 }, 652 }, 653 }, 654 }, 655 UncommittedViews: UncommittedViews{ 656 mtx: &sync.RWMutex{}, 657 Created: map[string]*FileInfo{}, 658 Updated: map[string]*FileInfo{ 659 strings.ToUpper(GetTestFilePath("TABLE1.CSV")): { 660 Path: GetTestFilePath("table1.csv"), 661 Delimiter: ',', 662 NoHeader: false, 663 Encoding: text.UTF8, 664 LineBreak: text.LF, 665 ForUpdate: true, 666 }, 667 }, 668 }, 669 Logs: fmt.Sprintf("2 records inserted on %q.\n", GetTestFilePath("table1.csv")), 670 }, 671 { 672 Input: parser.UpdateQuery{ 673 Tables: []parser.QueryExpression{ 674 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 675 }, 676 SetList: []parser.UpdateSet{ 677 { 678 Field: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 679 Value: parser.NewStringValue("update"), 680 }, 681 }, 682 WhereClause: parser.WhereClause{ 683 Filter: parser.Comparison{ 684 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 685 RHS: parser.NewIntegerValueFromString("2"), 686 Operator: parser.Token{Token: '=', Literal: "="}, 687 }, 688 }, 689 }, 690 UncommittedViews: UncommittedViews{ 691 mtx: &sync.RWMutex{}, 692 Created: map[string]*FileInfo{}, 693 Updated: map[string]*FileInfo{ 694 strings.ToUpper(GetTestFilePath("TABLE1.CSV")): { 695 Path: GetTestFilePath("table1.csv"), 696 Delimiter: ',', 697 NoHeader: false, 698 Encoding: text.UTF8, 699 LineBreak: text.LF, 700 ForUpdate: true, 701 }, 702 }, 703 }, 704 Logs: fmt.Sprintf("1 record updated on %q.\n", GetTestFilePath("table1.csv")), 705 }, 706 { 707 Input: parser.ReplaceQuery{ 708 Table: parser.Table{Object: parser.Identifier{Literal: "table1"}}, 709 Fields: []parser.QueryExpression{ 710 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 711 parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 712 }, 713 Keys: []parser.QueryExpression{ 714 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 715 }, 716 ValuesList: []parser.QueryExpression{ 717 parser.RowValue{ 718 Value: parser.ValueList{ 719 Values: []parser.QueryExpression{ 720 parser.NewIntegerValueFromString("4"), 721 parser.NewStringValue("str44"), 722 }, 723 }, 724 }, 725 parser.RowValue{ 726 Value: parser.ValueList{ 727 Values: []parser.QueryExpression{ 728 parser.NewIntegerValueFromString("6"), 729 parser.NewStringValue("str6"), 730 }, 731 }, 732 }, 733 }, 734 }, 735 UncommittedViews: UncommittedViews{ 736 mtx: &sync.RWMutex{}, 737 Created: map[string]*FileInfo{}, 738 Updated: map[string]*FileInfo{ 739 strings.ToUpper(GetTestFilePath("TABLE1.CSV")): { 740 Path: GetTestFilePath("table1.csv"), 741 Delimiter: ',', 742 NoHeader: false, 743 Encoding: text.UTF8, 744 LineBreak: text.LF, 745 ForUpdate: true, 746 }, 747 }, 748 }, 749 Logs: fmt.Sprintf("2 records replaced on %q.\n", GetTestFilePath("table1.csv")), 750 }, 751 { 752 Input: parser.DeleteQuery{ 753 FromClause: parser.FromClause{ 754 Tables: []parser.QueryExpression{ 755 parser.Table{ 756 Object: parser.Identifier{Literal: "table1"}, 757 }, 758 }, 759 }, 760 WhereClause: parser.WhereClause{ 761 Filter: parser.Comparison{ 762 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 763 RHS: parser.NewIntegerValueFromString("2"), 764 Operator: parser.Token{Token: '=', Literal: "="}, 765 }, 766 }, 767 }, 768 UncommittedViews: UncommittedViews{ 769 mtx: &sync.RWMutex{}, 770 Created: map[string]*FileInfo{}, 771 Updated: map[string]*FileInfo{ 772 strings.ToUpper(GetTestFilePath("TABLE1.CSV")): { 773 Path: GetTestFilePath("table1.csv"), 774 Delimiter: ',', 775 NoHeader: false, 776 Encoding: text.UTF8, 777 LineBreak: text.LF, 778 ForUpdate: true, 779 }, 780 }, 781 }, 782 Logs: fmt.Sprintf("1 record deleted on %q.\n", GetTestFilePath("table1.csv")), 783 }, 784 { 785 Input: parser.CreateTable{ 786 Table: parser.Identifier{Literal: "newtable.csv"}, 787 Fields: []parser.QueryExpression{ 788 parser.Identifier{Literal: "column1"}, 789 parser.Identifier{Literal: "column2"}, 790 }, 791 }, 792 UncommittedViews: UncommittedViews{ 793 mtx: &sync.RWMutex{}, 794 Created: map[string]*FileInfo{ 795 strings.ToUpper(GetTestFilePath("NEWTABLE.CSV")): { 796 Path: GetTestFilePath("newtable.csv"), 797 Delimiter: ',', 798 NoHeader: false, 799 Encoding: text.UTF8, 800 LineBreak: text.LF, 801 ForUpdate: true, 802 }, 803 }, 804 Updated: map[string]*FileInfo{}, 805 }, 806 Logs: fmt.Sprintf("file %q is created.\n", GetTestFilePath("newtable.csv")), 807 }, 808 { 809 Input: parser.CreateTable{ 810 Table: parser.Identifier{Literal: "table1.csv"}, 811 Fields: []parser.QueryExpression{ 812 parser.Identifier{Literal: "column1"}, 813 parser.Identifier{Literal: "column2"}, 814 }, 815 }, 816 Error: fmt.Sprintf("file %s already exists", GetTestFilePath("table1.csv")), 817 ReturnCode: ReturnCodeIOError, 818 }, 819 { 820 Input: parser.CreateTable{ 821 Table: parser.Identifier{Literal: "table1.csv"}, 822 Fields: []parser.QueryExpression{ 823 parser.Identifier{Literal: "column1"}, 824 parser.Identifier{Literal: "column2"}, 825 }, 826 IfNotExists: true, 827 }, 828 Logs: fmt.Sprintf("file %q already exists.\n", GetTestFilePath("table1.csv")), 829 }, 830 { 831 Input: parser.CreateTable{ 832 Table: parser.Identifier{Literal: "table1.csv"}, 833 Fields: []parser.QueryExpression{ 834 parser.Identifier{Literal: "column1"}, 835 parser.Identifier{Literal: "column2"}, 836 parser.Identifier{Literal: "column3"}, 837 }, 838 IfNotExists: true, 839 }, 840 Error: "field length does not match", 841 ReturnCode: ReturnCodeApplicationError, 842 }, 843 { 844 Input: parser.CreateTable{ 845 Table: parser.Identifier{Literal: "table1.csv"}, 846 Fields: []parser.QueryExpression{ 847 parser.Identifier{Literal: "column1"}, 848 parser.Identifier{Literal: "col"}, 849 }, 850 IfNotExists: true, 851 }, 852 Error: "field col does not exist", 853 ReturnCode: ReturnCodeApplicationError, 854 }, 855 { 856 Input: parser.AddColumns{ 857 Table: parser.Identifier{Literal: "table1.csv"}, 858 Columns: []parser.ColumnDefault{ 859 { 860 Column: parser.Identifier{Literal: "column3"}, 861 }, 862 }, 863 }, 864 UncommittedViews: UncommittedViews{ 865 mtx: &sync.RWMutex{}, 866 Created: map[string]*FileInfo{}, 867 Updated: map[string]*FileInfo{ 868 strings.ToUpper(GetTestFilePath("TABLE1.CSV")): { 869 Path: GetTestFilePath("table1.csv"), 870 Delimiter: ',', 871 NoHeader: false, 872 Encoding: text.UTF8, 873 LineBreak: text.LF, 874 ForUpdate: true, 875 }, 876 }, 877 }, 878 Logs: fmt.Sprintf("1 field added on %q.\n", GetTestFilePath("table1.csv")), 879 }, 880 { 881 Input: parser.DropColumns{ 882 Table: parser.Identifier{Literal: "table1"}, 883 Columns: []parser.QueryExpression{ 884 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 885 }, 886 }, 887 UncommittedViews: UncommittedViews{ 888 mtx: &sync.RWMutex{}, 889 Created: map[string]*FileInfo{}, 890 Updated: map[string]*FileInfo{ 891 strings.ToUpper(GetTestFilePath("TABLE1.CSV")): { 892 Path: GetTestFilePath("table1.csv"), 893 Delimiter: ',', 894 NoHeader: false, 895 Encoding: text.UTF8, 896 LineBreak: text.LF, 897 ForUpdate: true, 898 }, 899 }, 900 }, 901 Logs: fmt.Sprintf("1 field dropped on %q.\n", GetTestFilePath("table1.csv")), 902 }, 903 { 904 Input: parser.RenameColumn{ 905 Table: parser.Identifier{Literal: "table1"}, 906 Old: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 907 New: parser.Identifier{Literal: "newcolumn"}, 908 }, 909 UncommittedViews: UncommittedViews{ 910 mtx: &sync.RWMutex{}, 911 Created: map[string]*FileInfo{}, 912 Updated: map[string]*FileInfo{ 913 strings.ToUpper(GetTestFilePath("TABLE1.CSV")): { 914 Path: GetTestFilePath("table1.csv"), 915 Delimiter: ',', 916 NoHeader: false, 917 Encoding: text.UTF8, 918 LineBreak: text.LF, 919 ForUpdate: true, 920 }, 921 }, 922 }, 923 Logs: fmt.Sprintf("1 field renamed on %q.\n", GetTestFilePath("table1.csv")), 924 }, 925 { 926 Input: parser.SetTableAttribute{ 927 Table: parser.Identifier{Literal: "table1.csv"}, 928 Attribute: parser.Identifier{Literal: "delimiter"}, 929 Value: parser.NewStringValue(","), 930 }, 931 Logs: "Table attributes of " + GetTestFilePath("table1.csv") + " remain unchanged.\n", 932 }, 933 { 934 Input: parser.SetTableAttribute{ 935 Table: parser.Identifier{Literal: "table1.csv"}, 936 Attribute: parser.Identifier{Literal: "delimiter"}, 937 Value: parser.NewStringValue("\t"), 938 }, 939 UncommittedViews: UncommittedViews{ 940 mtx: &sync.RWMutex{}, 941 Created: map[string]*FileInfo{}, 942 Updated: map[string]*FileInfo{ 943 strings.ToUpper(GetTestFilePath("TABLE1.CSV")): { 944 Path: GetTestFilePath("table1.csv"), 945 Delimiter: '\t', 946 NoHeader: false, 947 Encoding: text.UTF8, 948 LineBreak: text.LF, 949 Format: option.TSV, 950 ForUpdate: true, 951 }, 952 }, 953 }, 954 Logs: "\n" + 955 strings.Repeat(" ", (calcShowFieldsWidth("table1.csv", "table1.csv", 22)-(22+len("table1.csv")))/2) + "Attributes Updated in table1.csv\n" + 956 strings.Repeat("-", calcShowFieldsWidth("table1.csv", "table1.csv", 22)) + "\n" + 957 " Path: " + GetTestFilePath("table1.csv") + "\n" + 958 " Format: TSV Delimiter: '\\t' Enclose All: false\n" + 959 " Encoding: UTF8 LineBreak: LF Header: true\n" + 960 "\n", 961 }, 962 { 963 Input: parser.Case{ 964 When: []parser.CaseWhen{ 965 { 966 Condition: parser.NewTernaryValue(ternary.FALSE), 967 Statements: []parser.Statement{ 968 parser.Print{Value: parser.NewStringValue("1")}, 969 }, 970 }, 971 { 972 Condition: parser.NewTernaryValue(ternary.TRUE), 973 Statements: []parser.Statement{ 974 parser.Print{Value: parser.NewStringValue("2")}, 975 }, 976 }, 977 }, 978 }, 979 Logs: "'2'\n", 980 }, 981 { 982 Input: parser.While{ 983 Condition: parser.Comparison{ 984 LHS: parser.Variable{Name: "while_test"}, 985 RHS: parser.NewIntegerValueFromString("3"), 986 Operator: parser.Token{Token: '<', Literal: "<"}, 987 }, 988 Statements: []parser.Statement{ 989 parser.VariableSubstitution{ 990 Variable: parser.Variable{Name: "while_test"}, 991 Value: parser.Arithmetic{ 992 LHS: parser.Variable{Name: "while_test"}, 993 RHS: parser.NewIntegerValueFromString("1"), 994 Operator: parser.Token{Token: '+', Literal: "+"}, 995 }, 996 }, 997 parser.Print{Value: parser.Variable{Name: "while_test"}}, 998 }, 999 }, 1000 Logs: "1\n2\n3\n", 1001 }, 1002 { 1003 Input: parser.Exit{ 1004 Code: value.NewInteger(1), 1005 }, 1006 Error: ExitMessage, 1007 ReturnCode: 1, 1008 }, 1009 { 1010 Input: parser.Print{ 1011 Value: parser.NewIntegerValue(12345), 1012 }, 1013 Logs: "12345\n", 1014 }, 1015 { 1016 Input: parser.Printf{ 1017 Format: parser.NewStringValue("value: %s"), 1018 Values: []parser.QueryExpression{ 1019 parser.NewIntegerValue(12345), 1020 }, 1021 }, 1022 Logs: "value: 12345\n", 1023 }, 1024 { 1025 Input: parser.Source{ 1026 FilePath: parser.NewStringValue(GetTestFilePath("source.sql")), 1027 }, 1028 Logs: "'external executable file'\n", 1029 }, 1030 { 1031 Input: parser.Execute{ 1032 BaseExpr: parser.NewBaseExpr(parser.Token{}), 1033 Statements: parser.NewStringValue("print 'execute';"), 1034 }, 1035 Logs: "'execute'\n", 1036 }, 1037 { 1038 Input: parser.Trigger{ 1039 Event: parser.Identifier{Literal: "error"}, 1040 Message: parser.NewStringValue("user error"), 1041 Code: value.NewInteger(200), 1042 }, 1043 Error: "user error", 1044 ReturnCode: 200, 1045 }, 1046 { 1047 Input: parser.Trigger{ 1048 Event: parser.Identifier{Literal: "error"}, 1049 Message: parser.NewIntegerValue(200), 1050 }, 1051 Error: DefaultUserTriggeredErrorMessage, 1052 ReturnCode: 200, 1053 }, 1054 { 1055 Input: parser.Trigger{ 1056 Event: parser.Identifier{Literal: "invalid"}, 1057 Message: parser.NewIntegerValue(200), 1058 }, 1059 Error: "invalid is an unknown event", 1060 ReturnCode: ReturnCodeApplicationError, 1061 }, 1062 { 1063 Input: parser.ShowObjects{ 1064 Type: parser.Identifier{Literal: "cursors"}, 1065 }, 1066 Logs: "No cursor is declared\n", 1067 }, 1068 { 1069 Input: parser.ShowFields{ 1070 Type: parser.Identifier{Literal: "fields"}, 1071 Table: parser.Identifier{Literal: "table1"}, 1072 }, 1073 Logs: "\n" + 1074 strings.Repeat(" ", (calcShowFieldsWidth("table1.csv", "table1", 10)-(10+len("table1")))/2) + "Fields in table1\n" + 1075 strings.Repeat("-", calcShowFieldsWidth("table1.csv", "table1", 10)) + "\n" + 1076 " Type: File\n" + 1077 " Path: " + GetTestFilePath("table1.csv") + "\n" + 1078 " Format: CSV Delimiter: ',' Enclose All: false\n" + 1079 " Encoding: UTF8 LineBreak: LF Header: true\n" + 1080 " Status: Fixed\n" + 1081 " Fields:\n" + 1082 " 1. column1\n" + 1083 " 2. column2\n" + 1084 "\n", 1085 }, 1086 } 1087 1088 func TestProcessor_ExecuteStatement(t *testing.T) { 1089 defer func() { 1090 _ = TestTx.ReleaseResources() 1091 TestTx.UncommittedViews.Clean() 1092 TestTx.Session.SetStdout(NewDiscard()) 1093 initFlag(TestTx.Flags) 1094 }() 1095 1096 TestTx.Flags.Repository = TestDir 1097 TestTx.Flags.ExportOptions.Format = option.CSV 1098 1099 tx := TestTx 1100 proc := NewProcessor(tx) 1101 _ = proc.ReferenceScope.DeclareVariableDirectly(parser.Variable{Name: "while_test"}, value.NewInteger(0)) 1102 ctx := context.Background() 1103 1104 for _, v := range processorExecuteStatementTests { 1105 _ = TestTx.ReleaseResources() 1106 TestTx.UncommittedViews = NewUncommittedViews() 1107 1108 out := NewOutput() 1109 tx.Session.SetStdout(out) 1110 _, err := proc.ExecuteStatement(ctx, v.Input) 1111 log := out.String() 1112 1113 if err != nil { 1114 var code int 1115 if apperr, ok := err.(Error); ok { 1116 if len(v.Error) < 1 { 1117 t.Errorf("unexpected error %q for %q", err, v.Input) 1118 } else if err.Error() != v.Error { 1119 t.Errorf("error %q, want error %q for %q", err, v.Error, v.Input) 1120 } 1121 1122 code = apperr.Code() 1123 } 1124 if code != v.ReturnCode { 1125 t.Errorf("error code %d, want error code %d for %q", code, v.ReturnCode, v.Input) 1126 } 1127 continue 1128 } 1129 if 0 < len(v.Error) { 1130 t.Errorf("no error, want error %q for %q", v.Error, v.Input) 1131 continue 1132 } 1133 1134 if v.UncommittedViews.mtx != nil { 1135 for _, r := range TestTx.UncommittedViews.Created { 1136 if r.Handler != nil { 1137 if r.Path != r.Handler.Path() { 1138 t.Errorf("file pointer = %q, want %q for %q", r.Handler.Path(), r.Path, v.Input) 1139 } 1140 _ = TestTx.FileContainer.Close(r.Handler) 1141 r.Handler = nil 1142 } 1143 } 1144 for _, r := range TestTx.UncommittedViews.Updated { 1145 if r.Handler != nil { 1146 if r.Path != r.Handler.Path() { 1147 t.Errorf("file pointer = %q, want %q for %q", r.Handler.Path(), r.Path, v.Input) 1148 } 1149 _ = TestTx.FileContainer.Close(r.Handler) 1150 r.Handler = nil 1151 } 1152 } 1153 1154 if !reflect.DeepEqual(TestTx.UncommittedViews, v.UncommittedViews) { 1155 t.Errorf("uncomitted views = %v, want %v for %q", TestTx.UncommittedViews, v.UncommittedViews, v.Input) 1156 } 1157 } 1158 if 0 < len(v.Logs) { 1159 if log != v.Logs { 1160 t.Errorf("logs = %s, want %s for %q", log, v.Logs, v.Input) 1161 } 1162 } 1163 if v.SelectLogs != nil { 1164 selectLog := log 1165 if !reflect.DeepEqual(selectLog, v.SelectLogs) { 1166 t.Errorf("select logs = %s, want %s for %q", selectLog, v.SelectLogs, v.Input) 1167 } 1168 } 1169 } 1170 } 1171 1172 var processorIfStmtTests = []struct { 1173 Name string 1174 Stmt parser.If 1175 ResultFlow StatementFlow 1176 ReturnValue value.Primary 1177 Result string 1178 Error string 1179 }{ 1180 { 1181 Name: "If Statement", 1182 Stmt: parser.If{ 1183 Condition: parser.NewTernaryValue(ternary.TRUE), 1184 Statements: []parser.Statement{ 1185 parser.Print{Value: parser.NewStringValue("1")}, 1186 }, 1187 }, 1188 ResultFlow: Terminate, 1189 Result: "'1'\n", 1190 }, 1191 { 1192 Name: "If Statement Execute Nothing", 1193 Stmt: parser.If{ 1194 Condition: parser.NewTernaryValue(ternary.FALSE), 1195 Statements: []parser.Statement{ 1196 parser.Print{Value: parser.NewStringValue("1")}, 1197 }, 1198 }, 1199 ResultFlow: Terminate, 1200 Result: "", 1201 }, 1202 { 1203 Name: "If Statement Execute ElseIf", 1204 Stmt: parser.If{ 1205 Condition: parser.NewTernaryValue(ternary.FALSE), 1206 Statements: []parser.Statement{ 1207 parser.Print{Value: parser.NewStringValue("1")}, 1208 }, 1209 ElseIf: []parser.ElseIf{ 1210 { 1211 Condition: parser.NewTernaryValue(ternary.TRUE), 1212 Statements: []parser.Statement{ 1213 parser.Print{Value: parser.NewStringValue("2")}, 1214 }, 1215 }, 1216 { 1217 Condition: parser.NewTernaryValue(ternary.FALSE), 1218 Statements: []parser.Statement{ 1219 parser.Print{Value: parser.NewStringValue("3")}, 1220 }, 1221 }, 1222 }, 1223 Else: parser.Else{ 1224 Statements: []parser.Statement{ 1225 parser.Print{Value: parser.NewStringValue("4")}, 1226 }, 1227 }, 1228 }, 1229 ResultFlow: Terminate, 1230 Result: "'2'\n", 1231 }, 1232 { 1233 Name: "If Statement Execute Else", 1234 Stmt: parser.If{ 1235 Condition: parser.NewTernaryValue(ternary.FALSE), 1236 Statements: []parser.Statement{ 1237 parser.Print{Value: parser.NewStringValue("1")}, 1238 }, 1239 ElseIf: []parser.ElseIf{ 1240 { 1241 Condition: parser.NewTernaryValue(ternary.FALSE), 1242 Statements: []parser.Statement{ 1243 parser.Print{Value: parser.NewStringValue("2")}, 1244 }, 1245 }, 1246 { 1247 Condition: parser.NewTernaryValue(ternary.FALSE), 1248 Statements: []parser.Statement{ 1249 parser.Print{Value: parser.NewStringValue("3")}, 1250 }, 1251 }, 1252 }, 1253 Else: parser.Else{ 1254 Statements: []parser.Statement{ 1255 parser.Print{Value: parser.NewStringValue("4")}, 1256 }, 1257 }, 1258 }, 1259 ResultFlow: Terminate, 1260 Result: "'4'\n", 1261 }, 1262 { 1263 Name: "If Statement Filter Error", 1264 Stmt: parser.If{ 1265 Condition: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 1266 Statements: []parser.Statement{ 1267 parser.Print{Value: parser.NewStringValue("1")}, 1268 }, 1269 }, 1270 Error: "field notexist does not exist", 1271 }, 1272 { 1273 Name: "If Statement Return Value", 1274 Stmt: parser.If{ 1275 Condition: parser.NewTernaryValue(ternary.TRUE), 1276 Statements: []parser.Statement{ 1277 parser.Return{Value: parser.NewStringValue("1")}, 1278 }, 1279 }, 1280 ResultFlow: Return, 1281 ReturnValue: value.NewString("1"), 1282 Result: "", 1283 }, 1284 } 1285 1286 func TestProcessor_IfStmt(t *testing.T) { 1287 defer func() { 1288 TestTx.Session.SetStdout(NewDiscard()) 1289 initFlag(TestTx.Flags) 1290 }() 1291 1292 TestTx.Flags.SetQuiet(true) 1293 tx := TestTx 1294 proc := NewProcessor(tx) 1295 1296 for _, v := range processorIfStmtTests { 1297 out := NewOutput() 1298 tx.Session.SetStdout(out) 1299 1300 proc.returnVal = nil 1301 flow, err := proc.IfStmt(context.Background(), v.Stmt) 1302 log := out.String() 1303 1304 if err != nil { 1305 if len(v.Error) < 1 { 1306 t.Errorf("%s: unexpected error %q", v.Name, err) 1307 } else if err.Error() != v.Error { 1308 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 1309 } 1310 continue 1311 } 1312 if 0 < len(v.Error) { 1313 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 1314 continue 1315 } 1316 if flow != v.ResultFlow { 1317 t.Errorf("%s: result flow = %q, want %q", v.Name, flow, v.ResultFlow) 1318 } 1319 if !reflect.DeepEqual(proc.returnVal, v.ReturnValue) { 1320 t.Errorf("%s: return = %t, want %t", v.Name, proc.returnVal, v.ReturnValue) 1321 } 1322 if log != v.Result { 1323 t.Errorf("%s: result = %q, want %q", v.Name, log, v.Result) 1324 } 1325 } 1326 } 1327 1328 var processorCaseStmtTests = []struct { 1329 Name string 1330 Stmt parser.Case 1331 ResultFlow StatementFlow 1332 Result string 1333 Error string 1334 }{ 1335 { 1336 Name: "Case", 1337 Stmt: parser.Case{ 1338 When: []parser.CaseWhen{ 1339 { 1340 Condition: parser.NewTernaryValue(ternary.FALSE), 1341 Statements: []parser.Statement{ 1342 parser.Print{Value: parser.NewStringValue("1")}, 1343 }, 1344 }, 1345 { 1346 Condition: parser.NewTernaryValue(ternary.TRUE), 1347 Statements: []parser.Statement{ 1348 parser.Print{Value: parser.NewStringValue("2")}, 1349 }, 1350 }, 1351 }, 1352 }, 1353 ResultFlow: Terminate, 1354 Result: "'2'\n", 1355 }, 1356 { 1357 Name: "Case Comparison", 1358 Stmt: parser.Case{ 1359 Value: parser.NewIntegerValue(2), 1360 When: []parser.CaseWhen{ 1361 { 1362 Condition: parser.NewIntegerValue(1), 1363 Statements: []parser.Statement{ 1364 parser.Print{Value: parser.NewStringValue("1")}, 1365 }, 1366 }, 1367 { 1368 Condition: parser.NewIntegerValue(2), 1369 Statements: []parser.Statement{ 1370 parser.Print{Value: parser.NewStringValue("2")}, 1371 }, 1372 }, 1373 }, 1374 }, 1375 ResultFlow: Terminate, 1376 Result: "'2'\n", 1377 }, 1378 { 1379 Name: "Case Else", 1380 Stmt: parser.Case{ 1381 When: []parser.CaseWhen{ 1382 { 1383 Condition: parser.NewTernaryValue(ternary.FALSE), 1384 Statements: []parser.Statement{ 1385 parser.Print{Value: parser.NewStringValue("1")}, 1386 }, 1387 }, 1388 { 1389 Condition: parser.NewTernaryValue(ternary.FALSE), 1390 Statements: []parser.Statement{ 1391 parser.Print{Value: parser.NewStringValue("2")}, 1392 }, 1393 }, 1394 }, 1395 Else: parser.CaseElse{ 1396 Statements: []parser.Statement{ 1397 parser.Print{Value: parser.NewStringValue("3")}, 1398 }, 1399 }, 1400 }, 1401 ResultFlow: Terminate, 1402 Result: "'3'\n", 1403 }, 1404 { 1405 Name: "Case No Match", 1406 Stmt: parser.Case{ 1407 When: []parser.CaseWhen{ 1408 { 1409 Condition: parser.NewTernaryValue(ternary.FALSE), 1410 Statements: []parser.Statement{ 1411 parser.Print{Value: parser.NewStringValue("1")}, 1412 }, 1413 }, 1414 { 1415 Condition: parser.NewTernaryValue(ternary.FALSE), 1416 Statements: []parser.Statement{ 1417 parser.Print{Value: parser.NewStringValue("2")}, 1418 }, 1419 }, 1420 }, 1421 }, 1422 ResultFlow: Terminate, 1423 Result: "", 1424 }, 1425 { 1426 Name: "Case Comparison Value Error", 1427 Stmt: parser.Case{ 1428 Value: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 1429 When: []parser.CaseWhen{ 1430 { 1431 Condition: parser.NewIntegerValue(1), 1432 Statements: []parser.Statement{ 1433 parser.Print{Value: parser.NewStringValue("1")}, 1434 }, 1435 }, 1436 { 1437 Condition: parser.NewIntegerValue(2), 1438 Statements: []parser.Statement{ 1439 parser.Print{Value: parser.NewStringValue("2")}, 1440 }, 1441 }, 1442 }, 1443 }, 1444 ResultFlow: TerminateWithError, 1445 Error: "field notexist does not exist", 1446 }, 1447 { 1448 Name: "Case Condition Error", 1449 Stmt: parser.Case{ 1450 When: []parser.CaseWhen{ 1451 { 1452 Condition: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 1453 Statements: []parser.Statement{ 1454 parser.Print{Value: parser.NewStringValue("1")}, 1455 }, 1456 }, 1457 }, 1458 }, 1459 ResultFlow: TerminateWithError, 1460 Error: "field notexist does not exist", 1461 }, 1462 } 1463 1464 func TestProcessor_Case(t *testing.T) { 1465 defer func() { 1466 TestTx.Session.SetStdout(NewDiscard()) 1467 initFlag(TestTx.Flags) 1468 }() 1469 1470 TestTx.Flags.SetQuiet(true) 1471 tx := TestTx 1472 proc := NewProcessor(tx) 1473 1474 for _, v := range processorCaseStmtTests { 1475 out := NewOutput() 1476 tx.Session.SetStdout(out) 1477 flow, err := proc.Case(context.Background(), v.Stmt) 1478 log := out.String() 1479 1480 if err != nil { 1481 if len(v.Error) < 1 { 1482 t.Errorf("%s: unexpected error %q", v.Name, err) 1483 } else if err.Error() != v.Error { 1484 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 1485 } 1486 continue 1487 } 1488 if 0 < len(v.Error) { 1489 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 1490 continue 1491 } 1492 if flow != v.ResultFlow { 1493 t.Errorf("%s: result flow = %q, want %q", v.Name, flow, v.ResultFlow) 1494 } 1495 if log != v.Result { 1496 t.Errorf("%s: result = %q, want %q", v.Name, log, v.Result) 1497 } 1498 } 1499 } 1500 1501 var processorWhileTests = []struct { 1502 Name string 1503 Stmt parser.While 1504 ResultFlow StatementFlow 1505 ReturnValue value.Primary 1506 Result string 1507 Error string 1508 }{ 1509 { 1510 Name: "While Statement", 1511 Stmt: parser.While{ 1512 Condition: parser.Comparison{ 1513 LHS: parser.Variable{Name: "while_test"}, 1514 RHS: parser.NewIntegerValueFromString("3"), 1515 Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: "<"}, 1516 }, 1517 Statements: []parser.Statement{ 1518 parser.VariableSubstitution{ 1519 Variable: parser.Variable{Name: "while_test"}, 1520 Value: parser.Arithmetic{ 1521 LHS: parser.Variable{Name: "while_test"}, 1522 RHS: parser.NewIntegerValueFromString("1"), 1523 Operator: parser.Token{Token: '+', Literal: "+"}, 1524 }, 1525 }, 1526 parser.Print{Value: parser.Variable{Name: "while_test"}}, 1527 parser.TransactionControl{Token: parser.COMMIT}, 1528 }, 1529 }, 1530 ResultFlow: Terminate, 1531 Result: "1\n2\n3\n", 1532 }, 1533 { 1534 Name: "While Statement Continue", 1535 Stmt: parser.While{ 1536 Condition: parser.Comparison{ 1537 LHS: parser.Variable{Name: "while_test_count"}, 1538 RHS: parser.NewIntegerValueFromString("3"), 1539 Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: "<"}, 1540 }, 1541 Statements: []parser.Statement{ 1542 parser.VariableSubstitution{ 1543 Variable: parser.Variable{Name: "while_test_count"}, 1544 Value: parser.Arithmetic{ 1545 LHS: parser.Variable{Name: "while_test_count"}, 1546 RHS: parser.NewIntegerValueFromString("1"), 1547 Operator: parser.Token{Token: '+', Literal: "+"}, 1548 }, 1549 }, 1550 parser.VariableSubstitution{ 1551 Variable: parser.Variable{Name: "while_test"}, 1552 Value: parser.Arithmetic{ 1553 LHS: parser.Variable{Name: "while_test"}, 1554 RHS: parser.NewIntegerValueFromString("1"), 1555 Operator: parser.Token{Token: '+', Literal: "+"}, 1556 }, 1557 }, 1558 parser.If{ 1559 Condition: parser.Comparison{ 1560 LHS: parser.Variable{Name: "while_test_count"}, 1561 RHS: parser.NewIntegerValueFromString("2"), 1562 Operator: parser.Token{Token: '=', Literal: "="}, 1563 }, 1564 Statements: []parser.Statement{ 1565 parser.FlowControl{Token: parser.CONTINUE}, 1566 }, 1567 }, 1568 parser.Print{Value: parser.Variable{Name: "while_test"}}, 1569 parser.TransactionControl{Token: parser.COMMIT}, 1570 }, 1571 }, 1572 ResultFlow: Terminate, 1573 Result: "1\n3\n", 1574 }, 1575 { 1576 Name: "While Statement Break", 1577 Stmt: parser.While{ 1578 Condition: parser.Comparison{ 1579 LHS: parser.Variable{Name: "while_test_count"}, 1580 RHS: parser.NewIntegerValueFromString("3"), 1581 Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: "<"}, 1582 }, 1583 Statements: []parser.Statement{ 1584 parser.VariableSubstitution{ 1585 Variable: parser.Variable{Name: "while_test_count"}, 1586 Value: parser.Arithmetic{ 1587 LHS: parser.Variable{Name: "while_test_count"}, 1588 RHS: parser.NewIntegerValueFromString("1"), 1589 Operator: parser.Token{Token: '+', Literal: "+"}, 1590 }, 1591 }, 1592 parser.VariableSubstitution{ 1593 Variable: parser.Variable{Name: "while_test"}, 1594 Value: parser.Arithmetic{ 1595 LHS: parser.Variable{Name: "while_test"}, 1596 RHS: parser.NewIntegerValueFromString("1"), 1597 Operator: parser.Token{Token: '+', Literal: "+"}, 1598 }, 1599 }, 1600 parser.If{ 1601 Condition: parser.Comparison{ 1602 LHS: parser.Variable{Name: "while_test_count"}, 1603 RHS: parser.NewIntegerValueFromString("2"), 1604 Operator: parser.Token{Token: '=', Literal: "="}, 1605 }, 1606 Statements: []parser.Statement{ 1607 parser.FlowControl{Token: parser.BREAK}, 1608 }, 1609 }, 1610 parser.Print{Value: parser.Variable{Name: "while_test"}}, 1611 parser.TransactionControl{Token: parser.COMMIT}, 1612 }, 1613 }, 1614 ResultFlow: Terminate, 1615 Result: "1\n", 1616 }, 1617 { 1618 Name: "While Statement Exit", 1619 Stmt: parser.While{ 1620 Condition: parser.Comparison{ 1621 LHS: parser.Variable{Name: "while_test_count"}, 1622 RHS: parser.NewIntegerValueFromString("3"), 1623 Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: "<"}, 1624 }, 1625 Statements: []parser.Statement{ 1626 parser.VariableSubstitution{ 1627 Variable: parser.Variable{Name: "while_test_count"}, 1628 Value: parser.Arithmetic{ 1629 LHS: parser.Variable{Name: "while_test_count"}, 1630 RHS: parser.NewIntegerValueFromString("1"), 1631 Operator: parser.Token{Token: '+', Literal: "+"}, 1632 }, 1633 }, 1634 parser.VariableSubstitution{ 1635 Variable: parser.Variable{Name: "while_test"}, 1636 Value: parser.Arithmetic{ 1637 LHS: parser.Variable{Name: "while_test"}, 1638 RHS: parser.NewIntegerValueFromString("1"), 1639 Operator: parser.Token{Token: '+', Literal: "+"}, 1640 }, 1641 }, 1642 parser.If{ 1643 Condition: parser.Comparison{ 1644 LHS: parser.Variable{Name: "while_test_count"}, 1645 RHS: parser.NewIntegerValueFromString("2"), 1646 Operator: parser.Token{Token: '=', Literal: "="}, 1647 }, 1648 Statements: []parser.Statement{ 1649 parser.Exit{}, 1650 }, 1651 }, 1652 parser.Print{Value: parser.Variable{Name: "while_test"}}, 1653 parser.TransactionControl{Token: parser.COMMIT}, 1654 }, 1655 }, 1656 ResultFlow: Exit, 1657 Result: "1\n", 1658 }, 1659 { 1660 Name: "While Statement Filter Error", 1661 Stmt: parser.While{ 1662 Condition: parser.Comparison{ 1663 LHS: parser.Variable{Name: "while_test"}, 1664 RHS: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 1665 Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: "<"}, 1666 }, 1667 Statements: []parser.Statement{ 1668 parser.VariableSubstitution{ 1669 Variable: parser.Variable{Name: "while_test"}, 1670 Value: parser.Arithmetic{ 1671 LHS: parser.Variable{Name: "while_test"}, 1672 RHS: parser.NewIntegerValueFromString("1"), 1673 Operator: parser.Token{Token: '+', Literal: "+"}, 1674 }, 1675 }, 1676 parser.Print{Value: parser.Variable{Name: "while_test"}}, 1677 parser.TransactionControl{Token: parser.COMMIT}, 1678 }, 1679 }, 1680 Error: "field notexist does not exist", 1681 }, 1682 { 1683 Name: "While Statement Execution Error", 1684 Stmt: parser.While{ 1685 Condition: parser.Comparison{ 1686 LHS: parser.Variable{Name: "while_test"}, 1687 RHS: parser.NewIntegerValueFromString("3"), 1688 Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: "<"}, 1689 }, 1690 Statements: []parser.Statement{ 1691 parser.VariableSubstitution{ 1692 Variable: parser.Variable{Name: "while_test"}, 1693 Value: parser.Arithmetic{ 1694 LHS: parser.Variable{Name: "while_test"}, 1695 RHS: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 1696 Operator: parser.Token{Token: '+', Literal: "+"}, 1697 }, 1698 }, 1699 parser.Print{Value: parser.Variable{Name: "while_test"}}, 1700 parser.TransactionControl{Token: parser.COMMIT}, 1701 }, 1702 }, 1703 Error: "field notexist does not exist", 1704 }, 1705 { 1706 Name: "While Statement Return Value", 1707 Stmt: parser.While{ 1708 Condition: parser.Comparison{ 1709 LHS: parser.Variable{Name: "while_test"}, 1710 RHS: parser.NewIntegerValueFromString("3"), 1711 Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: "<"}, 1712 }, 1713 Statements: []parser.Statement{ 1714 parser.Return{Value: parser.NewStringValue("1")}, 1715 parser.VariableSubstitution{ 1716 Variable: parser.Variable{Name: "while_test"}, 1717 Value: parser.Arithmetic{ 1718 LHS: parser.Variable{Name: "while_test"}, 1719 RHS: parser.NewIntegerValueFromString("1"), 1720 Operator: parser.Token{Token: '+', Literal: "+"}, 1721 }, 1722 }, 1723 parser.Print{Value: parser.Variable{Name: "while_test"}}, 1724 parser.TransactionControl{Token: parser.COMMIT}, 1725 }, 1726 }, 1727 ResultFlow: Return, 1728 ReturnValue: value.NewString("1"), 1729 }, 1730 } 1731 1732 func TestProcessor_While(t *testing.T) { 1733 defer func() { 1734 TestTx.Session.SetStdout(NewDiscard()) 1735 initFlag(TestTx.Flags) 1736 }() 1737 1738 TestTx.Flags.SetQuiet(true) 1739 tx := TestTx 1740 proc := NewProcessor(tx) 1741 1742 for _, v := range processorWhileTests { 1743 proc.returnVal = nil 1744 if _, ok := proc.ReferenceScope.CurrentBlock().Variables.Get(parser.Variable{Name: "while_test"}); !ok { 1745 _ = proc.ReferenceScope.DeclareVariableDirectly(parser.Variable{Name: "while_test"}, value.NewInteger(0)) 1746 } 1747 _ = proc.ReferenceScope.CurrentBlock().Variables.Set(parser.Variable{Name: "while_test"}, value.NewInteger(0)) 1748 1749 if _, ok := proc.ReferenceScope.CurrentBlock().Variables.Get(parser.Variable{Name: "while_test_count"}); !ok { 1750 _ = proc.ReferenceScope.DeclareVariableDirectly(parser.Variable{Name: "while_test_count"}, value.NewInteger(0)) 1751 } 1752 _ = proc.ReferenceScope.CurrentBlock().Variables.Set(parser.Variable{Name: "while_test_count"}, value.NewInteger(0)) 1753 1754 out := NewOutput() 1755 tx.Session.SetStdout(out) 1756 flow, err := proc.While(context.Background(), v.Stmt) 1757 log := out.String() 1758 1759 if err != nil { 1760 if len(v.Error) < 1 { 1761 t.Errorf("%s: unexpected error %q", v.Name, err) 1762 } else if err.Error() != v.Error { 1763 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 1764 } 1765 continue 1766 } 1767 if 0 < len(v.Error) { 1768 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 1769 continue 1770 } 1771 if flow != v.ResultFlow { 1772 t.Errorf("%s: result flow = %q, want %q", v.Name, flow, v.ResultFlow) 1773 } 1774 if !reflect.DeepEqual(proc.returnVal, v.ReturnValue) { 1775 t.Errorf("%s: return = %t, want %t", v.Name, proc.returnVal, v.ReturnValue) 1776 } 1777 if log != v.Result { 1778 t.Errorf("%s: result = %q, want %q", v.Name, log, v.Result) 1779 } 1780 } 1781 } 1782 1783 var processorWhileInCursorTests = []struct { 1784 Name string 1785 Stmt parser.WhileInCursor 1786 ResultFlow StatementFlow 1787 ReturnValue value.Primary 1788 Result string 1789 Error string 1790 }{ 1791 { 1792 Name: "While In Cursor", 1793 Stmt: parser.WhileInCursor{ 1794 Variables: []parser.Variable{ 1795 {Name: "var1"}, 1796 {Name: "var2"}, 1797 }, 1798 Cursor: parser.Identifier{Literal: "cur"}, 1799 Statements: []parser.Statement{ 1800 parser.Print{Value: parser.Variable{Name: "var1"}}, 1801 parser.TransactionControl{Token: parser.COMMIT}, 1802 }, 1803 }, 1804 ResultFlow: Terminate, 1805 Result: "'1'\n'2'\n'3'\n", 1806 }, 1807 { 1808 Name: "While In Cursor With Declaration", 1809 Stmt: parser.WhileInCursor{ 1810 WithDeclaration: true, 1811 Variables: []parser.Variable{ 1812 {Name: "declvar1"}, 1813 {Name: "declvar2"}, 1814 }, 1815 Cursor: parser.Identifier{Literal: "cur"}, 1816 Statements: []parser.Statement{ 1817 parser.Print{Value: parser.Variable{Name: "declvar1"}}, 1818 parser.TransactionControl{Token: parser.COMMIT}, 1819 }, 1820 }, 1821 ResultFlow: Terminate, 1822 Result: "'1'\n'2'\n'3'\n", 1823 }, 1824 { 1825 Name: "While In Cursor Continue", 1826 Stmt: parser.WhileInCursor{ 1827 Variables: []parser.Variable{ 1828 {Name: "var1"}, 1829 {Name: "var2"}, 1830 }, 1831 Cursor: parser.Identifier{Literal: "cur"}, 1832 Statements: []parser.Statement{ 1833 parser.If{ 1834 Condition: parser.Comparison{ 1835 LHS: parser.Variable{Name: "var1"}, 1836 RHS: parser.NewIntegerValueFromString("2"), 1837 Operator: parser.Token{Token: '=', Literal: "="}, 1838 }, 1839 Statements: []parser.Statement{ 1840 parser.FlowControl{Token: parser.CONTINUE}, 1841 }, 1842 }, 1843 parser.Print{Value: parser.Variable{Name: "var1"}}, 1844 parser.TransactionControl{Token: parser.COMMIT}, 1845 }, 1846 }, 1847 ResultFlow: Terminate, 1848 Result: "'1'\n'3'\n", 1849 }, 1850 { 1851 Name: "While In Cursor Break", 1852 Stmt: parser.WhileInCursor{ 1853 Variables: []parser.Variable{ 1854 {Name: "var1"}, 1855 {Name: "var2"}, 1856 }, 1857 Cursor: parser.Identifier{Literal: "cur"}, 1858 Statements: []parser.Statement{ 1859 parser.If{ 1860 Condition: parser.Comparison{ 1861 LHS: parser.Variable{Name: "var1"}, 1862 RHS: parser.NewIntegerValueFromString("2"), 1863 Operator: parser.Token{Token: '=', Literal: "="}, 1864 }, 1865 Statements: []parser.Statement{ 1866 parser.FlowControl{Token: parser.BREAK}, 1867 }, 1868 }, 1869 parser.Print{Value: parser.Variable{Name: "var1"}}, 1870 parser.TransactionControl{Token: parser.COMMIT}, 1871 }, 1872 }, 1873 ResultFlow: Terminate, 1874 Result: "'1'\n", 1875 }, 1876 { 1877 Name: "While In Cursor Exit With Code", 1878 Stmt: parser.WhileInCursor{ 1879 Variables: []parser.Variable{ 1880 {Name: "var1"}, 1881 {Name: "var2"}, 1882 }, 1883 Cursor: parser.Identifier{Literal: "cur"}, 1884 Statements: []parser.Statement{ 1885 parser.If{ 1886 Condition: parser.Comparison{ 1887 LHS: parser.Variable{Name: "var1"}, 1888 RHS: parser.NewIntegerValueFromString("2"), 1889 Operator: parser.Token{Token: '=', Literal: "="}, 1890 }, 1891 Statements: []parser.Statement{ 1892 parser.Exit{}, 1893 }, 1894 }, 1895 parser.Print{Value: parser.Variable{Name: "var1"}}, 1896 parser.TransactionControl{Token: parser.COMMIT}, 1897 }, 1898 }, 1899 ResultFlow: Exit, 1900 Result: "'1'\n", 1901 }, 1902 { 1903 Name: "While In Cursor Fetch Error", 1904 Stmt: parser.WhileInCursor{ 1905 Variables: []parser.Variable{ 1906 {Name: "var1"}, 1907 {Name: "var3"}, 1908 }, 1909 Cursor: parser.Identifier{Literal: "cur"}, 1910 Statements: []parser.Statement{ 1911 parser.Print{Value: parser.Variable{Name: "var1"}}, 1912 parser.TransactionControl{Token: parser.COMMIT}, 1913 }, 1914 }, 1915 Error: "variable @var3 is undeclared", 1916 }, 1917 { 1918 Name: "While In Cursor Statement Execution Error", 1919 Stmt: parser.WhileInCursor{ 1920 Variables: []parser.Variable{ 1921 {Name: "var1"}, 1922 {Name: "var2"}, 1923 }, 1924 Cursor: parser.Identifier{Literal: "cur"}, 1925 Statements: []parser.Statement{ 1926 parser.If{ 1927 Condition: parser.Comparison{ 1928 LHS: parser.Variable{Name: "var1"}, 1929 RHS: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 1930 Operator: parser.Token{Token: '=', Literal: "="}, 1931 }, 1932 Statements: []parser.Statement{ 1933 parser.FlowControl{Token: parser.BREAK}, 1934 }, 1935 }, 1936 parser.Print{Value: parser.Variable{Name: "var1"}}, 1937 parser.TransactionControl{Token: parser.COMMIT}, 1938 }, 1939 }, 1940 Error: "field notexist does not exist", 1941 }, 1942 { 1943 Name: "While In Cursor Return Value", 1944 Stmt: parser.WhileInCursor{ 1945 Variables: []parser.Variable{ 1946 {Name: "var1"}, 1947 {Name: "var2"}, 1948 }, 1949 Cursor: parser.Identifier{Literal: "cur"}, 1950 Statements: []parser.Statement{ 1951 parser.Return{Value: parser.NewStringValue("1")}, 1952 parser.Print{Value: parser.Variable{Name: "var1"}}, 1953 parser.TransactionControl{Token: parser.COMMIT}, 1954 }, 1955 }, 1956 ResultFlow: Return, 1957 ReturnValue: value.NewString("1"), 1958 }, 1959 } 1960 1961 func TestProcessor_WhileInCursor(t *testing.T) { 1962 defer func() { 1963 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 1964 TestTx.Session.SetStdout(NewDiscard()) 1965 initFlag(TestTx.Flags) 1966 }() 1967 1968 TestTx.Flags.Repository = TestDir 1969 1970 tx := TestTx 1971 proc := NewProcessor(tx) 1972 ctx := context.Background() 1973 1974 for _, v := range processorWhileInCursorTests { 1975 proc.ReferenceScope = GenerateReferenceScope([]map[string]map[string]interface{}{ 1976 { 1977 scopeNameVariables: { 1978 "var1": value.NewNull(), 1979 "var2": value.NewNull(), 1980 }, 1981 scopeNameCursors: { 1982 "CUR": &Cursor{ 1983 query: selectQueryForCursorTest, 1984 mtx: &sync.Mutex{}, 1985 }, 1986 }, 1987 }, 1988 }, nil, time.Time{}, nil) 1989 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 1990 _ = proc.ReferenceScope.OpenCursor(ctx, parser.Identifier{Literal: "cur"}, nil) 1991 1992 out := NewOutput() 1993 tx.Session.SetStdout(out) 1994 flow, err := proc.WhileInCursor(context.Background(), v.Stmt) 1995 log := out.String() 1996 1997 if err != nil { 1998 if len(v.Error) < 1 { 1999 t.Errorf("%s: unexpected error %q", v.Name, err) 2000 } else if err.Error() != v.Error { 2001 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 2002 } 2003 continue 2004 } 2005 if 0 < len(v.Error) { 2006 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 2007 continue 2008 } 2009 if flow != v.ResultFlow { 2010 t.Errorf("%s: result flow = %q, want %q", v.Name, flow, v.ResultFlow) 2011 } 2012 if !reflect.DeepEqual(proc.returnVal, v.ReturnValue) { 2013 t.Errorf("%s: return = %t, want %t", v.Name, proc.returnVal, v.ReturnValue) 2014 } 2015 if log != v.Result { 2016 t.Errorf("%s: result = %q, want %q", v.Name, log, v.Result) 2017 } 2018 } 2019 } 2020 2021 var processorExecExternalCommand = []struct { 2022 Name string 2023 Stmt parser.ExternalCommand 2024 Error string 2025 }{ 2026 { 2027 Name: "Error in Splitting Arguments", 2028 Stmt: parser.ExternalCommand{ 2029 Command: "cmd arg 'arg", 2030 }, 2031 Error: "external command: string not terminated", 2032 }, 2033 { 2034 Name: "Error in Scanning Argument", 2035 Stmt: parser.ExternalCommand{ 2036 Command: "cmd 'arg arg@'", 2037 }, 2038 Error: "external command: invalid variable symbol", 2039 }, 2040 { 2041 Name: "Error in Evaluation of Variable", 2042 Stmt: parser.ExternalCommand{ 2043 Command: "cmd @__not_exist__", 2044 }, 2045 Error: "external command: variable @__not_exist__ is undeclared", 2046 }, 2047 } 2048 2049 func TestProcessor_ExecExternalCommand(t *testing.T) { 2050 proc := NewProcessor(TestTx) 2051 2052 for _, v := range processorExecExternalCommand { 2053 err := proc.ExecExternalCommand(context.Background(), v.Stmt) 2054 2055 if err != nil { 2056 if len(v.Error) < 1 { 2057 t.Errorf("%s: unexpected error %q", v.Name, err) 2058 } else if err.Error() != v.Error { 2059 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 2060 } 2061 continue 2062 } 2063 if 0 < len(v.Error) { 2064 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 2065 continue 2066 } 2067 } 2068 }