github.com/mithrandie/csvq@v1.18.1/lib/query/query_test.go (about) 1 package query 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "sync" 8 "testing" 9 "time" 10 11 "github.com/mithrandie/csvq/lib/option" 12 "github.com/mithrandie/csvq/lib/parser" 13 "github.com/mithrandie/csvq/lib/value" 14 15 "github.com/mithrandie/go-text" 16 "github.com/mithrandie/go-text/json" 17 ) 18 19 var fetchCursorTests = []struct { 20 Name string 21 CurName parser.Identifier 22 FetchPosition parser.FetchPosition 23 Variables []parser.Variable 24 Success bool 25 ResultScopes *ReferenceScope 26 Error string 27 }{ 28 { 29 Name: "Fetch Cursor First Time", 30 CurName: parser.Identifier{Literal: "cur"}, 31 Variables: []parser.Variable{ 32 {Name: "var1"}, 33 {Name: "var2"}, 34 }, 35 Success: true, 36 ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{ 37 { 38 scopeNameVariables: { 39 "var1": value.NewString("1"), 40 "var2": value.NewString("str1"), 41 }, 42 }, 43 }, nil, time.Time{}, nil), 44 }, 45 { 46 Name: "Fetch Cursor Second Time", 47 CurName: parser.Identifier{Literal: "cur"}, 48 Variables: []parser.Variable{ 49 {Name: "var1"}, 50 {Name: "var2"}, 51 }, 52 Success: true, 53 ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{ 54 { 55 scopeNameVariables: { 56 "var1": value.NewString("2"), 57 "var2": value.NewString("str2"), 58 }, 59 }, 60 }, nil, time.Time{}, nil), 61 }, 62 { 63 Name: "Fetch Cursor Third Time", 64 CurName: parser.Identifier{Literal: "cur"}, 65 Variables: []parser.Variable{ 66 {Name: "var1"}, 67 {Name: "var2"}, 68 }, 69 Success: true, 70 ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{ 71 { 72 scopeNameVariables: { 73 "var1": value.NewString("3"), 74 "var2": value.NewString("str3"), 75 }, 76 }, 77 }, nil, time.Time{}, nil), 78 }, 79 { 80 Name: "Fetch Cursor Forth Time", 81 CurName: parser.Identifier{Literal: "cur"}, 82 Variables: []parser.Variable{ 83 {Name: "var1"}, 84 {Name: "var2"}, 85 }, 86 Success: false, 87 ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{ 88 { 89 scopeNameVariables: { 90 "var1": value.NewString("3"), 91 "var2": value.NewString("str3"), 92 }, 93 }, 94 }, nil, time.Time{}, nil), 95 }, 96 { 97 Name: "Fetch Cursor Absolute", 98 CurName: parser.Identifier{Literal: "cur"}, 99 FetchPosition: parser.FetchPosition{ 100 Position: parser.Token{Token: parser.ABSOLUTE, Literal: "absolute"}, 101 Number: parser.NewIntegerValueFromString("1"), 102 }, 103 Variables: []parser.Variable{ 104 {Name: "var1"}, 105 {Name: "var2"}, 106 }, 107 Success: true, 108 ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{ 109 { 110 scopeNameVariables: { 111 "var1": value.NewString("2"), 112 "var2": value.NewString("str2"), 113 }, 114 }, 115 }, nil, time.Time{}, nil), 116 }, 117 { 118 Name: "Fetch Cursor Fetch Error", 119 CurName: parser.Identifier{Literal: "notexist"}, 120 Variables: []parser.Variable{ 121 {Name: "var1"}, 122 {Name: "var2"}, 123 }, 124 Error: "cursor notexist is undeclared", 125 }, 126 { 127 Name: "Fetch Cursor Not Match Number Error", 128 CurName: parser.Identifier{Literal: "cur2"}, 129 Variables: []parser.Variable{ 130 {Name: "var1"}, 131 }, 132 Error: "fetching from cursor cur2 returns 2 values", 133 }, 134 { 135 Name: "Fetch Cursor Substitution Error", 136 CurName: parser.Identifier{Literal: "cur2"}, 137 Variables: []parser.Variable{ 138 {Name: "var1"}, 139 {Name: "notexist"}, 140 }, 141 Error: "variable @notexist is undeclared", 142 }, 143 { 144 Name: "Fetch Cursor Number Value Error", 145 CurName: parser.Identifier{Literal: "cur"}, 146 FetchPosition: parser.FetchPosition{ 147 Position: parser.Token{Token: parser.ABSOLUTE, Literal: "absolute"}, 148 Number: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 149 }, 150 Variables: []parser.Variable{ 151 {Name: "var1"}, 152 {Name: "var2"}, 153 }, 154 Error: "field notexist does not exist", 155 }, 156 { 157 Name: "Fetch Cursor Number Not Integer Error", 158 CurName: parser.Identifier{Literal: "cur"}, 159 FetchPosition: parser.FetchPosition{ 160 Position: parser.Token{Token: parser.ABSOLUTE, Literal: "absolute"}, 161 Number: parser.NewNullValue(), 162 }, 163 Variables: []parser.Variable{ 164 {Name: "var1"}, 165 {Name: "var2"}, 166 }, 167 Error: "fetching position NULL is not an integer value", 168 }, 169 } 170 171 func TestFetchCursor(t *testing.T) { 172 defer func() { 173 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 174 initFlag(TestTx.Flags) 175 }() 176 177 TestTx.Flags.Repository = TestDir 178 179 scope := GenerateReferenceScope([]map[string]map[string]interface{}{ 180 { 181 scopeNameVariables: { 182 "var1": value.NewNull(), 183 "var2": value.NewNull(), 184 }, 185 scopeNameCursors: { 186 "CUR": &Cursor{ 187 query: selectQueryForCursorTest, 188 mtx: &sync.Mutex{}, 189 }, 190 "CUR2": &Cursor{ 191 query: selectQueryForCursorTest, 192 mtx: &sync.Mutex{}, 193 }, 194 }, 195 }, 196 }, nil, time.Time{}, nil) 197 198 ctx := context.Background() 199 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 200 _ = scope.OpenCursor(ctx, parser.Identifier{Literal: "cur"}, nil) 201 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 202 _ = scope.OpenCursor(ctx, parser.Identifier{Literal: "cur2"}, nil) 203 204 for _, v := range fetchCursorTests { 205 success, err := FetchCursor(ctx, scope, v.CurName, v.FetchPosition, v.Variables) 206 if err != nil { 207 if len(v.Error) < 1 { 208 t.Errorf("%s: unexpected error %q", v.Name, err) 209 } else if err.Error() != v.Error { 210 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 211 } 212 continue 213 } 214 if 0 < len(v.Error) { 215 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 216 continue 217 } 218 if success != v.Success { 219 t.Errorf("%s: success = %t, want %t", v.Name, success, v.Success) 220 } 221 222 if !SyncMapEqual(scope.Blocks[0].Variables, v.ResultScopes.Blocks[0].Variables) { 223 t.Errorf("%s: variables = %v, want %v", v.Name, scope.Blocks, v.ResultScopes.Blocks) 224 } 225 } 226 } 227 228 var declareViewTests = []struct { 229 Name string 230 ViewMap ViewMap 231 Expr parser.ViewDeclaration 232 Result ViewMap 233 Error string 234 }{ 235 { 236 Name: "Declare View", 237 Expr: parser.ViewDeclaration{ 238 View: parser.Identifier{Literal: "tbl"}, 239 Fields: []parser.QueryExpression{ 240 parser.Identifier{Literal: "column1"}, 241 parser.Identifier{Literal: "column2"}, 242 }, 243 }, 244 Result: GenerateViewMap([]*View{ 245 { 246 FileInfo: &FileInfo{ 247 Path: "tbl", 248 ViewType: ViewTypeTemporaryTable, 249 restorePointHeader: NewHeader("tbl", []string{"column1", "column2"}), 250 restorePointRecordSet: RecordSet{}, 251 }, 252 Header: NewHeader("tbl", []string{"column1", "column2"}), 253 RecordSet: RecordSet{}, 254 }, 255 }), 256 }, 257 { 258 Name: "Declare View Field Duplicate Error", 259 Expr: parser.ViewDeclaration{ 260 View: parser.Identifier{Literal: "tbl"}, 261 Fields: []parser.QueryExpression{ 262 parser.Identifier{Literal: "column1"}, 263 parser.Identifier{Literal: "column1"}, 264 }, 265 }, 266 Error: "field name column1 is a duplicate", 267 }, 268 { 269 Name: "Declare View From Query", 270 Expr: parser.ViewDeclaration{ 271 View: parser.Identifier{Literal: "tbl"}, 272 Fields: []parser.QueryExpression{ 273 parser.Identifier{Literal: "column1"}, 274 parser.Identifier{Literal: "column2"}, 275 }, 276 Query: parser.SelectQuery{ 277 SelectEntity: parser.SelectEntity{ 278 SelectClause: parser.SelectClause{ 279 Fields: []parser.QueryExpression{ 280 parser.Field{Object: parser.NewIntegerValueFromString("1")}, 281 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 282 }, 283 }, 284 }, 285 }, 286 }, 287 Result: GenerateViewMap([]*View{ 288 { 289 FileInfo: &FileInfo{ 290 Path: "tbl", 291 ViewType: ViewTypeTemporaryTable, 292 restorePointHeader: NewHeader("tbl", []string{"column1", "column2"}), 293 restorePointRecordSet: RecordSet{ 294 NewRecord([]value.Primary{ 295 value.NewInteger(1), 296 value.NewInteger(2), 297 }), 298 }, 299 }, 300 Header: NewHeader("tbl", []string{"column1", "column2"}), 301 RecordSet: RecordSet{ 302 NewRecord([]value.Primary{ 303 value.NewInteger(1), 304 value.NewInteger(2), 305 }), 306 }, 307 }, 308 }), 309 }, 310 { 311 Name: "Declare View From Query Query Error", 312 Expr: parser.ViewDeclaration{ 313 View: parser.Identifier{Literal: "tbl"}, 314 Fields: []parser.QueryExpression{ 315 parser.Identifier{Literal: "column1"}, 316 parser.Identifier{Literal: "column2"}, 317 }, 318 Query: parser.SelectQuery{ 319 SelectEntity: parser.SelectEntity{ 320 SelectClause: parser.SelectClause{ 321 Fields: []parser.QueryExpression{ 322 parser.Field{Object: parser.NewIntegerValueFromString("1")}, 323 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}}, 324 }, 325 }, 326 }, 327 }, 328 }, 329 Error: "field notexist does not exist", 330 }, 331 { 332 Name: "Declare View From Query Field Update Error", 333 Expr: parser.ViewDeclaration{ 334 View: parser.Identifier{Literal: "tbl"}, 335 Fields: []parser.QueryExpression{ 336 parser.Identifier{Literal: "column1"}, 337 }, 338 Query: parser.SelectQuery{ 339 SelectEntity: parser.SelectEntity{ 340 SelectClause: parser.SelectClause{ 341 Fields: []parser.QueryExpression{ 342 parser.Field{Object: parser.NewIntegerValueFromString("1")}, 343 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 344 }, 345 }, 346 }, 347 }, 348 }, 349 Error: "select query should return exactly 1 field for view tbl", 350 }, 351 { 352 Name: "Declare View From Query Field Duplicate Error", 353 Expr: parser.ViewDeclaration{ 354 View: parser.Identifier{Literal: "tbl"}, 355 Fields: []parser.QueryExpression{ 356 parser.Identifier{Literal: "column1"}, 357 parser.Identifier{Literal: "column1"}, 358 }, 359 Query: parser.SelectQuery{ 360 SelectEntity: parser.SelectEntity{ 361 SelectClause: parser.SelectClause{ 362 Fields: []parser.QueryExpression{ 363 parser.Field{Object: parser.NewIntegerValueFromString("1")}, 364 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 365 }, 366 }, 367 }, 368 }, 369 }, 370 Error: "field name column1 is a duplicate", 371 }, 372 { 373 Name: "Declare View Redeclaration Error", 374 ViewMap: GenerateViewMap([]*View{ 375 { 376 FileInfo: &FileInfo{ 377 Path: "tbl", 378 ViewType: ViewTypeTemporaryTable, 379 }, 380 }, 381 }), 382 Expr: parser.ViewDeclaration{ 383 View: parser.Identifier{Literal: "tbl"}, 384 Fields: []parser.QueryExpression{ 385 parser.Identifier{Literal: "column1"}, 386 parser.Identifier{Literal: "column2"}, 387 }, 388 }, 389 Error: "view tbl is redeclared", 390 }, 391 } 392 393 func TestDeclareView(t *testing.T) { 394 scope := NewReferenceScope(TestTx) 395 ctx := context.Background() 396 397 for _, v := range declareViewTests { 398 if v.ViewMap.SyncMap == nil { 399 scope.Blocks[0].TemporaryTables = NewViewMap() 400 } else { 401 scope.Blocks[0].TemporaryTables = v.ViewMap 402 } 403 404 err := DeclareView(ctx, scope, v.Expr) 405 if err != nil { 406 if len(v.Error) < 1 { 407 t.Errorf("%s: unexpected error %q", v.Name, err) 408 } else if err.Error() != v.Error { 409 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 410 } 411 continue 412 } 413 if 0 < len(v.Error) { 414 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 415 continue 416 } 417 if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.Result) { 418 t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.Result) 419 } 420 } 421 } 422 423 var selectTests = []struct { 424 Name string 425 Query parser.SelectQuery 426 Result *View 427 SetVariables map[parser.Variable]value.Primary 428 Error string 429 }{ 430 { 431 Name: "Select", 432 Query: parser.SelectQuery{ 433 SelectEntity: parser.SelectEntity{ 434 SelectClause: parser.SelectClause{ 435 Fields: []parser.QueryExpression{ 436 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 437 parser.Field{Object: parser.AggregateFunction{Name: "count", Args: []parser.QueryExpression{parser.AllColumns{}}}}, 438 }, 439 }, 440 FromClause: parser.FromClause{ 441 Tables: []parser.QueryExpression{ 442 parser.Table{Object: parser.Identifier{Literal: "group_table"}}, 443 }, 444 }, 445 WhereClause: parser.WhereClause{ 446 Filter: parser.Comparison{ 447 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 448 RHS: parser.NewIntegerValueFromString("3"), 449 Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: "<"}, 450 }, 451 }, 452 GroupByClause: parser.GroupByClause{ 453 Items: []parser.QueryExpression{ 454 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 455 }, 456 }, 457 HavingClause: parser.HavingClause{ 458 Filter: parser.Comparison{ 459 LHS: parser.AggregateFunction{Name: "count", Args: []parser.QueryExpression{parser.AllColumns{}}}, 460 RHS: parser.NewIntegerValueFromString("1"), 461 Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: ">"}, 462 }, 463 }, 464 }, 465 OrderByClause: parser.OrderByClause{ 466 Items: []parser.QueryExpression{ 467 parser.OrderItem{Value: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 468 }, 469 }, 470 LimitClause: parser.LimitClause{ 471 Value: parser.NewIntegerValueFromString("5"), 472 OffsetClause: parser.OffsetClause{ 473 Value: parser.NewIntegerValue(0), 474 }, 475 }, 476 }, 477 Result: &View{ 478 FileInfo: &FileInfo{ 479 Path: GetTestFilePath("group_table.csv"), 480 Delimiter: ',', 481 NoHeader: false, 482 Encoding: text.UTF8, 483 LineBreak: text.LF, 484 }, 485 Header: []HeaderField{ 486 { 487 View: "group_table", 488 Column: "column1", 489 Number: 1, 490 IsFromTable: true, 491 }, 492 { 493 Column: "COUNT(*)", 494 Number: 2, 495 IsFromTable: true, 496 }, 497 }, 498 RecordSet: []Record{ 499 NewRecord([]value.Primary{ 500 value.NewString("1"), 501 value.NewInteger(2), 502 }), 503 NewRecord([]value.Primary{ 504 value.NewString("2"), 505 value.NewInteger(2), 506 }), 507 }, 508 }, 509 }, 510 { 511 Name: "Select Replace Fields", 512 Query: parser.SelectQuery{ 513 SelectEntity: parser.SelectEntity{ 514 SelectClause: parser.SelectClause{ 515 Fields: []parser.QueryExpression{ 516 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 517 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 518 }, 519 }, 520 FromClause: parser.FromClause{ 521 Tables: []parser.QueryExpression{ 522 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 523 }, 524 }, 525 }, 526 LimitClause: parser.LimitClause{ 527 Type: parser.Token{Token: parser.FETCH}, 528 Value: parser.NewIntegerValueFromString("1"), 529 }, 530 }, 531 Result: &View{ 532 FileInfo: &FileInfo{ 533 Path: GetTestFilePath("table1.csv"), 534 Delimiter: ',', 535 NoHeader: false, 536 Encoding: text.UTF8, 537 LineBreak: text.LF, 538 }, 539 Header: NewHeader("table1", []string{"column2", "column1"}), 540 RecordSet: []Record{ 541 NewRecord([]value.Primary{ 542 value.NewString("str1"), 543 value.NewString("1"), 544 }), 545 }, 546 }, 547 }, 548 { 549 Name: "Union", 550 Query: parser.SelectQuery{ 551 SelectEntity: parser.SelectSet{ 552 LHS: parser.SelectEntity{ 553 SelectClause: parser.SelectClause{ 554 Fields: []parser.QueryExpression{ 555 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 556 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 557 }, 558 }, 559 FromClause: parser.FromClause{ 560 Tables: []parser.QueryExpression{ 561 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 562 }, 563 }, 564 }, 565 Operator: parser.Token{Token: parser.UNION, Literal: "union"}, 566 RHS: parser.SelectEntity{ 567 SelectClause: parser.SelectClause{ 568 Fields: []parser.QueryExpression{ 569 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}}, 570 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}}, 571 }, 572 }, 573 FromClause: parser.FromClause{ 574 Tables: []parser.QueryExpression{ 575 parser.Table{Object: parser.Identifier{Literal: "table4"}}, 576 }, 577 }, 578 }, 579 }, 580 }, 581 Result: &View{ 582 Header: NewHeader("table1", []string{"column1", "column2"}), 583 RecordSet: []Record{ 584 NewRecord([]value.Primary{ 585 value.NewString("1"), 586 value.NewString("str1"), 587 }), 588 NewRecord([]value.Primary{ 589 value.NewString("2"), 590 value.NewString("str2"), 591 }), 592 NewRecord([]value.Primary{ 593 value.NewString("3"), 594 value.NewString("str3"), 595 }), 596 NewRecord([]value.Primary{ 597 value.NewString("4"), 598 value.NewString("str4"), 599 }), 600 }, 601 }, 602 }, 603 { 604 Name: "Intersect", 605 Query: parser.SelectQuery{ 606 SelectEntity: parser.SelectSet{ 607 LHS: parser.SelectEntity{ 608 SelectClause: parser.SelectClause{ 609 Fields: []parser.QueryExpression{ 610 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 611 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 612 }, 613 }, 614 FromClause: parser.FromClause{ 615 Tables: []parser.QueryExpression{ 616 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 617 }, 618 }, 619 }, 620 Operator: parser.Token{Token: parser.INTERSECT, Literal: "intersect"}, 621 RHS: parser.SelectEntity{ 622 SelectClause: parser.SelectClause{ 623 Fields: []parser.QueryExpression{ 624 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}}, 625 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}}, 626 }, 627 }, 628 FromClause: parser.FromClause{ 629 Tables: []parser.QueryExpression{ 630 parser.Table{Object: parser.Identifier{Literal: "table4"}}, 631 }, 632 }, 633 }, 634 }, 635 }, 636 Result: &View{ 637 Header: NewHeader("table1", []string{"column1", "column2"}), 638 RecordSet: []Record{ 639 NewRecord([]value.Primary{ 640 value.NewString("2"), 641 value.NewString("str2"), 642 }), 643 NewRecord([]value.Primary{ 644 value.NewString("3"), 645 value.NewString("str3"), 646 }), 647 }, 648 }, 649 }, 650 { 651 Name: "Except", 652 Query: parser.SelectQuery{ 653 SelectEntity: parser.SelectSet{ 654 LHS: parser.SelectEntity{ 655 SelectClause: parser.SelectClause{ 656 Fields: []parser.QueryExpression{ 657 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 658 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 659 }, 660 }, 661 FromClause: parser.FromClause{ 662 Tables: []parser.QueryExpression{ 663 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 664 }, 665 }, 666 }, 667 Operator: parser.Token{Token: parser.EXCEPT, Literal: "except"}, 668 RHS: parser.SelectEntity{ 669 SelectClause: parser.SelectClause{ 670 Fields: []parser.QueryExpression{ 671 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}}, 672 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}}, 673 }, 674 }, 675 FromClause: parser.FromClause{ 676 Tables: []parser.QueryExpression{ 677 parser.Table{Object: parser.Identifier{Literal: "table4"}}, 678 }, 679 }, 680 }, 681 }, 682 }, 683 Result: &View{ 684 Header: NewHeader("table1", []string{"column1", "column2"}), 685 RecordSet: []Record{ 686 NewRecord([]value.Primary{ 687 value.NewString("1"), 688 value.NewString("str1"), 689 }), 690 }, 691 }, 692 }, 693 { 694 Name: "Separate scopes on both sides of a set operator", 695 Query: parser.SelectQuery{ 696 SelectEntity: parser.SelectSet{ 697 LHS: parser.SelectEntity{ 698 SelectClause: parser.SelectClause{ 699 Fields: []parser.QueryExpression{ 700 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 701 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 702 }, 703 }, 704 FromClause: parser.FromClause{ 705 Tables: []parser.QueryExpression{ 706 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 707 }, 708 }, 709 }, 710 Operator: parser.Token{Token: parser.UNION, Literal: "union"}, 711 All: parser.Token{Token: parser.ALL, Literal: "all"}, 712 RHS: parser.SelectEntity{ 713 SelectClause: parser.SelectClause{ 714 Fields: []parser.QueryExpression{ 715 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 716 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 717 }, 718 }, 719 FromClause: parser.FromClause{ 720 Tables: []parser.QueryExpression{ 721 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 722 }, 723 }, 724 }, 725 }, 726 }, 727 Result: &View{ 728 Header: NewHeader("table1", []string{"column1", "column2"}), 729 RecordSet: []Record{ 730 NewRecord([]value.Primary{ 731 value.NewString("1"), 732 value.NewString("str1"), 733 }), 734 NewRecord([]value.Primary{ 735 value.NewString("2"), 736 value.NewString("str2"), 737 }), 738 NewRecord([]value.Primary{ 739 value.NewString("3"), 740 value.NewString("str3"), 741 }), 742 NewRecord([]value.Primary{ 743 value.NewString("1"), 744 value.NewString("str1"), 745 }), 746 NewRecord([]value.Primary{ 747 value.NewString("2"), 748 value.NewString("str2"), 749 }), 750 NewRecord([]value.Primary{ 751 value.NewString("3"), 752 value.NewString("str3"), 753 }), 754 }, 755 }, 756 }, 757 { 758 Name: "Union with SubQuery", 759 Query: parser.SelectQuery{ 760 SelectEntity: parser.SelectSet{ 761 LHS: parser.Subquery{ 762 Query: parser.SelectQuery{ 763 SelectEntity: parser.SelectEntity{ 764 SelectClause: parser.SelectClause{ 765 Fields: []parser.QueryExpression{ 766 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 767 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 768 }, 769 }, 770 FromClause: parser.FromClause{ 771 Tables: []parser.QueryExpression{ 772 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 773 }, 774 }, 775 }, 776 }, 777 }, 778 Operator: parser.Token{Token: parser.UNION, Literal: "union"}, 779 RHS: parser.SelectEntity{ 780 SelectClause: parser.SelectClause{ 781 Fields: []parser.QueryExpression{ 782 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}}, 783 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}}, 784 }, 785 }, 786 FromClause: parser.FromClause{ 787 Tables: []parser.QueryExpression{ 788 parser.Table{Object: parser.Identifier{Literal: "table4"}}, 789 }, 790 }, 791 }, 792 }, 793 }, 794 Result: &View{ 795 Header: NewHeader("table1", []string{"column1", "column2"}), 796 RecordSet: []Record{ 797 NewRecord([]value.Primary{ 798 value.NewString("1"), 799 value.NewString("str1"), 800 }), 801 NewRecord([]value.Primary{ 802 value.NewString("2"), 803 value.NewString("str2"), 804 }), 805 NewRecord([]value.Primary{ 806 value.NewString("3"), 807 value.NewString("str3"), 808 }), 809 NewRecord([]value.Primary{ 810 value.NewString("4"), 811 value.NewString("str4"), 812 }), 813 }, 814 }, 815 }, 816 { 817 Name: "Union Field Length Error", 818 Query: parser.SelectQuery{ 819 SelectEntity: parser.SelectSet{ 820 LHS: parser.SelectEntity{ 821 SelectClause: parser.SelectClause{ 822 Fields: []parser.QueryExpression{ 823 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 824 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 825 }, 826 }, 827 FromClause: parser.FromClause{ 828 Tables: []parser.QueryExpression{ 829 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 830 }, 831 }, 832 }, 833 Operator: parser.Token{Token: parser.UNION, Literal: "union"}, 834 RHS: parser.SelectEntity{ 835 SelectClause: parser.SelectClause{ 836 Fields: []parser.QueryExpression{ 837 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}}, 838 }, 839 }, 840 FromClause: parser.FromClause{ 841 Tables: []parser.QueryExpression{ 842 parser.Table{Object: parser.Identifier{Literal: "table4"}}, 843 }, 844 }, 845 }, 846 }, 847 }, 848 Error: "result set to be combined should contain exactly 2 fields", 849 }, 850 { 851 Name: "Union LHS Error", 852 Query: parser.SelectQuery{ 853 SelectEntity: parser.SelectSet{ 854 LHS: parser.SelectEntity{ 855 SelectClause: parser.SelectClause{ 856 Fields: []parser.QueryExpression{ 857 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 858 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}}, 859 }, 860 }, 861 FromClause: parser.FromClause{ 862 Tables: []parser.QueryExpression{ 863 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 864 }, 865 }, 866 }, 867 Operator: parser.Token{Token: parser.UNION, Literal: "union"}, 868 RHS: parser.SelectEntity{ 869 SelectClause: parser.SelectClause{ 870 Fields: []parser.QueryExpression{ 871 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}}, 872 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}}, 873 }, 874 }, 875 FromClause: parser.FromClause{ 876 Tables: []parser.QueryExpression{ 877 parser.Table{Object: parser.Identifier{Literal: "table4"}}, 878 }, 879 }, 880 }, 881 }, 882 }, 883 Error: "field notexist does not exist", 884 }, 885 { 886 Name: "Union RHS Error", 887 Query: parser.SelectQuery{ 888 SelectEntity: parser.SelectSet{ 889 LHS: parser.SelectEntity{ 890 SelectClause: parser.SelectClause{ 891 Fields: []parser.QueryExpression{ 892 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 893 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 894 }, 895 }, 896 FromClause: parser.FromClause{ 897 Tables: []parser.QueryExpression{ 898 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 899 }, 900 }, 901 }, 902 Operator: parser.Token{Token: parser.UNION, Literal: "union"}, 903 RHS: parser.SelectEntity{ 904 SelectClause: parser.SelectClause{ 905 Fields: []parser.QueryExpression{ 906 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}}, 907 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}}, 908 }, 909 }, 910 FromClause: parser.FromClause{ 911 Tables: []parser.QueryExpression{ 912 parser.Table{Object: parser.Identifier{Literal: "table4"}}, 913 }, 914 }, 915 }, 916 }, 917 }, 918 Error: "field notexist does not exist", 919 }, 920 { 921 Name: "Inline Tables", 922 Query: parser.SelectQuery{ 923 WithClause: parser.WithClause{ 924 InlineTables: []parser.QueryExpression{ 925 parser.InlineTable{ 926 Name: parser.Identifier{Literal: "it"}, 927 Fields: []parser.QueryExpression{ 928 parser.Identifier{Literal: "c1"}, 929 }, 930 Query: parser.SelectQuery{ 931 SelectEntity: parser.SelectEntity{ 932 SelectClause: parser.SelectClause{ 933 Fields: []parser.QueryExpression{ 934 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 935 }, 936 }, 937 }, 938 }, 939 }, 940 }, 941 }, 942 SelectEntity: parser.SelectEntity{ 943 SelectClause: parser.SelectClause{ 944 Fields: []parser.QueryExpression{ 945 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}}, 946 }, 947 }, 948 FromClause: parser.FromClause{ 949 Tables: []parser.QueryExpression{ 950 parser.Table{Object: parser.Identifier{Literal: "it"}}, 951 }, 952 }, 953 }, 954 }, 955 Result: &View{ 956 Header: NewHeader("it", []string{"c1"}), 957 RecordSet: []Record{ 958 NewRecord([]value.Primary{ 959 value.NewInteger(2), 960 }), 961 }, 962 }, 963 }, 964 { 965 Name: "Inline Tables Field Length Error", 966 Query: parser.SelectQuery{ 967 WithClause: parser.WithClause{ 968 InlineTables: []parser.QueryExpression{ 969 parser.InlineTable{ 970 Name: parser.Identifier{Literal: "it"}, 971 Fields: []parser.QueryExpression{ 972 parser.Identifier{Literal: "c1"}, 973 }, 974 Query: parser.SelectQuery{ 975 SelectEntity: parser.SelectSet{ 976 LHS: parser.SelectEntity{ 977 SelectClause: parser.SelectClause{ 978 Fields: []parser.QueryExpression{ 979 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 980 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 981 }, 982 }, 983 FromClause: parser.FromClause{ 984 Tables: []parser.QueryExpression{ 985 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 986 }, 987 }, 988 }, 989 Operator: parser.Token{Token: parser.UNION, Literal: "union"}, 990 RHS: parser.SelectEntity{ 991 SelectClause: parser.SelectClause{ 992 Fields: []parser.QueryExpression{ 993 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}}, 994 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}}, 995 }, 996 }, 997 FromClause: parser.FromClause{ 998 Tables: []parser.QueryExpression{ 999 parser.Table{Object: parser.Identifier{Literal: "table4"}}, 1000 }, 1001 }, 1002 }, 1003 }, 1004 }, 1005 }, 1006 }, 1007 }, 1008 SelectEntity: parser.SelectEntity{ 1009 SelectClause: parser.SelectClause{ 1010 Fields: []parser.QueryExpression{ 1011 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}}, 1012 }, 1013 }, 1014 FromClause: parser.FromClause{ 1015 Tables: []parser.QueryExpression{ 1016 parser.Table{Object: parser.Identifier{Literal: "it"}}, 1017 }, 1018 }, 1019 }, 1020 }, 1021 Error: "select query should return exactly 1 field for inline table it", 1022 }, 1023 { 1024 Name: "Inline Tables Recursion", 1025 Query: parser.SelectQuery{ 1026 WithClause: parser.WithClause{ 1027 InlineTables: []parser.QueryExpression{ 1028 parser.InlineTable{ 1029 Recursive: parser.Token{Token: parser.RECURSIVE, Literal: "recursive"}, 1030 Name: parser.Identifier{Literal: "it"}, 1031 Fields: []parser.QueryExpression{ 1032 parser.Identifier{Literal: "n"}, 1033 }, 1034 Query: parser.SelectQuery{ 1035 SelectEntity: parser.SelectSet{ 1036 LHS: parser.SelectEntity{ 1037 SelectClause: parser.SelectClause{ 1038 Fields: []parser.QueryExpression{ 1039 parser.Field{Object: parser.NewIntegerValueFromString("1")}, 1040 }, 1041 }, 1042 }, 1043 Operator: parser.Token{Token: parser.UNION, Literal: "union"}, 1044 RHS: parser.SelectEntity{ 1045 SelectClause: parser.SelectClause{ 1046 Fields: []parser.QueryExpression{ 1047 parser.Field{ 1048 Object: parser.Arithmetic{ 1049 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "n"}}, 1050 RHS: parser.NewIntegerValueFromString("1"), 1051 Operator: parser.Token{Token: '+', Literal: "+"}, 1052 }, 1053 }, 1054 }, 1055 }, 1056 FromClause: parser.FromClause{ 1057 Tables: []parser.QueryExpression{ 1058 parser.Table{Object: parser.Identifier{Literal: "it"}}, 1059 }, 1060 }, 1061 WhereClause: parser.WhereClause{ 1062 Filter: parser.Comparison{ 1063 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "n"}}, 1064 RHS: parser.NewIntegerValueFromString("3"), 1065 Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: "<"}, 1066 }, 1067 }, 1068 }, 1069 }, 1070 }, 1071 }, 1072 }, 1073 }, 1074 SelectEntity: parser.SelectEntity{ 1075 SelectClause: parser.SelectClause{ 1076 Fields: []parser.QueryExpression{ 1077 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "n"}}}, 1078 }, 1079 }, 1080 FromClause: parser.FromClause{ 1081 Tables: []parser.QueryExpression{ 1082 parser.Table{Object: parser.Identifier{Literal: "it"}}, 1083 }, 1084 }, 1085 }, 1086 }, 1087 Result: &View{ 1088 Header: []HeaderField{ 1089 { 1090 View: "it", 1091 Column: "n", 1092 Number: 1, 1093 IsFromTable: true, 1094 }, 1095 }, 1096 RecordSet: []Record{ 1097 NewRecord([]value.Primary{ 1098 value.NewInteger(1), 1099 }), 1100 NewRecord([]value.Primary{ 1101 value.NewInteger(2), 1102 }), 1103 NewRecord([]value.Primary{ 1104 value.NewInteger(3), 1105 }), 1106 }, 1107 }, 1108 }, 1109 { 1110 Name: "Inline Tables Recursion Field Length Error", 1111 Query: parser.SelectQuery{ 1112 WithClause: parser.WithClause{ 1113 InlineTables: []parser.QueryExpression{ 1114 parser.InlineTable{ 1115 Recursive: parser.Token{Token: parser.RECURSIVE, Literal: "recursive"}, 1116 Name: parser.Identifier{Literal: "it"}, 1117 Fields: []parser.QueryExpression{ 1118 parser.Identifier{Literal: "n"}, 1119 }, 1120 Query: parser.SelectQuery{ 1121 SelectEntity: parser.SelectSet{ 1122 LHS: parser.SelectEntity{ 1123 SelectClause: parser.SelectClause{ 1124 Fields: []parser.QueryExpression{ 1125 parser.Field{Object: parser.NewIntegerValueFromString("1")}, 1126 }, 1127 }, 1128 }, 1129 Operator: parser.Token{Token: parser.UNION, Literal: "union"}, 1130 RHS: parser.SelectEntity{ 1131 SelectClause: parser.SelectClause{ 1132 Fields: []parser.QueryExpression{ 1133 parser.Field{ 1134 Object: parser.Arithmetic{ 1135 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "n"}}, 1136 RHS: parser.NewIntegerValueFromString("1"), 1137 Operator: parser.Token{Token: '+', Literal: "+"}, 1138 }, 1139 }, 1140 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 1141 }, 1142 }, 1143 FromClause: parser.FromClause{ 1144 Tables: []parser.QueryExpression{ 1145 parser.Table{Object: parser.Identifier{Literal: "it"}}, 1146 }, 1147 }, 1148 WhereClause: parser.WhereClause{ 1149 Filter: parser.Comparison{ 1150 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "n"}}, 1151 RHS: parser.NewIntegerValueFromString("3"), 1152 Operator: parser.Token{Token: '<', Literal: "<"}, 1153 }, 1154 }, 1155 }, 1156 }, 1157 }, 1158 }, 1159 }, 1160 }, 1161 SelectEntity: parser.SelectEntity{ 1162 SelectClause: parser.SelectClause{ 1163 Fields: []parser.QueryExpression{ 1164 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "n"}}}, 1165 }, 1166 }, 1167 FromClause: parser.FromClause{ 1168 Tables: []parser.QueryExpression{ 1169 parser.Table{Object: parser.Identifier{Literal: "it"}}, 1170 }, 1171 }, 1172 }, 1173 }, 1174 Error: "result set to be combined should contain exactly 1 field", 1175 }, 1176 { 1177 Name: "Inline Tables Recursion Recursion Limit Exceeded Error", 1178 Query: parser.SelectQuery{ 1179 WithClause: parser.WithClause{ 1180 InlineTables: []parser.QueryExpression{ 1181 parser.InlineTable{ 1182 Recursive: parser.Token{Token: parser.RECURSIVE, Literal: "recursive"}, 1183 Name: parser.Identifier{Literal: "it"}, 1184 Fields: []parser.QueryExpression{ 1185 parser.Identifier{Literal: "n"}, 1186 }, 1187 Query: parser.SelectQuery{ 1188 SelectEntity: parser.SelectSet{ 1189 LHS: parser.SelectEntity{ 1190 SelectClause: parser.SelectClause{ 1191 Fields: []parser.QueryExpression{ 1192 parser.Field{Object: parser.NewIntegerValueFromString("1")}, 1193 }, 1194 }, 1195 }, 1196 Operator: parser.Token{Token: parser.UNION, Literal: "union"}, 1197 RHS: parser.SelectEntity{ 1198 SelectClause: parser.SelectClause{ 1199 Fields: []parser.QueryExpression{ 1200 parser.Field{ 1201 Object: parser.Arithmetic{ 1202 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "n"}}, 1203 RHS: parser.NewIntegerValueFromString("1"), 1204 Operator: parser.Token{Token: '+', Literal: "+"}, 1205 }, 1206 }, 1207 }, 1208 }, 1209 FromClause: parser.FromClause{ 1210 Tables: []parser.QueryExpression{ 1211 parser.Table{Object: parser.Identifier{Literal: "it"}}, 1212 }, 1213 }, 1214 WhereClause: parser.WhereClause{ 1215 Filter: parser.Comparison{ 1216 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "n"}}, 1217 RHS: parser.NewIntegerValueFromString("10"), 1218 Operator: parser.Token{Token: '<', Literal: "<"}, 1219 }, 1220 }, 1221 }, 1222 }, 1223 }, 1224 }, 1225 }, 1226 }, 1227 SelectEntity: parser.SelectEntity{ 1228 SelectClause: parser.SelectClause{ 1229 Fields: []parser.QueryExpression{ 1230 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "n"}}}, 1231 }, 1232 }, 1233 FromClause: parser.FromClause{ 1234 Tables: []parser.QueryExpression{ 1235 parser.Table{Object: parser.Identifier{Literal: "it"}}, 1236 }, 1237 }, 1238 }, 1239 }, 1240 Error: "iteration of recursive query exceeded the limit 5", 1241 }, 1242 { 1243 Name: "Select Into Variables", 1244 Query: parser.SelectQuery{ 1245 SelectEntity: parser.SelectEntity{ 1246 SelectClause: parser.SelectClause{ 1247 Fields: []parser.QueryExpression{ 1248 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 1249 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 1250 }, 1251 }, 1252 IntoClause: parser.IntoClause{ 1253 Variables: []parser.Variable{ 1254 {Name: "var1"}, 1255 {Name: "var2"}, 1256 }, 1257 }, 1258 FromClause: parser.FromClause{ 1259 Tables: []parser.QueryExpression{ 1260 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 1261 }, 1262 }, 1263 }, 1264 LimitClause: parser.LimitClause{ 1265 Type: parser.Token{Token: parser.LIMIT}, 1266 Value: parser.NewIntegerValueFromString("1"), 1267 }, 1268 }, 1269 SetVariables: map[parser.Variable]value.Primary{ 1270 parser.Variable{Name: "var1"}: value.NewString("1"), 1271 parser.Variable{Name: "var2"}: value.NewString("str1"), 1272 }, 1273 }, 1274 { 1275 Name: "Select Into Variables Empty Result Set", 1276 Query: parser.SelectQuery{ 1277 SelectEntity: parser.SelectEntity{ 1278 SelectClause: parser.SelectClause{ 1279 Fields: []parser.QueryExpression{ 1280 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 1281 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 1282 }, 1283 }, 1284 IntoClause: parser.IntoClause{ 1285 Variables: []parser.Variable{ 1286 {Name: "var1"}, 1287 {Name: "var2"}, 1288 }, 1289 }, 1290 FromClause: parser.FromClause{ 1291 Tables: []parser.QueryExpression{ 1292 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 1293 }, 1294 }, 1295 WhereClause: parser.WhereClause{ 1296 Filter: parser.NewTernaryValueFromString("false"), 1297 }, 1298 }, 1299 LimitClause: parser.LimitClause{ 1300 Value: parser.NewIntegerValueFromString("1"), 1301 }, 1302 }, 1303 SetVariables: map[parser.Variable]value.Primary{ 1304 parser.Variable{Name: "var1"}: value.NewNull(), 1305 parser.Variable{Name: "var2"}: value.NewNull(), 1306 }, 1307 }, 1308 { 1309 Name: "Select Into Variables Too Many Records", 1310 Query: parser.SelectQuery{ 1311 SelectEntity: parser.SelectEntity{ 1312 SelectClause: parser.SelectClause{ 1313 Fields: []parser.QueryExpression{ 1314 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 1315 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 1316 }, 1317 }, 1318 IntoClause: parser.IntoClause{ 1319 Variables: []parser.Variable{ 1320 {Name: "var1"}, 1321 {Name: "var2"}, 1322 }, 1323 }, 1324 FromClause: parser.FromClause{ 1325 Tables: []parser.QueryExpression{ 1326 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 1327 }, 1328 }, 1329 }, 1330 }, 1331 Error: "select into query returns too many records, should return only one record", 1332 }, 1333 { 1334 Name: "Select Into Variables Field Length Not Match", 1335 Query: parser.SelectQuery{ 1336 SelectEntity: parser.SelectEntity{ 1337 SelectClause: parser.SelectClause{ 1338 Fields: []parser.QueryExpression{ 1339 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 1340 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 1341 }, 1342 }, 1343 IntoClause: parser.IntoClause{ 1344 Variables: []parser.Variable{ 1345 {Name: "var1"}, 1346 }, 1347 }, 1348 FromClause: parser.FromClause{ 1349 Tables: []parser.QueryExpression{ 1350 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 1351 }, 1352 }, 1353 }, 1354 LimitClause: parser.LimitClause{ 1355 Value: parser.NewIntegerValueFromString("1"), 1356 }, 1357 }, 1358 Error: "select into query should return exactly 1 field", 1359 }, 1360 { 1361 Name: "Select Into Variables Undeclared Variable", 1362 Query: parser.SelectQuery{ 1363 SelectEntity: parser.SelectEntity{ 1364 SelectClause: parser.SelectClause{ 1365 Fields: []parser.QueryExpression{ 1366 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 1367 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 1368 }, 1369 }, 1370 IntoClause: parser.IntoClause{ 1371 Variables: []parser.Variable{ 1372 {Name: "var1"}, 1373 {Name: "undeclared"}, 1374 }, 1375 }, 1376 FromClause: parser.FromClause{ 1377 Tables: []parser.QueryExpression{ 1378 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 1379 }, 1380 }, 1381 }, 1382 LimitClause: parser.LimitClause{ 1383 Value: parser.NewIntegerValueFromString("1"), 1384 }, 1385 }, 1386 Error: "variable @undeclared is undeclared", 1387 }, 1388 } 1389 1390 func TestSelect(t *testing.T) { 1391 defer func() { 1392 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 1393 initFlag(TestTx.Flags) 1394 }() 1395 1396 TestTx.Flags.Repository = TestDir 1397 1398 scope := NewReferenceScope(TestTx) 1399 ctx := context.Background() 1400 _ = scope.DeclareVariable(ctx, parser.VariableDeclaration{Assignments: []parser.VariableAssignment{ 1401 {Variable: parser.Variable{Name: "var1"}}, 1402 {Variable: parser.Variable{Name: "var2"}, Value: parser.NewIntegerValueFromString("2")}, 1403 }}) 1404 1405 for _, v := range selectTests { 1406 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 1407 result, err := Select(ctx, scope, v.Query) 1408 if err != nil { 1409 if len(v.Error) < 1 { 1410 t.Errorf("%s: unexpected error %q", v.Name, err) 1411 } else if err.Error() != v.Error { 1412 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 1413 } 1414 continue 1415 } 1416 if 0 < len(v.Error) { 1417 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 1418 continue 1419 } 1420 if v.Result != nil { 1421 if !reflect.DeepEqual(result, v.Result) { 1422 t.Errorf("%s: result = %v, want %v", v.Name, result, v.Result) 1423 } 1424 } 1425 if 0 < len(v.SetVariables) { 1426 for variable, expectValue := range v.SetVariables { 1427 val, _ := scope.GetVariable(variable) 1428 if !reflect.DeepEqual(val, expectValue) { 1429 t.Errorf("%s: variable %s = %v, want %v", v.Name, variable, val, expectValue) 1430 } 1431 } 1432 } 1433 } 1434 } 1435 1436 var insertTests = []struct { 1437 Name string 1438 Query parser.InsertQuery 1439 ResultFile *FileInfo 1440 UpdateCount int 1441 ViewCache ViewMap 1442 ResultScopes *ReferenceScope 1443 Error string 1444 }{ 1445 { 1446 Name: "Insert Query", 1447 Query: parser.InsertQuery{ 1448 WithClause: parser.WithClause{ 1449 InlineTables: []parser.QueryExpression{ 1450 parser.InlineTable{ 1451 Name: parser.Identifier{Literal: "it"}, 1452 Fields: []parser.QueryExpression{ 1453 parser.Identifier{Literal: "c1"}, 1454 }, 1455 Query: parser.SelectQuery{ 1456 SelectEntity: parser.SelectEntity{ 1457 SelectClause: parser.SelectClause{ 1458 Fields: []parser.QueryExpression{ 1459 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 1460 }, 1461 }, 1462 }, 1463 }, 1464 }, 1465 }, 1466 }, 1467 Table: parser.Table{Object: parser.Identifier{Literal: "table1"}}, 1468 Fields: []parser.QueryExpression{ 1469 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 1470 }, 1471 ValuesList: []parser.QueryExpression{ 1472 parser.RowValue{ 1473 Value: parser.ValueList{ 1474 Values: []parser.QueryExpression{ 1475 parser.NewIntegerValueFromString("4"), 1476 }, 1477 }, 1478 }, 1479 parser.RowValue{ 1480 Value: parser.ValueList{ 1481 Values: []parser.QueryExpression{ 1482 parser.Subquery{ 1483 Query: parser.SelectQuery{ 1484 SelectEntity: parser.SelectEntity{ 1485 SelectClause: parser.SelectClause{ 1486 Fields: []parser.QueryExpression{ 1487 parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c1"}}}, 1488 }, 1489 }, 1490 FromClause: parser.FromClause{ 1491 Tables: []parser.QueryExpression{ 1492 parser.Table{Object: parser.Identifier{Literal: "it"}}, 1493 }, 1494 }, 1495 }, 1496 }, 1497 }, 1498 }, 1499 }, 1500 }, 1501 }, 1502 }, 1503 ResultFile: &FileInfo{ 1504 Path: GetTestFilePath("table1.csv"), 1505 Delimiter: ',', 1506 NoHeader: false, 1507 Encoding: text.UTF8, 1508 LineBreak: text.LF, 1509 ForUpdate: true, 1510 }, 1511 UpdateCount: 2, 1512 ViewCache: GenerateViewMap([]*View{ 1513 { 1514 FileInfo: &FileInfo{ 1515 Path: GetTestFilePath("table1.csv"), 1516 Delimiter: ',', 1517 NoHeader: false, 1518 Encoding: text.UTF8, 1519 LineBreak: text.LF, 1520 ForUpdate: true, 1521 }, 1522 Header: NewHeader("table1", []string{"column1", "column2"}), 1523 RecordSet: []Record{ 1524 NewRecord([]value.Primary{ 1525 value.NewString("1"), 1526 value.NewString("str1"), 1527 }), 1528 NewRecord([]value.Primary{ 1529 value.NewString("2"), 1530 value.NewString("str2"), 1531 }), 1532 NewRecord([]value.Primary{ 1533 value.NewString("3"), 1534 value.NewString("str3"), 1535 }), 1536 NewRecord([]value.Primary{ 1537 value.NewInteger(4), 1538 value.NewNull(), 1539 }), 1540 NewRecord([]value.Primary{ 1541 value.NewInteger(2), 1542 value.NewNull(), 1543 }), 1544 }, 1545 }, 1546 }), 1547 }, 1548 { 1549 Name: "Insert Query For Temporary View", 1550 Query: parser.InsertQuery{ 1551 Table: parser.Table{Object: parser.Identifier{Literal: "tmpview"}, Alias: parser.Identifier{Literal: "t"}}, 1552 Fields: []parser.QueryExpression{ 1553 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 1554 }, 1555 ValuesList: []parser.QueryExpression{ 1556 parser.RowValue{ 1557 Value: parser.ValueList{ 1558 Values: []parser.QueryExpression{ 1559 parser.NewIntegerValueFromString("4"), 1560 }, 1561 }, 1562 }, 1563 parser.RowValue{ 1564 Value: parser.ValueList{ 1565 Values: []parser.QueryExpression{ 1566 parser.NewIntegerValueFromString("2"), 1567 }, 1568 }, 1569 }, 1570 }, 1571 }, 1572 ResultFile: &FileInfo{ 1573 Path: "tmpview", 1574 Delimiter: ',', 1575 ViewType: ViewTypeTemporaryTable, 1576 }, 1577 UpdateCount: 2, 1578 ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{ 1579 { 1580 scopeNameTempTables: { 1581 "TMPVIEW": &View{ 1582 Header: NewHeader("tmpview", []string{"column1", "column2"}), 1583 RecordSet: []Record{ 1584 NewRecord([]value.Primary{ 1585 value.NewString("1"), 1586 value.NewString("str1"), 1587 }), 1588 NewRecord([]value.Primary{ 1589 value.NewString("2"), 1590 value.NewString("str2"), 1591 }), 1592 NewRecord([]value.Primary{ 1593 value.NewInteger(4), 1594 value.NewNull(), 1595 }), 1596 NewRecord([]value.Primary{ 1597 value.NewInteger(2), 1598 value.NewNull(), 1599 }), 1600 }, 1601 FileInfo: &FileInfo{ 1602 Path: "tmpview", 1603 Delimiter: ',', 1604 ViewType: ViewTypeTemporaryTable, 1605 }, 1606 }, 1607 }, 1608 }, 1609 }, nil, time.Time{}, nil), 1610 }, 1611 { 1612 Name: "Insert Query All Fields", 1613 Query: parser.InsertQuery{ 1614 Table: parser.Table{Object: parser.Identifier{Literal: "table1"}}, 1615 ValuesList: []parser.QueryExpression{ 1616 parser.RowValue{ 1617 Value: parser.ValueList{ 1618 Values: []parser.QueryExpression{ 1619 parser.NewIntegerValueFromString("4"), 1620 parser.NewStringValue("str4"), 1621 }, 1622 }, 1623 }, 1624 parser.RowValue{ 1625 Value: parser.ValueList{ 1626 Values: []parser.QueryExpression{ 1627 parser.NewIntegerValueFromString("5"), 1628 parser.NewStringValue("str5"), 1629 }, 1630 }, 1631 }, 1632 }, 1633 }, 1634 ResultFile: &FileInfo{ 1635 Path: GetTestFilePath("table1.csv"), 1636 Delimiter: ',', 1637 NoHeader: false, 1638 Encoding: text.UTF8, 1639 LineBreak: text.LF, 1640 ForUpdate: true, 1641 }, 1642 UpdateCount: 2, 1643 }, 1644 { 1645 Name: "Insert Query Inline Table Cannot Be Updated Error", 1646 Query: parser.InsertQuery{ 1647 WithClause: parser.WithClause{ 1648 InlineTables: []parser.QueryExpression{ 1649 parser.InlineTable{ 1650 Name: parser.Identifier{Literal: "it"}, 1651 Fields: []parser.QueryExpression{ 1652 parser.Identifier{Literal: "c1"}, 1653 }, 1654 Query: parser.SelectQuery{ 1655 SelectEntity: parser.SelectEntity{ 1656 SelectClause: parser.SelectClause{ 1657 Fields: []parser.QueryExpression{ 1658 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 1659 }, 1660 }, 1661 }, 1662 }, 1663 }, 1664 }, 1665 }, 1666 Table: parser.Table{Object: parser.Identifier{Literal: "it"}}, 1667 Fields: []parser.QueryExpression{ 1668 parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}, 1669 }, 1670 ValuesList: []parser.QueryExpression{ 1671 parser.RowValue{ 1672 Value: parser.ValueList{ 1673 Values: []parser.QueryExpression{ 1674 parser.NewIntegerValueFromString("4"), 1675 }, 1676 }, 1677 }, 1678 parser.RowValue{ 1679 Value: parser.ValueList{ 1680 Values: []parser.QueryExpression{ 1681 parser.Subquery{ 1682 Query: parser.SelectQuery{ 1683 SelectEntity: parser.SelectEntity{ 1684 SelectClause: parser.SelectClause{ 1685 Fields: []parser.QueryExpression{ 1686 parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c1"}}}, 1687 }, 1688 }, 1689 FromClause: parser.FromClause{ 1690 Tables: []parser.QueryExpression{ 1691 parser.Table{Object: parser.Identifier{Literal: "it"}}, 1692 }, 1693 }, 1694 }, 1695 }, 1696 }, 1697 }, 1698 }, 1699 }, 1700 }, 1701 }, 1702 Error: "inline table cannot be updated", 1703 }, 1704 { 1705 Name: "Insert Query File Does Not Exist Error", 1706 Query: parser.InsertQuery{ 1707 Table: parser.Table{Object: parser.Identifier{Literal: "notexist"}}, 1708 Fields: []parser.QueryExpression{ 1709 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 1710 }, 1711 ValuesList: []parser.QueryExpression{ 1712 parser.RowValue{ 1713 Value: parser.ValueList{ 1714 Values: []parser.QueryExpression{ 1715 parser.NewIntegerValueFromString("4"), 1716 }, 1717 }, 1718 }, 1719 parser.RowValue{ 1720 Value: parser.ValueList{ 1721 Values: []parser.QueryExpression{ 1722 parser.NewIntegerValueFromString("5"), 1723 }, 1724 }, 1725 }, 1726 }, 1727 }, 1728 Error: "file notexist does not exist", 1729 }, 1730 { 1731 Name: "Insert Query Field Does Not Exist Error", 1732 Query: parser.InsertQuery{ 1733 Table: parser.Table{Object: parser.Identifier{Literal: "table1"}}, 1734 Fields: []parser.QueryExpression{ 1735 parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 1736 }, 1737 ValuesList: []parser.QueryExpression{ 1738 parser.RowValue{ 1739 Value: parser.ValueList{ 1740 Values: []parser.QueryExpression{ 1741 parser.NewIntegerValueFromString("4"), 1742 }, 1743 }, 1744 }, 1745 parser.RowValue{ 1746 Value: parser.ValueList{ 1747 Values: []parser.QueryExpression{ 1748 parser.NewIntegerValueFromString("5"), 1749 }, 1750 }, 1751 }, 1752 }, 1753 }, 1754 Error: "field notexist does not exist", 1755 }, 1756 { 1757 Name: "Insert Select Query", 1758 Query: parser.InsertQuery{ 1759 Table: parser.Table{Object: parser.Identifier{Literal: "table1"}}, 1760 Fields: []parser.QueryExpression{ 1761 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 1762 parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 1763 }, 1764 Query: parser.SelectQuery{ 1765 SelectEntity: parser.SelectEntity{ 1766 SelectClause: parser.SelectClause{ 1767 Fields: []parser.QueryExpression{ 1768 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}}, 1769 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}}, 1770 }, 1771 }, 1772 FromClause: parser.FromClause{ 1773 Tables: []parser.QueryExpression{ 1774 parser.Table{Object: parser.Identifier{Literal: "table2"}}, 1775 }, 1776 }, 1777 }, 1778 }, 1779 }, 1780 ResultFile: &FileInfo{ 1781 Path: GetTestFilePath("table1.csv"), 1782 Delimiter: ',', 1783 NoHeader: false, 1784 Encoding: text.UTF8, 1785 LineBreak: text.LF, 1786 ForUpdate: true, 1787 }, 1788 UpdateCount: 3, 1789 }, 1790 { 1791 Name: "Insert Select Query Field Does Not Exist Error", 1792 Query: parser.InsertQuery{ 1793 Table: parser.Table{Object: parser.Identifier{Literal: "table1"}}, 1794 Fields: []parser.QueryExpression{ 1795 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 1796 }, 1797 Query: parser.SelectQuery{ 1798 SelectEntity: parser.SelectEntity{ 1799 SelectClause: parser.SelectClause{ 1800 Fields: []parser.QueryExpression{ 1801 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}}, 1802 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}}, 1803 }, 1804 }, 1805 FromClause: parser.FromClause{ 1806 Tables: []parser.QueryExpression{ 1807 parser.Table{Object: parser.Identifier{Literal: "table2"}}, 1808 }, 1809 }, 1810 }, 1811 }, 1812 }, 1813 Error: "select query should return exactly 1 field", 1814 }, 1815 } 1816 1817 func TestInsert(t *testing.T) { 1818 defer func() { 1819 _ = TestTx.ReleaseResources() 1820 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 1821 initFlag(TestTx.Flags) 1822 }() 1823 1824 TestTx.Flags.Repository = TestDir 1825 TestTx.Flags.Quiet = false 1826 1827 scope := GenerateReferenceScope([]map[string]map[string]interface{}{ 1828 { 1829 scopeNameTempTables: { 1830 "TMPVIEW": &View{ 1831 Header: NewHeader("tmpview", []string{"column1", "column2"}), 1832 RecordSet: []Record{ 1833 NewRecord([]value.Primary{ 1834 value.NewString("1"), 1835 value.NewString("str1"), 1836 }), 1837 NewRecord([]value.Primary{ 1838 value.NewString("2"), 1839 value.NewString("str2"), 1840 }), 1841 }, 1842 FileInfo: &FileInfo{ 1843 Path: "tmpview", 1844 Delimiter: ',', 1845 ViewType: ViewTypeTemporaryTable, 1846 }, 1847 }, 1848 }, 1849 }, 1850 }, nil, time.Time{}, nil) 1851 1852 ctx := context.Background() 1853 for _, v := range insertTests { 1854 _ = TestTx.ReleaseResources() 1855 result, cnt, err := Insert(ctx, scope, v.Query) 1856 if err != nil { 1857 if len(v.Error) < 1 { 1858 t.Errorf("%s: unexpected error %q", v.Name, err) 1859 } else if err.Error() != v.Error { 1860 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 1861 } 1862 continue 1863 } 1864 if 0 < len(v.Error) { 1865 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 1866 continue 1867 } 1868 1869 TestTx.CachedViews.Range(func(key, value interface{}) bool { 1870 view := value.(*View) 1871 if view.FileInfo.Handler != nil { 1872 if view.FileInfo.Path != view.FileInfo.Handler.Path() { 1873 t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name) 1874 } 1875 _ = TestTx.FileContainer.Close(view.FileInfo.Handler) 1876 view.FileInfo.Handler = nil 1877 } 1878 return true 1879 }) 1880 1881 if !reflect.DeepEqual(result, v.ResultFile) { 1882 t.Errorf("%s: fileinfo = %v, want %v", v.Name, result, v.ResultFile) 1883 } 1884 1885 if !reflect.DeepEqual(cnt, v.UpdateCount) { 1886 t.Errorf("%s: update count = %d, want %d", v.Name, cnt, v.UpdateCount) 1887 } 1888 1889 if v.ViewCache.SyncMap != nil { 1890 if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) { 1891 t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache) 1892 } 1893 } 1894 if v.ResultScopes != nil { 1895 if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) { 1896 t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) 1897 } 1898 } 1899 } 1900 } 1901 1902 var updateTests = []struct { 1903 Name string 1904 Query parser.UpdateQuery 1905 ResultFiles []*FileInfo 1906 UpdateCounts []int 1907 ViewCache ViewMap 1908 ResultScopes *ReferenceScope 1909 Error string 1910 }{ 1911 { 1912 Name: "Update Query", 1913 Query: parser.UpdateQuery{ 1914 WithClause: parser.WithClause{ 1915 InlineTables: []parser.QueryExpression{ 1916 parser.InlineTable{ 1917 Name: parser.Identifier{Literal: "it"}, 1918 Fields: []parser.QueryExpression{ 1919 parser.Identifier{Literal: "c1"}, 1920 }, 1921 Query: parser.SelectQuery{ 1922 SelectEntity: parser.SelectEntity{ 1923 SelectClause: parser.SelectClause{ 1924 Fields: []parser.QueryExpression{ 1925 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 1926 }, 1927 }, 1928 }, 1929 }, 1930 }, 1931 }, 1932 }, 1933 Tables: []parser.QueryExpression{ 1934 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 1935 }, 1936 SetList: []parser.UpdateSet{ 1937 { 1938 Field: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 1939 Value: parser.NewStringValue("update1"), 1940 }, 1941 { 1942 Field: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 1943 Value: parser.NewStringValue("update2"), 1944 }, 1945 }, 1946 WhereClause: parser.WhereClause{ 1947 Filter: parser.Comparison{ 1948 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 1949 RHS: parser.Subquery{ 1950 Query: parser.SelectQuery{ 1951 SelectEntity: parser.SelectEntity{ 1952 SelectClause: parser.SelectClause{ 1953 Fields: []parser.QueryExpression{ 1954 parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c1"}}}, 1955 }, 1956 }, 1957 FromClause: parser.FromClause{ 1958 Tables: []parser.QueryExpression{ 1959 parser.Table{Object: parser.Identifier{Literal: "it"}}, 1960 }, 1961 }, 1962 }, 1963 }, 1964 }, 1965 Operator: parser.Token{Token: '=', Literal: "="}, 1966 }, 1967 }, 1968 }, 1969 ResultFiles: []*FileInfo{ 1970 { 1971 Path: GetTestFilePath("table1.csv"), 1972 Delimiter: ',', 1973 NoHeader: false, 1974 Encoding: text.UTF8, 1975 LineBreak: text.LF, 1976 ForUpdate: true, 1977 }, 1978 }, 1979 UpdateCounts: []int{1}, 1980 ViewCache: GenerateViewMap([]*View{ 1981 { 1982 FileInfo: &FileInfo{ 1983 Path: GetTestFilePath("table1.csv"), 1984 Delimiter: ',', 1985 NoHeader: false, 1986 Encoding: text.UTF8, 1987 LineBreak: text.LF, 1988 ForUpdate: true, 1989 }, 1990 Header: NewHeader("table1", []string{"column1", "column2"}), 1991 RecordSet: []Record{ 1992 NewRecord([]value.Primary{ 1993 value.NewString("1"), 1994 value.NewString("str1"), 1995 }), 1996 NewRecord([]value.Primary{ 1997 value.NewString("update1"), 1998 value.NewString("update2"), 1999 }), 2000 NewRecord([]value.Primary{ 2001 value.NewString("3"), 2002 value.NewString("str3"), 2003 }), 2004 }, 2005 }, 2006 }), 2007 }, 2008 { 2009 Name: "Update Query For Temporary View", 2010 Query: parser.UpdateQuery{ 2011 Tables: []parser.QueryExpression{ 2012 parser.Table{Object: parser.Identifier{Literal: "tmpview"}, Alias: parser.Identifier{Literal: "t1"}}, 2013 }, 2014 SetList: []parser.UpdateSet{ 2015 { 2016 Field: parser.ColumnNumber{View: parser.Identifier{Literal: "t1"}, Number: value.NewInteger(2)}, 2017 Value: parser.NewStringValue("update"), 2018 }, 2019 }, 2020 }, 2021 ResultFiles: []*FileInfo{ 2022 { 2023 Path: "tmpview", 2024 Delimiter: ',', 2025 ViewType: ViewTypeTemporaryTable, 2026 }, 2027 }, 2028 UpdateCounts: []int{2}, 2029 ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{ 2030 { 2031 scopeNameTempTables: { 2032 "TMPVIEW": &View{ 2033 Header: NewHeader("tmpview", []string{"column1", "column2"}), 2034 RecordSet: []Record{ 2035 NewRecord([]value.Primary{ 2036 value.NewString("1"), 2037 value.NewString("update"), 2038 }), 2039 NewRecord([]value.Primary{ 2040 value.NewString("2"), 2041 value.NewString("update"), 2042 }), 2043 }, 2044 FileInfo: &FileInfo{ 2045 Path: "tmpview", 2046 Delimiter: ',', 2047 ViewType: ViewTypeTemporaryTable, 2048 }, 2049 }, 2050 }, 2051 }, 2052 }, nil, time.Time{}, nil), 2053 }, 2054 { 2055 Name: "Update Query Multiple Table", 2056 Query: parser.UpdateQuery{ 2057 Tables: []parser.QueryExpression{ 2058 parser.Table{Object: parser.Identifier{Literal: "t1"}}, 2059 }, 2060 SetList: []parser.UpdateSet{ 2061 { 2062 Field: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 2063 Value: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}, 2064 }, 2065 }, 2066 FromClause: parser.FromClause{ 2067 Tables: []parser.QueryExpression{ 2068 parser.Table{Object: parser.Join{ 2069 Table: parser.Table{ 2070 Object: parser.Identifier{Literal: "table1"}, 2071 Alias: parser.Identifier{Literal: "t1"}, 2072 }, 2073 JoinTable: parser.Table{ 2074 Object: parser.Identifier{Literal: "table2"}, 2075 Alias: parser.Identifier{Literal: "t2"}, 2076 }, 2077 Condition: parser.JoinCondition{ 2078 On: parser.Comparison{ 2079 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2080 RHS: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}, 2081 Operator: parser.Token{Token: '=', Literal: "="}, 2082 }, 2083 }, 2084 }}, 2085 }, 2086 }, 2087 }, 2088 ResultFiles: []*FileInfo{ 2089 { 2090 Path: GetTestFilePath("table1.csv"), 2091 Delimiter: ',', 2092 NoHeader: false, 2093 Encoding: text.UTF8, 2094 LineBreak: text.LF, 2095 ForUpdate: true, 2096 }, 2097 }, 2098 UpdateCounts: []int{2}, 2099 }, 2100 { 2101 Name: "Update Query Alias Must Be Specified Error", 2102 Query: parser.UpdateQuery{ 2103 Tables: []parser.QueryExpression{ 2104 parser.Table{ 2105 Object: parser.TableFunction{ 2106 Name: "data", 2107 Args: []parser.QueryExpression{ 2108 parser.NewStringValue("c1,c2\n1,a\n2,b"), 2109 }, 2110 }, 2111 }, 2112 }, 2113 SetList: []parser.UpdateSet{ 2114 { 2115 Field: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}, 2116 Value: parser.NewStringValue("update1"), 2117 }, 2118 { 2119 Field: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}, 2120 Value: parser.NewStringValue("update2"), 2121 }, 2122 }, 2123 }, 2124 Error: "alias to table identification function or URL must be specified for update", 2125 }, 2126 { 2127 Name: "Update Query Inline Table Cannot Be Updated Error", 2128 Query: parser.UpdateQuery{ 2129 Tables: []parser.QueryExpression{ 2130 parser.Table{ 2131 Object: parser.TableFunction{ 2132 Name: "data", 2133 Args: []parser.QueryExpression{ 2134 parser.NewStringValue("c1,c2\n1,a\n2,b"), 2135 }, 2136 }, 2137 Alias: parser.Identifier{ 2138 Literal: "t", 2139 }, 2140 }, 2141 }, 2142 SetList: []parser.UpdateSet{ 2143 { 2144 Field: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}, 2145 Value: parser.NewStringValue("update1"), 2146 }, 2147 { 2148 Field: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}, 2149 Value: parser.NewStringValue("update2"), 2150 }, 2151 }, 2152 }, 2153 Error: "inline table cannot be updated", 2154 }, 2155 { 2156 Name: "Update Query File Does Not Exist Error", 2157 Query: parser.UpdateQuery{ 2158 Tables: []parser.QueryExpression{ 2159 parser.Table{Object: parser.Identifier{Literal: "notexist"}}, 2160 }, 2161 SetList: []parser.UpdateSet{ 2162 { 2163 Field: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 2164 Value: parser.NewStringValue("update"), 2165 }, 2166 }, 2167 WhereClause: parser.WhereClause{ 2168 Filter: parser.Comparison{ 2169 LHS: parser.Identifier{Literal: "column1"}, 2170 RHS: parser.NewIntegerValueFromString("2"), 2171 Operator: parser.Token{Token: '=', Literal: "="}, 2172 }, 2173 }, 2174 }, 2175 Error: "file notexist does not exist", 2176 }, 2177 { 2178 Name: "Update Query Filter Error", 2179 Query: parser.UpdateQuery{ 2180 Tables: []parser.QueryExpression{ 2181 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 2182 }, 2183 SetList: []parser.UpdateSet{ 2184 { 2185 Field: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2186 Value: parser.NewStringValue("update"), 2187 }, 2188 }, 2189 WhereClause: parser.WhereClause{ 2190 Filter: parser.Comparison{ 2191 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 2192 RHS: parser.NewIntegerValueFromString("2"), 2193 Operator: parser.Token{Token: '=', Literal: "="}, 2194 }, 2195 }, 2196 }, 2197 Error: "field notexist does not exist", 2198 }, 2199 { 2200 Name: "Update Query File Is Not Loaded Error", 2201 Query: parser.UpdateQuery{ 2202 Tables: []parser.QueryExpression{ 2203 parser.Table{Object: parser.Identifier{Literal: "notexist"}}, 2204 }, 2205 SetList: []parser.UpdateSet{ 2206 { 2207 Field: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 2208 Value: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}, 2209 }, 2210 }, 2211 FromClause: parser.FromClause{ 2212 Tables: []parser.QueryExpression{ 2213 parser.Table{Object: parser.Join{ 2214 Table: parser.Table{ 2215 Object: parser.Identifier{Literal: "table1"}, 2216 Alias: parser.Identifier{Literal: "t1"}, 2217 }, 2218 JoinTable: parser.Table{ 2219 Object: parser.Identifier{Literal: "table2"}, 2220 Alias: parser.Identifier{Literal: "t2"}, 2221 }, 2222 Condition: parser.JoinCondition{ 2223 On: parser.Comparison{ 2224 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2225 RHS: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}, 2226 Operator: parser.Token{Token: '=', Literal: "="}, 2227 }, 2228 }, 2229 }}, 2230 }, 2231 }, 2232 }, 2233 Error: "table notexist is not loaded", 2234 }, 2235 { 2236 Name: "Update Query Update Table Is Not Specified Error", 2237 Query: parser.UpdateQuery{ 2238 Tables: []parser.QueryExpression{ 2239 parser.Table{Object: parser.Identifier{Literal: "t2"}}, 2240 }, 2241 SetList: []parser.UpdateSet{ 2242 { 2243 Field: parser.FieldReference{View: parser.Identifier{Literal: "t1"}, Column: parser.Identifier{Literal: "column2"}}, 2244 Value: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}, 2245 }, 2246 }, 2247 FromClause: parser.FromClause{ 2248 Tables: []parser.QueryExpression{ 2249 parser.Table{Object: parser.Join{ 2250 Table: parser.Table{ 2251 Object: parser.Identifier{Literal: "table1"}, 2252 Alias: parser.Identifier{Literal: "t1"}, 2253 }, 2254 JoinTable: parser.Table{ 2255 Object: parser.Identifier{Literal: "table2"}, 2256 Alias: parser.Identifier{Literal: "t2"}, 2257 }, 2258 Condition: parser.JoinCondition{ 2259 On: parser.Comparison{ 2260 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2261 RHS: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}, 2262 Operator: parser.Token{Token: '=', Literal: "="}, 2263 }, 2264 }, 2265 }}, 2266 }, 2267 }, 2268 }, 2269 Error: "field t1.column2 does not exist in the tables to update", 2270 }, 2271 { 2272 Name: "Update Query Update Field Error", 2273 Query: parser.UpdateQuery{ 2274 Tables: []parser.QueryExpression{ 2275 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 2276 }, 2277 SetList: []parser.UpdateSet{ 2278 { 2279 Field: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 2280 Value: parser.NewStringValue("update"), 2281 }, 2282 }, 2283 WhereClause: parser.WhereClause{ 2284 Filter: parser.Comparison{ 2285 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2286 RHS: parser.NewIntegerValueFromString("2"), 2287 Operator: parser.Token{Token: '=', Literal: "="}, 2288 }, 2289 }, 2290 }, 2291 Error: "field notexist does not exist", 2292 }, 2293 { 2294 Name: "Update Query Update Value Error", 2295 Query: parser.UpdateQuery{ 2296 Tables: []parser.QueryExpression{ 2297 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 2298 }, 2299 SetList: []parser.UpdateSet{ 2300 { 2301 Field: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2302 Value: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 2303 }, 2304 }, 2305 WhereClause: parser.WhereClause{ 2306 Filter: parser.Comparison{ 2307 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2308 RHS: parser.NewIntegerValueFromString("2"), 2309 Operator: parser.Token{Token: '=', Literal: "="}, 2310 }, 2311 }, 2312 }, 2313 Error: "field notexist does not exist", 2314 }, 2315 { 2316 Name: "Update Query Record Is Ambiguous Error", 2317 Query: parser.UpdateQuery{ 2318 Tables: []parser.QueryExpression{ 2319 parser.Table{Object: parser.Identifier{Literal: "t1"}}, 2320 }, 2321 SetList: []parser.UpdateSet{ 2322 { 2323 Field: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 2324 Value: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}, 2325 }, 2326 }, 2327 FromClause: parser.FromClause{ 2328 Tables: []parser.QueryExpression{ 2329 parser.Table{Object: parser.Join{ 2330 Table: parser.Table{ 2331 Object: parser.Identifier{Literal: "table1"}, 2332 Alias: parser.Identifier{Literal: "t1"}, 2333 }, 2334 JoinTable: parser.Table{ 2335 Object: parser.Identifier{Literal: "table2"}, 2336 Alias: parser.Identifier{Literal: "t2"}, 2337 }, 2338 JoinType: parser.Token{Token: parser.CROSS, Literal: "cross"}, 2339 }}, 2340 }, 2341 }, 2342 }, 2343 Error: "value column4 to set in the field column2 is ambiguous", 2344 }, 2345 } 2346 2347 func TestUpdate(t *testing.T) { 2348 defer func() { 2349 _ = TestTx.ReleaseResources() 2350 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 2351 initFlag(TestTx.Flags) 2352 }() 2353 2354 TestTx.Flags.Repository = TestDir 2355 TestTx.Flags.Quiet = false 2356 2357 scope := GenerateReferenceScope([]map[string]map[string]interface{}{ 2358 { 2359 scopeNameTempTables: { 2360 "TMPVIEW": &View{ 2361 Header: NewHeader("tmpview", []string{"column1", "column2"}), 2362 RecordSet: []Record{ 2363 NewRecord([]value.Primary{ 2364 value.NewString("1"), 2365 value.NewString("str1"), 2366 }), 2367 NewRecord([]value.Primary{ 2368 value.NewString("2"), 2369 value.NewString("str2"), 2370 }), 2371 }, 2372 FileInfo: &FileInfo{ 2373 Path: "tmpview", 2374 Delimiter: ',', 2375 ViewType: ViewTypeTemporaryTable, 2376 }, 2377 }, 2378 }, 2379 }, 2380 }, nil, time.Time{}, nil) 2381 2382 ctx := context.Background() 2383 for _, v := range updateTests { 2384 _ = TestTx.ReleaseResources() 2385 files, cnt, err := Update(ctx, scope, v.Query) 2386 if err != nil { 2387 if len(v.Error) < 1 { 2388 t.Errorf("%s: unexpected error %q", v.Name, err) 2389 } else if err.Error() != v.Error { 2390 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 2391 } 2392 continue 2393 } 2394 if 0 < len(v.Error) { 2395 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 2396 continue 2397 } 2398 2399 TestTx.CachedViews.Range(func(key, value interface{}) bool { 2400 view := value.(*View) 2401 if view.FileInfo.Handler != nil { 2402 if view.FileInfo.Path != view.FileInfo.Handler.Path() { 2403 t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name) 2404 } 2405 _ = TestTx.FileContainer.Close(view.FileInfo.Handler) 2406 view.FileInfo.Handler = nil 2407 } 2408 return true 2409 }) 2410 2411 if !reflect.DeepEqual(files, v.ResultFiles) { 2412 t.Errorf("%s: fileinfo = %v, want %v", v.Name, files, v.ResultFiles) 2413 } 2414 2415 if !reflect.DeepEqual(cnt, v.UpdateCounts) { 2416 t.Errorf("%s: update count = %v, want %v", v.Name, cnt, v.UpdateCounts) 2417 } 2418 2419 if v.ViewCache.SyncMap != nil { 2420 if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) { 2421 t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache) 2422 } 2423 } 2424 if v.ResultScopes != nil { 2425 if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) { 2426 t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) 2427 } 2428 } 2429 } 2430 } 2431 2432 var replaceTests = []struct { 2433 Name string 2434 Query parser.ReplaceQuery 2435 ResultFile *FileInfo 2436 UpdateCount int 2437 ViewCache ViewMap 2438 ResultScopes *ReferenceScope 2439 Error string 2440 }{ 2441 { 2442 Name: "Replace Query", 2443 Query: parser.ReplaceQuery{ 2444 WithClause: parser.WithClause{ 2445 InlineTables: []parser.QueryExpression{ 2446 parser.InlineTable{ 2447 Name: parser.Identifier{Literal: "it"}, 2448 Fields: []parser.QueryExpression{ 2449 parser.Identifier{Literal: "c1"}, 2450 parser.Identifier{Literal: "c2"}, 2451 }, 2452 Query: parser.SelectQuery{ 2453 SelectEntity: parser.SelectEntity{ 2454 SelectClause: parser.SelectClause{ 2455 Fields: []parser.QueryExpression{ 2456 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 2457 parser.Field{Object: parser.NewStringValue("str3")}, 2458 }, 2459 }, 2460 }, 2461 }, 2462 }, 2463 }, 2464 }, 2465 Table: parser.Table{Object: parser.Identifier{Literal: "table1"}}, 2466 Fields: []parser.QueryExpression{ 2467 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2468 parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 2469 }, 2470 Keys: []parser.QueryExpression{ 2471 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2472 }, 2473 ValuesList: []parser.QueryExpression{ 2474 parser.RowValue{ 2475 Value: parser.ValueList{ 2476 Values: []parser.QueryExpression{ 2477 parser.NewIntegerValueFromString("4"), 2478 parser.NewStringValue("str4"), 2479 }, 2480 }, 2481 }, 2482 parser.RowValue{ 2483 Value: parser.Subquery{ 2484 Query: parser.SelectQuery{ 2485 SelectEntity: parser.SelectEntity{ 2486 SelectClause: parser.SelectClause{ 2487 Fields: []parser.QueryExpression{ 2488 parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c1"}}}, 2489 parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c2"}}}, 2490 }, 2491 }, 2492 FromClause: parser.FromClause{ 2493 Tables: []parser.QueryExpression{ 2494 parser.Table{Object: parser.Identifier{Literal: "it"}}, 2495 }, 2496 }, 2497 }, 2498 }, 2499 }, 2500 }, 2501 }, 2502 }, 2503 ResultFile: &FileInfo{ 2504 Path: GetTestFilePath("table1.csv"), 2505 Delimiter: ',', 2506 NoHeader: false, 2507 Encoding: text.UTF8, 2508 LineBreak: text.LF, 2509 ForUpdate: true, 2510 }, 2511 UpdateCount: 2, 2512 ViewCache: GenerateViewMap([]*View{ 2513 { 2514 FileInfo: &FileInfo{ 2515 Path: GetTestFilePath("table1.csv"), 2516 Delimiter: ',', 2517 NoHeader: false, 2518 Encoding: text.UTF8, 2519 LineBreak: text.LF, 2520 ForUpdate: true, 2521 }, 2522 Header: NewHeader("table1", []string{"column1", "column2"}), 2523 RecordSet: []Record{ 2524 NewRecord([]value.Primary{ 2525 value.NewString("1"), 2526 value.NewString("str1"), 2527 }), 2528 NewRecord([]value.Primary{ 2529 value.NewString("2"), 2530 value.NewString("str3"), 2531 }), 2532 NewRecord([]value.Primary{ 2533 value.NewString("3"), 2534 value.NewString("str3"), 2535 }), 2536 NewRecord([]value.Primary{ 2537 value.NewInteger(4), 2538 value.NewString("str4"), 2539 }), 2540 }, 2541 }, 2542 }), 2543 }, 2544 { 2545 Name: "Replace Query to Empty Table", 2546 Query: parser.ReplaceQuery{ 2547 Table: parser.Table{Object: parser.Identifier{Literal: "table_empty"}}, 2548 Fields: []parser.QueryExpression{ 2549 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2550 parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 2551 }, 2552 Keys: []parser.QueryExpression{ 2553 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2554 }, 2555 ValuesList: []parser.QueryExpression{ 2556 parser.RowValue{ 2557 Value: parser.ValueList{ 2558 Values: []parser.QueryExpression{ 2559 parser.NewIntegerValueFromString("4"), 2560 parser.NewStringValue("str4"), 2561 }, 2562 }, 2563 }, 2564 }, 2565 }, 2566 ResultFile: &FileInfo{ 2567 Path: GetTestFilePath("table_empty.csv"), 2568 Delimiter: ',', 2569 NoHeader: false, 2570 Encoding: text.UTF8, 2571 LineBreak: text.LF, 2572 ForUpdate: true, 2573 }, 2574 UpdateCount: 1, 2575 ViewCache: GenerateViewMap([]*View{ 2576 { 2577 FileInfo: &FileInfo{ 2578 Path: GetTestFilePath("table_empty.csv"), 2579 Delimiter: ',', 2580 NoHeader: false, 2581 Encoding: text.UTF8, 2582 LineBreak: text.LF, 2583 ForUpdate: true, 2584 }, 2585 Header: NewHeader("table_empty", []string{"column1", "column2"}), 2586 RecordSet: []Record{ 2587 NewRecord([]value.Primary{ 2588 value.NewInteger(4), 2589 value.NewString("str4"), 2590 }), 2591 }, 2592 }, 2593 }), 2594 }, 2595 { 2596 Name: "Replace Query For Temporary View", 2597 Query: parser.ReplaceQuery{ 2598 Table: parser.Table{Object: parser.Identifier{Literal: "tmpview"}, Alias: parser.Identifier{Literal: "t"}}, 2599 Fields: []parser.QueryExpression{ 2600 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2601 }, 2602 Keys: []parser.QueryExpression{ 2603 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2604 }, 2605 ValuesList: []parser.QueryExpression{ 2606 parser.RowValue{ 2607 Value: parser.ValueList{ 2608 Values: []parser.QueryExpression{ 2609 parser.NewIntegerValueFromString("4"), 2610 }, 2611 }, 2612 }, 2613 parser.RowValue{ 2614 Value: parser.ValueList{ 2615 Values: []parser.QueryExpression{ 2616 parser.NewIntegerValueFromString("2"), 2617 }, 2618 }, 2619 }, 2620 }, 2621 }, 2622 ResultFile: &FileInfo{ 2623 Path: "tmpview", 2624 Delimiter: ',', 2625 ViewType: ViewTypeTemporaryTable, 2626 }, 2627 UpdateCount: 2, 2628 ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{ 2629 { 2630 scopeNameTempTables: { 2631 "TMPVIEW": &View{ 2632 Header: NewHeader("tmpview", []string{"column1", "column2"}), 2633 RecordSet: []Record{ 2634 NewRecord([]value.Primary{ 2635 value.NewString("1"), 2636 value.NewString("str1"), 2637 }), 2638 NewRecord([]value.Primary{ 2639 value.NewString("2"), 2640 value.NewString("str2"), 2641 }), 2642 NewRecord([]value.Primary{ 2643 value.NewInteger(4), 2644 value.NewNull(), 2645 }), 2646 }, 2647 FileInfo: &FileInfo{ 2648 Path: "tmpview", 2649 Delimiter: ',', 2650 ViewType: ViewTypeTemporaryTable, 2651 }, 2652 }, 2653 }, 2654 }, 2655 }, nil, time.Time{}, nil), 2656 }, 2657 { 2658 Name: "Replace Query All Fields", 2659 Query: parser.ReplaceQuery{ 2660 Table: parser.Table{Object: parser.Identifier{Literal: "table1"}}, 2661 Keys: []parser.QueryExpression{ 2662 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2663 }, 2664 ValuesList: []parser.QueryExpression{ 2665 parser.RowValue{ 2666 Value: parser.ValueList{ 2667 Values: []parser.QueryExpression{ 2668 parser.NewIntegerValueFromString("4"), 2669 parser.NewStringValue("str4"), 2670 }, 2671 }, 2672 }, 2673 parser.RowValue{ 2674 Value: parser.ValueList{ 2675 Values: []parser.QueryExpression{ 2676 parser.NewIntegerValueFromString("5"), 2677 parser.NewStringValue("str5"), 2678 }, 2679 }, 2680 }, 2681 }, 2682 }, 2683 ResultFile: &FileInfo{ 2684 Path: GetTestFilePath("table1.csv"), 2685 Delimiter: ',', 2686 NoHeader: false, 2687 Encoding: text.UTF8, 2688 LineBreak: text.LF, 2689 ForUpdate: true, 2690 }, 2691 UpdateCount: 2, 2692 }, 2693 { 2694 Name: "Replace Query Inline Table Cannot Be Updated Error", 2695 Query: parser.ReplaceQuery{ 2696 WithClause: parser.WithClause{ 2697 InlineTables: []parser.QueryExpression{ 2698 parser.InlineTable{ 2699 Name: parser.Identifier{Literal: "it"}, 2700 Fields: []parser.QueryExpression{ 2701 parser.Identifier{Literal: "c1"}, 2702 parser.Identifier{Literal: "c2"}, 2703 }, 2704 Query: parser.SelectQuery{ 2705 SelectEntity: parser.SelectEntity{ 2706 SelectClause: parser.SelectClause{ 2707 Fields: []parser.QueryExpression{ 2708 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 2709 parser.Field{Object: parser.NewStringValue("str3")}, 2710 }, 2711 }, 2712 }, 2713 }, 2714 }, 2715 }, 2716 }, 2717 Table: parser.Table{Object: parser.Identifier{Literal: "it"}}, 2718 Fields: []parser.QueryExpression{ 2719 parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}, 2720 parser.FieldReference{Column: parser.Identifier{Literal: "c2"}}, 2721 }, 2722 Keys: []parser.QueryExpression{ 2723 parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}, 2724 }, 2725 ValuesList: []parser.QueryExpression{ 2726 parser.RowValue{ 2727 Value: parser.ValueList{ 2728 Values: []parser.QueryExpression{ 2729 parser.NewIntegerValueFromString("4"), 2730 parser.NewStringValue("str4"), 2731 }, 2732 }, 2733 }, 2734 parser.RowValue{ 2735 Value: parser.Subquery{ 2736 Query: parser.SelectQuery{ 2737 SelectEntity: parser.SelectEntity{ 2738 SelectClause: parser.SelectClause{ 2739 Fields: []parser.QueryExpression{ 2740 parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c1"}}}, 2741 parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c2"}}}, 2742 }, 2743 }, 2744 FromClause: parser.FromClause{ 2745 Tables: []parser.QueryExpression{ 2746 parser.Table{Object: parser.Identifier{Literal: "it"}}, 2747 }, 2748 }, 2749 }, 2750 }, 2751 }, 2752 }, 2753 }, 2754 }, 2755 Error: "inline table cannot be updated", 2756 }, 2757 { 2758 Name: "Replace Query File Does Not Exist Error", 2759 Query: parser.ReplaceQuery{ 2760 Table: parser.Table{Object: parser.Identifier{Literal: "notexist"}}, 2761 Fields: []parser.QueryExpression{ 2762 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2763 }, 2764 Keys: []parser.QueryExpression{ 2765 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2766 }, 2767 ValuesList: []parser.QueryExpression{ 2768 parser.RowValue{ 2769 Value: parser.ValueList{ 2770 Values: []parser.QueryExpression{ 2771 parser.NewIntegerValueFromString("4"), 2772 }, 2773 }, 2774 }, 2775 parser.RowValue{ 2776 Value: parser.ValueList{ 2777 Values: []parser.QueryExpression{ 2778 parser.NewIntegerValueFromString("5"), 2779 }, 2780 }, 2781 }, 2782 }, 2783 }, 2784 Error: "file notexist does not exist", 2785 }, 2786 { 2787 Name: "Replace Query Field Does Not Exist Error", 2788 Query: parser.ReplaceQuery{ 2789 Table: parser.Table{Object: parser.Identifier{Literal: "table1"}}, 2790 Fields: []parser.QueryExpression{ 2791 parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 2792 }, 2793 Keys: []parser.QueryExpression{ 2794 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2795 }, 2796 ValuesList: []parser.QueryExpression{ 2797 parser.RowValue{ 2798 Value: parser.ValueList{ 2799 Values: []parser.QueryExpression{ 2800 parser.NewIntegerValueFromString("4"), 2801 }, 2802 }, 2803 }, 2804 parser.RowValue{ 2805 Value: parser.ValueList{ 2806 Values: []parser.QueryExpression{ 2807 parser.NewIntegerValueFromString("5"), 2808 }, 2809 }, 2810 }, 2811 }, 2812 }, 2813 Error: "field notexist does not exist", 2814 }, 2815 { 2816 Name: "Replace Select Query", 2817 Query: parser.ReplaceQuery{ 2818 Table: parser.Table{Object: parser.Identifier{Literal: "table1"}}, 2819 Fields: []parser.QueryExpression{ 2820 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2821 parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 2822 }, 2823 Keys: []parser.QueryExpression{ 2824 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2825 }, 2826 Query: parser.SelectQuery{ 2827 SelectEntity: parser.SelectEntity{ 2828 SelectClause: parser.SelectClause{ 2829 Fields: []parser.QueryExpression{ 2830 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}}, 2831 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}}, 2832 }, 2833 }, 2834 FromClause: parser.FromClause{ 2835 Tables: []parser.QueryExpression{ 2836 parser.Table{Object: parser.Identifier{Literal: "table2"}}, 2837 }, 2838 }, 2839 }, 2840 }, 2841 }, 2842 ResultFile: &FileInfo{ 2843 Path: GetTestFilePath("table1.csv"), 2844 Delimiter: ',', 2845 NoHeader: false, 2846 Encoding: text.UTF8, 2847 LineBreak: text.LF, 2848 ForUpdate: true, 2849 }, 2850 UpdateCount: 3, 2851 }, 2852 { 2853 Name: "Replace Select Query Field Does Not Exist Error", 2854 Query: parser.ReplaceQuery{ 2855 Table: parser.Table{Object: parser.Identifier{Literal: "table1"}}, 2856 Fields: []parser.QueryExpression{ 2857 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2858 }, 2859 Keys: []parser.QueryExpression{ 2860 parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 2861 }, 2862 Query: parser.SelectQuery{ 2863 SelectEntity: parser.SelectEntity{ 2864 SelectClause: parser.SelectClause{ 2865 Fields: []parser.QueryExpression{ 2866 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}}, 2867 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}}, 2868 }, 2869 }, 2870 FromClause: parser.FromClause{ 2871 Tables: []parser.QueryExpression{ 2872 parser.Table{Object: parser.Identifier{Literal: "table2"}}, 2873 }, 2874 }, 2875 }, 2876 }, 2877 }, 2878 Error: "select query should return exactly 1 field", 2879 }, 2880 } 2881 2882 func TestReplace(t *testing.T) { 2883 defer func() { 2884 _ = TestTx.ReleaseResources() 2885 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 2886 initFlag(TestTx.Flags) 2887 }() 2888 2889 TestTx.Flags.Repository = TestDir 2890 TestTx.Flags.Quiet = false 2891 2892 scope := GenerateReferenceScope([]map[string]map[string]interface{}{ 2893 { 2894 scopeNameTempTables: { 2895 "TMPVIEW": &View{ 2896 Header: NewHeader("tmpview", []string{"column1", "column2"}), 2897 RecordSet: []Record{ 2898 NewRecord([]value.Primary{ 2899 value.NewString("1"), 2900 value.NewString("str1"), 2901 }), 2902 NewRecord([]value.Primary{ 2903 value.NewString("2"), 2904 value.NewString("str2"), 2905 }), 2906 }, 2907 FileInfo: &FileInfo{ 2908 Path: "tmpview", 2909 Delimiter: ',', 2910 ViewType: ViewTypeTemporaryTable, 2911 }, 2912 }, 2913 }, 2914 }, 2915 }, nil, time.Time{}, nil) 2916 2917 ctx := context.Background() 2918 for _, v := range replaceTests { 2919 _ = TestTx.ReleaseResources() 2920 result, cnt, err := Replace(ctx, scope, v.Query) 2921 if err != nil { 2922 if len(v.Error) < 1 { 2923 t.Errorf("%s: unexpected error %q", v.Name, err) 2924 } else if err.Error() != v.Error { 2925 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 2926 } 2927 continue 2928 } 2929 if 0 < len(v.Error) { 2930 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 2931 continue 2932 } 2933 2934 TestTx.CachedViews.Range(func(key, value interface{}) bool { 2935 view := value.(*View) 2936 if view.FileInfo.Handler != nil { 2937 if view.FileInfo.Path != view.FileInfo.Handler.Path() { 2938 t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name) 2939 } 2940 _ = TestTx.FileContainer.Close(view.FileInfo.Handler) 2941 view.FileInfo.Handler = nil 2942 } 2943 return true 2944 }) 2945 2946 if !reflect.DeepEqual(result, v.ResultFile) { 2947 t.Errorf("%s: fileinfo = %v, want %v", v.Name, result, v.ResultFile) 2948 } 2949 2950 if !reflect.DeepEqual(cnt, v.UpdateCount) { 2951 t.Errorf("%s: update count = %d, want %d", v.Name, cnt, v.UpdateCount) 2952 } 2953 2954 if v.ViewCache.SyncMap != nil { 2955 if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) { 2956 t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache) 2957 } 2958 } 2959 if v.ResultScopes != nil { 2960 if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) { 2961 t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) 2962 } 2963 } 2964 } 2965 } 2966 2967 var deleteTests = []struct { 2968 Name string 2969 Query parser.DeleteQuery 2970 ResultFiles []*FileInfo 2971 UpdateCounts []int 2972 ViewCache ViewMap 2973 ResultScopes *ReferenceScope 2974 Error string 2975 }{ 2976 { 2977 Name: "Delete Query", 2978 Query: parser.DeleteQuery{ 2979 WithClause: parser.WithClause{ 2980 InlineTables: []parser.QueryExpression{ 2981 parser.InlineTable{ 2982 Name: parser.Identifier{Literal: "it"}, 2983 Fields: []parser.QueryExpression{ 2984 parser.Identifier{Literal: "c1"}, 2985 }, 2986 Query: parser.SelectQuery{ 2987 SelectEntity: parser.SelectEntity{ 2988 SelectClause: parser.SelectClause{ 2989 Fields: []parser.QueryExpression{ 2990 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 2991 }, 2992 }, 2993 }, 2994 }, 2995 }, 2996 }, 2997 }, 2998 FromClause: parser.FromClause{ 2999 Tables: []parser.QueryExpression{ 3000 parser.Table{ 3001 Object: parser.Identifier{Literal: "table1"}, 3002 }, 3003 }, 3004 }, 3005 WhereClause: parser.WhereClause{ 3006 Filter: parser.Comparison{ 3007 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 3008 RHS: parser.Subquery{ 3009 Query: parser.SelectQuery{ 3010 SelectEntity: parser.SelectEntity{ 3011 SelectClause: parser.SelectClause{ 3012 Fields: []parser.QueryExpression{ 3013 parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c1"}}}, 3014 }, 3015 }, 3016 FromClause: parser.FromClause{ 3017 Tables: []parser.QueryExpression{ 3018 parser.Table{Object: parser.Identifier{Literal: "it"}}, 3019 }, 3020 }, 3021 }, 3022 }, 3023 }, 3024 Operator: parser.Token{Token: '=', Literal: "="}, 3025 }, 3026 }, 3027 }, 3028 ResultFiles: []*FileInfo{ 3029 { 3030 Path: GetTestFilePath("table1.csv"), 3031 Delimiter: ',', 3032 NoHeader: false, 3033 Encoding: text.UTF8, 3034 LineBreak: text.LF, 3035 ForUpdate: true, 3036 }, 3037 }, 3038 UpdateCounts: []int{1}, 3039 ViewCache: GenerateViewMap([]*View{ 3040 { 3041 FileInfo: &FileInfo{ 3042 Path: GetTestFilePath("table1.csv"), 3043 Delimiter: ',', 3044 NoHeader: false, 3045 Encoding: text.UTF8, 3046 LineBreak: text.LF, 3047 ForUpdate: true, 3048 }, 3049 Header: NewHeader("table1", []string{"column1", "column2"}), 3050 RecordSet: []Record{ 3051 NewRecord([]value.Primary{ 3052 value.NewString("1"), 3053 value.NewString("str1"), 3054 }), 3055 NewRecord([]value.Primary{ 3056 value.NewString("3"), 3057 value.NewString("str3"), 3058 }), 3059 }, 3060 }, 3061 }), 3062 }, 3063 { 3064 Name: "Delete Query For Temporary View", 3065 Query: parser.DeleteQuery{ 3066 FromClause: parser.FromClause{ 3067 Tables: []parser.QueryExpression{ 3068 parser.Table{ 3069 Object: parser.Identifier{Literal: "tmpview"}, 3070 Alias: parser.Identifier{Literal: "t1"}, 3071 }, 3072 }, 3073 }, 3074 WhereClause: parser.WhereClause{ 3075 Filter: parser.Comparison{ 3076 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 3077 RHS: parser.NewIntegerValueFromString("2"), 3078 Operator: parser.Token{Token: '=', Literal: "="}, 3079 }, 3080 }, 3081 }, 3082 ResultFiles: []*FileInfo{ 3083 { 3084 Path: "tmpview", 3085 Delimiter: ',', 3086 ViewType: ViewTypeTemporaryTable, 3087 }, 3088 }, 3089 UpdateCounts: []int{1}, 3090 ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{ 3091 { 3092 scopeNameTempTables: { 3093 "TMPVIEW": &View{ 3094 Header: NewHeader("tmpview", []string{"column1", "column2"}), 3095 RecordSet: []Record{ 3096 NewRecord([]value.Primary{ 3097 value.NewString("1"), 3098 value.NewString("str1"), 3099 }), 3100 }, 3101 FileInfo: &FileInfo{ 3102 Path: "tmpview", 3103 Delimiter: ',', 3104 ViewType: ViewTypeTemporaryTable, 3105 }, 3106 }, 3107 }, 3108 }, 3109 }, nil, time.Time{}, nil), 3110 }, 3111 { 3112 Name: "Delete Query Multiple Table", 3113 Query: parser.DeleteQuery{ 3114 Tables: []parser.QueryExpression{ 3115 parser.Table{Object: parser.Identifier{Literal: "t1"}}, 3116 }, 3117 FromClause: parser.FromClause{ 3118 Tables: []parser.QueryExpression{ 3119 parser.Table{Object: parser.Join{ 3120 Table: parser.Table{ 3121 Object: parser.Identifier{Literal: "table1"}, 3122 Alias: parser.Identifier{Literal: "t1"}, 3123 }, 3124 JoinTable: parser.Table{ 3125 Object: parser.Identifier{Literal: "table2"}, 3126 Alias: parser.Identifier{Literal: "t2"}, 3127 }, 3128 Condition: parser.JoinCondition{ 3129 On: parser.Comparison{ 3130 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 3131 RHS: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}, 3132 Operator: parser.Token{Token: '=', Literal: "="}, 3133 }, 3134 }, 3135 }}, 3136 }, 3137 }, 3138 }, 3139 ResultFiles: []*FileInfo{ 3140 { 3141 Path: GetTestFilePath("table1.csv"), 3142 Delimiter: ',', 3143 NoHeader: false, 3144 Encoding: text.UTF8, 3145 LineBreak: text.LF, 3146 ForUpdate: true, 3147 }, 3148 }, 3149 UpdateCounts: []int{2}, 3150 }, 3151 { 3152 Name: "Delete Query Alias to Table Identification Function Is Not Specified Error ", 3153 Query: parser.DeleteQuery{ 3154 FromClause: parser.FromClause{ 3155 Tables: []parser.QueryExpression{ 3156 parser.Table{ 3157 Object: parser.TableFunction{ 3158 Name: "data", 3159 Args: []parser.QueryExpression{ 3160 parser.NewStringValue("c1,c2\n1,a\n2,b"), 3161 }, 3162 }, 3163 }, 3164 }, 3165 }, 3166 WhereClause: parser.WhereClause{ 3167 Filter: parser.Comparison{ 3168 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}, 3169 RHS: parser.NewIntegerValueFromString("2"), 3170 Operator: parser.Token{Token: '=', Literal: "="}, 3171 }, 3172 }, 3173 }, 3174 Error: "alias to table identification function or URL must be specified for update", 3175 }, 3176 { 3177 Name: "Delete Query Inline Table Cannot Be Updated Error ", 3178 Query: parser.DeleteQuery{ 3179 FromClause: parser.FromClause{ 3180 Tables: []parser.QueryExpression{ 3181 parser.Table{ 3182 Object: parser.TableFunction{ 3183 Name: "data", 3184 Args: []parser.QueryExpression{ 3185 parser.NewStringValue("c1,c2\n1,a\n2,b"), 3186 }, 3187 }, 3188 Alias: parser.Identifier{ 3189 Literal: "a", 3190 }, 3191 }, 3192 }, 3193 }, 3194 WhereClause: parser.WhereClause{ 3195 Filter: parser.Comparison{ 3196 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}, 3197 RHS: parser.NewIntegerValueFromString("2"), 3198 Operator: parser.Token{Token: '=', Literal: "="}, 3199 }, 3200 }, 3201 }, 3202 Error: "inline table cannot be updated", 3203 }, 3204 { 3205 Name: "Delete Query Tables Not Specified Error", 3206 Query: parser.DeleteQuery{ 3207 FromClause: parser.FromClause{ 3208 Tables: []parser.QueryExpression{ 3209 parser.Table{ 3210 Object: parser.Join{ 3211 Table: parser.Table{ 3212 Object: parser.Identifier{Literal: "table1"}, 3213 Alias: parser.Identifier{Literal: "t1"}, 3214 }, 3215 JoinTable: parser.Table{ 3216 Object: parser.Identifier{Literal: "table2"}, 3217 Alias: parser.Identifier{Literal: "t2"}, 3218 }, 3219 Condition: parser.JoinCondition{ 3220 On: parser.Comparison{ 3221 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 3222 RHS: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}, 3223 Operator: parser.Token{Token: '=', Literal: "="}, 3224 }, 3225 }, 3226 }, 3227 }, 3228 parser.Table{ 3229 Object: parser.TableFunction{ 3230 Name: "data", 3231 Args: []parser.QueryExpression{ 3232 parser.NewStringValue("c1,c2\n1,a\n2,b"), 3233 }, 3234 }, 3235 }, 3236 }, 3237 }, 3238 }, 3239 Error: "tables to delete records are not specified", 3240 }, 3241 { 3242 Name: "Delete Query File Does Not Exist Error", 3243 Query: parser.DeleteQuery{ 3244 FromClause: parser.FromClause{ 3245 Tables: []parser.QueryExpression{ 3246 parser.Table{ 3247 Object: parser.Identifier{Literal: "notexist"}, 3248 }, 3249 }, 3250 }, 3251 WhereClause: parser.WhereClause{ 3252 Filter: parser.Comparison{ 3253 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 3254 RHS: parser.NewIntegerValueFromString("2"), 3255 Operator: parser.Token{Token: '=', Literal: "="}, 3256 }, 3257 }, 3258 }, 3259 Error: "file notexist does not exist", 3260 }, 3261 { 3262 Name: "Delete Query Filter Error", 3263 Query: parser.DeleteQuery{ 3264 FromClause: parser.FromClause{ 3265 Tables: []parser.QueryExpression{ 3266 parser.Table{ 3267 Object: parser.Identifier{Literal: "table1"}, 3268 }, 3269 }, 3270 }, 3271 WhereClause: parser.WhereClause{ 3272 Filter: parser.Comparison{ 3273 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 3274 RHS: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 3275 Operator: parser.Token{Token: '=', Literal: "="}, 3276 }, 3277 }, 3278 }, 3279 Error: "field notexist does not exist", 3280 }, 3281 { 3282 Name: "Delete Query File Is Not Loaded Error", 3283 Query: parser.DeleteQuery{ 3284 Tables: []parser.QueryExpression{ 3285 parser.Table{Object: parser.Identifier{Literal: "notexist"}}, 3286 }, 3287 FromClause: parser.FromClause{ 3288 Tables: []parser.QueryExpression{ 3289 parser.Table{Object: parser.Join{ 3290 Table: parser.Table{ 3291 Object: parser.Identifier{Literal: "table1"}, 3292 Alias: parser.Identifier{Literal: "t1"}, 3293 }, 3294 JoinTable: parser.Table{ 3295 Object: parser.Identifier{Literal: "table2"}, 3296 Alias: parser.Identifier{Literal: "t2"}, 3297 }, 3298 Condition: parser.JoinCondition{ 3299 On: parser.Comparison{ 3300 LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 3301 RHS: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}, 3302 Operator: parser.Token{Token: '=', Literal: "="}, 3303 }, 3304 }, 3305 }}, 3306 }, 3307 }, 3308 }, 3309 Error: "table notexist is not loaded", 3310 }, 3311 } 3312 3313 func TestDelete(t *testing.T) { 3314 defer func() { 3315 _ = TestTx.ReleaseResources() 3316 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 3317 initFlag(TestTx.Flags) 3318 }() 3319 3320 TestTx.Flags.Repository = TestDir 3321 TestTx.Flags.Quiet = false 3322 3323 scope := GenerateReferenceScope([]map[string]map[string]interface{}{ 3324 { 3325 scopeNameTempTables: { 3326 "TMPVIEW": &View{ 3327 Header: NewHeader("tmpview", []string{"column1", "column2"}), 3328 RecordSet: []Record{ 3329 NewRecord([]value.Primary{ 3330 value.NewString("1"), 3331 value.NewString("str1"), 3332 }), 3333 NewRecord([]value.Primary{ 3334 value.NewString("2"), 3335 value.NewString("str2"), 3336 }), 3337 }, 3338 FileInfo: &FileInfo{ 3339 Path: "tmpview", 3340 Delimiter: ',', 3341 ViewType: ViewTypeTemporaryTable, 3342 }, 3343 }, 3344 }, 3345 }, 3346 }, nil, time.Time{}, nil) 3347 3348 ctx := context.Background() 3349 for _, v := range deleteTests { 3350 _ = TestTx.ReleaseResources() 3351 files, cnt, err := Delete(ctx, scope, v.Query) 3352 if err != nil { 3353 if len(v.Error) < 1 { 3354 t.Errorf("%s: unexpected error %q", v.Name, err) 3355 } else if err.Error() != v.Error { 3356 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 3357 } 3358 continue 3359 } 3360 if 0 < len(v.Error) { 3361 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 3362 continue 3363 } 3364 3365 TestTx.CachedViews.Range(func(key, value interface{}) bool { 3366 view := value.(*View) 3367 if view.FileInfo.Handler != nil { 3368 if view.FileInfo.Path != view.FileInfo.Handler.Path() { 3369 t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name) 3370 } 3371 _ = TestTx.FileContainer.Close(view.FileInfo.Handler) 3372 view.FileInfo.Handler = nil 3373 } 3374 return true 3375 }) 3376 3377 if !reflect.DeepEqual(files, v.ResultFiles) { 3378 t.Errorf("%s: fileinfo = %v, want %v", v.Name, files, v.ResultFiles) 3379 } 3380 3381 if !reflect.DeepEqual(cnt, v.UpdateCounts) { 3382 t.Errorf("%s: update count = %v, want %v", v.Name, cnt, v.UpdateCounts) 3383 } 3384 3385 if v.ViewCache.SyncMap != nil { 3386 if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) { 3387 t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache) 3388 } 3389 } 3390 if v.ResultScopes != nil { 3391 if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) { 3392 t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) 3393 } 3394 } 3395 } 3396 } 3397 3398 var createTableTests = []struct { 3399 Name string 3400 Query parser.CreateTable 3401 ResultFile *FileInfo 3402 ViewCache ViewMap 3403 Error string 3404 }{ 3405 { 3406 Name: "Create Table", 3407 Query: parser.CreateTable{ 3408 Table: parser.Identifier{Literal: "create_table_1.csv"}, 3409 Fields: []parser.QueryExpression{ 3410 parser.Identifier{Literal: "column1"}, 3411 parser.Identifier{Literal: "column2"}, 3412 }, 3413 }, 3414 ResultFile: &FileInfo{ 3415 Path: GetTestFilePath("create_table_1.csv"), 3416 Delimiter: ',', 3417 NoHeader: false, 3418 Encoding: text.UTF8, 3419 LineBreak: text.LF, 3420 ForUpdate: true, 3421 }, 3422 ViewCache: GenerateViewMap([]*View{ 3423 { 3424 FileInfo: &FileInfo{ 3425 Path: GetTestFilePath("create_table_1.csv"), 3426 Delimiter: ',', 3427 NoHeader: false, 3428 Encoding: text.UTF8, 3429 LineBreak: text.LF, 3430 ForUpdate: true, 3431 }, 3432 Header: NewHeader("create_table_1", []string{"column1", "column2"}), 3433 RecordSet: RecordSet{}, 3434 }, 3435 }), 3436 }, 3437 { 3438 Name: "Create Table From Select Query", 3439 Query: parser.CreateTable{ 3440 Table: parser.Identifier{Literal: "create_table_1.csv"}, 3441 Fields: []parser.QueryExpression{ 3442 parser.Identifier{Literal: "column1"}, 3443 parser.Identifier{Literal: "column2"}, 3444 }, 3445 Query: parser.SelectQuery{ 3446 SelectEntity: parser.SelectEntity{ 3447 SelectClause: parser.SelectClause{ 3448 Fields: []parser.QueryExpression{ 3449 parser.Field{Object: parser.NewIntegerValueFromString("1")}, 3450 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 3451 }, 3452 }, 3453 }, 3454 }, 3455 }, 3456 ResultFile: &FileInfo{ 3457 Path: GetTestFilePath("create_table_1.csv"), 3458 Delimiter: ',', 3459 NoHeader: false, 3460 Encoding: text.UTF8, 3461 LineBreak: text.LF, 3462 ForUpdate: true, 3463 }, 3464 ViewCache: GenerateViewMap([]*View{ 3465 { 3466 FileInfo: &FileInfo{ 3467 Path: GetTestFilePath("create_table_1.csv"), 3468 Delimiter: ',', 3469 NoHeader: false, 3470 Encoding: text.UTF8, 3471 LineBreak: text.LF, 3472 ForUpdate: true, 3473 }, 3474 Header: NewHeader("create_table_1", []string{"column1", "column2"}), 3475 RecordSet: RecordSet{ 3476 NewRecord([]value.Primary{ 3477 value.NewInteger(1), 3478 value.NewInteger(2), 3479 }), 3480 }, 3481 }, 3482 }), 3483 }, 3484 { 3485 Name: "Create Table File Already Exist Error", 3486 Query: parser.CreateTable{ 3487 Table: parser.Identifier{Literal: "table1.csv"}, 3488 Fields: []parser.QueryExpression{ 3489 parser.Identifier{Literal: "column1"}, 3490 parser.Identifier{Literal: "column2"}, 3491 }, 3492 }, 3493 Error: fmt.Sprintf("file %s already exists", GetTestFilePath("table1.csv")), 3494 }, 3495 { 3496 Name: "Create Table Field Duplicate Error", 3497 Query: parser.CreateTable{ 3498 Table: parser.Identifier{Literal: "create_table_1.csv"}, 3499 Fields: []parser.QueryExpression{ 3500 parser.Identifier{Literal: "column1"}, 3501 parser.Identifier{Literal: "column1"}, 3502 }, 3503 }, 3504 Error: "field name column1 is a duplicate", 3505 }, 3506 { 3507 Name: "Create Table Select Query Execution Error", 3508 Query: parser.CreateTable{ 3509 Table: parser.Identifier{Literal: "create_table_1.csv"}, 3510 Fields: []parser.QueryExpression{ 3511 parser.Identifier{Literal: "column1"}, 3512 parser.Identifier{Literal: "column2"}, 3513 }, 3514 Query: parser.SelectQuery{ 3515 SelectEntity: parser.SelectEntity{ 3516 SelectClause: parser.SelectClause{ 3517 Fields: []parser.QueryExpression{ 3518 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}}, 3519 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 3520 }, 3521 }, 3522 }, 3523 }, 3524 }, 3525 Error: "field notexist does not exist", 3526 }, 3527 { 3528 Name: "Create Table From Select Query Field Length Not Match Error", 3529 Query: parser.CreateTable{ 3530 Table: parser.Identifier{Literal: "create_table_1.csv"}, 3531 Fields: []parser.QueryExpression{ 3532 parser.Identifier{Literal: "column1"}, 3533 }, 3534 Query: parser.SelectQuery{ 3535 SelectEntity: parser.SelectEntity{ 3536 SelectClause: parser.SelectClause{ 3537 Fields: []parser.QueryExpression{ 3538 parser.Field{Object: parser.NewIntegerValueFromString("1")}, 3539 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 3540 }, 3541 }, 3542 }, 3543 }, 3544 }, 3545 Error: "select query should return exactly 1 field for table create_table_1.csv", 3546 }, 3547 { 3548 Name: "Create Table From Select Query Field Name Duplicate Error", 3549 Query: parser.CreateTable{ 3550 Table: parser.Identifier{Literal: "create_table_1.csv"}, 3551 Fields: []parser.QueryExpression{ 3552 parser.Identifier{Literal: "column1"}, 3553 parser.Identifier{Literal: "column1"}, 3554 }, 3555 Query: parser.SelectQuery{ 3556 SelectEntity: parser.SelectEntity{ 3557 SelectClause: parser.SelectClause{ 3558 Fields: []parser.QueryExpression{ 3559 parser.Field{Object: parser.NewIntegerValueFromString("1")}, 3560 parser.Field{Object: parser.NewIntegerValueFromString("2")}, 3561 }, 3562 }, 3563 }, 3564 }, 3565 }, 3566 Error: "field name column1 is a duplicate", 3567 }, 3568 } 3569 3570 func TestCreateTable(t *testing.T) { 3571 defer func() { 3572 _ = TestTx.ReleaseResources() 3573 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 3574 initFlag(TestTx.Flags) 3575 }() 3576 3577 TestTx.Flags.Repository = TestDir 3578 TestTx.Flags.Quiet = false 3579 3580 scope := NewReferenceScope(TestTx) 3581 ctx := context.Background() 3582 for _, v := range createTableTests { 3583 _ = TestTx.ReleaseResources() 3584 3585 result, err := CreateTable(ctx, scope, v.Query) 3586 3587 if result != nil { 3588 _ = TestTx.FileContainer.Close(result.Handler) 3589 result.Handler = nil 3590 } 3591 TestTx.CachedViews.Range(func(key, value interface{}) bool { 3592 view := value.(*View) 3593 if view.FileInfo != nil { 3594 _ = TestTx.FileContainer.Close(view.FileInfo.Handler) 3595 view.FileInfo.Handler = nil 3596 } 3597 return true 3598 }) 3599 3600 if err != nil { 3601 if len(v.Error) < 1 { 3602 t.Errorf("%s: unexpected error %q", v.Name, err) 3603 } else if err.Error() != v.Error { 3604 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 3605 } 3606 continue 3607 } 3608 if 0 < len(v.Error) { 3609 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 3610 continue 3611 } 3612 3613 if !reflect.DeepEqual(result, v.ResultFile) { 3614 t.Errorf("%s: result = %v, want %v", v.Name, result, v.ResultFile) 3615 } 3616 3617 if v.ViewCache.SyncMap != nil { 3618 if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) { 3619 t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache) 3620 } 3621 } 3622 } 3623 } 3624 3625 var addColumnsTests = []struct { 3626 Name string 3627 Query parser.AddColumns 3628 ResultFile *FileInfo 3629 UpdateCount int 3630 ViewCache ViewMap 3631 ResultScopes *ReferenceScope 3632 Error string 3633 }{ 3634 { 3635 Name: "Add Fields", 3636 Query: parser.AddColumns{ 3637 Table: parser.Identifier{Literal: "table1.csv"}, 3638 Columns: []parser.ColumnDefault{ 3639 { 3640 Column: parser.Identifier{Literal: "column3"}, 3641 }, 3642 { 3643 Column: parser.Identifier{Literal: "column4"}, 3644 }, 3645 }, 3646 }, 3647 ResultFile: &FileInfo{ 3648 Path: GetTestFilePath("table1.csv"), 3649 Delimiter: ',', 3650 NoHeader: false, 3651 Encoding: text.UTF8, 3652 LineBreak: text.LF, 3653 ForUpdate: true, 3654 }, 3655 UpdateCount: 2, 3656 ViewCache: GenerateViewMap([]*View{ 3657 { 3658 FileInfo: &FileInfo{ 3659 Path: GetTestFilePath("table1.csv"), 3660 Delimiter: ',', 3661 NoHeader: false, 3662 Encoding: text.UTF8, 3663 LineBreak: text.LF, 3664 ForUpdate: true, 3665 }, 3666 Header: NewHeader("table1", []string{"column1", "column2", "column3", "column4"}), 3667 RecordSet: []Record{ 3668 NewRecord([]value.Primary{ 3669 value.NewString("1"), 3670 value.NewString("str1"), 3671 value.NewNull(), 3672 value.NewNull(), 3673 }), 3674 NewRecord([]value.Primary{ 3675 value.NewString("2"), 3676 value.NewString("str2"), 3677 value.NewNull(), 3678 value.NewNull(), 3679 }), 3680 NewRecord([]value.Primary{ 3681 value.NewString("3"), 3682 value.NewString("str3"), 3683 value.NewNull(), 3684 value.NewNull(), 3685 }), 3686 }, 3687 }, 3688 }), 3689 }, 3690 { 3691 Name: "Add Fields For Temporary View", 3692 Query: parser.AddColumns{ 3693 Table: parser.Identifier{Literal: "tmpview"}, 3694 Columns: []parser.ColumnDefault{ 3695 { 3696 Column: parser.Identifier{Literal: "column3"}, 3697 }, 3698 { 3699 Column: parser.Identifier{Literal: "column4"}, 3700 }, 3701 }, 3702 }, 3703 ResultFile: &FileInfo{ 3704 Path: "tmpview", 3705 Delimiter: ',', 3706 ViewType: ViewTypeTemporaryTable, 3707 }, 3708 UpdateCount: 2, 3709 ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{ 3710 { 3711 scopeNameTempTables: { 3712 "TMPVIEW": &View{ 3713 Header: NewHeader("tmpview", []string{"column1", "column2", "column3", "column4"}), 3714 RecordSet: []Record{ 3715 NewRecord([]value.Primary{ 3716 value.NewString("1"), 3717 value.NewString("str1"), 3718 value.NewNull(), 3719 value.NewNull(), 3720 }), 3721 NewRecord([]value.Primary{ 3722 value.NewString("2"), 3723 value.NewString("str2"), 3724 value.NewNull(), 3725 value.NewNull(), 3726 }), 3727 }, 3728 FileInfo: &FileInfo{ 3729 Path: "tmpview", 3730 Delimiter: ',', 3731 ViewType: ViewTypeTemporaryTable, 3732 }, 3733 }, 3734 }, 3735 }, 3736 }, nil, time.Time{}, nil), 3737 }, 3738 { 3739 Name: "Add Fields First", 3740 Query: parser.AddColumns{ 3741 Table: parser.Identifier{Literal: "table1.csv"}, 3742 Columns: []parser.ColumnDefault{ 3743 { 3744 Column: parser.Identifier{Literal: "column3"}, 3745 Value: parser.NewIntegerValueFromString("2"), 3746 }, 3747 { 3748 Column: parser.Identifier{Literal: "column4"}, 3749 Value: parser.NewIntegerValueFromString("1"), 3750 }, 3751 }, 3752 Position: parser.ColumnPosition{ 3753 Position: parser.Token{Token: parser.FIRST}, 3754 }, 3755 }, 3756 ResultFile: &FileInfo{ 3757 Path: GetTestFilePath("table1.csv"), 3758 Delimiter: ',', 3759 NoHeader: false, 3760 Encoding: text.UTF8, 3761 LineBreak: text.LF, 3762 ForUpdate: true, 3763 }, 3764 UpdateCount: 2, 3765 ViewCache: GenerateViewMap([]*View{ 3766 { 3767 FileInfo: &FileInfo{ 3768 Path: GetTestFilePath("table1.csv"), 3769 Delimiter: ',', 3770 NoHeader: false, 3771 Encoding: text.UTF8, 3772 LineBreak: text.LF, 3773 ForUpdate: true, 3774 }, 3775 Header: NewHeader("table1", []string{"column3", "column4", "column1", "column2"}), 3776 RecordSet: []Record{ 3777 NewRecord([]value.Primary{ 3778 value.NewInteger(2), 3779 value.NewInteger(1), 3780 value.NewString("1"), 3781 value.NewString("str1"), 3782 }), 3783 NewRecord([]value.Primary{ 3784 value.NewInteger(2), 3785 value.NewInteger(1), 3786 value.NewString("2"), 3787 value.NewString("str2"), 3788 }), 3789 NewRecord([]value.Primary{ 3790 value.NewInteger(2), 3791 value.NewInteger(1), 3792 value.NewString("3"), 3793 value.NewString("str3"), 3794 }), 3795 }, 3796 }, 3797 }), 3798 }, 3799 { 3800 Name: "Add Fields After", 3801 Query: parser.AddColumns{ 3802 Table: parser.Identifier{Literal: "table1.csv"}, 3803 Columns: []parser.ColumnDefault{ 3804 { 3805 Column: parser.Identifier{Literal: "column3"}, 3806 }, 3807 { 3808 Column: parser.Identifier{Literal: "column4"}, 3809 Value: parser.NewIntegerValueFromString("1"), 3810 }, 3811 }, 3812 Position: parser.ColumnPosition{ 3813 Position: parser.Token{Token: parser.AFTER}, 3814 Column: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}, 3815 }, 3816 }, 3817 ResultFile: &FileInfo{ 3818 Path: GetTestFilePath("table1.csv"), 3819 Delimiter: ',', 3820 NoHeader: false, 3821 Encoding: text.UTF8, 3822 LineBreak: text.LF, 3823 ForUpdate: true, 3824 }, 3825 UpdateCount: 2, 3826 ViewCache: GenerateViewMap([]*View{ 3827 { 3828 FileInfo: &FileInfo{ 3829 Path: GetTestFilePath("table1.csv"), 3830 Delimiter: ',', 3831 NoHeader: false, 3832 Encoding: text.UTF8, 3833 LineBreak: text.LF, 3834 ForUpdate: true, 3835 }, 3836 Header: NewHeader("table1", []string{"column1", "column3", "column4", "column2"}), 3837 RecordSet: []Record{ 3838 NewRecord([]value.Primary{ 3839 value.NewString("1"), 3840 value.NewNull(), 3841 value.NewInteger(1), 3842 value.NewString("str1"), 3843 }), 3844 NewRecord([]value.Primary{ 3845 value.NewString("2"), 3846 value.NewNull(), 3847 value.NewInteger(1), 3848 value.NewString("str2"), 3849 }), 3850 NewRecord([]value.Primary{ 3851 value.NewString("3"), 3852 value.NewNull(), 3853 value.NewInteger(1), 3854 value.NewString("str3"), 3855 }), 3856 }, 3857 }, 3858 }), 3859 }, 3860 { 3861 Name: "Add Fields Before", 3862 Query: parser.AddColumns{ 3863 Table: parser.Identifier{Literal: "table1.csv"}, 3864 Columns: []parser.ColumnDefault{ 3865 { 3866 Column: parser.Identifier{Literal: "column3"}, 3867 }, 3868 { 3869 Column: parser.Identifier{Literal: "column4"}, 3870 Value: parser.NewIntegerValueFromString("1"), 3871 }, 3872 }, 3873 Position: parser.ColumnPosition{ 3874 Position: parser.Token{Token: parser.BEFORE}, 3875 Column: parser.ColumnNumber{View: parser.Identifier{Literal: "table1"}, Number: value.NewInteger(2)}, 3876 }, 3877 }, 3878 ResultFile: &FileInfo{ 3879 Path: GetTestFilePath("table1.csv"), 3880 Delimiter: ',', 3881 NoHeader: false, 3882 Encoding: text.UTF8, 3883 LineBreak: text.LF, 3884 ForUpdate: true, 3885 }, 3886 UpdateCount: 2, 3887 ViewCache: GenerateViewMap([]*View{ 3888 { 3889 FileInfo: &FileInfo{ 3890 Path: GetTestFilePath("table1.csv"), 3891 Delimiter: ',', 3892 NoHeader: false, 3893 Encoding: text.UTF8, 3894 LineBreak: text.LF, 3895 ForUpdate: true, 3896 }, 3897 Header: NewHeader("table1", []string{"column1", "column3", "column4", "column2"}), 3898 RecordSet: []Record{ 3899 NewRecord([]value.Primary{ 3900 value.NewString("1"), 3901 value.NewNull(), 3902 value.NewInteger(1), 3903 value.NewString("str1"), 3904 }), 3905 NewRecord([]value.Primary{ 3906 value.NewString("2"), 3907 value.NewNull(), 3908 value.NewInteger(1), 3909 value.NewString("str2"), 3910 }), 3911 NewRecord([]value.Primary{ 3912 value.NewString("3"), 3913 value.NewNull(), 3914 value.NewInteger(1), 3915 value.NewString("str3"), 3916 }), 3917 }, 3918 }, 3919 }), 3920 }, 3921 { 3922 Name: "Add Fields Load Error", 3923 Query: parser.AddColumns{ 3924 Table: parser.Identifier{Literal: "notexist"}, 3925 Columns: []parser.ColumnDefault{ 3926 { 3927 Column: parser.Identifier{Literal: "column3"}, 3928 }, 3929 { 3930 Column: parser.Identifier{Literal: "column4"}, 3931 }, 3932 }, 3933 }, 3934 Error: "file notexist does not exist", 3935 }, 3936 { 3937 Name: "Add Fields Position Column Does Not Exist Error", 3938 Query: parser.AddColumns{ 3939 Table: parser.Identifier{Literal: "table1.csv"}, 3940 Columns: []parser.ColumnDefault{ 3941 { 3942 Column: parser.Identifier{Literal: "column3"}, 3943 }, 3944 { 3945 Column: parser.Identifier{Literal: "column2"}, 3946 Value: parser.NewIntegerValueFromString("1"), 3947 }, 3948 }, 3949 Position: parser.ColumnPosition{ 3950 Position: parser.Token{Token: parser.BEFORE}, 3951 Column: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 3952 }, 3953 }, 3954 Error: "field notexist does not exist", 3955 }, 3956 { 3957 Name: "Add Fields Field Duplicate Error", 3958 Query: parser.AddColumns{ 3959 Table: parser.Identifier{Literal: "table1.csv"}, 3960 Columns: []parser.ColumnDefault{ 3961 { 3962 Column: parser.Identifier{Literal: "column3"}, 3963 }, 3964 { 3965 Column: parser.Identifier{Literal: "column1"}, 3966 Value: parser.NewIntegerValueFromString("1"), 3967 }, 3968 }, 3969 }, 3970 Error: "field name column1 is a duplicate", 3971 }, 3972 { 3973 Name: "Add Fields Default Value Error", 3974 Query: parser.AddColumns{ 3975 Table: parser.Identifier{Literal: "table1.csv"}, 3976 Columns: []parser.ColumnDefault{ 3977 { 3978 Column: parser.Identifier{Literal: "column3"}, 3979 }, 3980 { 3981 Column: parser.Identifier{Literal: "column4"}, 3982 Value: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 3983 }, 3984 }, 3985 }, 3986 Error: "field notexist does not exist", 3987 }, 3988 } 3989 3990 func TestAddColumns(t *testing.T) { 3991 defer func() { 3992 _ = TestTx.ReleaseResources() 3993 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 3994 initFlag(TestTx.Flags) 3995 }() 3996 3997 TestTx.Flags.Repository = TestDir 3998 TestTx.Flags.Quiet = false 3999 4000 scope := GenerateReferenceScope([]map[string]map[string]interface{}{ 4001 { 4002 scopeNameTempTables: { 4003 "TMPVIEW": &View{ 4004 Header: NewHeader("tmpview", []string{"column1", "column2"}), 4005 RecordSet: []Record{ 4006 NewRecord([]value.Primary{ 4007 value.NewString("1"), 4008 value.NewString("str1"), 4009 }), 4010 NewRecord([]value.Primary{ 4011 value.NewString("2"), 4012 value.NewString("str2"), 4013 }), 4014 }, 4015 FileInfo: &FileInfo{ 4016 Path: "tmpview", 4017 Delimiter: ',', 4018 ViewType: ViewTypeTemporaryTable, 4019 }, 4020 }, 4021 }, 4022 }, 4023 }, nil, time.Time{}, nil) 4024 4025 ctx := context.Background() 4026 for _, v := range addColumnsTests { 4027 _ = TestTx.ReleaseResources() 4028 result, cnt, err := AddColumns(ctx, scope, v.Query) 4029 if err != nil { 4030 if len(v.Error) < 1 { 4031 t.Errorf("%s: unexpected error %q", v.Name, err) 4032 } else if err.Error() != v.Error { 4033 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 4034 } 4035 continue 4036 } 4037 if 0 < len(v.Error) { 4038 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 4039 continue 4040 } 4041 4042 TestTx.CachedViews.Range(func(key, value interface{}) bool { 4043 view := value.(*View) 4044 if view.FileInfo.Handler != nil { 4045 if view.FileInfo.Path != view.FileInfo.Handler.Path() { 4046 t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name) 4047 } 4048 _ = TestTx.FileContainer.Close(view.FileInfo.Handler) 4049 view.FileInfo.Handler = nil 4050 } 4051 return true 4052 }) 4053 4054 if !reflect.DeepEqual(result, v.ResultFile) { 4055 t.Errorf("%s: fileinfo = %v, want %v", v.Name, result, v.ResultFile) 4056 } 4057 4058 if cnt != v.UpdateCount { 4059 t.Errorf("%s: update count = %d, want %d", v.Name, cnt, v.UpdateCount) 4060 } 4061 4062 if v.ViewCache.SyncMap != nil { 4063 if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) { 4064 t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache) 4065 } 4066 } 4067 if v.ResultScopes != nil { 4068 if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) { 4069 t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) 4070 } 4071 } 4072 } 4073 } 4074 4075 var dropColumnsTests = []struct { 4076 Name string 4077 Query parser.DropColumns 4078 Result *FileInfo 4079 UpdateCount int 4080 ViewCache ViewMap 4081 ResultScopes *ReferenceScope 4082 Error string 4083 }{ 4084 { 4085 Name: "Drop Fields", 4086 Query: parser.DropColumns{ 4087 Table: parser.Identifier{Literal: "table1"}, 4088 Columns: []parser.QueryExpression{ 4089 parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 4090 }, 4091 }, 4092 Result: &FileInfo{ 4093 Path: GetTestFilePath("table1.csv"), 4094 Delimiter: ',', 4095 NoHeader: false, 4096 Encoding: text.UTF8, 4097 LineBreak: text.LF, 4098 ForUpdate: true, 4099 }, 4100 UpdateCount: 1, 4101 ViewCache: GenerateViewMap([]*View{ 4102 { 4103 FileInfo: &FileInfo{ 4104 Path: GetTestFilePath("table1.csv"), 4105 Delimiter: ',', 4106 NoHeader: false, 4107 Encoding: text.UTF8, 4108 LineBreak: text.LF, 4109 ForUpdate: true, 4110 }, 4111 Header: NewHeader("table1", []string{"column1"}), 4112 RecordSet: []Record{ 4113 NewRecord([]value.Primary{ 4114 value.NewString("1"), 4115 }), 4116 NewRecord([]value.Primary{ 4117 value.NewString("2"), 4118 }), 4119 NewRecord([]value.Primary{ 4120 value.NewString("3"), 4121 }), 4122 }, 4123 }, 4124 }), 4125 }, 4126 { 4127 Name: "Drop Fields For Temporary View", 4128 Query: parser.DropColumns{ 4129 Table: parser.Identifier{Literal: "tmpview"}, 4130 Columns: []parser.QueryExpression{ 4131 parser.ColumnNumber{View: parser.Identifier{Literal: "tmpview"}, Number: value.NewInteger(2)}, 4132 }, 4133 }, 4134 Result: &FileInfo{ 4135 Path: "tmpview", 4136 Delimiter: ',', 4137 ViewType: ViewTypeTemporaryTable, 4138 }, 4139 UpdateCount: 1, 4140 ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{ 4141 { 4142 scopeNameTempTables: { 4143 "TMPVIEW": &View{ 4144 Header: NewHeader("tmpview", []string{"column1"}), 4145 RecordSet: []Record{ 4146 NewRecord([]value.Primary{ 4147 value.NewString("1"), 4148 }), 4149 NewRecord([]value.Primary{ 4150 value.NewString("2"), 4151 }), 4152 }, 4153 FileInfo: &FileInfo{ 4154 Path: "tmpview", 4155 Delimiter: ',', 4156 ViewType: ViewTypeTemporaryTable, 4157 }, 4158 }, 4159 }, 4160 }, 4161 }, nil, time.Time{}, nil), 4162 }, 4163 { 4164 Name: "Drop Fields Load Error", 4165 Query: parser.DropColumns{ 4166 Table: parser.Identifier{Literal: "notexist"}, 4167 Columns: []parser.QueryExpression{ 4168 parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 4169 }, 4170 }, 4171 Error: "file notexist does not exist", 4172 }, 4173 { 4174 Name: "Drop Fields Field Does Not Exist Error", 4175 Query: parser.DropColumns{ 4176 Table: parser.Identifier{Literal: "table1"}, 4177 Columns: []parser.QueryExpression{ 4178 parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 4179 }, 4180 }, 4181 Error: "field notexist does not exist", 4182 }, 4183 } 4184 4185 func TestDropColumns(t *testing.T) { 4186 defer func() { 4187 _ = TestTx.ReleaseResources() 4188 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 4189 initFlag(TestTx.Flags) 4190 }() 4191 4192 TestTx.Flags.Repository = TestDir 4193 TestTx.Flags.Quiet = false 4194 4195 scope := GenerateReferenceScope([]map[string]map[string]interface{}{ 4196 { 4197 scopeNameTempTables: { 4198 "TMPVIEW": &View{ 4199 Header: NewHeader("tmpview", []string{"column1", "column2"}), 4200 RecordSet: []Record{ 4201 NewRecord([]value.Primary{ 4202 value.NewString("1"), 4203 value.NewString("str1"), 4204 }), 4205 NewRecord([]value.Primary{ 4206 value.NewString("2"), 4207 value.NewString("str2"), 4208 }), 4209 }, 4210 FileInfo: &FileInfo{ 4211 Path: "tmpview", 4212 Delimiter: ',', 4213 ViewType: ViewTypeTemporaryTable, 4214 }, 4215 }, 4216 }, 4217 }, 4218 }, nil, time.Time{}, nil) 4219 4220 ctx := context.Background() 4221 for _, v := range dropColumnsTests { 4222 _ = TestTx.ReleaseResources() 4223 result, cnt, err := DropColumns(ctx, scope, v.Query) 4224 if err != nil { 4225 if len(v.Error) < 1 { 4226 t.Errorf("%s: unexpected error %q", v.Name, err) 4227 } else if err.Error() != v.Error { 4228 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 4229 } 4230 continue 4231 } 4232 if 0 < len(v.Error) { 4233 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 4234 continue 4235 } 4236 4237 TestTx.CachedViews.Range(func(key, value interface{}) bool { 4238 view := value.(*View) 4239 if view.FileInfo.Handler != nil { 4240 if view.FileInfo.Path != view.FileInfo.Handler.Path() { 4241 t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name) 4242 } 4243 _ = TestTx.FileContainer.Close(view.FileInfo.Handler) 4244 view.FileInfo.Handler = nil 4245 } 4246 return true 4247 }) 4248 4249 if !reflect.DeepEqual(result, v.Result) { 4250 t.Errorf("%s: fileinfo = %v, want %v", v.Name, result, v.Result) 4251 } 4252 4253 if cnt != v.UpdateCount { 4254 t.Errorf("%s: update count = %d, want %d", v.Name, cnt, v.UpdateCount) 4255 } 4256 4257 if v.ViewCache.SyncMap != nil { 4258 if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) { 4259 t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache) 4260 } 4261 } 4262 if v.ResultScopes != nil { 4263 if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) { 4264 t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) 4265 } 4266 } 4267 } 4268 } 4269 4270 var renameColumnTests = []struct { 4271 Name string 4272 Query parser.RenameColumn 4273 Result *FileInfo 4274 ViewCache ViewMap 4275 ResultScopes *ReferenceScope 4276 Error string 4277 }{ 4278 { 4279 Name: "Rename Column", 4280 Query: parser.RenameColumn{ 4281 Table: parser.Identifier{Literal: "table1"}, 4282 Old: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 4283 New: parser.Identifier{Literal: "newcolumn"}, 4284 }, 4285 Result: &FileInfo{ 4286 Path: GetTestFilePath("table1.csv"), 4287 Delimiter: ',', 4288 NoHeader: false, 4289 Encoding: text.UTF8, 4290 LineBreak: text.LF, 4291 ForUpdate: true, 4292 }, 4293 ViewCache: GenerateViewMap([]*View{ 4294 { 4295 FileInfo: &FileInfo{ 4296 Path: GetTestFilePath("table1.csv"), 4297 Delimiter: ',', 4298 NoHeader: false, 4299 Encoding: text.UTF8, 4300 LineBreak: text.LF, 4301 ForUpdate: true, 4302 }, 4303 Header: NewHeader("table1", []string{"column1", "newcolumn"}), 4304 RecordSet: []Record{ 4305 NewRecord([]value.Primary{ 4306 value.NewString("1"), 4307 value.NewString("str1"), 4308 }), 4309 NewRecord([]value.Primary{ 4310 value.NewString("2"), 4311 value.NewString("str2"), 4312 }), 4313 NewRecord([]value.Primary{ 4314 value.NewString("3"), 4315 value.NewString("str3"), 4316 }), 4317 }, 4318 }, 4319 }), 4320 }, 4321 { 4322 Name: "Rename Column For Temporary View", 4323 Query: parser.RenameColumn{ 4324 Table: parser.Identifier{Literal: "tmpview"}, 4325 Old: parser.ColumnNumber{View: parser.Identifier{Literal: "tmpview"}, Number: value.NewInteger(2)}, 4326 New: parser.Identifier{Literal: "newcolumn"}, 4327 }, 4328 Result: &FileInfo{ 4329 Path: "tmpview", 4330 Delimiter: ',', 4331 ViewType: ViewTypeTemporaryTable, 4332 }, 4333 ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{ 4334 { 4335 scopeNameTempTables: { 4336 "TMPVIEW": &View{ 4337 Header: NewHeader("tmpview", []string{"column1", "newcolumn"}), 4338 RecordSet: []Record{ 4339 NewRecord([]value.Primary{ 4340 value.NewString("1"), 4341 value.NewString("str1"), 4342 }), 4343 NewRecord([]value.Primary{ 4344 value.NewString("2"), 4345 value.NewString("str2"), 4346 }), 4347 }, 4348 FileInfo: &FileInfo{ 4349 Path: "tmpview", 4350 Delimiter: ',', 4351 ViewType: ViewTypeTemporaryTable, 4352 }, 4353 }, 4354 }, 4355 }, 4356 }, nil, time.Time{}, nil), 4357 }, 4358 { 4359 Name: "Rename Column Load Error", 4360 Query: parser.RenameColumn{ 4361 Table: parser.Identifier{Literal: "notexist"}, 4362 Old: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 4363 New: parser.Identifier{Literal: "newcolumn"}, 4364 }, 4365 Error: "file notexist does not exist", 4366 }, 4367 { 4368 Name: "Rename Column Field Duplicate Error", 4369 Query: parser.RenameColumn{ 4370 Table: parser.Identifier{Literal: "table1"}, 4371 Old: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}, 4372 New: parser.Identifier{Literal: "column1"}, 4373 }, 4374 Error: "field name column1 is a duplicate", 4375 }, 4376 { 4377 Name: "Rename Column Field Does Not Exist Error", 4378 Query: parser.RenameColumn{ 4379 Table: parser.Identifier{Literal: "table1"}, 4380 Old: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 4381 New: parser.Identifier{Literal: "newcolumn"}, 4382 }, 4383 Error: "field notexist does not exist", 4384 }, 4385 } 4386 4387 func TestRenameColumn(t *testing.T) { 4388 defer func() { 4389 _ = TestTx.ReleaseResources() 4390 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 4391 initFlag(TestTx.Flags) 4392 }() 4393 4394 TestTx.Flags.Repository = TestDir 4395 TestTx.Flags.Quiet = false 4396 4397 scope := GenerateReferenceScope([]map[string]map[string]interface{}{ 4398 { 4399 scopeNameTempTables: { 4400 "TMPVIEW": &View{ 4401 Header: NewHeader("tmpview", []string{"column1", "column2"}), 4402 RecordSet: []Record{ 4403 NewRecord([]value.Primary{ 4404 value.NewString("1"), 4405 value.NewString("str1"), 4406 }), 4407 NewRecord([]value.Primary{ 4408 value.NewString("2"), 4409 value.NewString("str2"), 4410 }), 4411 }, 4412 FileInfo: &FileInfo{ 4413 Path: "tmpview", 4414 Delimiter: ',', 4415 ViewType: ViewTypeTemporaryTable, 4416 }, 4417 }, 4418 }, 4419 }, 4420 }, nil, time.Time{}, nil) 4421 4422 ctx := context.Background() 4423 for _, v := range renameColumnTests { 4424 _ = TestTx.ReleaseResources() 4425 result, err := RenameColumn(ctx, scope, v.Query) 4426 if err != nil { 4427 if len(v.Error) < 1 { 4428 t.Errorf("%s: unexpected error %q", v.Name, err) 4429 } else if err.Error() != v.Error { 4430 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 4431 } 4432 continue 4433 } 4434 if 0 < len(v.Error) { 4435 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 4436 continue 4437 } 4438 4439 TestTx.CachedViews.Range(func(key, value interface{}) bool { 4440 view := value.(*View) 4441 if view.FileInfo.Handler != nil { 4442 if view.FileInfo.Path != view.FileInfo.Handler.Path() { 4443 t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name) 4444 } 4445 _ = TestTx.FileContainer.Close(view.FileInfo.Handler) 4446 view.FileInfo.Handler = nil 4447 } 4448 return true 4449 }) 4450 4451 if !reflect.DeepEqual(result, v.Result) { 4452 t.Errorf("%s: result = %v, want %v", v.Name, result, v.Result) 4453 } 4454 4455 if v.ViewCache.SyncMap != nil { 4456 if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) { 4457 t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache) 4458 } 4459 } 4460 if v.ResultScopes != nil { 4461 if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) { 4462 t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) 4463 } 4464 } 4465 } 4466 } 4467 4468 var setTableAttributeTests = []struct { 4469 Name string 4470 Query parser.SetTableAttribute 4471 Expect *FileInfo 4472 Error string 4473 }{ 4474 { 4475 Name: "Set Delimiter to TSV", 4476 Query: parser.SetTableAttribute{ 4477 Table: parser.Identifier{Literal: "table1.csv"}, 4478 Attribute: parser.Identifier{Literal: "delimiter"}, 4479 Value: parser.NewStringValue("\t"), 4480 }, 4481 Expect: &FileInfo{ 4482 Path: GetTestFilePath("table1.csv"), 4483 Delimiter: '\t', 4484 Format: option.TSV, 4485 Encoding: text.UTF8, 4486 LineBreak: text.LF, 4487 ForUpdate: true, 4488 }, 4489 }, 4490 { 4491 Name: "Set Delimiter to TSV with FormatSpecifiedFunction", 4492 Query: parser.SetTableAttribute{ 4493 Table: parser.FormatSpecifiedFunction{ 4494 Type: parser.Token{Token: parser.CSV, Literal: "csv"}, 4495 Path: parser.Identifier{Literal: "table1.csv"}, 4496 FormatElement: parser.NewStringValue(","), 4497 }, 4498 Attribute: parser.Identifier{Literal: "delimiter"}, 4499 Value: parser.NewStringValue("\t"), 4500 }, 4501 Expect: &FileInfo{ 4502 Path: GetTestFilePath("table1.csv"), 4503 Delimiter: '\t', 4504 Format: option.TSV, 4505 Encoding: text.UTF8, 4506 LineBreak: text.LF, 4507 ForUpdate: true, 4508 }, 4509 }, 4510 { 4511 Name: "Set Delimiter to CSV", 4512 Query: parser.SetTableAttribute{ 4513 Table: parser.Identifier{Literal: "table1.csv"}, 4514 Attribute: parser.Identifier{Literal: "delimiter"}, 4515 Value: parser.NewStringValue(";"), 4516 }, 4517 Expect: &FileInfo{ 4518 Path: GetTestFilePath("table1.csv"), 4519 Delimiter: ';', 4520 Format: option.CSV, 4521 Encoding: text.UTF8, 4522 LineBreak: text.LF, 4523 ForUpdate: true, 4524 }, 4525 }, 4526 { 4527 Name: "Set Delimiter Error", 4528 Query: parser.SetTableAttribute{ 4529 Table: parser.Identifier{Literal: "table1.csv"}, 4530 Attribute: parser.Identifier{Literal: "delimiter"}, 4531 Value: parser.NewStringValue("aa"), 4532 }, 4533 Error: "delimiter must be one character", 4534 }, 4535 { 4536 Name: "Set Delimiter Not Allowed Value", 4537 Query: parser.SetTableAttribute{ 4538 Table: parser.Identifier{Literal: "table1.csv"}, 4539 Attribute: parser.Identifier{Literal: "delimiter"}, 4540 Value: parser.NewNullValue(), 4541 }, 4542 Error: "NULL for delimiter is not allowed", 4543 }, 4544 { 4545 Name: "Set DelimiterPositions", 4546 Query: parser.SetTableAttribute{ 4547 Table: parser.Identifier{Literal: "table1.csv"}, 4548 Attribute: parser.Identifier{Literal: "delimiter_positions"}, 4549 Value: parser.NewStringValue("S[2, 5, 10]"), 4550 }, 4551 Expect: &FileInfo{ 4552 Path: GetTestFilePath("table1.csv"), 4553 Delimiter: ',', 4554 DelimiterPositions: []int{2, 5, 10}, 4555 Format: option.FIXED, 4556 Encoding: text.UTF8, 4557 SingleLine: true, 4558 LineBreak: text.LF, 4559 ForUpdate: true, 4560 }, 4561 }, 4562 { 4563 Name: "Set DelimiterPositions Error", 4564 Query: parser.SetTableAttribute{ 4565 Table: parser.Identifier{Literal: "table1.csv"}, 4566 Attribute: parser.Identifier{Literal: "delimiter_positions"}, 4567 Value: parser.NewStringValue("invalid"), 4568 }, 4569 Error: "delimiter positions must be \"SPACES\" or a JSON array of integers", 4570 }, 4571 { 4572 Name: "Set Format to Text", 4573 Query: parser.SetTableAttribute{ 4574 Table: parser.Identifier{Literal: "table1.csv"}, 4575 Attribute: parser.Identifier{Literal: "format"}, 4576 Value: parser.NewStringValue("text"), 4577 }, 4578 Expect: &FileInfo{ 4579 Path: GetTestFilePath("table1.csv"), 4580 Delimiter: ',', 4581 Format: option.TEXT, 4582 Encoding: text.UTF8, 4583 LineBreak: text.LF, 4584 ForUpdate: true, 4585 }, 4586 }, 4587 { 4588 Name: "Set Format to JSON", 4589 Query: parser.SetTableAttribute{ 4590 Table: parser.Identifier{Literal: "table1.csv"}, 4591 Attribute: parser.Identifier{Literal: "format"}, 4592 Value: parser.NewStringValue("json"), 4593 }, 4594 Expect: &FileInfo{ 4595 Path: GetTestFilePath("table1.csv"), 4596 Delimiter: ',', 4597 Format: option.JSON, 4598 Encoding: text.UTF8, 4599 LineBreak: text.LF, 4600 ForUpdate: true, 4601 }, 4602 }, 4603 { 4604 Name: "Set Format to TSV", 4605 Query: parser.SetTableAttribute{ 4606 Table: parser.Identifier{Literal: "table1.csv"}, 4607 Attribute: parser.Identifier{Literal: "format"}, 4608 Value: parser.NewStringValue("tsv"), 4609 }, 4610 Expect: &FileInfo{ 4611 Path: GetTestFilePath("table1.csv"), 4612 Delimiter: '\t', 4613 Format: option.TSV, 4614 Encoding: text.UTF8, 4615 LineBreak: text.LF, 4616 ForUpdate: true, 4617 }, 4618 }, 4619 { 4620 Name: "Set Format Error", 4621 Query: parser.SetTableAttribute{ 4622 Table: parser.Identifier{Literal: "table1.csv"}, 4623 Attribute: parser.Identifier{Literal: "format"}, 4624 Value: parser.NewStringValue("invalid"), 4625 }, 4626 Error: "format must be one of CSV|TSV|FIXED|JSON|JSONL|LTSV|GFM|ORG|BOX|TEXT", 4627 }, 4628 { 4629 Name: "Set Encoding to SJIS", 4630 Query: parser.SetTableAttribute{ 4631 Table: parser.Identifier{Literal: "table1.csv"}, 4632 Attribute: parser.Identifier{Literal: "encoding"}, 4633 Value: parser.NewStringValue("sjis"), 4634 }, 4635 Expect: &FileInfo{ 4636 Path: GetTestFilePath("table1.csv"), 4637 Delimiter: ',', 4638 Format: option.CSV, 4639 Encoding: text.SJIS, 4640 LineBreak: text.LF, 4641 ForUpdate: true, 4642 }, 4643 }, 4644 { 4645 Name: "Set Encoding to SJIS with Identifier", 4646 Query: parser.SetTableAttribute{ 4647 Table: parser.Identifier{Literal: "table1.csv"}, 4648 Attribute: parser.Identifier{Literal: "encoding"}, 4649 Value: parser.Identifier{Literal: "sjis"}, 4650 }, 4651 Expect: &FileInfo{ 4652 Path: GetTestFilePath("table1.csv"), 4653 Delimiter: ',', 4654 Format: option.CSV, 4655 Encoding: text.SJIS, 4656 LineBreak: text.LF, 4657 ForUpdate: true, 4658 }, 4659 }, 4660 { 4661 Name: "Set Encoding Error", 4662 Query: parser.SetTableAttribute{ 4663 Table: parser.Identifier{Literal: "table1.csv"}, 4664 Attribute: parser.Identifier{Literal: "encoding"}, 4665 Value: parser.NewStringValue("invalid"), 4666 }, 4667 Error: "encoding must be one of UTF8|UTF8M|UTF16|UTF16BE|UTF16LE|UTF16BEM|UTF16LEM|SJIS", 4668 }, 4669 { 4670 Name: "Set Encoding Error in JSON Format", 4671 Query: parser.SetTableAttribute{ 4672 Table: parser.Identifier{Literal: "table.json"}, 4673 Attribute: parser.Identifier{Literal: "encoding"}, 4674 Value: parser.NewStringValue("sjis"), 4675 }, 4676 Error: "json format is supported only UTF8", 4677 }, 4678 { 4679 Name: "Set LineBreak to CRLF", 4680 Query: parser.SetTableAttribute{ 4681 Table: parser.Identifier{Literal: "table1.csv"}, 4682 Attribute: parser.Identifier{Literal: "line_break"}, 4683 Value: parser.NewStringValue("crlf"), 4684 }, 4685 Expect: &FileInfo{ 4686 Path: GetTestFilePath("table1.csv"), 4687 Delimiter: ',', 4688 Format: option.CSV, 4689 Encoding: text.UTF8, 4690 LineBreak: text.CRLF, 4691 ForUpdate: true, 4692 }, 4693 }, 4694 { 4695 Name: "Set LineBreak Error", 4696 Query: parser.SetTableAttribute{ 4697 Table: parser.Identifier{Literal: "table1.csv"}, 4698 Attribute: parser.Identifier{Literal: "line_break"}, 4699 Value: parser.NewStringValue("invalid"), 4700 }, 4701 Error: "line-break must be one of CRLF|CR|LF", 4702 }, 4703 { 4704 Name: "Set NoHeader to true", 4705 Query: parser.SetTableAttribute{ 4706 Table: parser.Identifier{Literal: "table1.csv"}, 4707 Attribute: parser.Identifier{Literal: "header"}, 4708 Value: parser.NewTernaryValueFromString("false"), 4709 }, 4710 Expect: &FileInfo{ 4711 Path: GetTestFilePath("table1.csv"), 4712 Delimiter: ',', 4713 Format: option.CSV, 4714 Encoding: text.UTF8, 4715 LineBreak: text.LF, 4716 NoHeader: true, 4717 ForUpdate: true, 4718 }, 4719 }, 4720 { 4721 Name: "Set NoHeader Not Allowed Value", 4722 Query: parser.SetTableAttribute{ 4723 Table: parser.Identifier{Literal: "table1.csv"}, 4724 Attribute: parser.Identifier{Literal: "header"}, 4725 Value: parser.NewNullValue(), 4726 }, 4727 Error: "NULL for header is not allowed", 4728 }, 4729 { 4730 Name: "Set EncloseAll to true", 4731 Query: parser.SetTableAttribute{ 4732 Table: parser.Identifier{Literal: "table1.csv"}, 4733 Attribute: parser.Identifier{Literal: "enclose_all"}, 4734 Value: parser.NewStringValue("true"), 4735 }, 4736 Expect: &FileInfo{ 4737 Path: GetTestFilePath("table1.csv"), 4738 Delimiter: ',', 4739 Format: option.CSV, 4740 Encoding: text.UTF8, 4741 LineBreak: text.LF, 4742 EncloseAll: true, 4743 ForUpdate: true, 4744 }, 4745 }, 4746 { 4747 Name: "Set JsonEscape to HEX", 4748 Query: parser.SetTableAttribute{ 4749 Table: parser.Identifier{Literal: "table.json"}, 4750 Attribute: parser.Identifier{Literal: "json_escape"}, 4751 Value: parser.NewStringValue("hex"), 4752 }, 4753 Expect: &FileInfo{ 4754 Path: GetTestFilePath("table.json"), 4755 Delimiter: ',', 4756 Format: option.JSON, 4757 Encoding: text.UTF8, 4758 LineBreak: text.LF, 4759 JsonEscape: json.HexDigits, 4760 PrettyPrint: false, 4761 ForUpdate: true, 4762 }, 4763 }, 4764 { 4765 Name: "Set JsonEscape Error", 4766 Query: parser.SetTableAttribute{ 4767 Table: parser.Identifier{Literal: "table.json"}, 4768 Attribute: parser.Identifier{Literal: "json_escape"}, 4769 Value: parser.NewStringValue("invalid"), 4770 }, 4771 Error: "json escape type must be one of BACKSLASH|HEX|HEXALL", 4772 }, 4773 { 4774 Name: "Set PrettyPring to true", 4775 Query: parser.SetTableAttribute{ 4776 Table: parser.Identifier{Literal: "table.json"}, 4777 Attribute: parser.Identifier{Literal: "pretty_print"}, 4778 Value: parser.NewTernaryValueFromString("true"), 4779 }, 4780 Expect: &FileInfo{ 4781 Path: GetTestFilePath("table.json"), 4782 Delimiter: ',', 4783 Format: option.JSON, 4784 Encoding: text.UTF8, 4785 LineBreak: text.LF, 4786 PrettyPrint: true, 4787 ForUpdate: true, 4788 }, 4789 }, 4790 { 4791 Name: "Not Exist Table Error", 4792 Query: parser.SetTableAttribute{ 4793 Table: parser.Identifier{Literal: "notexist.csv"}, 4794 Attribute: parser.Identifier{Literal: "delimiter"}, 4795 Value: parser.NewStringValue(","), 4796 }, 4797 Error: "file notexist.csv does not exist", 4798 }, 4799 { 4800 Name: "Temporary View Error", 4801 Query: parser.SetTableAttribute{ 4802 Table: parser.Identifier{Literal: "tmpview"}, 4803 Attribute: parser.Identifier{Literal: "delimiter"}, 4804 Value: parser.NewStringValue(","), 4805 }, 4806 Error: "table attributes can only be set on files", 4807 }, 4808 { 4809 Name: "Value Evaluation Error", 4810 Query: parser.SetTableAttribute{ 4811 Table: parser.Identifier{Literal: "table1.csv"}, 4812 Attribute: parser.Identifier{Literal: "delimiter"}, 4813 Value: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 4814 }, 4815 Error: "field notexist does not exist", 4816 }, 4817 { 4818 Name: "Not Exist Attribute Error", 4819 Query: parser.SetTableAttribute{ 4820 Table: parser.Identifier{Literal: "table1.csv"}, 4821 Attribute: parser.Identifier{Literal: "notexist"}, 4822 Value: parser.NewStringValue(","), 4823 }, 4824 Error: "table attribute notexist does not exist", 4825 }, 4826 } 4827 4828 func TestSetTableAttribute(t *testing.T) { 4829 defer func() { 4830 _ = TestTx.ReleaseResources() 4831 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 4832 initFlag(TestTx.Flags) 4833 }() 4834 4835 TestTx.Flags.Repository = TestDir 4836 TestTx.Flags.Quiet = false 4837 4838 scope := GenerateReferenceScope([]map[string]map[string]interface{}{ 4839 { 4840 scopeNameTempTables: { 4841 "TMPVIEW": &View{ 4842 Header: NewHeader("tmpview", []string{"column1", "column2"}), 4843 RecordSet: []Record{ 4844 NewRecord([]value.Primary{ 4845 value.NewString("1"), 4846 value.NewString("str1"), 4847 }), 4848 NewRecord([]value.Primary{ 4849 value.NewString("2"), 4850 value.NewString("str2"), 4851 }), 4852 }, 4853 FileInfo: &FileInfo{ 4854 Path: "tmpview", 4855 Delimiter: ',', 4856 ViewType: ViewTypeTemporaryTable, 4857 }, 4858 }, 4859 }, 4860 }, 4861 }, nil, time.Time{}, nil) 4862 4863 ctx := context.Background() 4864 for _, v := range setTableAttributeTests { 4865 _ = TestTx.ReleaseResources() 4866 4867 _, _, err := SetTableAttribute(ctx, scope, v.Query) 4868 if err != nil { 4869 if len(v.Error) < 1 { 4870 t.Errorf("%s: unexpected error %q", v.Name, err) 4871 } else if err.Error() != v.Error { 4872 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 4873 } 4874 continue 4875 } 4876 if 0 < len(v.Error) { 4877 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 4878 continue 4879 } 4880 4881 TestTx.CachedViews.Range(func(key, value interface{}) bool { 4882 view := value.(*View) 4883 if view.FileInfo.Handler != nil { 4884 if view.FileInfo.Path != view.FileInfo.Handler.Path() { 4885 t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name) 4886 } 4887 _ = TestTx.FileContainer.Close(view.FileInfo.Handler) 4888 view.FileInfo.Handler = nil 4889 } 4890 return true 4891 }) 4892 4893 view, _ := LoadViewFromTableIdentifier(ctx, scope.CreateNode(), v.Query.Table, false, false) 4894 4895 if !reflect.DeepEqual(view.FileInfo, v.Expect) { 4896 t.Errorf("%s: result = %v, want %v", v.Name, view.FileInfo, v.Expect) 4897 } 4898 4899 _, _, err = SetTableAttribute(ctx, scope, v.Query) 4900 if err == nil { 4901 t.Errorf("%s: no error, want TableAttributeUnchangedError for duplicate set", v.Name) 4902 } else if _, ok := err.(*TableAttributeUnchangedError); !ok { 4903 t.Errorf("%s: error = %T, want TableAttributeUnchangedError for duplicate set", v.Name, err) 4904 } 4905 } 4906 }