• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/db-4.8.30/test/scr037/
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