1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2009 Oracle. All rights reserved. 5 * 6 */ 7using System; 8using System.Collections; 9using System.Collections.Generic; 10using System.IO; 11using System.Text; 12using System.Threading; 13using NUnit.Framework; 14using BerkeleyDB; 15 16namespace CsharpAPITest 17{ 18 [TestFixture] 19 public class CursorTest 20 { 21 private string testFixtureName; 22 private string testFixtureHome; 23 private string testName; 24 private string testHome; 25 private DatabaseEnvironment paramEnv; 26 private BTreeDatabase paramDB; 27 private Transaction readTxn; 28 private Transaction updateTxn; 29 30 private EventWaitHandle signal; 31 32 private delegate void CursorMoveFuncDelegate( 33 Cursor cursor, LockingInfo lockingInfo); 34 private CursorMoveFuncDelegate cursorFunc; 35 36 [TestFixtureSetUp] 37 public void RunBeforeTests() 38 { 39 testFixtureName = "CursorTest"; 40 testFixtureHome = "./TestOut/" + testFixtureName; 41 } 42 43 [Test] 44 public void TestAdd() 45 { 46 BTreeDatabase db; 47 BTreeCursor cursor; 48 49 testName = "TestAdd"; 50 testHome = testFixtureHome + "/" + testName; 51 52 Configuration.ClearDir(testHome); 53 54 // Open a database and a cursor. 55 GetCursorInBtreeDBWithoutEnv(testHome, testName, 56 out db, out cursor); 57 58 // Add a record and confirm that it exists. 59 AddOneByCursor(db, cursor); 60 61 cursor.Close(); 62 db.Close(); 63 } 64 65 [Test] 66 public void TestCompare() { 67 BTreeDatabase db; 68 BTreeCursor dbc1, dbc2; 69 DatabaseEntry data, key; 70 71 testName = "TestCompare"; 72 testHome = testFixtureHome + "/" + testName; 73 74 Configuration.ClearDir(testHome); 75 76 // Open a database and a cursor. Then close it. 77 GetCursorInBtreeDBWithoutEnv(testHome, testName, 78 out db, out dbc1); 79 dbc2 = db.Cursor(); 80 81 for (int i = 0; i < 10; i++) { 82 key = new DatabaseEntry(BitConverter.GetBytes(i)); 83 data = new DatabaseEntry(BitConverter.GetBytes(i)); 84 db.Put(key, data); 85 } 86 key = new DatabaseEntry(BitConverter.GetBytes(5)); 87 Assert.IsTrue(dbc1.Move(key, true)); 88 Assert.IsTrue(dbc2.Move(key, true)); 89 Assert.IsTrue(dbc1.Compare(dbc2)); 90 Assert.IsTrue(dbc1.MoveNext()); 91 Assert.IsFalse(dbc1.Compare(dbc2)); 92 dbc1.Close(); 93 dbc2.Close(); 94 db.Close(); 95 } 96 97 [Test] 98 public void TestClose() 99 { 100 BTreeDatabase db; 101 BTreeCursor cursor; 102 103 testName = "TestClose"; 104 testHome = testFixtureHome + "/" + testName; 105 106 Configuration.ClearDir(testHome); 107 108 // Open a database and a cursor. Then close it. 109 GetCursorInBtreeDBWithoutEnv(testHome, testName, 110 out db, out cursor); 111 cursor.Close(); 112 db.Close(); 113 } 114 115 [Test] 116 public void TestCount() 117 { 118 BTreeDatabase db; 119 BTreeCursor cursor; 120 121 testName = "TestCount"; 122 testHome = testFixtureHome + "/" + testName; 123 124 Configuration.ClearDir(testHome); 125 126 // Write one record into database with cursor. 127 GetCursorInBtreeDBWithoutEnv(testHome, testName, 128 out db, out cursor); 129 AddOneByCursor(db, cursor); 130 131 /* 132 * Confirm that that the count operation returns 1 as 133 * the number of records in the database. 134 */ 135 Assert.AreEqual(1, cursor.Count()); 136 cursor.Close(); 137 db.Close(); 138 } 139 140 [Test] 141 public void TestCurrent() 142 { 143 BTreeDatabase db; 144 BTreeCursor cursor; 145 146 testName = "TestCurrent"; 147 testHome = testFixtureHome + "/" + testName; 148 149 Configuration.ClearDir(testHome); 150 151 // Write a record into database with cursor. 152 GetCursorInBtreeDBWithoutEnv(testHome, 153 testName, out db, out cursor); 154 AddOneByCursor(db, cursor); 155 156 /* 157 * Confirm the current record that the cursor 158 * points to is the one that just added by the 159 * cursor. 160 */ 161 Assert.IsTrue(cursor.MoveFirst()); 162 Assert.AreEqual( 163 ASCIIEncoding.ASCII.GetBytes("key"), 164 cursor.Current.Key.Data); 165 Assert.AreEqual( 166 ASCIIEncoding.ASCII.GetBytes("data"), 167 cursor.Current.Value.Data); 168 cursor.Close(); 169 db.Close(); 170 } 171 172 [Test] 173 public void TestDelete() 174 { 175 BTreeDatabase db; 176 BTreeCursor cursor; 177 178 testName = "TestDelete"; 179 testHome = testFixtureHome + "/" + testName; 180 181 Configuration.ClearDir(testHome); 182 183 // Write a record into database with cursor. 184 GetCursorInBtreeDBWithoutEnv(testHome, 185 testName, out db, out cursor); 186 AddOneByCursor(db, cursor); 187 188 // Delete the current record. 189 cursor.Delete(); 190 191 // Confirm that the record no longer exists in the db. 192 Assert.AreEqual(0, cursor.Count()); 193 194 cursor.Close(); 195 db.Close(); 196 } 197 198 [Test] 199 public void TestDispose() 200 { 201 BTreeDatabase db; 202 BTreeCursor cursor; 203 204 testName = "TestDispose"; 205 testHome = testFixtureHome + "/" + testName; 206 207 Configuration.ClearDir(testHome); 208 209 // Write a record into database with cursor. 210 GetCursorInBtreeDBWithoutEnv(testHome, 211 testName, out db, out cursor); 212 213 // Dispose the cursor. 214 cursor.Dispose(); 215 216 db.Close(); 217 } 218 219 [Test] 220 public void TestIsolationDegree() 221 { 222 BTreeDatabase db; 223 BTreeCursor cursor; 224 CursorConfig cursorConfig; 225 DatabaseEnvironment env; 226 Transaction txn; 227 228 testName = "TestIsolationDegree"; 229 testHome = testFixtureHome + "/" + testName; 230 231 Isolation[] isolationDegrees = new Isolation[3]; 232 isolationDegrees[0] = Isolation.DEGREE_ONE; 233 isolationDegrees[1] = Isolation.DEGREE_TWO; 234 isolationDegrees[2] = Isolation.DEGREE_THREE; 235 236 IsolationDelegate[] delegates = { 237 new IsolationDelegate(CursorReadUncommited), 238 new IsolationDelegate(CursorReadCommited), 239 new IsolationDelegate(CursorRead)}; 240 241 cursorConfig = new CursorConfig(); 242 for (int i = 0; i < 3; i++) 243 { 244 cursorConfig.IsolationDegree = isolationDegrees[i]; 245 GetCursorInBtreeDBInTDS(testHome + "/" + i.ToString(), 246 testName, cursorConfig, out env, out db, 247 out cursor, out txn); 248 cursor.Close(); 249 db.Close(); 250 txn.Commit(); 251 env.Close(); 252 } 253 } 254 255 public delegate void IsolationDelegate( 256 DatabaseEnvironment env, BTreeDatabase db, 257 Cursor cursor, Transaction txn); 258 259 /* 260 * Configure a transactional cursor to have degree 2 261 * isolation, which permits data read by this cursor 262 * to be deleted prior to the commit of the transaction 263 * for this cursor. 264 */ 265 public void CursorReadCommited( 266 DatabaseEnvironment env, BTreeDatabase db, 267 Cursor cursor, Transaction txn) 268 { 269 Console.WriteLine("CursorReadCommited"); 270 } 271 272 /* 273 * Configure a transactional cursor to have degree 1 274 * isolation. The cursor's read operations could return 275 * modified but not yet commited data. 276 */ 277 public void CursorReadUncommited( 278 DatabaseEnvironment env, BTreeDatabase db, 279 Cursor cursor, Transaction txn) 280 { 281 Console.WriteLine("CursorReadUncommited"); 282 } 283 284 /* 285 * Only return committed data. 286 */ 287 public void CursorRead( 288 DatabaseEnvironment env, BTreeDatabase db, 289 Cursor cursor, Transaction txn) 290 { 291 Console.WriteLine("CursorRead"); 292 } 293 294 295 [Test] 296 public void TestMoveToExactKey() 297 { 298 BTreeDatabase db; 299 BTreeCursor cursor; 300 301 testName = "TestMoveToExactKey"; 302 testHome = testFixtureHome + "/" + testName; 303 Configuration.ClearDir(testHome); 304 305 GetCursorInBtreeDBWithoutEnv(testHome, 306 testName, out db, out cursor); 307 308 // Add one record into database. 309 DatabaseEntry key = new DatabaseEntry( 310 BitConverter.GetBytes((int)0)); 311 DatabaseEntry data = new DatabaseEntry( 312 BitConverter.GetBytes((int)0)); 313 KeyValuePair<DatabaseEntry, DatabaseEntry> pair = 314 new KeyValuePair<DatabaseEntry,DatabaseEntry>(key, data); 315 cursor.Add(pair); 316 317 // Move the cursor exactly to the specified key. 318 MoveCursor(cursor, false); 319 cursor.Close(); 320 db.Close(); 321 } 322 323 [Test] 324 public void TestMoveToExactPair() 325 { 326 BTreeDatabase db; 327 BTreeCursor cursor; 328 DatabaseEntry key, data; 329 330 testName = "TestMoveToExactPair"; 331 testHome = testFixtureHome + "/" + testName; 332 Configuration.ClearDir(testHome); 333 334 GetCursorInBtreeDBWithoutEnv(testHome, 335 testName, out db, out cursor); 336 337 // Add one record into database. 338 key = new DatabaseEntry( 339 BitConverter.GetBytes((int)0)); 340 data = new DatabaseEntry( 341 BitConverter.GetBytes((int)0)); 342 KeyValuePair<DatabaseEntry, DatabaseEntry> pair = 343 new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data); 344 cursor.Add(pair); 345 346 // Move the cursor exactly to the specified key/data pair. 347 MoveCursor(cursor, true); 348 cursor.Close(); 349 db.Close(); 350 351 } 352 353 [Test] 354 public void TestMoveWithRMW() 355 { 356 testName = "TestMoveWithRMW"; 357 testHome = testFixtureHome + "/" + testName; 358 Configuration.ClearDir(testHome); 359 360 // Use MoveCursor() as its move function. 361 cursorFunc = new CursorMoveFuncDelegate(MoveCursor); 362 363 // Move to a specified key and a key/data pair. 364 MoveWithRMW(testHome, testName); 365 } 366 367 [Test] 368 public void TestMoveFirst() 369 { 370 BTreeDatabase db; 371 BTreeCursor cursor; 372 373 testName = "TestMoveFirst"; 374 testHome = testFixtureHome + "/" + testName; 375 Configuration.ClearDir(testHome); 376 377 GetCursorInBtreeDBWithoutEnv(testHome, testName, 378 out db, out cursor); 379 AddOneByCursor(db, cursor); 380 381 // Move to the first record. 382 MoveCursorToFirst(cursor, null); 383 384 cursor.Close(); 385 db.Close(); 386 } 387 388 [Test] 389 public void TestMoveFirstWithRMW() 390 { 391 testName = "TestMoveFirstWithRMW"; 392 testHome = testFixtureHome + "/" + testName; 393 Configuration.ClearDir(testHome); 394 395 // Use MoveCursorToFirst() as its move function. 396 cursorFunc = new CursorMoveFuncDelegate(MoveCursorToFirst); 397 398 // Read the first record with write lock. 399 MoveWithRMW(testHome, testName); 400 } 401 402 [Test] 403 public void TestMoveLast() 404 { 405 BTreeDatabase db; 406 BTreeCursor cursor; 407 408 testName = "TestMoveLast"; 409 testHome = testFixtureHome + "/" + testName; 410 Configuration.ClearDir(testHome); 411 412 GetCursorInBtreeDBWithoutEnv(testHome, testName, 413 out db, out cursor); 414 AddOneByCursor(db, cursor); 415 416 // Move the cursor to the last record. 417 MoveCursorToLast(cursor, null); 418 419 cursor.Close(); 420 db.Close(); 421 } 422 423 [Test] 424 public void TestMoveLastWithRMW() 425 { 426 testName = "TestMoveLastWithRMW"; 427 testHome = testFixtureHome + "/" + testName; 428 Configuration.ClearDir(testHome); 429 430 // Use MoveCursorToLast() as its move function. 431 cursorFunc = new CursorMoveFuncDelegate(MoveCursorToLast); 432 433 // Read the last recod with write lock. 434 MoveWithRMW(testHome, testName); 435 } 436 437 [Test] 438 public void TestMoveNext() 439 { 440 BTreeDatabase db; 441 BTreeCursor cursor; 442 443 testName = "TestMoveCursorToNext"; 444 testHome = testFixtureHome + "/" + testName; 445 Configuration.ClearDir(testHome); 446 447 GetCursorInBtreeDBWithoutEnv(testHome, testName, 448 out db, out cursor); 449 450 // Put ten records to the database. 451 for (int i = 0; i < 10; i++) 452 db.Put(new DatabaseEntry(BitConverter.GetBytes(i)), 453 new DatabaseEntry(BitConverter.GetBytes(i))); 454 455 // Move the cursor from the first record to the fifth. 456 MoveCursorToNext(cursor, null); 457 458 cursor.Close(); 459 db.Close(); 460 } 461 462 [Test] 463 public void TestMoveNextWithRMW() 464 { 465 testName = "TestMoveLastWithRMW"; 466 testHome = testFixtureHome + "/" + testName; 467 Configuration.ClearDir(testHome); 468 469 // Use MoveCursorToNext() as its move function. 470 cursorFunc = new CursorMoveFuncDelegate( 471 MoveCursorToNext); 472 473 /* 474 * Read the first record to the fifth record with 475 * write lock. 476 */ 477 MoveWithRMW(testHome, testName); 478 } 479 480 [Test] 481 public void TestMoveNextDuplicate() 482 { 483 BTreeDatabase db; 484 BTreeCursor cursor; 485 486 testName = "TestMoveNextDuplicate"; 487 testHome = testFixtureHome + "/" + testName; 488 Configuration.ClearDir(testHome); 489 490 GetCursorInBtreeDBWithoutEnv(testHome, testName, 491 out db, out cursor); 492 493 // Add ten duplicate records to the database. 494 for (int i = 0; i < 10; i++) 495 db.Put(new DatabaseEntry( 496 ASCIIEncoding.ASCII.GetBytes("key")), 497 new DatabaseEntry( 498 BitConverter.GetBytes(i))); 499 500 /* 501 * Move the cursor from one duplicate record to 502 * another duplicate one. 503 */ 504 MoveCursorToNextDuplicate(cursor, null); 505 506 cursor.Close(); 507 db.Close(); 508 } 509 510 [Test] 511 public void TestMoveNextDuplicateWithRMW() 512 { 513 testName = "TestMoveNextDuplicateWithRMW"; 514 testHome = testFixtureHome + "/" + testName; 515 Configuration.ClearDir(testHome); 516 517 /* 518 * Use MoveCursorToNextDuplicate() as its 519 * move function. 520 */ 521 cursorFunc = new CursorMoveFuncDelegate( 522 MoveCursorToNextDuplicate); 523 524 /* 525 * Read the first record to the fifth record with 526 * write lock. 527 */ 528 MoveWithRMW(testHome, testName); 529 } 530 531 532 [Test] 533 public void TestMoveNextUnique() 534 { 535 BTreeDatabase db; 536 BTreeCursor cursor; 537 538 testName = "TestMoveNextUnique"; 539 testHome = testFixtureHome + "/" + testName; 540 Configuration.ClearDir(testHome); 541 542 GetCursorInBtreeDBWithoutEnv(testHome, testName, 543 out db, out cursor); 544 545 // Add ten different records to the database. 546 for (int i = 0; i < 10; i++) 547 db.Put(new DatabaseEntry( 548 BitConverter.GetBytes(i)), 549 new DatabaseEntry( 550 BitConverter.GetBytes(i))); 551 552 // Move to five unique records. 553 MoveCursorToNextUnique(cursor, null); 554 555 cursor.Close(); 556 db.Close(); 557 } 558 559 [Test] 560 public void TestMoveNextUniqueWithRMW() 561 { 562 testName = "TestMoveNextUniqueWithRMW"; 563 testHome = testFixtureHome + "/" + testName; 564 Configuration.ClearDir(testHome); 565 566 /* 567 * Use MoveCursorToNextUnique() as its 568 * move function. 569 */ 570 cursorFunc = new CursorMoveFuncDelegate( 571 MoveCursorToNextUnique); 572 573 /* 574 * Move to five unique records. 575 */ 576 MoveWithRMW(testHome, testName); 577 } 578 579 [Test] 580 public void TestMovePrev() 581 { 582 BTreeDatabase db; 583 BTreeCursor cursor; 584 585 testName = "TestMovePrev"; 586 testHome = testFixtureHome + "/" + testName; 587 Configuration.ClearDir(testHome); 588 589 GetCursorInBtreeDBWithoutEnv(testHome, testName, 590 out db, out cursor); 591 592 // Add ten records to the database. 593 for (int i = 0; i < 10; i++) 594 AddOneByCursor(db, cursor); 595 596 // Move the cursor to previous five records 597 MoveCursorToPrev(cursor, null); 598 599 cursor.Close(); 600 db.Close(); 601 } 602 603 [Test] 604 public void TestMovePrevWithRMW() 605 { 606 testName = "TestMovePrevWithRMW"; 607 testHome = testFixtureHome + "/" + testName; 608 Configuration.ClearDir(testHome); 609 610 /* 611 * Use MoveCursorToNextDuplicate() as its 612 * move function. 613 */ 614 cursorFunc = new CursorMoveFuncDelegate( 615 MoveCursorToPrev); 616 617 // Read previous record in write lock. 618 MoveWithRMW(testHome, testName); 619 } 620 621 622 [Test] 623 public void TestMovePrevDuplicate() 624 { 625 BTreeDatabase db; 626 BTreeCursor cursor; 627 628 testName = "TestMovePrevDuplicate"; 629 testHome = testFixtureHome + "/" + testName; 630 Configuration.ClearDir(testHome); 631 632 GetCursorInBtreeDBWithoutEnv(testHome, testName, 633 out db, out cursor); 634 635 // Add ten records to the database. 636 for (int i = 0; i < 10; i++) 637 AddOneByCursor(db, cursor); 638 639 // Move the cursor to previous duplicate records. 640 MoveCursorToPrevDuplicate(cursor, null); 641 642 cursor.Close(); 643 db.Close(); 644 } 645 646 [Test] 647 public void TestMovePrevDuplicateWithRMW() 648 { 649 testName = "TestMovePrevDuplicateWithRMW"; 650 testHome = testFixtureHome + "/" + testName; 651 Configuration.ClearDir(testHome); 652 653 /* 654 * Use MoveCursorToNextDuplicate() as its 655 * move function. 656 */ 657 cursorFunc = new CursorMoveFuncDelegate( 658 MoveCursorToPrevDuplicate); 659 660 // Read the previous duplicate record in write lock. 661 MoveWithRMW(testHome, testName); 662 } 663 664 665 [Test] 666 public void TestMovePrevUnique() 667 { 668 BTreeDatabase db; 669 BTreeCursor cursor; 670 671 testName = "TestMovePrevUnique"; 672 testHome = testFixtureHome + "/" + testName; 673 Configuration.ClearDir(testHome); 674 675 GetCursorInBtreeDBWithoutEnv(testHome, testName, 676 out db, out cursor); 677 678 // Add ten records to the database. 679 for (int i = 0; i < 10; i++) 680 db.Put(new DatabaseEntry(BitConverter.GetBytes(i)), 681 new DatabaseEntry(BitConverter.GetBytes(i))); 682 683 // Move the cursor to previous unique records. 684 MoveCursorToPrevUnique(cursor, null); 685 686 cursor.Close(); 687 db.Close(); 688 } 689 690 [Test] 691 public void TestMovePrevUniqueWithRMW() 692 { 693 testName = "TestMovePrevDuplicateWithRMW"; 694 testHome = testFixtureHome + "/" + testName; 695 Configuration.ClearDir(testHome); 696 697 /* 698 * Use MoveCursorToPrevUnique() as its 699 * move function. 700 */ 701 cursorFunc = new CursorMoveFuncDelegate( 702 MoveCursorToPrevUnique); 703 704 // Read the previous unique record in write lock. 705 MoveWithRMW(testHome, testName); 706 } 707 708 [Test] 709 public void TestPriority() 710 { 711 BTreeCursor cursor; 712 BTreeDatabase db; 713 CachePriority[] priorities; 714 CursorConfig cursorConfig; 715 DatabaseEnvironment env; 716 717 cursorConfig = new CursorConfig(); 718 719 priorities = new CachePriority[5]; 720 priorities[0] = CachePriority.DEFAULT; 721 priorities[1] = CachePriority.HIGH; 722 priorities[2] = CachePriority.LOW; 723 priorities[3] = CachePriority.VERY_HIGH; 724 priorities[4] = CachePriority.VERY_LOW; 725 726 testName = "TestPriority"; 727 testHome = testFixtureHome + "/" + testName; 728 729 Configuration.ClearDir(testHome); 730 731 for (int i = 0; i < 5; i++) 732 { 733 // Configure the cursor priority. 734 cursorConfig.Priority = priorities[i]; 735 736 // Open a database to test a specified priority. 737 GetCursorInBtreeDBInCDS(testHome, testName, 738 cursorConfig, out env, out db, out cursor); 739 Assert.AreEqual(priorities[i], cursorConfig.Priority); 740 cursor.Close(); 741 db.Close(); 742 env.Close(); 743 } 744 } 745 746 [Test] 747 public void TestRefresh() 748 { 749 BTreeDatabase db; 750 BTreeCursor cursor; 751 752 testName = "TestRefresh"; 753 testHome = testFixtureHome + "/" + testName; 754 Configuration.ClearDir(testHome); 755 756 GetCursorInBtreeDBWithoutEnv(testHome, testName, 757 out db, out cursor); 758 759 // Write a record with cursor and Refresh the cursor. 760 MoveCursorToCurrentRec(cursor, null); 761 762 cursor.Close(); 763 db.Close(); 764 } 765 766 [Test] 767 public void TestRefreshWithRMW() 768 { 769 testName = "TestRefreshWithRMW"; 770 testHome = testFixtureHome + "/" + testName; 771 772 Configuration.ClearDir(testHome); 773 774 cursorFunc = new CursorMoveFuncDelegate( 775 MoveCursorToCurrentRec); 776 777 // Read the previous unique record in write lock. 778 MoveWithRMW(testHome, testName); 779 } 780 781 [Test] 782 public void TestSnapshotIsolation() 783 { 784 BTreeDatabaseConfig dbConfig; 785 DatabaseEntry key, data; 786 DatabaseEnvironmentConfig envConfig; 787 Thread readThread, updateThread; 788 Transaction txn; 789 790 updateTxn = null; 791 readTxn = null; 792 paramDB = null; 793 paramEnv = null; 794 testName = "TestSnapshotIsolation"; 795 testHome = testFixtureHome + "/" + testName; 796 797 Configuration.ClearDir(testHome); 798 799 /* 800 * Open environment with DB_MULTIVERSION 801 * which is required by DB_TXN_SNAPSHOT. 802 */ 803 envConfig = new DatabaseEnvironmentConfig(); 804 envConfig.Create = true; 805 envConfig.UseMVCC = true; 806 envConfig.UseTxns = true; 807 envConfig.UseMPool = true; 808 envConfig.UseLocking = true; 809 envConfig.TxnTimeout = 1000; 810 paramEnv = DatabaseEnvironment.Open( 811 testHome, envConfig); 812 paramEnv.DetectDeadlocks(DeadlockPolicy.YOUNGEST); 813 814 /* 815 * Open a transactional database and put 1000 records 816 * into it within transaction. 817 */ 818 txn = paramEnv.BeginTransaction(); 819 dbConfig = new BTreeDatabaseConfig(); 820 dbConfig.Creation = CreatePolicy.IF_NEEDED; 821 dbConfig.UseMVCC = true; 822 dbConfig.Env = paramEnv; 823 paramDB = BTreeDatabase.Open( 824 testName + ".db", dbConfig, txn); 825 for (int i = 0; i < 256; i++) 826 { 827 key = new DatabaseEntry( 828 BitConverter.GetBytes(i)); 829 data = new DatabaseEntry( 830 BitConverter.GetBytes(i)); 831 paramDB.Put(key, data, txn); 832 } 833 txn.Commit(); 834 835 /* 836 * Begin two threads, read and update thread. 837 * The update thread runs a update transaction 838 * using full read/write locking. The read thread 839 * set DB_TXN_SNAPSHOT on read-only cursor. 840 */ 841 readThread = new Thread(new ThreadStart(ReadTxn)); 842 updateThread = new Thread(new ThreadStart(UpdateTxn)); 843 updateThread.Start(); 844 Thread.Sleep(1000); 845 readThread.Start(); 846 readThread.Join(); 847 updateThread.Join(); 848 849 // Commit transacion in both two threads. 850 if (updateTxn != null) 851 updateTxn.Commit(); 852 if (readTxn != null) 853 readTxn.Commit(); 854 855 /* 856 * Confirm that the overwrite operation works. 857 */ 858 ConfirmOverwrite(); 859 860 paramDB.Close(); 861 paramEnv.Close(); 862 } 863 864 public void ReadTxn() 865 { 866 // Get a new transaction for reading the db. 867 TransactionConfig txnConfig = 868 new TransactionConfig(); 869 txnConfig.Snapshot = true; 870 readTxn = paramEnv.BeginTransaction( 871 txnConfig); 872 873 // Get a new cursor for putting record into db. 874 CursorConfig cursorConfig = new CursorConfig(); 875 cursorConfig.WriteCursor = false; 876 BTreeCursor cursor = paramDB.Cursor( 877 cursorConfig, readTxn); 878 879 // Continually reading record from db. 880 try 881 { 882 Assert.IsTrue(cursor.MoveFirst()); 883 int i = 0; 884 do 885 { 886 Assert.AreEqual( 887 BitConverter.ToInt32( 888 cursor.Current.Key.Data, 0), 889 BitConverter.ToInt32( 890 cursor.Current.Value.Data, 0)); 891 } while (i <= 1000 && cursor.MoveNext()); 892 } 893 catch (DeadlockException) 894 { 895 } 896 finally 897 { 898 cursor.Close(); 899 } 900 } 901 902 public void UpdateTxn() 903 { 904 int int32Value; 905 DatabaseEntry data; 906 907 // Get a new transaction for updating the db. 908 TransactionConfig txnConfig = 909 new TransactionConfig(); 910 txnConfig.IsolationDegree = 911 Isolation.DEGREE_THREE; 912 913 updateTxn = 914 paramEnv.BeginTransaction(txnConfig); 915 916 // Continually putting record to db. 917 918 BTreeCursor cursor = 919 paramDB.Cursor(updateTxn); 920 921 // Move the cursor to the first record. 922 Assert.IsTrue(cursor.MoveFirst()); 923 int i = 0; 924 try 925 { 926 do 927 { 928 929 int32Value = BitConverter.ToInt32( 930 cursor.Current.Value.Data, 0); 931 data = new DatabaseEntry( 932 BitConverter.GetBytes(int32Value - 1)); 933 cursor.Overwrite(data); 934 } while (i <= 1000 && cursor.MoveNext()); 935 } 936 catch (DeadlockException) 937 { 938 } 939 finally 940 { 941 cursor.Close(); 942 } 943 } 944 945 946 [Test] 947 public void TestWriteCursor() 948 { 949 BTreeCursor cursor; 950 BTreeDatabase db; 951 CursorConfig cursorConfig; 952 DatabaseEnvironment env; 953 954 testName = "TestWriteCursor"; 955 testHome = testFixtureHome + "/" + testName; 956 957 Configuration.ClearDir(testHome); 958 959 cursorConfig = new CursorConfig(); 960 cursorConfig.WriteCursor = true; 961 962 GetCursorInBtreeDBInCDS(testHome, testName, 963 cursorConfig, out env, out db, out cursor); 964 965 /* 966 * Add a record by cursor to the database. If the 967 * WriteCursor doesn't work, exception will be 968 * throwed in the environment which is configured 969 * with DB_INIT_CDB. 970 */ 971 try 972 { 973 AddOneByCursor(db, cursor); 974 } 975 catch (DatabaseException) 976 { 977 throw new TestException(); 978 } 979 finally 980 { 981 cursor.Close(); 982 db.Close(); 983 env.Close(); 984 } 985 } 986 987 public void ConfirmOverwrite() 988 { 989 Transaction confirmTxn = paramEnv.BeginTransaction(); 990 BTreeCursor cursor = paramDB.Cursor(confirmTxn); 991 992 int i = 0; 993 Assert.IsTrue(cursor.MoveFirst()); 994 do 995 { 996 Assert.AreNotEqual( 997 cursor.Current.Key.Data, 998 cursor.Current.Value.Data); 999 } while (i <= 1000 && cursor.MoveNext()); 1000 1001 cursor.Close(); 1002 confirmTxn.Commit(); 1003 } 1004 1005 public static void AddOneByCursor(Database db, Cursor cursor) 1006 { 1007 DatabaseEntry key, data; 1008 KeyValuePair<DatabaseEntry, DatabaseEntry> pair; 1009 1010 // Add a record to db via cursor. 1011 key = new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key")); 1012 data = new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data")); 1013 pair = new KeyValuePair<DatabaseEntry,DatabaseEntry>(key, data); 1014 cursor.Add(pair); 1015 1016 // Confirm that the record has been put to the database. 1017 Assert.IsTrue(db.Exists(key)); 1018 } 1019 1020 1021 public static void GetCursorInBtreeDBWithoutEnv( 1022 string home, string name, out BTreeDatabase db, 1023 out BTreeCursor cursor) 1024 { 1025 string dbFileName = home + "/" + name + ".db"; 1026 1027 BTreeDatabaseConfig dbConfig = 1028 new BTreeDatabaseConfig(); 1029 dbConfig.Creation = CreatePolicy.IF_NEEDED; 1030 dbConfig.Duplicates = DuplicatesPolicy.UNSORTED; 1031 db = BTreeDatabase.Open(dbFileName, dbConfig); 1032 cursor = db.Cursor(); 1033 } 1034 1035 public static void GetCursorInBtreeDBInTDS( 1036 string home, string name, 1037 CursorConfig cursorConfig, 1038 out DatabaseEnvironment env, out BTreeDatabase db, 1039 out BTreeCursor cursor, out Transaction txn) 1040 { 1041 string dbFileName = name + ".db"; 1042 1043 Configuration.ClearDir(home); 1044 1045 // Open an environment. 1046 DatabaseEnvironmentConfig envConfig = 1047 new DatabaseEnvironmentConfig(); 1048 envConfig.Create = true; 1049 envConfig.UseMPool = true; 1050 envConfig.UseTxns = true; 1051 envConfig.NoMMap = false; 1052 envConfig.UseLocking = true; 1053 env = DatabaseEnvironment.Open(home, envConfig); 1054 1055 // Begin a transaction. 1056 txn = env.BeginTransaction(); 1057 1058 /* 1059 * Open an btree database. The underlying database 1060 * should be opened with ReadUncommitted if the 1061 * cursor's isolation degree will be set to be 1. 1062 */ 1063 BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig(); 1064 dbConfig.Creation = CreatePolicy.IF_NEEDED; 1065 dbConfig.Env = env; 1066 if (cursorConfig.IsolationDegree == Isolation.DEGREE_ONE) 1067 dbConfig.ReadUncommitted = true; 1068 1069 db = BTreeDatabase.Open(dbFileName, dbConfig, txn); 1070 1071 // Get a cursor in the transaction. 1072 cursor = db.Cursor(cursorConfig, txn); 1073 } 1074 1075 // Get a cursor in CDS. 1076 public static void GetCursorInBtreeDBInCDS( 1077 string home, string name, 1078 CursorConfig cursorConfig, 1079 out DatabaseEnvironment env, out BTreeDatabase db, 1080 out BTreeCursor cursor) 1081 { 1082 string dbFileName = name + ".db"; 1083 1084 // Open an environment. 1085 DatabaseEnvironmentConfig envConfig = 1086 new DatabaseEnvironmentConfig(); 1087 envConfig.Create = true; 1088 envConfig.UseCDB = true; 1089 envConfig.UseMPool = true; 1090 env = DatabaseEnvironment.Open(home, envConfig); 1091 1092 /* 1093 * Open an btree database. The underlying database 1094 * should be opened with ReadUncommitted if the 1095 * cursor's isolation degree will be set to be 1. 1096 */ 1097 BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig(); 1098 dbConfig.Creation = CreatePolicy.IF_NEEDED; 1099 dbConfig.Env = env; 1100 1101 if (cursorConfig.IsolationDegree == Isolation.DEGREE_ONE) 1102 dbConfig.ReadUncommitted = true; 1103 1104 db = BTreeDatabase.Open(dbFileName, dbConfig); 1105 1106 // Get a cursor in the transaction. 1107 cursor = db.Cursor(cursorConfig); 1108 } 1109 1110 public void RdMfWt() 1111 { 1112 Transaction txn = paramEnv.BeginTransaction(); 1113 Cursor dbc = paramDB.Cursor(txn); 1114 1115 try 1116 { 1117 LockingInfo lck = new LockingInfo(); 1118 lck.ReadModifyWrite = true; 1119 1120 // Read record. 1121 cursorFunc(dbc, lck); 1122 1123 // Block the current thread until event is set. 1124 signal.WaitOne(); 1125 1126 // Write new records into database. 1127 DatabaseEntry key = new DatabaseEntry( 1128 BitConverter.GetBytes(55)); 1129 DatabaseEntry data = new DatabaseEntry( 1130 BitConverter.GetBytes(55)); 1131 dbc.Add(new KeyValuePair<DatabaseEntry, 1132 DatabaseEntry>(key, data)); 1133 1134 dbc.Close(); 1135 txn.Commit(); 1136 } 1137 catch (DeadlockException) 1138 { 1139 dbc.Close(); 1140 txn.Abort(); 1141 } 1142 } 1143 1144 1145 public void MoveWithRMW(string home, string name) 1146 { 1147 paramEnv = null; 1148 paramDB = null; 1149 1150 // Open the environment. 1151 DatabaseEnvironmentConfig envCfg = 1152 new DatabaseEnvironmentConfig(); 1153 envCfg.Create = true; 1154 envCfg.FreeThreaded = true; 1155 envCfg.UseLocking = true; 1156 envCfg.UseLogging = true; 1157 envCfg.UseMPool = true; 1158 envCfg.UseTxns = true; 1159 paramEnv = DatabaseEnvironment.Open(home, envCfg); 1160 1161 // Open database in transaction. 1162 Transaction openTxn = paramEnv.BeginTransaction(); 1163 BTreeDatabaseConfig cfg = new BTreeDatabaseConfig(); 1164 cfg.Creation = CreatePolicy.ALWAYS; 1165 cfg.Env = paramEnv; 1166 cfg.FreeThreaded = true; 1167 cfg.PageSize = 4096; 1168 cfg.Duplicates = DuplicatesPolicy.UNSORTED; 1169 paramDB = BTreeDatabase.Open(name + ".db", cfg, openTxn); 1170 openTxn.Commit(); 1171 1172 /* 1173 * Put 10 different, 2 duplicate and another different 1174 * records into database. 1175 */ 1176 Transaction txn = paramEnv.BeginTransaction(); 1177 for (int i = 0; i < 13; i++) 1178 { 1179 DatabaseEntry key, data; 1180 if (i == 10 || i == 11) 1181 { 1182 key = new DatabaseEntry( 1183 ASCIIEncoding.ASCII.GetBytes("key")); 1184 data = new DatabaseEntry( 1185 ASCIIEncoding.ASCII.GetBytes("data")); 1186 } 1187 else 1188 { 1189 key = new DatabaseEntry( 1190 BitConverter.GetBytes(i)); 1191 data = new DatabaseEntry( 1192 BitConverter.GetBytes(i)); 1193 } 1194 paramDB.Put(key, data, txn); 1195 } 1196 1197 txn.Commit(); 1198 1199 // Get a event wait handle. 1200 signal = new EventWaitHandle(false, EventResetMode.ManualReset); 1201 1202 /* 1203 * Start RdMfWt() in two threads. RdMfWt() reads 1204 * and writes data into database. 1205 */ 1206 Thread t1 = new Thread(new ThreadStart(RdMfWt)); 1207 Thread t2 = new Thread(new ThreadStart(RdMfWt)); 1208 t1.Start(); 1209 t2.Start(); 1210 1211 /* 1212 * Give both threads time to read before signalling 1213 * them to write. 1214 */ 1215 Thread.Sleep(1000); 1216 1217 // Invoke the write operation in both threads. 1218 signal.Set(); 1219 1220 // Return the number of deadlocks. 1221 while (t1.IsAlive || t2.IsAlive) 1222 { 1223 /* 1224 * Give both threads time to write before 1225 * counting the number of deadlocks. 1226 */ 1227 Thread.Sleep(1000); 1228 uint deadlocks = paramEnv.DetectDeadlocks( 1229 DeadlockPolicy.DEFAULT); 1230 1231 // Confirm that there won't be any deadlock. 1232 Assert.AreEqual(0, deadlocks); 1233 } 1234 1235 t1.Join(); 1236 t2.Join(); 1237 paramDB.Close(); 1238 paramEnv.Close(); 1239 } 1240 1241 /* 1242 * Move the cursor to an exisiting key or key/data pair. 1243 */ 1244 public void MoveCursor(Cursor dbc, bool ifPair) 1245 { 1246 DatabaseEntry key, data; 1247 KeyValuePair<DatabaseEntry, DatabaseEntry> pair; 1248 1249 key = new DatabaseEntry( 1250 BitConverter.GetBytes((int)0)); 1251 if (ifPair == false) 1252 Assert.IsTrue(dbc.Move(key, true)); 1253 else 1254 { 1255 data = new DatabaseEntry( 1256 BitConverter.GetBytes((int)0)); 1257 pair = new KeyValuePair<DatabaseEntry, 1258 DatabaseEntry>(key, data); 1259 Assert.IsTrue(dbc.Move(pair, true)); 1260 } 1261 } 1262 1263 /* 1264 * Move the cursor to an exisiting key and key/data 1265 * pair with LockingInfo. 1266 */ 1267 public void MoveCursor(Cursor dbc, LockingInfo lck) 1268 { 1269 DatabaseEntry key, data; 1270 KeyValuePair<DatabaseEntry, DatabaseEntry> pair; 1271 1272 key = new DatabaseEntry( 1273 BitConverter.GetBytes((int)0)); 1274 data = new DatabaseEntry( 1275 BitConverter.GetBytes((int)0)); 1276 pair = new KeyValuePair<DatabaseEntry, 1277 DatabaseEntry>(key, data); 1278 1279 // Move to an existing key. 1280 Assert.IsTrue(dbc.Move(key, true, lck)); 1281 1282 // Move to an existing key/data pair. 1283 Assert.IsTrue(dbc.Move(pair, true, lck)); 1284 } 1285 1286 /* 1287 * Move the cursor to the first record in a nonempty 1288 * database. The returning value should be true. 1289 */ 1290 public void MoveCursorToFirst(Cursor dbc, LockingInfo lck) 1291 { 1292 if (lck == null) 1293 Assert.IsTrue(dbc.MoveFirst()); 1294 else 1295 Assert.IsTrue(dbc.MoveFirst(lck)); 1296 } 1297 1298 /* 1299 * Move the cursor to last record in a nonempty 1300 * database. The returning value should be true. 1301 */ 1302 public void MoveCursorToLast(Cursor dbc, LockingInfo lck) 1303 { 1304 if (lck == null) 1305 Assert.IsTrue(dbc.MoveLast()); 1306 else 1307 Assert.IsTrue(dbc.MoveLast(lck)); 1308 } 1309 1310 /* 1311 * Move the cursor to the next record in the database 1312 * with more than five records. The returning values of 1313 * every move operation should be true. 1314 */ 1315 public void MoveCursorToNext(Cursor dbc, LockingInfo lck) 1316 { 1317 for (int i = 0; i < 5; i++) 1318 if (lck == null) 1319 Assert.IsTrue(dbc.MoveNext()); 1320 else 1321 Assert.IsTrue(dbc.MoveNext(lck)); 1322 } 1323 1324 /* 1325 * Move the cursor to the next duplicate record in 1326 * the database which has more than 2 duplicate 1327 * records. The returning value should be true. 1328 */ 1329 public void MoveCursorToNextDuplicate(Cursor dbc, 1330 LockingInfo lck) 1331 { 1332 DatabaseEntry key = new DatabaseEntry( 1333 ASCIIEncoding.ASCII.GetBytes("key")); 1334 1335 /* 1336 * The cursor should point to any record in the 1337 * database before it move to the next duplicate 1338 * record. 1339 */ 1340 if (lck == null) 1341 { 1342 dbc.Move(key, true); 1343 Assert.IsTrue(dbc.MoveNextDuplicate()); 1344 } 1345 else 1346 { 1347 /* 1348 * Both the move and move next duplicate 1349 * operation should use LockingInfo. If any 1350 * one doesn't use LockingInfo, deadlock still 1351 * occurs. 1352 */ 1353 dbc.Move(key, true, lck); 1354 Assert.IsTrue(dbc.MoveNextDuplicate(lck)); 1355 } 1356 } 1357 1358 /* 1359 * Move the cursor to next unique record in the database. 1360 * The returning value should be true. 1361 */ 1362 public void MoveCursorToNextUnique(Cursor dbc, 1363 LockingInfo lck) 1364 { 1365 for (int i = 0; i < 5; i++) 1366 { 1367 if (lck == null) 1368 Assert.IsTrue(dbc.MoveNextUnique()); 1369 else 1370 Assert.IsTrue(dbc.MoveNextUnique(lck)); 1371 } 1372 } 1373 1374 /* 1375 * Move the cursor to previous record in the database. 1376 * The returning value should be true; 1377 */ 1378 public void MoveCursorToPrev(Cursor dbc, 1379 LockingInfo lck) 1380 { 1381 if (lck == null) 1382 { 1383 dbc.MoveLast(); 1384 for (int i = 0; i < 5; i++) 1385 Assert.IsTrue(dbc.MovePrev()); 1386 } 1387 else 1388 { 1389 dbc.MoveLast(lck); 1390 for (int i = 0; i < 5; i++) 1391 Assert.IsTrue(dbc.MovePrev(lck)); 1392 } 1393 1394 } 1395 1396 /* 1397 * Move the cursor to a duplicate record and then to 1398 * another duplicate one. And finally move to it previous 1399 * one. Since the previous duplicate one exist, the return 1400 * value of move previous duplicate record should be 1401 * true; 1402 */ 1403 public void MoveCursorToPrevDuplicate(Cursor dbc, 1404 LockingInfo lck) 1405 { 1406 if (lck == null) 1407 { 1408 dbc.Move(new DatabaseEntry( 1409 ASCIIEncoding.ASCII.GetBytes("key")), true); 1410 dbc.MoveNextDuplicate(); 1411 Assert.IsTrue(dbc.MovePrevDuplicate()); 1412 } 1413 else 1414 { 1415 dbc.Move(new DatabaseEntry( 1416 ASCIIEncoding.ASCII.GetBytes("key")),true, lck); 1417 dbc.MoveNextDuplicate(lck); 1418 Assert.IsTrue(dbc.MovePrevDuplicate(lck)); 1419 } 1420 } 1421 1422 /* 1423 * Move the cursor to previous unique record in a 1424 * database with more than 2 records. The returning 1425 * value should be true. 1426 */ 1427 public void MoveCursorToPrevUnique(Cursor dbc, 1428 LockingInfo lck) 1429 { 1430 for (int i = 0; i < 5; i++) 1431 if (lck == null) 1432 dbc.MovePrevUnique(); 1433 else 1434 dbc.MovePrevUnique(lck); 1435 } 1436 1437 /* 1438 * Move the cursor to current existing record. The returning 1439 * value should be true. 1440 */ 1441 public void MoveCursorToCurrentRec(Cursor dbc, 1442 LockingInfo lck) 1443 { 1444 // Add a record to the database. 1445 KeyValuePair<DatabaseEntry, DatabaseEntry> pair; 1446 pair = new KeyValuePair<DatabaseEntry,DatabaseEntry>( 1447 new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key")), 1448 new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key"))); 1449 dbc.Add(pair); 1450 1451 if (lck == null) 1452 dbc.Refresh(); 1453 else 1454 dbc.Refresh(lck); 1455 1456 Assert.IsNotNull(dbc.Current.Key); 1457 } 1458 } 1459} 1460