github.com/mithrandie/csvq@v1.18.1/lib/query/cursor_test.go (about) 1 package query 2 3 import ( 4 "context" 5 "reflect" 6 "sync" 7 "testing" 8 9 "github.com/mithrandie/go-text" 10 11 "github.com/mithrandie/csvq/lib/parser" 12 "github.com/mithrandie/csvq/lib/value" 13 14 "github.com/mithrandie/ternary" 15 ) 16 17 var selectQueryForCursorTest = parser.SelectQuery{ 18 SelectEntity: parser.SelectEntity{ 19 SelectClause: parser.SelectClause{ 20 Fields: []parser.QueryExpression{ 21 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}}, 22 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 23 }, 24 }, 25 FromClause: parser.FromClause{ 26 Tables: []parser.QueryExpression{ 27 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 28 }, 29 }, 30 }, 31 } 32 33 var selectQueryForCursorQueryErrorTest = parser.SelectQuery{ 34 SelectEntity: parser.SelectEntity{ 35 SelectClause: parser.SelectClause{ 36 Fields: []parser.QueryExpression{ 37 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}}, 38 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}}, 39 }, 40 }, 41 FromClause: parser.FromClause{ 42 Tables: []parser.QueryExpression{ 43 parser.Table{Object: parser.Identifier{Literal: "table1"}}, 44 }, 45 }, 46 }, 47 } 48 49 var cursorMapDeclareTests = []struct { 50 Name string 51 Expr parser.CursorDeclaration 52 Result CursorMap 53 Error string 54 }{ 55 { 56 Name: "CursorMap Declare", 57 Expr: parser.CursorDeclaration{ 58 Cursor: parser.Identifier{Literal: "cur"}, 59 Query: selectQueryForCursorTest, 60 }, 61 Result: GenerateCursorMap([]*Cursor{ 62 { 63 Name: "cur", 64 query: selectQueryForCursorTest, 65 mtx: &sync.Mutex{}, 66 }, 67 }), 68 }, 69 { 70 Name: "CursorMap Declare for Statement", 71 Expr: parser.CursorDeclaration{ 72 Cursor: parser.Identifier{Literal: "stmtcur"}, 73 Statement: parser.Identifier{Literal: "stmt"}, 74 }, 75 Result: GenerateCursorMap([]*Cursor{ 76 { 77 Name: "cur", 78 query: selectQueryForCursorTest, 79 mtx: &sync.Mutex{}, 80 }, 81 { 82 Name: "stmtcur", 83 statement: parser.Identifier{Literal: "stmt"}, 84 mtx: &sync.Mutex{}, 85 }, 86 }), 87 }, 88 { 89 Name: "CursorMap Declare Redeclaration Error", 90 Expr: parser.CursorDeclaration{ 91 Cursor: parser.Identifier{Literal: "cur"}, 92 Query: parser.SelectQuery{}, 93 }, 94 Error: "cursor cur is redeclared", 95 }, 96 } 97 98 func TestCursorMap_Declare(t *testing.T) { 99 cursors := NewCursorMap() 100 101 for _, v := range cursorMapDeclareTests { 102 err := cursors.Declare(v.Expr) 103 if err != nil { 104 if len(v.Error) < 1 { 105 t.Errorf("%s: unexpected error %q", v.Name, err) 106 } else if err.Error() != v.Error { 107 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 108 } 109 continue 110 } 111 if 0 < len(v.Error) { 112 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 113 continue 114 } 115 if !SyncMapEqual(cursors, v.Result) { 116 t.Errorf("%s: result = %v, want %v", v.Name, cursors, v.Result) 117 } 118 } 119 } 120 121 var cursorMapAddPseudoCursorTests = []struct { 122 Name string 123 Cursor parser.Identifier 124 Values []value.Primary 125 Result CursorMap 126 Error string 127 }{ 128 { 129 Name: "CursorMap AddPseudoCursor", 130 Cursor: parser.Identifier{Literal: "pcur"}, 131 Values: []value.Primary{ 132 value.NewInteger(1), 133 value.NewInteger(2), 134 }, 135 Result: GenerateCursorMap([]*Cursor{ 136 { 137 Name: "pcur", 138 view: &View{ 139 Header: NewHeader("", []string{"c1"}), 140 RecordSet: RecordSet{ 141 NewRecord([]value.Primary{value.NewInteger(1)}), 142 NewRecord([]value.Primary{value.NewInteger(2)}), 143 }, 144 }, 145 index: -1, 146 isPseudo: true, 147 mtx: &sync.Mutex{}, 148 }, 149 }), 150 }, 151 { 152 Name: "CursorMap AddPseudoCursor Redeclaration Error", 153 Cursor: parser.Identifier{Literal: "pcur"}, 154 Values: []value.Primary{}, 155 Error: "cursor pcur is redeclared", 156 }, 157 } 158 159 func TestCursorMap_AddPseudoCursor(t *testing.T) { 160 cursors := NewCursorMap() 161 for _, v := range cursorMapAddPseudoCursorTests { 162 err := cursors.AddPseudoCursor(v.Cursor, v.Values) 163 if err != nil { 164 if len(v.Error) < 1 { 165 t.Errorf("%s: unexpected error %q", v.Name, err) 166 } else if err.Error() != v.Error { 167 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 168 } 169 continue 170 } 171 if 0 < len(v.Error) { 172 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 173 continue 174 } 175 if !SyncMapEqual(cursors, v.Result) { 176 t.Errorf("%s: result = %v, want %v", v.Name, cursors, v.Result) 177 } 178 } 179 } 180 181 var cursorMapDisposeTests = []struct { 182 Name string 183 CurName parser.Identifier 184 Result CursorMap 185 Error string 186 }{ 187 { 188 Name: "CursorMap Dispose", 189 CurName: parser.Identifier{Literal: "cur"}, 190 Result: GenerateCursorMap([]*Cursor{ 191 { 192 Name: "pcur", 193 view: &View{ 194 Header: NewHeader("", []string{"c1"}), 195 RecordSet: RecordSet{ 196 NewRecord([]value.Primary{value.NewInteger(1)}), 197 NewRecord([]value.Primary{value.NewInteger(2)}), 198 }, 199 }, 200 index: -1, 201 isPseudo: true, 202 mtx: &sync.Mutex{}, 203 }, 204 }), 205 }, 206 { 207 Name: "CursorMap Dispose Undeclared Error", 208 CurName: parser.Identifier{Literal: "notexist"}, 209 Error: "undeclared cursor", 210 }, 211 { 212 Name: "CursorMap Dispose Rseudo Cursor Error", 213 CurName: parser.Identifier{Literal: "pcur"}, 214 Error: "unpermmitted pseudo cursor usage", 215 }, 216 } 217 218 func TestCursorMap_Dispose(t *testing.T) { 219 cursors := GenerateCursorMap([]*Cursor{ 220 { 221 Name: "cur", 222 query: selectQueryForCursorTest, 223 mtx: &sync.Mutex{}, 224 }, 225 }) 226 _ = cursors.AddPseudoCursor( 227 parser.Identifier{Literal: "pcur"}, 228 []value.Primary{ 229 value.NewInteger(1), 230 value.NewInteger(2), 231 }, 232 ) 233 234 for _, v := range cursorMapDisposeTests { 235 err := cursors.Dispose(v.CurName) 236 if err != nil { 237 if len(v.Error) < 1 { 238 t.Errorf("%s: unexpected error %q", v.Name, err) 239 } else if err.Error() != v.Error { 240 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 241 } 242 continue 243 } 244 if 0 < len(v.Error) { 245 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 246 continue 247 } 248 if !SyncMapEqual(cursors, v.Result) { 249 t.Errorf("%s: result = %v, want %v", v.Name, cursors, v.Result) 250 } 251 } 252 } 253 254 var cursorMapOpenTests = []struct { 255 Name string 256 CurName parser.Identifier 257 CurValues []parser.ReplaceValue 258 Result CursorMap 259 Error string 260 }{ 261 { 262 Name: "CursorMap Open", 263 CurName: parser.Identifier{Literal: "cur"}, 264 Result: GenerateCursorMap([]*Cursor{ 265 { 266 Name: "cur", 267 query: selectQueryForCursorTest, 268 view: &View{ 269 Header: NewHeader("table1", []string{"column1", "column2"}), 270 RecordSet: []Record{ 271 NewRecord([]value.Primary{ 272 value.NewString("1"), 273 value.NewString("str1"), 274 }), 275 NewRecord([]value.Primary{ 276 value.NewString("2"), 277 value.NewString("str2"), 278 }), 279 NewRecord([]value.Primary{ 280 value.NewString("3"), 281 value.NewString("str3"), 282 }), 283 }, 284 FileInfo: &FileInfo{ 285 Path: GetTestFilePath("table1.csv"), 286 Delimiter: ',', 287 NoHeader: false, 288 Encoding: text.UTF8, 289 LineBreak: text.LF, 290 }, 291 }, 292 index: -1, 293 mtx: &sync.Mutex{}, 294 }, 295 { 296 Name: "cur2", 297 query: selectQueryForCursorQueryErrorTest, 298 mtx: &sync.Mutex{}, 299 }, 300 { 301 Name: "pcur", 302 view: &View{ 303 Header: NewHeader("", []string{"c1"}), 304 RecordSet: RecordSet{ 305 NewRecord([]value.Primary{value.NewInteger(1)}), 306 NewRecord([]value.Primary{value.NewInteger(2)}), 307 }, 308 }, 309 index: -1, 310 isPseudo: true, 311 mtx: &sync.Mutex{}, 312 }, 313 { 314 Name: "stmt", 315 statement: parser.Identifier{Literal: "stmt"}, 316 mtx: &sync.Mutex{}, 317 }, 318 { 319 Name: "not_exist_stmt", 320 statement: parser.Identifier{Literal: "not_exist_stmt"}, 321 mtx: &sync.Mutex{}, 322 }, 323 { 324 Name: "invalid_stmt", 325 statement: parser.Identifier{Literal: "invalid_stmt"}, 326 mtx: &sync.Mutex{}, 327 }, 328 { 329 Name: "invalid_stmt2", 330 statement: parser.Identifier{Literal: "invalid_stmt2"}, 331 mtx: &sync.Mutex{}, 332 }, 333 }), 334 }, 335 { 336 Name: "CursorMap Open Statement", 337 CurName: parser.Identifier{Literal: "stmt"}, 338 CurValues: []parser.ReplaceValue{ 339 {Value: parser.NewIntegerValueFromString("2")}, 340 }, 341 Result: GenerateCursorMap([]*Cursor{ 342 { 343 Name: "stmt", 344 statement: parser.Identifier{Literal: "stmt"}, 345 view: &View{ 346 Header: NewHeader("table1", []string{"column1", "column2"}), 347 RecordSet: []Record{ 348 NewRecord([]value.Primary{ 349 value.NewString("2"), 350 value.NewString("str2"), 351 }), 352 }, 353 FileInfo: &FileInfo{ 354 Path: GetTestFilePath("table1.csv"), 355 Delimiter: ',', 356 NoHeader: false, 357 Encoding: text.UTF8, 358 LineBreak: text.LF, 359 }, 360 }, 361 index: -1, 362 mtx: &sync.Mutex{}, 363 }, 364 { 365 Name: "cur", 366 query: selectQueryForCursorTest, 367 view: &View{ 368 Header: NewHeader("table1", []string{"column1", "column2"}), 369 RecordSet: []Record{ 370 NewRecord([]value.Primary{ 371 value.NewString("1"), 372 value.NewString("str1"), 373 }), 374 NewRecord([]value.Primary{ 375 value.NewString("2"), 376 value.NewString("str2"), 377 }), 378 NewRecord([]value.Primary{ 379 value.NewString("3"), 380 value.NewString("str3"), 381 }), 382 }, 383 FileInfo: &FileInfo{ 384 Path: GetTestFilePath("table1.csv"), 385 Delimiter: ',', 386 NoHeader: false, 387 Encoding: text.UTF8, 388 LineBreak: text.LF, 389 }, 390 }, 391 index: -1, 392 mtx: &sync.Mutex{}, 393 }, 394 { 395 Name: "cur2", 396 query: selectQueryForCursorQueryErrorTest, 397 mtx: &sync.Mutex{}, 398 }, 399 { 400 Name: "pcur", 401 view: &View{ 402 Header: NewHeader("", []string{"c1"}), 403 RecordSet: RecordSet{ 404 NewRecord([]value.Primary{value.NewInteger(1)}), 405 NewRecord([]value.Primary{value.NewInteger(2)}), 406 }, 407 }, 408 index: -1, 409 isPseudo: true, 410 mtx: &sync.Mutex{}, 411 }, 412 { 413 Name: "not_exist_stmt", 414 statement: parser.Identifier{Literal: "not_exist_stmt"}, 415 mtx: &sync.Mutex{}, 416 }, 417 { 418 Name: "invalid_stmt", 419 statement: parser.Identifier{Literal: "invalid_stmt"}, 420 mtx: &sync.Mutex{}, 421 }, 422 { 423 Name: "invalid_stmt2", 424 statement: parser.Identifier{Literal: "invalid_stmt2"}, 425 mtx: &sync.Mutex{}, 426 }, 427 }), 428 }, 429 { 430 Name: "CursorMap Open Undeclared Error", 431 CurName: parser.Identifier{Literal: "notexist"}, 432 Error: "undeclared cursor", 433 }, 434 { 435 Name: "CursorMap Open Open Error", 436 CurName: parser.Identifier{Literal: "cur"}, 437 Error: "cursor cur is already open", 438 }, 439 { 440 Name: "CursorMap Open Query Error", 441 CurName: parser.Identifier{Literal: "cur2"}, 442 Error: "field notexist does not exist", 443 }, 444 { 445 Name: "CursorMap Open Rseudo Cursor Error", 446 CurName: parser.Identifier{Literal: "pcur"}, 447 Error: "cursor pcur is a pseudo cursor", 448 }, 449 { 450 Name: "CursorMap Open Unprepared Statement", 451 CurName: parser.Identifier{Literal: "not_exist_stmt"}, 452 Error: "statement not_exist_stmt does not exist", 453 }, 454 { 455 Name: "CursorMap Open Not Select Query Error", 456 CurName: parser.Identifier{Literal: "invalid_stmt"}, 457 Error: "invalid cursor statement: invalid_stmt", 458 }, 459 { 460 Name: "CursorMap Open Multiple Statements Error", 461 CurName: parser.Identifier{Literal: "invalid_stmt2"}, 462 Error: "invalid cursor statement: invalid_stmt2", 463 }, 464 } 465 466 func TestCursorMap_Open(t *testing.T) { 467 defer func() { 468 TestTx.PreparedStatements = NewPreparedStatementMap() 469 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 470 initFlag(TestTx.Flags) 471 }() 472 473 scope := NewReferenceScope(TestTx) 474 TestTx.Flags.Repository = TestDir 475 _ = TestTx.PreparedStatements.Prepare(scope.Tx.Flags, parser.StatementPreparation{ 476 Name: parser.Identifier{Literal: "stmt"}, 477 Statement: value.NewString("select * from table1 where column1 = ?"), 478 }) 479 _ = TestTx.PreparedStatements.Prepare(scope.Tx.Flags, parser.StatementPreparation{ 480 Name: parser.Identifier{Literal: "invalid_stmt"}, 481 Statement: value.NewString("insert into table1 values (?, ?)"), 482 }) 483 _ = TestTx.PreparedStatements.Prepare(scope.Tx.Flags, parser.StatementPreparation{ 484 Name: parser.Identifier{Literal: "invalid_stmt2"}, 485 Statement: value.NewString("select 1; insert into table1 values (?, ?);"), 486 }) 487 488 scope.Blocks[0].Cursors = GenerateCursorMap([]*Cursor{ 489 { 490 Name: "cur", 491 query: selectQueryForCursorTest, 492 mtx: &sync.Mutex{}, 493 }, 494 { 495 Name: "cur2", 496 query: selectQueryForCursorQueryErrorTest, 497 mtx: &sync.Mutex{}, 498 }, 499 { 500 Name: "stmt", 501 statement: parser.Identifier{Literal: "stmt"}, 502 mtx: &sync.Mutex{}, 503 }, 504 { 505 Name: "not_exist_stmt", 506 statement: parser.Identifier{Literal: "not_exist_stmt"}, 507 mtx: &sync.Mutex{}, 508 }, 509 { 510 Name: "invalid_stmt", 511 statement: parser.Identifier{Literal: "invalid_stmt"}, 512 mtx: &sync.Mutex{}, 513 }, 514 { 515 Name: "invalid_stmt2", 516 statement: parser.Identifier{Literal: "invalid_stmt2"}, 517 mtx: &sync.Mutex{}, 518 }, 519 }) 520 _ = scope.Blocks[0].Cursors.AddPseudoCursor( 521 parser.Identifier{Literal: "pcur"}, 522 []value.Primary{ 523 value.NewInteger(1), 524 value.NewInteger(2), 525 }, 526 ) 527 528 ctx := context.Background() 529 for _, v := range cursorMapOpenTests { 530 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 531 err := scope.Blocks[0].Cursors.Open(ctx, scope, v.CurName, v.CurValues) 532 if err != nil { 533 if len(v.Error) < 1 { 534 t.Errorf("%s: unexpected error %q", v.Name, err) 535 } else if err.Error() != v.Error { 536 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 537 } 538 continue 539 } 540 if 0 < len(v.Error) { 541 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 542 continue 543 } 544 if !SyncMapEqual(scope.Blocks[0].Cursors, v.Result) { 545 t.Errorf("%s: result = %v, want %v", v.Name, scope.Blocks[0].Cursors, v.Result) 546 } 547 } 548 } 549 550 var cursorMapCloseTests = []struct { 551 Name string 552 CurName parser.Identifier 553 Result CursorMap 554 Error string 555 }{ 556 { 557 Name: "CursorMap Close", 558 CurName: parser.Identifier{Literal: "cur"}, 559 Result: GenerateCursorMap([]*Cursor{ 560 { 561 Name: "cur", 562 query: selectQueryForCursorTest, 563 mtx: &sync.Mutex{}, 564 }, 565 { 566 Name: "pcur", 567 view: &View{ 568 Header: NewHeader("", []string{"c1"}), 569 RecordSet: RecordSet{ 570 NewRecord([]value.Primary{value.NewInteger(1)}), 571 NewRecord([]value.Primary{value.NewInteger(2)}), 572 }, 573 }, 574 index: -1, 575 isPseudo: true, 576 mtx: &sync.Mutex{}, 577 }, 578 }), 579 }, 580 { 581 Name: "CursorMap Close Rseudo Cursor Error", 582 CurName: parser.Identifier{Literal: "pcur"}, 583 Error: "cursor pcur is a pseudo cursor", 584 }, 585 { 586 Name: "CursorMap Close Undeclared Error", 587 CurName: parser.Identifier{Literal: "notexist"}, 588 Error: "undeclared cursor", 589 }, 590 } 591 592 func TestCursorMap_Close(t *testing.T) { 593 defer func() { 594 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 595 initFlag(TestTx.Flags) 596 }() 597 598 TestTx.Flags.Repository = TestDir 599 600 cursors := GenerateCursorMap([]*Cursor{ 601 { 602 Name: "cur", 603 query: selectQueryForCursorTest, 604 mtx: &sync.Mutex{}, 605 }, 606 }) 607 _ = cursors.AddPseudoCursor( 608 parser.Identifier{Literal: "pcur"}, 609 []value.Primary{ 610 value.NewInteger(1), 611 value.NewInteger(2), 612 }, 613 ) 614 scope := NewReferenceScope(TestTx) 615 ctx := context.Background() 616 _ = cursors.Open(ctx, scope, parser.Identifier{Literal: "cur"}, nil) 617 618 for _, v := range cursorMapCloseTests { 619 err := cursors.Close(v.CurName) 620 if err != nil { 621 if len(v.Error) < 1 { 622 t.Errorf("%s: unexpected error %q", v.Name, err) 623 } else if err.Error() != v.Error { 624 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 625 } 626 continue 627 } 628 if 0 < len(v.Error) { 629 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 630 continue 631 } 632 if !SyncMapEqual(cursors, v.Result) { 633 t.Errorf("%s: result = %v, want %v", v.Name, cursors, v.Result) 634 } 635 } 636 } 637 638 var cursorMapFetchTests = []struct { 639 Name string 640 CurName parser.Identifier 641 Position int 642 Number int 643 Result []value.Primary 644 Error string 645 }{ 646 { 647 Name: "CursorMap Fetch First Time", 648 CurName: parser.Identifier{Literal: "cur"}, 649 Position: parser.NEXT, 650 Result: []value.Primary{ 651 value.NewString("1"), 652 value.NewString("str1"), 653 }, 654 }, 655 { 656 Name: "CursorMap Fetch Second Time", 657 CurName: parser.Identifier{Literal: "cur"}, 658 Position: parser.NEXT, 659 Result: []value.Primary{ 660 value.NewString("2"), 661 value.NewString("str2"), 662 }, 663 }, 664 { 665 Name: "CursorMap Fetch Third Time", 666 CurName: parser.Identifier{Literal: "cur"}, 667 Position: parser.NEXT, 668 Result: []value.Primary{ 669 value.NewString("3"), 670 value.NewString("str3"), 671 }, 672 }, 673 { 674 Name: "CursorMap Fetch Fourth Time", 675 CurName: parser.Identifier{Literal: "cur"}, 676 Position: parser.NEXT, 677 Result: nil, 678 }, 679 { 680 Name: "CursorMap Fetch First", 681 CurName: parser.Identifier{Literal: "cur"}, 682 Position: parser.FIRST, 683 Result: []value.Primary{ 684 value.NewString("1"), 685 value.NewString("str1"), 686 }, 687 }, 688 { 689 Name: "CursorMap Fetch Last", 690 CurName: parser.Identifier{Literal: "cur"}, 691 Position: parser.LAST, 692 Result: []value.Primary{ 693 value.NewString("3"), 694 value.NewString("str3"), 695 }, 696 }, 697 { 698 Name: "CursorMap Fetch Prior", 699 CurName: parser.Identifier{Literal: "cur"}, 700 Position: parser.PRIOR, 701 Result: []value.Primary{ 702 value.NewString("2"), 703 value.NewString("str2"), 704 }, 705 }, 706 { 707 Name: "CursorMap Fetch Absolute", 708 CurName: parser.Identifier{Literal: "cur"}, 709 Position: parser.ABSOLUTE, 710 Number: 1, 711 Result: []value.Primary{ 712 value.NewString("2"), 713 value.NewString("str2"), 714 }, 715 }, 716 { 717 Name: "CursorMap Fetch Relative", 718 CurName: parser.Identifier{Literal: "cur"}, 719 Position: parser.RELATIVE, 720 Number: -1, 721 Result: []value.Primary{ 722 value.NewString("1"), 723 value.NewString("str1"), 724 }, 725 }, 726 { 727 Name: "CursorMap Fetch Prior to Last", 728 CurName: parser.Identifier{Literal: "cur"}, 729 Position: parser.ABSOLUTE, 730 Number: -2, 731 Result: nil, 732 }, 733 { 734 Name: "CursorMap Fetch Later than Last", 735 CurName: parser.Identifier{Literal: "cur"}, 736 Position: parser.ABSOLUTE, 737 Number: 100, 738 Result: nil, 739 }, 740 { 741 Name: "CursorMap Fetch Undeclared Error", 742 CurName: parser.Identifier{Literal: "notexist"}, 743 Position: parser.NEXT, 744 Error: "undeclared cursor", 745 }, 746 { 747 Name: "CursorMap Fetch Closed Error", 748 CurName: parser.Identifier{Literal: "cur2"}, 749 Position: parser.NEXT, 750 Error: "cursor cur2 is closed", 751 }, 752 } 753 754 func TestCursorMap_Fetch(t *testing.T) { 755 defer func() { 756 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 757 initFlag(TestTx.Flags) 758 }() 759 760 TestTx.Flags.Repository = TestDir 761 762 cursors := GenerateCursorMap([]*Cursor{ 763 { 764 Name: "cur", 765 query: selectQueryForCursorTest, 766 mtx: &sync.Mutex{}, 767 }, 768 { 769 Name: "cur2", 770 query: selectQueryForCursorTest, 771 mtx: &sync.Mutex{}, 772 }, 773 }) 774 scope := NewReferenceScope(TestTx) 775 ctx := context.Background() 776 _ = cursors.Open(ctx, scope, parser.Identifier{Literal: "cur"}, nil) 777 778 for _, v := range cursorMapFetchTests { 779 result, err := cursors.Fetch(v.CurName, v.Position, v.Number) 780 if err != nil { 781 if len(v.Error) < 1 { 782 t.Errorf("%s: unexpected error %q", v.Name, err) 783 } else if err.Error() != v.Error { 784 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 785 } 786 continue 787 } 788 if 0 < len(v.Error) { 789 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 790 continue 791 } 792 if !reflect.DeepEqual(result, v.Result) { 793 t.Errorf("%s: result = %s, want %s", v.Name, result, v.Result) 794 } 795 } 796 } 797 798 var cursorMapIsOpenTests = []struct { 799 Name string 800 CurName parser.Identifier 801 Result ternary.Value 802 Error string 803 }{ 804 { 805 Name: "CursorMap IsOpen TRUE", 806 CurName: parser.Identifier{Literal: "cur"}, 807 Result: ternary.TRUE, 808 }, 809 { 810 Name: "CursorMap IsOpen FALSE", 811 CurName: parser.Identifier{Literal: "cur2"}, 812 Result: ternary.FALSE, 813 }, 814 { 815 Name: "CursorMap IsOpen Undeclared Error", 816 CurName: parser.Identifier{Literal: "notexist"}, 817 Error: "undeclared cursor", 818 }, 819 } 820 821 func TestCursorMap_IsOpen(t *testing.T) { 822 defer func() { 823 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 824 initFlag(TestTx.Flags) 825 }() 826 827 TestTx.Flags.Repository = TestDir 828 829 cursors := GenerateCursorMap([]*Cursor{ 830 { 831 Name: "cur", 832 query: selectQueryForCursorTest, 833 mtx: &sync.Mutex{}, 834 }, 835 { 836 Name: "cur2", 837 query: selectQueryForCursorTest, 838 mtx: &sync.Mutex{}, 839 }, 840 }) 841 scope := NewReferenceScope(TestTx) 842 ctx := context.Background() 843 _ = cursors.Open(ctx, scope, parser.Identifier{Literal: "cur"}, nil) 844 845 for _, v := range cursorMapIsOpenTests { 846 result, err := cursors.IsOpen(v.CurName) 847 if err != nil { 848 if len(v.Error) < 1 { 849 t.Errorf("%s: unexpected error %q", v.Name, err) 850 } else if err.Error() != v.Error { 851 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 852 } 853 continue 854 } 855 if 0 < len(v.Error) { 856 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 857 continue 858 } 859 if result != v.Result { 860 t.Errorf("%s: result = %s, want %s", v.Name, result, v.Result) 861 } 862 } 863 } 864 865 var cursorMapIsInRangeTests = []struct { 866 Name string 867 CurName parser.Identifier 868 Index int 869 Result ternary.Value 870 Error string 871 }{ 872 { 873 Name: "CursorMap Is In Range UNKNOWN", 874 CurName: parser.Identifier{Literal: "cur"}, 875 Result: ternary.UNKNOWN, 876 }, 877 { 878 Name: "CursorMap Is In Range TRUE", 879 CurName: parser.Identifier{Literal: "cur2"}, 880 Index: 1, 881 Result: ternary.TRUE, 882 }, 883 { 884 Name: "CursorMap Is In Range FALSE", 885 CurName: parser.Identifier{Literal: "cur2"}, 886 Index: -1, 887 Result: ternary.FALSE, 888 }, 889 { 890 Name: "CursorMap Is In Range Not Open Error", 891 CurName: parser.Identifier{Literal: "cur3"}, 892 Error: "cursor cur3 is closed", 893 }, 894 { 895 Name: "CursorMap Is In Range Undeclared Error", 896 CurName: parser.Identifier{Literal: "notexist"}, 897 Error: "undeclared cursor", 898 }, 899 } 900 901 func TestCursorMap_IsInRange(t *testing.T) { 902 defer func() { 903 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 904 initFlag(TestTx.Flags) 905 }() 906 907 TestTx.Flags.Repository = TestDir 908 909 cursors := GenerateCursorMap([]*Cursor{ 910 { 911 Name: "cur", 912 query: selectQueryForCursorTest, 913 mtx: &sync.Mutex{}, 914 }, 915 { 916 Name: "cur2", 917 query: selectQueryForCursorTest, 918 mtx: &sync.Mutex{}, 919 }, 920 { 921 Name: "cur3", 922 query: selectQueryForCursorTest, 923 mtx: &sync.Mutex{}, 924 }, 925 }) 926 scope := NewReferenceScope(TestTx) 927 ctx := context.Background() 928 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 929 _ = cursors.Open(ctx, scope, parser.Identifier{Literal: "cur"}, nil) 930 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 931 _ = cursors.Open(ctx, scope, parser.Identifier{Literal: "cur2"}, nil) 932 _, _ = cursors.Fetch(parser.Identifier{Literal: "cur2"}, parser.NEXT, 0) 933 934 for _, v := range cursorMapIsInRangeTests { 935 if 0 != v.Index { 936 c, _ := cursors.Load("CUR2") 937 c.index = v.Index 938 } 939 result, err := cursors.IsInRange(v.CurName) 940 if err != nil { 941 if len(v.Error) < 1 { 942 t.Errorf("%s: unexpected error %q", v.Name, err) 943 } else if err.Error() != v.Error { 944 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 945 } 946 continue 947 } 948 if 0 < len(v.Error) { 949 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 950 continue 951 } 952 if result != v.Result { 953 t.Errorf("%s: result = %s, want %s", v.Name, result, v.Result) 954 } 955 } 956 } 957 958 var cursorMapCountTests = []struct { 959 Name string 960 CurName parser.Identifier 961 Result int 962 Error string 963 }{ 964 { 965 Name: "CursorMap Count", 966 CurName: parser.Identifier{Literal: "cur"}, 967 Result: 3, 968 }, 969 { 970 Name: "CursorMap Count Not Open Error", 971 CurName: parser.Identifier{Literal: "cur2"}, 972 Error: "cursor cur2 is closed", 973 }, 974 { 975 Name: "CursorMap Count Undeclared Error", 976 CurName: parser.Identifier{Literal: "notexist"}, 977 Error: "undeclared cursor", 978 }, 979 } 980 981 func TestCursorMap_Count(t *testing.T) { 982 defer func() { 983 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 984 initFlag(TestTx.Flags) 985 }() 986 987 TestTx.Flags.Repository = TestDir 988 989 cursors := GenerateCursorMap([]*Cursor{ 990 { 991 Name: "cur", 992 query: selectQueryForCursorTest, 993 mtx: &sync.Mutex{}, 994 }, 995 { 996 Name: "cur2", 997 query: selectQueryForCursorTest, 998 mtx: &sync.Mutex{}, 999 }, 1000 }) 1001 _ = TestTx.CachedViews.Clean(TestTx.FileContainer) 1002 scope := NewReferenceScope(TestTx) 1003 ctx := context.Background() 1004 _ = cursors.Open(ctx, scope, parser.Identifier{Literal: "cur"}, nil) 1005 1006 for _, v := range cursorMapCountTests { 1007 result, err := cursors.Count(v.CurName) 1008 if err != nil { 1009 if len(v.Error) < 1 { 1010 t.Errorf("%s: unexpected error %q", v.Name, err) 1011 } else if err.Error() != v.Error { 1012 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 1013 } 1014 continue 1015 } 1016 if 0 < len(v.Error) { 1017 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 1018 continue 1019 } 1020 if result != v.Result { 1021 t.Errorf("%s: result = %v, want %v", v.Name, result, v.Result) 1022 } 1023 } 1024 }