/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2009, 2011 Oracle and/or its affiliates. All rights reserved. * */ using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using System.Xml; using NUnit.Framework; using BerkeleyDB; namespace CsharpAPITest { [TestFixture] public class SecondaryDatabaseTest : CSharpTestFixture { [TestFixtureSetUp] public void SetUpTestFixture() { testFixtureName = "SecondaryDatabaseTest"; base.SetUpTestfixture(); } [Test] public void TestDeleteMultiple() { testName = "TestDeleteMultiple"; SetUpTest(true); DeleteMultipleAndMultipleKey( testHome + "/" + testName + ".db", DatabaseType.BTREE.ToString(), DatabaseType.BTREE, false); DeleteMultipleAndMultipleKey( testHome + "/" + testName + ".db", DatabaseType.HASH.ToString(), DatabaseType.HASH, false); DeleteMultipleAndMultipleKey( testHome + "/" + testName + ".db", DatabaseType.QUEUE.ToString(), DatabaseType.QUEUE, false); DeleteMultipleAndMultipleKey( testHome + "/" + testName + ".db", DatabaseType.RECNO.ToString(), DatabaseType.RECNO, false); } [Test] public void TestDeleteMultipleKey() { testName = "TestDeleteMultipleKey"; SetUpTest(true); DeleteMultipleAndMultipleKey( testHome + "/" + testName + ".db", DatabaseType.BTREE.ToString(), DatabaseType.BTREE, true); DeleteMultipleAndMultipleKey( testHome + "/" + testName + ".db", DatabaseType.HASH.ToString(), DatabaseType.HASH, true); DeleteMultipleAndMultipleKey( testHome + "/" + testName + ".db", DatabaseType.QUEUE.ToString(), DatabaseType.QUEUE, true); DeleteMultipleAndMultipleKey( testHome + "/" + testName + ".db", DatabaseType.RECNO.ToString(), DatabaseType.RECNO, true); } private void DeleteMultipleAndMultipleKey(string dbFileName, string dbName, DatabaseType type, bool mulKey) { List kList = new List(); List rList = new List(); List> pList = new List>(); DatabaseEntry key; Database db; SecondaryDatabase secDb; Configuration.ClearDir(testHome); if (type == DatabaseType.BTREE) { BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig(); dbConfig.Creation = CreatePolicy.IF_NEEDED; db = BTreeDatabase.Open( dbFileName, dbName, dbConfig); SecondaryBTreeDatabaseConfig secDbConfig = new SecondaryBTreeDatabaseConfig(db, null); secDbConfig.Creation = CreatePolicy.IF_NEEDED; secDbConfig.Duplicates = DuplicatesPolicy.SORTED; secDbConfig.KeyGen = new SecondaryKeyGenDelegate(SecondaryKeyGen); secDb = SecondaryBTreeDatabase.Open( dbFileName, dbName + "_sec", secDbConfig); } else if (type == DatabaseType.HASH) { HashDatabaseConfig dbConfig = new HashDatabaseConfig(); dbConfig.Creation = CreatePolicy.IF_NEEDED; db = HashDatabase.Open( dbFileName, dbName, dbConfig); SecondaryHashDatabaseConfig secDbConfig = new SecondaryHashDatabaseConfig(db, null); secDbConfig.Creation = CreatePolicy.IF_NEEDED; secDbConfig.Duplicates = DuplicatesPolicy.SORTED; secDbConfig.KeyGen = new SecondaryKeyGenDelegate(SecondaryKeyGen); secDb = SecondaryHashDatabase.Open( dbFileName, dbName + "_sec", secDbConfig); } else if (type == DatabaseType.QUEUE) { QueueDatabaseConfig dbConfig = new QueueDatabaseConfig(); dbConfig.Creation = CreatePolicy.IF_NEEDED; dbConfig.Length = 4; db = QueueDatabase.Open(dbFileName, dbConfig); SecondaryQueueDatabaseConfig secDbConfig = new SecondaryQueueDatabaseConfig(db, null); secDbConfig.Creation = CreatePolicy.IF_NEEDED; secDbConfig.Length = 4; secDbConfig.KeyGen = new SecondaryKeyGenDelegate(SecondaryKeyGen); secDb = SecondaryQueueDatabase.Open( dbFileName + "_sec", secDbConfig); } else if (type == DatabaseType.RECNO) { RecnoDatabaseConfig dbConfig = new RecnoDatabaseConfig(); dbConfig.Creation = CreatePolicy.IF_NEEDED; db = RecnoDatabase.Open( dbFileName, dbName, dbConfig); SecondaryRecnoDatabaseConfig secDbConfig = new SecondaryRecnoDatabaseConfig(db, null); secDbConfig.Creation = CreatePolicy.IF_NEEDED; secDbConfig.KeyGen = new SecondaryKeyGenDelegate(SecondaryKeyGen); secDb = SecondaryRecnoDatabase.Open( dbFileName, dbName + "_sec", secDbConfig); } else throw new TestException(); for (uint i = 1; i <= 100; i++) { key = new DatabaseEntry( BitConverter.GetBytes(i)); if (i >= 50 && i < 60) kList.Add(key); else if (i > 80) pList.Add(new KeyValuePair< DatabaseEntry, DatabaseEntry>( key, key)); else if (type == DatabaseType.QUEUE || type == DatabaseType.RECNO) rList.Add(i); db.Put(key, key); } if (mulKey) { // Create bulk buffer for key/value pairs. MultipleKeyDatabaseEntry pBuff; if (type == DatabaseType.BTREE) pBuff = new MultipleKeyDatabaseEntry( pList, false); else if (type == DatabaseType.HASH) pBuff = new MultipleKeyDatabaseEntry( pList, false); else if (type == DatabaseType.QUEUE) pBuff = new MultipleKeyDatabaseEntry( pList, true); else pBuff = new MultipleKeyDatabaseEntry( pList, true); // Bulk delete with the key/value pair bulk buffer. secDb.Delete(pBuff); foreach (KeyValuePairpair in pList) { try { db.GetBoth(pair.Key, pair.Value); throw new TestException(); } catch (NotFoundException e1) { if (type == DatabaseType.QUEUE) throw e1; } catch (KeyEmptyException e2) { if (type == DatabaseType.BTREE || type == DatabaseType.HASH || type == DatabaseType.RECNO) throw e2; } } /* * Dump the database to verify that 80 records * remain after bulk delete. */ Assert.AreEqual(80, db.Truncate()); } else { // Create bulk buffer for key. MultipleDatabaseEntry kBuff; if (type == DatabaseType.BTREE) kBuff = new MultipleDatabaseEntry( kList, false); else if (type == DatabaseType.HASH) kBuff = new MultipleDatabaseEntry( kList, false); else if (type == DatabaseType.QUEUE) kBuff = new MultipleDatabaseEntry( kList, true); else kBuff = new MultipleDatabaseEntry( kList, true); /* * Bulk delete in secondary database with key * buffer. Primary records that the deleted * records in secondar database should be * deleted as well. */ secDb.Delete(kBuff); foreach (DatabaseEntry dbt in kList) { try { db.Get(dbt); throw new TestException(); } catch (NotFoundException e1) { if (type == DatabaseType.QUEUE || type == DatabaseType.RECNO) throw e1; } catch (KeyEmptyException e2) { if (type == DatabaseType.BTREE || type == DatabaseType.HASH) throw e2; } } /* * Bulk delete in secondary database with recno * based key buffer. */ if (type == DatabaseType.QUEUE || type == DatabaseType.RECNO) { MultipleDatabaseEntry rBuff = new MultipleDatabaseEntry(rList); secDb.Delete(rBuff); Assert.AreEqual(20, db.Truncate()); } } secDb.Close(); db.Close(); } [Test] public void TestKeyGen() { testName = "TestKeyGen"; SetUpTest(true); string dbFileName = testHome + "/" + testName + ".db"; // Open primary database. BTreeDatabaseConfig primaryDBConfig = new BTreeDatabaseConfig(); primaryDBConfig.Creation = CreatePolicy.IF_NEEDED; BTreeDatabase primaryDB = BTreeDatabase.Open(dbFileName, primaryDBConfig); // Open secondary database. SecondaryBTreeDatabaseConfig secDBConfig = new SecondaryBTreeDatabaseConfig(primaryDB, new SecondaryKeyGenDelegate(SecondaryKeyGen)); SecondaryBTreeDatabase secDB = SecondaryBTreeDatabase.Open(dbFileName, secDBConfig); primaryDB.Put(new DatabaseEntry( BitConverter.GetBytes((int)1)), new DatabaseEntry(BitConverter.GetBytes((int)11))); KeyValuePair pair; pair = secDB.Get(new DatabaseEntry( BitConverter.GetBytes((int)11))); Assert.IsNotNull(pair.Value); // Close secondary database. secDB.Close(); // Close primary database. primaryDB.Close(); } [Test] public void TestSecondaryCursor() { testName = "TestSecondaryCursor"; SetUpTest(true); string dbFileName = testHome + "/" + testName + ".db"; // Open primary database. BTreeDatabaseConfig primaryDBConfig = new BTreeDatabaseConfig(); primaryDBConfig.Creation = CreatePolicy.IF_NEEDED; BTreeDatabase primaryDB = BTreeDatabase.Open(dbFileName, primaryDBConfig); // Open secondary database. SecondaryBTreeDatabaseConfig secDBConfig = new SecondaryBTreeDatabaseConfig(primaryDB, new SecondaryKeyGenDelegate(SecondaryKeyGen)); SecondaryBTreeDatabase secDB = SecondaryBTreeDatabase.Open(dbFileName, secDBConfig); primaryDB.Put(new DatabaseEntry( BitConverter.GetBytes((int)1)), new DatabaseEntry(BitConverter.GetBytes((int)11))); SecondaryCursor cursor = secDB.SecondaryCursor(); cursor.Move(new DatabaseEntry( BitConverter.GetBytes((int)11)), true); Assert.AreEqual(BitConverter.GetBytes((int)11), cursor.Current.Key.Data); // Close the cursor. cursor.Close(); // Close secondary database. secDB.Close(); // Close primary database. primaryDB.Close(); } [Test] public void TestSecondaryCursorWithConfig() { testName = "TestSecondaryCursorWithConfig"; SetUpTest(true); string dbFileName = testHome + "/" + testName + ".db"; BTreeDatabase db; SecondaryBTreeDatabase secDB; OpenPrimaryAndSecondaryDB(dbFileName, out db, out secDB); for (int i = 0; i < 10; i++) db.Put(new DatabaseEntry(BitConverter.GetBytes(i)), new DatabaseEntry(BitConverter.GetBytes((int)i))); CursorConfig cursorConfig = new CursorConfig(); cursorConfig.WriteCursor = false; SecondaryCursor cursor = secDB.SecondaryCursor(cursorConfig); cursor.Move(new DatabaseEntry( BitConverter.GetBytes((int)5)), true); Assert.AreEqual(1, cursor.Count()); // Close the cursor. cursor.Close(); // Close secondary database. secDB.Close(); // Close primary database. db.Close(); } [Test] public void TestSecondaryCursorWithTxn() { testName = "TestSecondaryCursorWithTxn"; SetUpTest(true); string dbFileName = testHome + "/" + testName + ".db"; GetSecondaryCursurWithTxn(testHome, testName, false); } [Test] public void TestSecondaryCursorWithConfigAndTxn() { testName = "TestSecondaryCursorWithConfigAndTxn"; SetUpTest(true); string dbFileName = testHome + "/" + testName + ".db"; GetSecondaryCursurWithTxn(testHome, testName, true); } public void GetSecondaryCursurWithTxn(string home, string name, bool ifCfg) { string dbFileName = name + ".db"; SecondaryCursor cursor; // Open env. DatabaseEnvironmentConfig envConfig = new DatabaseEnvironmentConfig(); envConfig.Create = true; envConfig.UseTxns = true; envConfig.UseMPool = true; DatabaseEnvironment env = DatabaseEnvironment.Open(home, envConfig); // Open primary/secondary database. Transaction txn = env.BeginTransaction(); BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig(); dbConfig.Creation = CreatePolicy.IF_NEEDED; dbConfig.Env = env; BTreeDatabase db = BTreeDatabase.Open(dbFileName, dbConfig, txn); SecondaryBTreeDatabaseConfig secDBConfig = new SecondaryBTreeDatabaseConfig(db, new SecondaryKeyGenDelegate(SecondaryKeyGen)); secDBConfig.Env = env; SecondaryBTreeDatabase secDB = SecondaryBTreeDatabase.Open(dbFileName, secDBConfig, txn); for (int i = 0; i < 10; i++) db.Put(new DatabaseEntry(BitConverter.GetBytes(i)), new DatabaseEntry(BitConverter.GetBytes((int)i)), txn); // Create secondary cursor. if (ifCfg == false) secDB.SecondaryCursor(txn); else if (ifCfg == true) { CursorConfig cursorConfig = new CursorConfig(); cursorConfig.WriteCursor = false; cursor = secDB.SecondaryCursor(cursorConfig, txn); cursor.Close(); } secDB.Close(); db.Close(); txn.Commit(); env.Close(); } [Test] public void TestOpen() { testName = "TestOpen"; SetUpTest(true); string dbFileName = testHome + "/" + testName + ".db"; string dbSecFileName = testHome + "/" + testName + "_sec.db"; OpenSecQueueDB(dbFileName, dbSecFileName, false); } [Test] public void TestOpenWithDBName() { testName = "TestOpenWithDBName"; SetUpTest(true); string dbFileName = testHome + "/" + testName + ".db"; string dbSecFileName = testHome + "/" + testName + "_sec.db"; OpenSecQueueDB(dbFileName, dbSecFileName, true); } public void OpenSecQueueDB(string dbFileName, string dbSecFileName, bool ifDBName) { // Open a primary btree database. BTreeDatabaseConfig primaryDBConfig = new BTreeDatabaseConfig(); primaryDBConfig.Creation = CreatePolicy.IF_NEEDED; BTreeDatabase primaryDB; /* * If secondary database name is given, the primary * database is also opened with database name. */ if (ifDBName == false) primaryDB = BTreeDatabase.Open(dbFileName, primaryDBConfig); else primaryDB = BTreeDatabase.Open(dbFileName, "primary", primaryDBConfig); try { // Open a new secondary database. SecondaryBTreeDatabaseConfig secBTDBConfig = new SecondaryBTreeDatabaseConfig( primaryDB, null); secBTDBConfig.Creation = CreatePolicy.IF_NEEDED; SecondaryBTreeDatabase secBTDB; if (ifDBName == false) secBTDB = SecondaryBTreeDatabase.Open( dbSecFileName, secBTDBConfig); else secBTDB = SecondaryBTreeDatabase.Open( dbSecFileName, "secondary", secBTDBConfig); // Close the secondary database. secBTDB.Close(); // Open the existing secondary database. SecondaryDatabaseConfig secDBConfig = new SecondaryDatabaseConfig( primaryDB, null); SecondaryDatabase secDB; if (ifDBName == false) secDB = SecondaryBTreeDatabase.Open( dbSecFileName, secDBConfig); else secDB = SecondaryBTreeDatabase.Open( dbSecFileName, "secondary", secDBConfig); // Close secondary database. secDB.Close(); } catch (DatabaseException) { throw new TestException(); } finally { // Close primary database. primaryDB.Close(); } } [Test] public void TestOpenWithinTxn() { testName = "TestOpenWithinTxn"; SetUpTest(true); string dbFileName = testName + ".db"; string dbSecFileName = testName + "_sec.db"; OpenSecDBWithinTxn(testHome, dbFileName, dbSecFileName, false); } [Test] public void TestOpenDBNameWithinTxn() { testName = "TestOpenDBNameWithinTxn"; SetUpTest(true); string dbFileName = testName + ".db"; string dbSecFileName = testName + "_sec.db"; OpenSecDBWithinTxn(testHome, dbFileName, dbSecFileName, true); } public void OpenSecDBWithinTxn(string home, string dbFileName, string dbSecFileName, bool ifDbName) { // Open an environment. DatabaseEnvironmentConfig envConfig = new DatabaseEnvironmentConfig(); envConfig.Create = true; envConfig.UseTxns = true; envConfig.UseMPool = true; envConfig.UseLogging = true; envConfig.UseLocking = true; DatabaseEnvironment env = DatabaseEnvironment.Open( home, envConfig); // Open a primary btree database. Transaction openDBTxn = env.BeginTransaction(); BTreeDatabaseConfig dbConfig = new BTreeDatabaseConfig(); dbConfig.Creation = CreatePolicy.IF_NEEDED; dbConfig.Env = env; BTreeDatabase db = BTreeDatabase.Open( dbFileName, dbConfig, openDBTxn); // Open a secondary btree database. SecondaryBTreeDatabaseConfig secDBConfig = new SecondaryBTreeDatabaseConfig(db, new SecondaryKeyGenDelegate(SecondaryKeyGen)); secDBConfig.Env = env; secDBConfig.Creation = CreatePolicy.IF_NEEDED; SecondaryBTreeDatabase secDB; if (ifDbName == false) secDB = SecondaryBTreeDatabase.Open( dbSecFileName, secDBConfig, openDBTxn); else secDB = SecondaryBTreeDatabase.Open( dbSecFileName, "secondary", secDBConfig, openDBTxn); openDBTxn.Commit(); secDB.Close(); // Open the existing secondary database. Transaction secTxn = env.BeginTransaction(); SecondaryDatabaseConfig secConfig = new SecondaryDatabaseConfig(db, new SecondaryKeyGenDelegate(SecondaryKeyGen)); secConfig.Env = env; SecondaryDatabase secExDB; if (ifDbName == false) secExDB = SecondaryBTreeDatabase.Open( dbSecFileName, secConfig, secTxn); else secExDB = SecondaryBTreeDatabase.Open( dbSecFileName, "secondary", secConfig, secTxn); secExDB.Close(); secTxn.Commit(); db.Close(); env.Close(); } public void OpenPrimaryAndSecondaryDB(string dbFileName, out BTreeDatabase primaryDB, out SecondaryBTreeDatabase secDB) { // Open primary database. BTreeDatabaseConfig primaryDBConfig = new BTreeDatabaseConfig(); primaryDBConfig.Creation = CreatePolicy.IF_NEEDED; primaryDB = BTreeDatabase.Open(dbFileName, primaryDBConfig); // Open secondary database. SecondaryBTreeDatabaseConfig secDBConfig = new SecondaryBTreeDatabaseConfig(primaryDB, new SecondaryKeyGenDelegate(SecondaryKeyGen)); secDB = SecondaryBTreeDatabase.Open(dbFileName, secDBConfig); } [Test, ExpectedException(typeof(ExpectedTestException))] public void TestBadSecondaryException() { testName = "TestBadSecondaryException"; SetUpTest(true); string dbFileName = testHome + "/" + testName + ".db"; string secDBFileName = testHome + "/" + testName + "_sec.db"; // Open primary database. BTreeDatabaseConfig btreeDBConfig = new BTreeDatabaseConfig(); btreeDBConfig.Creation = CreatePolicy.IF_NEEDED; BTreeDatabase btreeDB = BTreeDatabase.Open(dbFileName, btreeDBConfig); // Open secondary database. SecondaryBTreeDatabaseConfig secBtDbConfig = new SecondaryBTreeDatabaseConfig(btreeDB, new SecondaryKeyGenDelegate(SecondaryKeyGen)); secBtDbConfig.Creation = CreatePolicy.IF_NEEDED; SecondaryBTreeDatabase secBtDb = SecondaryBTreeDatabase.Open(secDBFileName, secBtDbConfig); // Put some data into primary database. for (int i = 0; i < 10; i++) btreeDB.Put(new DatabaseEntry( BitConverter.GetBytes(i)), new DatabaseEntry(BitConverter.GetBytes(i))); // Close the secondary database. secBtDb.Close(); // Delete record(5, 5) in primary database. btreeDB.Delete(new DatabaseEntry( BitConverter.GetBytes((int)5))); // Reopen the secondary database. SecondaryDatabase secDB = SecondaryDatabase.Open( secDBFileName, new SecondaryDatabaseConfig(btreeDB, new SecondaryKeyGenDelegate(SecondaryKeyGen))); /* * Getting record(5, 5) by secondary database should * throw BadSecondaryException since it has been * deleted in the primary database. */ try { secDB.Exists(new DatabaseEntry( BitConverter.GetBytes((int)5))); } catch (BadSecondaryException) { throw new ExpectedTestException(); } finally { secDB.Close(); btreeDB.Close(); } } public DatabaseEntry SecondaryKeyGen( DatabaseEntry key, DatabaseEntry data) { DatabaseEntry dbtGen; dbtGen = new DatabaseEntry(data.Data); return dbtGen; } } }