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 BTreeCursorTest
20	{
21		private string testFixtureName;
22		private string testFixtureHome;
23		private string testName;
24		private string testHome;
25
26		private DatabaseEnvironment paramEnv;
27		private BTreeDatabase paramDB;
28		private EventWaitHandle signal;
29
30		private delegate void BTCursorMoveFuncDelegate(
31                    BTreeCursor cursor, LockingInfo lockingInfo);
32		private BTCursorMoveFuncDelegate btCursorFunc;
33
34		[TestFixtureSetUp]
35		public void RunBeforeTests()
36		{
37			testFixtureName = "BTreeCursorTest";
38			testFixtureHome = "./TestOut/" + testFixtureName;
39		}
40
41		[Test]
42		public void TestAddKeyFirst()
43		{
44			BTreeDatabase db;
45			BTreeCursor cursor;
46			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
47
48			testName = "TestAddKeyFirst";
49			testHome = testFixtureHome + "/" + testName;
50
51			Configuration.ClearDir(testHome);
52
53			// Add record("key", "data") into database.
54			CursorTest.GetCursorInBtreeDBWithoutEnv(
55                            testHome, testName, out db, out cursor);
56			CursorTest.AddOneByCursor(db, cursor);
57
58			// Add record("key","data1") as the first of the data item of "key".
59			pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(
60			    new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key")),
61			    new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data1")));
62			cursor.Add(pair, Cursor.InsertLocation.FIRST);
63
64			// Confirm the record is added as the first of the data item of "key".
65			cursor.Move(new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key")), true);
66			Assert.AreEqual(ASCIIEncoding.ASCII.GetBytes("data1"),
67                            cursor.Current.Value.Data);
68
69			cursor.Close();
70			db.Close();
71		}
72
73		[Test]
74		public void TestAddKeyLast()
75		{
76			BTreeDatabase db;
77			BTreeCursor cursor;
78			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
79
80			testName = "TestAddKeyLast";
81			testHome = testFixtureHome + "/" + testName;
82
83			Configuration.ClearDir(testHome);
84
85			// Add record("key", "data") into database.
86			CursorTest.GetCursorInBtreeDBWithoutEnv(testHome, testName,
87                            out db, out cursor);
88			CursorTest.AddOneByCursor(db, cursor);
89
90			// Add new record("key","data1") as the last of the data item of "key".
91			pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(
92			    new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key")),
93			    new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data1")));
94			cursor.Add(pair, Cursor.InsertLocation.LAST);
95
96			// Confirm the record is added as the first of the data item of "key".
97			cursor.Move(new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key")), true);
98			Assert.AreNotEqual(ASCIIEncoding.ASCII.GetBytes("data1"),
99                            cursor.Current.Value.Data);
100
101			cursor.Close();
102			db.Close();
103		}
104
105		[Test]
106		public void TestAddUnique()
107		{
108			BTreeDatabase db;
109			BTreeCursor cursor;
110			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
111
112			testName = "TestAddUnique";
113			testHome = testFixtureHome + "/" + testName;
114
115			Configuration.ClearDir(testHome);
116
117			// Open a database and cursor.
118			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
119			dbConfig.Creation = CreatePolicy.IF_NEEDED;
120
121			// To put no duplicate data, the database should be set to be sorted.
122			dbConfig.Duplicates = DuplicatesPolicy.SORTED;
123			db = BTreeDatabase.Open(testHome + "/" + testName + ".db", dbConfig);
124			cursor = db.Cursor();
125
126			// Add record("key", "data") into database.
127			CursorTest.AddOneByCursor(db, cursor);
128
129			// Fail to add duplicate record("key","data").
130			pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(
131			    new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key")),
132			    new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data")));
133			try
134			{
135				cursor.AddUnique(pair);
136			}
137			catch (KeyExistException)
138			{
139			}
140			finally
141			{
142				cursor.Close();
143				db.Close();
144			}
145		}
146
147		[Test]
148		public void TestDuplicateWithSamePos()
149		{
150			BTreeDatabase db;
151			BTreeDatabaseConfig dbConfig;
152			BTreeCursor cursor, dupCursor;
153			DatabaseEnvironment env;
154			DatabaseEnvironmentConfig envConfig;
155			DatabaseEntry key, data;
156			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
157			Transaction txn;
158
159			testName = "TestDuplicateWithSamePos";
160			testHome = testFixtureHome + "/" + testName;
161
162			Configuration.ClearDir(testHome);
163
164			envConfig = new DatabaseEnvironmentConfig();
165			envConfig.Create = true;
166			envConfig.UseMPool = true;
167			envConfig.UseTxns = true;
168			envConfig.NoMMap = false;
169			env = DatabaseEnvironment.Open(testHome, envConfig);
170
171			txn = env.BeginTransaction();
172			dbConfig = new BTreeDatabaseConfig();
173			dbConfig.Creation = CreatePolicy.IF_NEEDED;
174			dbConfig.Env = env;
175			db = BTreeDatabase.Open(testName + ".db", dbConfig, txn);
176			key = new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key"));
177			data = new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data"));
178			pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data);
179			db.Put(key, data, txn);
180			txn.Commit();
181
182			txn = env.BeginTransaction();
183			cursor = db.Cursor(txn);
184			cursor.Move(key, true);
185
186			//Duplicate a new cursor to the same position.
187			dupCursor = cursor.Duplicate(true);
188
189			// Overwrite the record.
190			dupCursor.Overwrite(new DatabaseEntry(
191                            ASCIIEncoding.ASCII.GetBytes("newdata")));
192
193			// Confirm that the original data doesn't exist.
194			Assert.IsFalse(dupCursor.Move(pair, true));
195
196			dupCursor.Close();
197			cursor.Close();
198			txn.Commit();
199			db.Close();
200			env.Close();
201		}
202
203		[Test, ExpectedException(typeof(ExpectedTestException))]
204		public void TestDuplicateToDifferentPos()
205		{
206			BTreeDatabase db;
207			BTreeDatabaseConfig dbConfig;
208			BTreeCursor cursor, dupCursor;
209			DatabaseEnvironment env;
210			DatabaseEnvironmentConfig envConfig;
211			DatabaseEntry key, data;
212			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
213			Transaction txn;
214
215			testName = "TestDuplicateToDifferentPos";
216			testHome = testFixtureHome + "/" + testName;
217
218			Configuration.ClearDir(testHome);
219
220			envConfig = new DatabaseEnvironmentConfig();
221			envConfig.Create = true;
222			envConfig.UseMPool = true;
223			envConfig.UseTxns = true;
224			envConfig.NoMMap = false;
225			env = DatabaseEnvironment.Open(testHome, envConfig);
226
227			txn = env.BeginTransaction();
228			dbConfig = new BTreeDatabaseConfig();
229			dbConfig.Creation = CreatePolicy.IF_NEEDED;
230			dbConfig.Env = env;
231			db = BTreeDatabase.Open(testName + ".db", dbConfig, txn);
232			key = new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key"));
233			data = new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data"));
234			pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data);
235			db.Put(key, data, txn);
236			txn.Commit();
237
238			txn = env.BeginTransaction();
239			cursor = db.Cursor(txn);
240			cursor.Move(key, true);
241
242			//Duplicate a new cursor to the same position.
243			dupCursor = cursor.Duplicate(false);
244
245			/*
246			 * The duplicate cursor points to nothing so overwriting the
247			 * record is not allowed.
248			 */
249			try
250			{
251				dupCursor.Overwrite(new DatabaseEntry(
252                                    ASCIIEncoding.ASCII.GetBytes("newdata")));
253			}
254			catch (DatabaseException)
255			{
256				throw new ExpectedTestException();
257			}
258			finally
259			{
260				dupCursor.Close();
261				cursor.Close();
262				txn.Commit();
263				db.Close();
264				env.Close();
265			}
266		}
267
268		[Test]
269		public void TestInsertAfter()
270		{
271			BTreeDatabase db;
272			BTreeCursor cursor;
273			DatabaseEntry data;
274			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
275
276			testName = "TestInsertAfter";
277			testHome = testFixtureHome + "/" + testName;
278
279			Configuration.ClearDir(testHome);
280
281			// Add record("key", "data") into database.
282			CursorTest.GetCursorInBtreeDBWithoutEnv(testHome, testName,
283                            out db, out cursor);
284			CursorTest.AddOneByCursor(db, cursor);
285
286			// Insert the new record("key","data1") after the record("key", "data").
287			data = new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data1"));
288			cursor.Insert(data, Cursor.InsertLocation.AFTER);
289
290			/*
291			 * Move the cursor to the record("key", "data") and confirm that
292			 * the next record is the one just inserted.
293			 */
294			pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(
295			    new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key")),
296			    new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data")));
297			Assert.IsTrue(cursor.Move(pair, true));
298			Assert.IsTrue(cursor.MoveNext());
299			Assert.AreEqual(ASCIIEncoding.ASCII.GetBytes("key"), cursor.Current.Key.Data);
300			Assert.AreEqual(ASCIIEncoding.ASCII.GetBytes("data1"), cursor.Current.Value.Data);
301
302			cursor.Close();
303			db.Close();
304		}
305
306		[Test]
307		public void TestMoveFirstMultipleAndMultipleKey()
308		{
309			testName = "TestMoveFirstMultipleAndMultipleKey";
310			testHome = testFixtureHome + "/" + testName;
311			string btreeDBFileName = testHome + "/" +
312			    testName + ".db";
313			BTreeDatabase db;
314			BTreeCursor cursor;
315			KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> pair;
316			MultipleKeyDatabaseEntry multiPair;
317			int cnt;
318			int[] size = new int[2];
319			size[0] = 0;
320			size[1] = 1024;
321
322			Configuration.ClearDir(testHome);
323
324			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
325			dbConfig.Creation = CreatePolicy.ALWAYS;
326			dbConfig.Duplicates = DuplicatesPolicy.UNSORTED;
327			dbConfig.PageSize = 1024;
328			GetMultipleDB(btreeDBFileName, dbConfig, out db, out cursor);
329
330			for (int i = 0; i < 2; i++) {
331				cnt = 0;
332				if (size[i] == 0)
333					cursor.MoveFirstMultiple();
334				else
335					cursor.MoveFirstMultiple(size[i]);
336				pair = cursor.CurrentMultiple;
337				foreach (DatabaseEntry dbt in pair.Value)
338					cnt++;
339				Assert.AreEqual(1, cnt);
340			}
341
342			for (int i = 0; i < 2; i++) {
343				cnt = 0;
344				if (size[i] == 0)
345					cursor.MoveFirstMultipleKey();
346				else
347					cursor.MoveFirstMultipleKey(size[i]);
348				multiPair = cursor.CurrentMultipleKey;
349				foreach (KeyValuePair<DatabaseEntry, DatabaseEntry>
350                                    dbt in multiPair)
351					cnt++;
352				Assert.Less(1, cnt);
353			}
354
355			cursor.Close();
356			db.Close();
357		}
358
359		[Test]
360		public void TestMoveMultiple()
361		{
362			testName = "TestMoveMultiple";
363			testHome = testFixtureHome + "/" + testName;
364			string btreeDBFileName = testHome + "/" +
365			    testName + ".db";
366			BTreeDatabase db;
367			BTreeCursor cursor;
368			DatabaseEntry key;
369			KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> pair;
370			int cnt;
371
372			Configuration.ClearDir(testHome);
373
374			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
375			dbConfig.Creation = CreatePolicy.ALWAYS;
376			dbConfig.Duplicates = DuplicatesPolicy.UNSORTED;
377			dbConfig.PageSize = 1024;
378			GetMultipleDB(btreeDBFileName, dbConfig, out db, out cursor);
379
380			// Move cursor to pairs with exact 99 as its key.
381			cnt = 0;
382			key = new DatabaseEntry(BitConverter.GetBytes((int)99));
383			cursor.MoveMultiple(key, true);
384			pair = cursor.CurrentMultiple;
385			Assert.AreEqual(99, BitConverter.ToInt32(pair.Key.Data, 0));
386			foreach (DatabaseEntry dbt in pair.Value)
387				cnt++;
388			Assert.AreEqual(2, cnt);
389
390			// Move cursor to pairs with the smallest key larger than 100.
391			cnt = 0;
392			key = new DatabaseEntry(BitConverter.GetBytes((int)100));
393			cursor.MoveMultiple(key, false);
394			pair = cursor.CurrentMultiple;
395			Assert.AreEqual(101, BitConverter.ToInt32(pair.Key.Data, 0));
396			foreach (DatabaseEntry dbt in pair.Value)
397				cnt++;
398			Assert.AreEqual(1, cnt);
399
400			cursor.Close();
401			db.Close();
402		}
403
404		[Test]
405		public void TestMoveMultipleKey()
406		{
407			testName = "TestMoveMultipleKey";
408			testHome = testFixtureHome + "/" + testName;
409			string btreeDBFileName = testHome + "/" +
410			    testName + ".db";
411			BTreeDatabase db;
412			BTreeCursor cursor;
413			DatabaseEntry key;
414			MultipleKeyDatabaseEntry mulPair; ;
415			int cnt;
416
417			Configuration.ClearDir(testHome);
418
419			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
420			dbConfig.Creation = CreatePolicy.ALWAYS;
421			dbConfig.Duplicates = DuplicatesPolicy.UNSORTED;
422			dbConfig.PageSize = 1024;
423			GetMultipleDB(btreeDBFileName, dbConfig, out db, out cursor);
424
425			/*
426                         * Bulk retrieve key/value pair from the pair whose key
427                         * is exact 99.
428                         */
429			cnt = 0;
430			key = new DatabaseEntry(BitConverter.GetBytes((int)99));
431			cursor.MoveMultipleKey(key, true);
432			mulPair = cursor.CurrentMultipleKey;
433			foreach (KeyValuePair<DatabaseEntry, DatabaseEntry>
434                            pair in mulPair) {
435				Assert.GreaterOrEqual(3,
436                                    BitConverter.ToInt32(pair.Key.Data, 0) - 98);
437				cnt++;
438			}
439			Assert.AreEqual(3, cnt);
440
441			/*
442			 * Bulk retrieve key/value pair from the pair whose key
443			 * is the smallest one larger than 100.
444			 */
445			cnt = 0;
446			key = new DatabaseEntry(BitConverter.GetBytes((int)100));
447			cursor.MoveMultipleKey(key, false);
448			mulPair = cursor.CurrentMultipleKey;
449			foreach (KeyValuePair<DatabaseEntry, DatabaseEntry>
450                            pair in mulPair) {
451				Assert.AreEqual(101,
452                                    BitConverter.ToInt32(pair.Key.Data, 0));
453				cnt++;
454			}
455			Assert.LessOrEqual(1, cnt);
456
457			cnt = 0;
458			key = new DatabaseEntry(BitConverter.GetBytes((int)100));
459			cursor.MoveMultipleKey(key, false, 1024);
460			mulPair = cursor.CurrentMultipleKey;
461			foreach (KeyValuePair<DatabaseEntry, DatabaseEntry>
462                            pair in mulPair) {
463				Assert.AreEqual(101,
464                                    BitConverter.ToInt32(pair.Key.Data, 0));
465				Assert.AreEqual(101,
466                                    BitConverter.ToInt32(pair.Value.Data, 0));
467				cnt++;
468			}
469			Assert.LessOrEqual(1, cnt);
470
471			cursor.Close();
472			db.Close();
473		}
474
475		[Test]
476		public void TestMoveMultipleKeyWithRecno()
477		{
478			testName = "TestMoveMultipleKeyWithRecno";
479			testHome = testFixtureHome + "/" + testName;
480			string btreeDBFileName = testHome + "/" +
481			    testName + ".db";
482			BTreeDatabase db;
483			BTreeCursor cursor;
484			MultipleKeyDatabaseEntry multiDBT;
485			int cnt;
486
487			Configuration.ClearDir(testHome);
488
489			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
490			dbConfig.UseRecordNumbers = true;
491			dbConfig.Creation = CreatePolicy.IF_NEEDED;
492			dbConfig.PageSize = 1024;
493			GetMultipleDB(btreeDBFileName, dbConfig, out db, out cursor);
494
495			cnt = 0;
496			cursor.MoveMultipleKey(98);
497			multiDBT = cursor.CurrentMultipleKey;
498			foreach (KeyValuePair<DatabaseEntry, DatabaseEntry>
499                            pair in multiDBT)
500				cnt++;
501			Assert.AreEqual(3, cnt);
502
503			cnt = 0;
504			cursor.MoveMultipleKey(98, 1024);
505			multiDBT = cursor.CurrentMultipleKey;
506			foreach (KeyValuePair<DatabaseEntry, DatabaseEntry>
507                            pair in multiDBT)
508				cnt++;
509			Assert.AreEqual(3, cnt);
510
511			cursor.Close();
512			db.Close();
513		}
514
515		[Test]
516		public void TestMoveMultiplePairs()
517		{
518			testName = "TestMoveMultiplePairs";
519			testHome = testFixtureHome + "/" + testName;
520			string btreeDBFileName = testHome + "/" +
521			    testName + ".db";
522			BTreeDatabase db;
523			BTreeCursor cursor;
524			DatabaseEntry key, data;
525			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
526			MultipleKeyDatabaseEntry multiKeyDBTs1, multiKeyDBTs2;
527			int cnt;
528
529			Configuration.ClearDir(testHome);
530
531			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
532			dbConfig.Creation = CreatePolicy.ALWAYS;
533			dbConfig.Duplicates = DuplicatesPolicy.SORTED;
534			dbConfig.PageSize = 1024;
535			GetMultipleDB(btreeDBFileName, dbConfig, out db, out cursor);
536
537			/*
538                         * Bulk retrieve pairs from the pair whose key/data
539                         * is exact 99/99.
540                         */
541			cnt = 0;
542			key = new DatabaseEntry(BitConverter.GetBytes((int)99));
543			data = new DatabaseEntry(BitConverter.GetBytes((int)99));
544			pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data);
545			cursor.MoveMultipleKey(pair, true);
546			multiKeyDBTs1 = cursor.CurrentMultipleKey;
547			foreach (KeyValuePair<DatabaseEntry, DatabaseEntry>
548                            p in multiKeyDBTs1)
549				cnt++;
550			Assert.AreEqual(3, cnt);
551
552			// Bulk retrieve pairs from the pair whose key is exact 99.
553			cnt = 0;
554			key = new DatabaseEntry(BitConverter.GetBytes((int)99));
555			data = new DatabaseEntry(BitConverter.GetBytes((int)98));
556			cursor.MoveMultipleKey(pair, true);
557			multiKeyDBTs2 = cursor.CurrentMultipleKey;
558			foreach (KeyValuePair<DatabaseEntry, DatabaseEntry>
559                            dbts in multiKeyDBTs2)
560				cnt++;
561			Assert.AreEqual(3, cnt);
562
563			/*
564                         * Bulk retrieve pairs from the pair whose key is
565                         * exact 99 in buffer size of 1024k.
566                         */
567			cnt = 0;
568			key = new DatabaseEntry(BitConverter.GetBytes((int)99));
569			data = new DatabaseEntry(BitConverter.GetBytes((int)102));
570			cursor.MoveMultipleKey(pair, true, 1024);
571			multiKeyDBTs2 = cursor.CurrentMultipleKey;
572			foreach (KeyValuePair<DatabaseEntry, DatabaseEntry>
573                            dbts in multiKeyDBTs2)
574				cnt++;
575			Assert.AreEqual(3, cnt);
576
577			cursor.Close();
578			db.Close();
579		}
580
581		[Test]
582		public void TestMoveMultiplePairWithKey()
583		{
584			testName = "TestMoveMultiplePairWithKey";
585			testHome = testFixtureHome + "/" + testName;
586			string btreeDBFileName = testHome + "/" +
587			    testName + ".db";
588			BTreeDatabase db;
589			BTreeCursor cursor;
590			DatabaseEntry key, data;
591			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
592			KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> mulPair;
593			int cnt;
594
595			Configuration.ClearDir(testHome);
596
597			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
598			dbConfig.Creation = CreatePolicy.ALWAYS;
599			dbConfig.Duplicates = DuplicatesPolicy.UNSORTED;
600			dbConfig.PageSize = 1024;
601			GetMultipleDB(btreeDBFileName, dbConfig, out db, out cursor);
602
603			/*
604                         * Move the cursor to pairs with exact 99 as its key
605                         * and 99 as its data.
606                         */
607			cnt = 0;
608			key = new DatabaseEntry(BitConverter.GetBytes((int)99));
609			data = new DatabaseEntry(BitConverter.GetBytes((int)99));
610			pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data);
611			cursor.MoveMultiple(pair, true);
612			mulPair = cursor.CurrentMultiple;
613			Assert.AreEqual(99, BitConverter.ToInt32(mulPair.Key.Data, 0));
614			foreach (DatabaseEntry dbt in mulPair.Value) {
615				Assert.AreEqual(99, BitConverter.ToInt32(dbt.Data, 0));
616				cnt++;
617			}
618			Assert.AreEqual(1, cnt);
619
620			// Move cursor to pairs with the smallest key larger than 100.
621			cnt = 0;
622			key = new DatabaseEntry(BitConverter.GetBytes((int)100));
623			data = new DatabaseEntry(BitConverter.GetBytes((int)100));
624			cursor.MoveMultiple(pair, false);
625			mulPair = cursor.CurrentMultiple;
626			Assert.AreEqual(99, BitConverter.ToInt32(mulPair.Key.Data, 0));
627			foreach (DatabaseEntry dbt in mulPair.Value) {
628				Assert.GreaterOrEqual(1,
629                                   BitConverter.ToInt32(dbt.Data, 0) - 99);
630				cnt++;
631			}
632			Assert.AreEqual(1, cnt);
633
634			cursor.Close();
635			db.Close();
636		}
637
638		[Test]
639		public void TestMoveMultipleWithRecno()
640		{
641			testName = "TestMoveMultipleWithRecno";
642			testHome = testFixtureHome + "/" + testName;
643			string btreeDBFileName = testHome + "/" +
644			    testName + ".db";
645			BTreeDatabase db;
646			BTreeCursor cursor;
647			KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> pair;
648			int cnt;
649
650			Configuration.ClearDir(testHome);
651
652			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
653			dbConfig.Creation = CreatePolicy.ALWAYS;
654			dbConfig.PageSize = 1024;
655			dbConfig.UseRecordNumbers = true;
656			GetMultipleDB(btreeDBFileName, dbConfig, out db, out cursor);
657
658			// Move cursor to the No.100 record.
659			cnt = 0;
660			cursor.MoveMultiple(100);
661			pair = cursor.CurrentMultiple;
662			Assert.AreEqual(100, BitConverter.ToInt32(pair.Key.Data, 0));
663			foreach (DatabaseEntry dbt in pair.Value)
664				cnt++;
665			Assert.AreEqual(1, cnt);
666
667			// Move cursor to the No.100 record with buffer size of 1024k.
668			cnt = 0;
669			cursor.MoveMultiple(100, 1024);
670			pair = cursor.CurrentMultiple;
671			Assert.AreEqual(100, BitConverter.ToInt32(pair.Key.Data, 0));
672			foreach (DatabaseEntry dbt in pair.Value)
673				cnt++;
674			Assert.AreEqual(1, cnt);
675
676			cursor.Close();
677			db.Close();
678		}
679
680		[Test]
681		public void TestMoveNextDuplicateMultipleAndMultipleKey()
682		{
683			testName = "TestMoveNextDuplicateMultipleAndMultipleKey";
684			testHome = testFixtureHome + "/" + testName;
685			string btreeDBFileName = testHome + "/" +
686			    testName + ".db";
687			BTreeDatabase db;
688			BTreeCursor cursor;
689			DatabaseEntry key, data;
690			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
691			KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> pairs;
692			MultipleKeyDatabaseEntry multiPair;
693			int cnt;
694			int[] size = new int[2];
695			size[0] = 0;
696			size[1] = 1024;
697
698			Configuration.ClearDir(testHome);
699
700			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
701			dbConfig.Creation = CreatePolicy.ALWAYS;
702			dbConfig.Duplicates = DuplicatesPolicy.SORTED;
703			dbConfig.PageSize = 1024;
704			GetMultipleDB(btreeDBFileName, dbConfig, out db, out cursor);
705
706			key = new DatabaseEntry(BitConverter.GetBytes(99));
707			data = new DatabaseEntry(BitConverter.GetBytes(99));
708			pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data);
709
710			for (int j = 0; j < 2; j++) {
711				for (int i = 0; i < 2; i++) {
712					cnt = 0;
713					cursor.Move(pair, true);
714					if (j == 0) {
715						if (size[i] == 0)
716							Assert.IsTrue(cursor.MoveNextDuplicateMultiple());
717						else
718							Assert.IsTrue(cursor.MoveNextDuplicateMultiple(size[i]));
719						pairs = cursor.CurrentMultiple;
720						foreach (DatabaseEntry dbt in pairs.Value) {
721							Assert.AreEqual(100, BitConverter.ToInt32(dbt.Data, 0));
722							cnt++;
723						}
724						Assert.AreEqual(1, cnt);
725					} else {
726						if (size[i] == 0)
727							Assert.IsTrue(cursor.MoveNextDuplicateMultipleKey());
728						else
729							Assert.IsTrue(cursor.MoveNextDuplicateMultipleKey(size[i]));
730						multiPair = cursor.CurrentMultipleKey;
731						foreach (KeyValuePair<DatabaseEntry, DatabaseEntry> p in multiPair) {
732							Assert.AreEqual(100, BitConverter.ToInt32(p.Value.Data, 0));
733							cnt++;
734						}
735						Assert.AreEqual(1, cnt);
736					}
737				}
738			}
739
740			cursor.Close();
741			db.Close();
742		}
743
744		[Test]
745		public void TestMoveNextUniqueMultipleAndMultipleKey()
746		{
747			testName = "TestMoveNextUniqueMultipleAndMultipleKey";
748			testHome = testFixtureHome + "/" + testName;
749			string btreeDBFileName = testHome + "/" +
750			    testName + ".db";
751			BTreeDatabase db;
752			BTreeCursor cursor;
753			DatabaseEntry key, data;
754			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
755			KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> pairs;
756			MultipleKeyDatabaseEntry multiPair;
757			int cnt;
758			int[] size = new int[2];
759			size[0] = 0;
760			size[1] = 1024;
761
762			Configuration.ClearDir(testHome);
763
764			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
765			dbConfig.Creation = CreatePolicy.ALWAYS;
766			dbConfig.Duplicates = DuplicatesPolicy.UNSORTED;
767			dbConfig.PageSize = 1024;
768			GetMultipleDB(btreeDBFileName, dbConfig, out db, out cursor);
769
770			key = new DatabaseEntry(BitConverter.GetBytes(99));
771			data = new DatabaseEntry(BitConverter.GetBytes(99));
772			pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data);
773
774			for (int j = 0; j < 2; j++) {
775				for (int i = 0; i < 2; i++) {
776					cnt = 0;
777					cursor.Move(pair, true);
778					if (j == 0) {
779						if (size[i] == 0)
780							Assert.IsTrue(cursor.MoveNextUniqueMultiple());
781						else
782							Assert.IsTrue(cursor.MoveNextUniqueMultiple(size[i]));
783						pairs = cursor.CurrentMultiple;
784						foreach (DatabaseEntry dbt in pairs.Value) {
785							Assert.AreEqual(101,  BitConverter.ToInt32(dbt.Data, 0));
786							cnt++;
787						}
788						Assert.AreEqual(1, cnt);
789					} else {
790						if (size[i] == 0)
791							Assert.IsTrue(cursor.MoveNextUniqueMultipleKey());
792						else
793							Assert.IsTrue(cursor.MoveNextUniqueMultipleKey(size[i]));
794						multiPair = cursor.CurrentMultipleKey;
795						foreach (KeyValuePair<DatabaseEntry, DatabaseEntry> p in multiPair) {
796							Assert.AreEqual(101, BitConverter.ToInt32(p.Value.Data, 0));
797							cnt++;
798						}
799						Assert.AreEqual(1, cnt);
800					}
801				}
802			}
803
804			cursor.Close();
805			db.Close();
806		}
807
808		[Test]
809		public void TestRefreshMultipleAndMultipleKey()
810		{
811			testName = "TestRefreshMultipleAndMultipleKey";
812			testHome = testFixtureHome + "/" + testName;
813			string btreeDBFileName = testHome + "/" +
814			    testName + ".db";
815			BTreeDatabase db;
816			BTreeCursor cursor;
817			DatabaseEntry key, data;
818			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
819			KeyValuePair<DatabaseEntry, MultipleDatabaseEntry> pairs;
820			MultipleKeyDatabaseEntry multiPair;
821			int cnt;
822			int[] size = new int[2];
823			size[0] = 0;
824			size[1] = 1024;
825
826			Configuration.ClearDir(testHome);
827
828			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
829			dbConfig.Creation = CreatePolicy.ALWAYS;
830			dbConfig.Duplicates = DuplicatesPolicy.SORTED;
831			dbConfig.PageSize = 1024;
832			GetMultipleDB(btreeDBFileName, dbConfig, out db, out cursor);
833
834			key = new DatabaseEntry(BitConverter.GetBytes(99));
835			data = new DatabaseEntry(BitConverter.GetBytes(99));
836			pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data);
837
838			for (int j = 0; j < 2; j++) {
839				for (int i = 0; i < 2; i++) {
840					cnt = 0;
841					cursor.Move(pair, true);
842					if (j == 0) {
843						if (size[i] == 0)
844							Assert.IsTrue(cursor.RefreshMultiple());
845						else
846							Assert.IsTrue(cursor.RefreshMultiple(size[i]));
847						pairs = cursor.CurrentMultiple;
848						foreach (DatabaseEntry dbt in pairs.Value)
849							cnt++;
850						Assert.AreEqual(2, cnt);
851					} else {
852						if (size[i] == 0)
853							Assert.IsTrue(cursor.RefreshMultipleKey());
854						else
855							Assert.IsTrue(cursor.RefreshMultipleKey(size[i]));
856						multiPair = cursor.CurrentMultipleKey;
857						foreach (KeyValuePair<DatabaseEntry, DatabaseEntry> p in multiPair)
858							cnt++;
859						Assert.AreEqual(3, cnt);
860					}
861				}
862			}
863
864			cursor.Close();
865			db.Close();
866		}
867
868		private void GetMultipleDB(string dbFileName, BTreeDatabaseConfig dbConfig,
869		    out BTreeDatabase db, out BTreeCursor cursor)
870		{
871			db = BTreeDatabase.Open(dbFileName, dbConfig);
872			cursor = db.Cursor();
873
874			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
875			DatabaseEntry key, data;
876			for (int i = 1; i < 100; i++) {
877				key = new DatabaseEntry(BitConverter.GetBytes(i));
878				data = new DatabaseEntry(BitConverter.GetBytes(i));
879				pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data);
880				cursor.Add(pair);
881			}
882
883			if (dbConfig.UseRecordNumbers == true) {
884				byte[] bytes = new byte[512];
885				for (int i = 0; i < 512; i++)
886					bytes[i] = (byte)i;
887				key = new DatabaseEntry(BitConverter.GetBytes(100));
888				data = new DatabaseEntry(bytes);
889				pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data);
890				cursor.Add(pair);
891			} else {
892				if (dbConfig.Duplicates == DuplicatesPolicy.UNSORTED ||
893				    dbConfig.Duplicates == DuplicatesPolicy.SORTED) {
894					key = new DatabaseEntry(BitConverter.GetBytes(99));
895					data = new DatabaseEntry(BitConverter.GetBytes(100));
896					pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data);
897					cursor.Add(pair);
898				}
899
900				key = new DatabaseEntry(BitConverter.GetBytes(101));
901				data = new DatabaseEntry(BitConverter.GetBytes(101));
902				pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data);
903				cursor.Add(pair);
904			}
905		}
906
907		[Test]
908		public void TestInsertBefore()
909		{
910			BTreeDatabase db;
911			BTreeCursor cursor;
912			DatabaseEntry data;
913			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
914
915			testName = "TestInsertBefore";
916			testHome = testFixtureHome + "/" + testName;
917
918			Configuration.ClearDir(testHome);
919
920			// Add record("key", "data") into database.
921			CursorTest.GetCursorInBtreeDBWithoutEnv(
922			    testHome, testName, out db, out cursor);
923			CursorTest.AddOneByCursor(db, cursor);
924
925			// Insert the new record("key","data1") before the record("key", "data").
926			data = new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data1"));
927			cursor.Insert(data, Cursor.InsertLocation.BEFORE);
928
929			/*
930			 * Move the cursor to the record("key", "data") and confirm
931			 * that the previous record is the one just inserted.
932			 */
933			pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(
934			    new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key")),
935			    new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data")));
936			Assert.IsTrue(cursor.Move(pair, true));
937			Assert.IsTrue(cursor.MovePrev());
938			Assert.AreEqual(ASCIIEncoding.ASCII.GetBytes("key"),cursor.Current.Key.Data);
939			Assert.AreEqual(ASCIIEncoding.ASCII.GetBytes("data1"), cursor.Current.Value.Data);
940
941			cursor.Close();
942			db.Close();
943		}
944
945		[Test]
946		public void TestMoveToRecno()
947		{
948			BTreeDatabase db;
949			BTreeCursor cursor;
950
951			testName = "TestMoveToRecno";
952			testHome = testFixtureHome + "/" + testName;
953
954			Configuration.ClearDir(testHome);
955
956			GetCursorInBtreeDBUsingRecno(testHome, testName,
957			    out db, out cursor);
958			for (int i = 0; i < 10; i++)
959				db.Put(
960				    new DatabaseEntry(BitConverter.GetBytes(i)),
961				    new DatabaseEntry(BitConverter.GetBytes(i)));
962
963			MoveCursorToRecno(cursor, null);
964			cursor.Close();
965			db.Close();
966		}
967
968		[Test]
969		public void TestMoveToRecnoWithRMW()
970		{
971			testName = "TestMoveToRecnoWithRMW";
972			testHome = testFixtureHome + "/" + testName;
973			Configuration.ClearDir(testHome);
974
975			btCursorFunc = new BTCursorMoveFuncDelegate(
976			    MoveCursorToRecno);
977
978			// Move to a specified key and a key/data pair.
979			MoveWithRMW(testHome, testName);
980		}
981
982		[Test]
983		public void TestRecno()
984		{
985			BTreeDatabase db;
986			BTreeCursor cursor;
987
988			testName = "TestRecno";
989			testHome = testFixtureHome + "/" + testName;
990
991			Configuration.ClearDir(testHome);
992
993			GetCursorInBtreeDBUsingRecno(testHome, testName,
994			    out db, out cursor);
995			for (int i = 0; i < 10; i++)
996				db.Put(
997				    new DatabaseEntry(BitConverter.GetBytes(i)),
998				    new DatabaseEntry(BitConverter.GetBytes(i)));
999
1000			ReturnRecno(cursor, null);
1001			cursor.Close();
1002			db.Close();
1003		}
1004
1005		[Test]
1006		public void TestRecnoWithRMW()
1007		{
1008			testName = "TestRecnoWithRMW";
1009			testHome = testFixtureHome + "/" + testName;
1010			Configuration.ClearDir(testHome);
1011
1012			// Use MoveCursorToRecno() as its move function.
1013			btCursorFunc = new BTCursorMoveFuncDelegate(
1014			    ReturnRecno);
1015
1016			// Move to a specified key and a key/data pair.
1017			MoveWithRMW(testHome, testName);
1018		}
1019
1020		/*
1021		 * Move the cursor according to recno. The recno
1022		 * starts from 1 and is increased by 1.
1023		 */
1024		public void MoveCursorToRecno(BTreeCursor cursor,
1025		    LockingInfo lck)
1026		{
1027			for (uint i = 1; i <= 5; i++)
1028				if (lck == null)
1029					Assert.IsTrue(cursor.Move(i));
1030				else
1031					Assert.IsTrue(cursor.Move(i, lck));
1032		}
1033
1034		/*l
1035		 * Move the cursor according to a given recno and
1036		 * return the current record's recno. The given recno
1037		 * and the one got from the cursor should be the same.
1038		 */
1039		public void ReturnRecno(BTreeCursor cursor,
1040		    LockingInfo lck)
1041		{
1042			for (uint i = 1; i <= 5; i++)
1043				if (lck == null)
1044				{
1045					if (cursor.Move(i) == true)
1046						Assert.AreEqual(i, cursor.Recno());
1047				}
1048				else
1049				{
1050					if (cursor.Move(i, lck) == true)
1051						Assert.AreEqual(i, cursor.Recno(lck));
1052				}
1053		}
1054
1055		public void GetCursorInBtreeDBUsingRecno(string home,
1056		    string name, out BTreeDatabase db,
1057		    out BTreeCursor cursor)
1058		{
1059			string dbFileName = home + "/" + name + ".db";
1060			BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig();
1061			dbConfig.UseRecordNumbers = true;
1062			dbConfig.Creation = CreatePolicy.IF_NEEDED;
1063			db = BTreeDatabase.Open(dbFileName, dbConfig);
1064			cursor = db.Cursor();
1065		}
1066
1067		public void RdMfWt()
1068		{
1069			Transaction txn = paramEnv.BeginTransaction();
1070			BTreeCursor dbc = paramDB.Cursor(txn);
1071
1072			try
1073			{
1074				LockingInfo lck = new LockingInfo();
1075				lck.ReadModifyWrite = true;
1076
1077				// Read record.
1078				btCursorFunc(dbc, lck);
1079
1080				// Block the current thread until event is set.
1081				signal.WaitOne();
1082
1083				// Write new records into database.
1084				DatabaseEntry key = new DatabaseEntry(BitConverter.GetBytes(55));
1085				DatabaseEntry data = new DatabaseEntry(BitConverter.GetBytes(55));
1086				dbc.Add(new KeyValuePair<DatabaseEntry, DatabaseEntry>(key, data));
1087
1088				dbc.Close();
1089				txn.Commit();
1090			}
1091			catch (DeadlockException)
1092			{
1093				dbc.Close();
1094				txn.Abort();
1095			}
1096		}
1097
1098
1099		public void MoveWithRMW(string home, string name)
1100		{
1101			paramEnv = null;
1102			paramDB = null;
1103
1104			// Open the environment.
1105			DatabaseEnvironmentConfig envCfg =
1106			    new DatabaseEnvironmentConfig();
1107			envCfg.Create = true;
1108			envCfg.FreeThreaded = true;
1109			envCfg.UseLocking = true;
1110			envCfg.UseLogging = true;
1111			envCfg.UseMPool = true;
1112			envCfg.UseTxns = true;
1113			paramEnv = DatabaseEnvironment.Open(home, envCfg);
1114
1115			// Open database in transaction.
1116			Transaction openTxn = paramEnv.BeginTransaction();
1117			BTreeDatabaseConfig cfg = new BTreeDatabaseConfig();
1118			cfg.Creation = CreatePolicy.ALWAYS;
1119			cfg.Env = paramEnv;
1120			cfg.FreeThreaded = true;
1121			cfg.PageSize = 4096;
1122			// Use record number.
1123			cfg.UseRecordNumbers = true;
1124			paramDB = BTreeDatabase.Open(name + ".db", cfg, openTxn);
1125			openTxn.Commit();
1126
1127			/*
1128			 * Put 10 different, 2 duplicate and another different
1129			 * records into database.
1130			 */
1131			Transaction txn = paramEnv.BeginTransaction();
1132			for (int i = 0; i < 13; i++)
1133			{
1134				DatabaseEntry key, data;
1135				if (i == 10 || i == 11)
1136				{
1137					key = new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("key"));
1138					data = new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data"));
1139				}
1140				else
1141				{
1142					key = new DatabaseEntry(BitConverter.GetBytes(i));
1143					data = new DatabaseEntry(BitConverter.GetBytes(i));
1144				}
1145				paramDB.Put(key, data, txn);
1146			}
1147
1148			txn.Commit();
1149
1150			// Get a event wait handle.
1151			signal = new EventWaitHandle(false,
1152			    EventResetMode.ManualReset);
1153
1154			/*
1155			 * Start RdMfWt() in two threads. RdMfWt() reads
1156			 * and writes data into database.
1157			 */
1158			Thread t1 = new Thread(new ThreadStart(RdMfWt));
1159			Thread t2 = new Thread(new ThreadStart(RdMfWt));
1160			t1.Start();
1161			t2.Start();
1162
1163			// Give both threads time to read before signalling them to write.
1164			Thread.Sleep(1000);
1165
1166			// Invoke the write operation in both threads.
1167			signal.Set();
1168
1169			// Return the number of deadlocks.
1170			while (t1.IsAlive || t2.IsAlive)
1171			{
1172				/*
1173				 * Give both threads time to write before
1174				 * counting the number of deadlocks.
1175				 */
1176				Thread.Sleep(1000);
1177				uint deadlocks = paramEnv.DetectDeadlocks(DeadlockPolicy.DEFAULT);
1178
1179				// Confirm that there won't be any deadlock.
1180				Assert.AreEqual(0, deadlocks);
1181			}
1182
1183			t1.Join();
1184			t2.Join();
1185			paramDB.Close();
1186			paramEnv.Close();
1187		}
1188
1189	}
1190}
1191
1192
1193