/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved.
*
*/
/*
* Alexg 23-4-06
* Based on scr016 TestConstruct01 and TestConstruct02
* test applications.
*/
package com.sleepycat.db.test;
import org.junit.Before;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.sleepycat.db.BtreeStats;
import com.sleepycat.db.Cursor;
import com.sleepycat.db.CursorConfig;
import com.sleepycat.db.Database;
import com.sleepycat.db.DatabaseConfig;
import com.sleepycat.db.DatabaseEntry;
import com.sleepycat.db.DatabaseException;
import com.sleepycat.db.DatabaseStream;
import com.sleepycat.db.DatabaseStreamConfig;
import com.sleepycat.db.DatabaseType;
import com.sleepycat.db.Environment;
import com.sleepycat.db.EnvironmentConfig;
import com.sleepycat.db.HashStats;
import com.sleepycat.db.HeapStats;
import com.sleepycat.db.LockMode;
import com.sleepycat.db.MultipleDataEntry;
import com.sleepycat.db.OperationStatus;
import com.sleepycat.db.PartitionHandler;
import com.sleepycat.db.Transaction;
import java.io.File;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.lang.reflect.Array;
import com.sleepycat.db.test.TestUtils;
public class DatabaseTest {
public static final String DATABASETEST_DBNAME = "databasetest.db";
private static int itemcount; // count the number of items in the database
@BeforeClass public static void ClassInit() {
TestUtils.loadConfig(null);
TestUtils.check_file_removed(TestUtils.getDBFileName(DATABASETEST_DBNAME), true, true);
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR, TestUtils.getDBFileName(DATABASETEST_DBNAME));
itemcount = 0;
}
@AfterClass public static void ClassShutdown() {
TestUtils.check_file_removed(TestUtils.getDBFileName(DATABASETEST_DBNAME), true, true);
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR, TestUtils.getDBFileName(DATABASETEST_DBNAME));
}
@Before public void PerTestInit()
throws Exception {
}
@After public void PerTestShutdown()
throws Exception {
}
/*
* Test creating a new database, and then
* opening and adding records to an existing database.
*/
@Test public void test1()
throws DatabaseException, FileNotFoundException
{
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR,
TestUtils.getDBFileName(DATABASETEST_DBNAME));
itemcount = 0;
// Create a new database.
TestOptions options = new TestOptions();
options.db_config.setErrorPrefix("DatabaseTest::test1 ");
rundb(itemcount++, options);
// Open and add records to the existing database.
rundb(itemcount++, options);
}
/*
* Test modifying the error prefix multiple times.
*/
@Test public void test2()
throws DatabaseException, FileNotFoundException
{
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR,
TestUtils.getDBFileName(DATABASETEST_DBNAME));
itemcount = 0;
TestOptions options = new TestOptions();
options.db_config.setErrorPrefix("DatabaseTest::test2 ");
for (int i=0; i<100; i++)
options.db_config.setErrorPrefix("str" + i);
rundb(itemcount++, options);
}
/*
* Test opening a database with an env.
*/
@Test public void test3()
throws DatabaseException, FileNotFoundException
{
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR,
TestUtils.getDBFileName(DATABASETEST_DBNAME));
itemcount = 0;
TestOptions options = new TestOptions();
options.db_config.setErrorPrefix("DatabaseTest::test3 ");
EnvironmentConfig envc = new EnvironmentConfig();
envc.setAllowCreate(true);
envc.setInitializeCache(true);
options.db_env = new Environment(TestUtils.BASETEST_DBFILE, envc);
rundb(itemcount++, options);
options.db_env.close();
}
/*
* Test opening multiple databases using the same env.
*/
@Test public void test4()
throws DatabaseException, FileNotFoundException
{
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR,
TestUtils.getDBFileName(DATABASETEST_DBNAME));
itemcount = 0;
TestOptions options = new TestOptions();
options.db_config.setErrorPrefix("DatabaseTest::test4 ");
EnvironmentConfig envc = new EnvironmentConfig();
envc.setAllowCreate(true);
envc.setInitializeCache(true);
options.db_env = new Environment(TestUtils.BASETEST_DBFILE, envc);
rundb(itemcount++, options);
rundb(itemcount++, options);
options.db_env.close();
}
/*
* Test just opening and closing a DB and an Env without
* doing any operations.
*/
@Test public void test5()
throws DatabaseException, FileNotFoundException
{
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR,
TestUtils.getDBFileName(DATABASETEST_DBNAME));
TestOptions options = new TestOptions();
options.db_config.setErrorPrefix("DatabaseTest::test5 ");
options.db_config.setAllowCreate(true);
Database db =
new Database(TestUtils.getDBFileName(DATABASETEST_DBNAME),
null, options.db_config);
EnvironmentConfig envc = new EnvironmentConfig();
envc.setAllowCreate(true);
envc.setInitializeCache(true);
Environment db_env = new Environment(TestUtils.BASETEST_DBFILE, envc);
db.close();
db_env.close();
System.gc();
System.runFinalization();
}
/*
* test6 leaves a db and dbenv open; it should be detected.
*/
/* Not sure if relevant with current API.
@Test public void test6()
throws DatabaseException, FileNotFoundException
{
TestOptions options = new TestOptions();
options.db_config.setErrorPrefix("DatabaseTest::test6 ");
Database db = new Database(TestUtils.getDBFileName(DATABASETEST_DBNAME), null, options.db_config);
EnvironmentConfig envc = new EnvironmentConfig();
envc.setAllowCreate(true);
envc.setInitializeCache(true);
Environment db_env = new Environment(TestUtils.BASETEST_DBFILE, envc);
System.gc();
System.runFinalization();
}
*/
/*
* Test leaving database and cursors open won't harm.
*/
@Test public void test8()
throws DatabaseException, FileNotFoundException
{
System.out.println("\nTest 10 transactional.");
test8_int(true);
System.out.println("\nTest 10 non-transactional.");
test8_int(false);
}
void test8_int(boolean havetxn)
throws DatabaseException, FileNotFoundException
{
String name;
Transaction txn = null;
itemcount = 0;
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR, TestUtils.getDBFileName(DATABASETEST_DBNAME));
TestOptions options = new TestOptions();
options.save_db = true;
options.db_config.setErrorPrefix("DatabaseTest::test8 ");
EnvironmentConfig envc = new EnvironmentConfig();
envc.setAllowCreate(true);
envc.setInitializeCache(true);
envc.setPrivate(true);
if (havetxn) {
envc.setInitializeLogging(true);
envc.setInitializeLocking(true);
envc.setTransactional(true);
}
options.db_env = new Environment(null, envc);
if (havetxn)
txn = options.db_env.beginTransaction(null, null);
try {
options.db_config.setAllowCreate(true);
options.database = options.db_env.openDatabase(txn, null, null, options.db_config);
// Acquire a cursor for the database and use it to insert data, but don't close it.
Cursor dbc = options.database.openCursor(txn, CursorConfig.DEFAULT);
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry data = new DatabaseEntry();
byte bytes[] = new byte[4];
int ii;
for (int i = 0; i < 100; i++) {
ii = i;
bytes[0] = (byte)ii;
ii >>= 8;
bytes[1] = (byte)ii;
ii >>= 8;
bytes[2] = (byte)ii;
ii >>= 8;
bytes[3] = (byte)ii;
key.setData(bytes);
key.setSize(bytes.length);
data.setData(bytes);
data.setSize(bytes.length);
dbc.putKeyLast(key, data);
}
// Do not close dbc.
// Acquire a cursor for the database and use it to read data, but don't close it.
Cursor csr = options.database.openCursor(txn, CursorConfig.DEFAULT);
int i = 0;
while (i < 100 && csr.getNext(key, data, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
i++;
}
// Do not close csr.
if (havetxn)
txn.commit();
} catch (DatabaseException e) {
if (havetxn && txn != null)
txn.abort();
}
// Do not close options.database
options.db_env.closeForceSync();
}
/*
* Test creating a new database.
*/
@Test public void test7()
throws DatabaseException, FileNotFoundException
{
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR,
TestUtils.getDBFileName(DATABASETEST_DBNAME));
itemcount = 0;
TestOptions options = new TestOptions();
options.save_db = true;
options.db_config.setErrorPrefix("DatabaseTest::test7 ");
EnvironmentConfig envc = new EnvironmentConfig();
envc.setAllowCreate(true);
envc.setInitializeCache(true);
options.db_env = new Environment(TestUtils.BASETEST_DBFILE, envc);
// stop rundb from closing the database, and pass in one created.
rundb(itemcount++, options);
rundb(itemcount++, options);
rundb(itemcount++, options);
rundb(itemcount++, options);
rundb(itemcount++, options);
rundb(itemcount++, options);
options.database.close();
options.database = null;
// reopen the same database.
rundb(itemcount++, options);
rundb(itemcount++, options);
rundb(itemcount++, options);
rundb(itemcount++, options);
rundb(itemcount++, options);
rundb(itemcount++, options);
options.database.close();
options.database = null;
options.db_env.close();
}
/*
* Test setting database handle exclusive lock.
*/
@Test public void test9()
throws DatabaseException, FileNotFoundException
{
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR, TestUtils.getDBFileName(DATABASETEST_DBNAME));
itemcount = 0;
TestOptions options = new TestOptions();
options.save_db = true;
options.db_config.setErrorPrefix("DatabaseTest::test9 ");
EnvironmentConfig envc = new EnvironmentConfig();
envc.setAllowCreate(true);
envc.setInitializeCache(true);
envc.setInitializeLogging(true);
envc.setInitializeLocking(true);
envc.setTransactional(true);
envc.setThreaded(false);
options.db_env = new Environment(TestUtils.BASETEST_DBFILE, envc);
rundb(itemcount++, options);
assertNull(options.database.getConfig().getNoWaitDbExclusiveLock());
options.database.close();
options.database = null;
options.db_config.setNoWaitDbExclusiveLock(Boolean.TRUE);
rundb(itemcount++, options);
assertEquals(options.database.getConfig().getNoWaitDbExclusiveLock(), Boolean.TRUE);
options.database.close();
options.database = null;
options.db_config.setNoWaitDbExclusiveLock(Boolean.FALSE);
rundb(itemcount++, options);
assertEquals(options.database.getConfig().getNoWaitDbExclusiveLock(), Boolean.FALSE);
// Test noWaitDbExclusiveLock can not be set after database is opened.
try {
DatabaseConfig newConfig = options.database.getConfig();
newConfig.setNoWaitDbExclusiveLock(Boolean.TRUE);
options.database.setConfig(newConfig);
} catch (IllegalArgumentException e) {
} finally {
options.database.close();
options.database = null;
options.db_env.close();
}
}
/*
* Test setting metadata directory
*/
@Test public void test10()
throws DatabaseException, FileNotFoundException
{
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR, TestUtils.getDBFileName(DATABASETEST_DBNAME));
itemcount = 0;
TestOptions options = new TestOptions();
options.db_config.setErrorPrefix("DatabaseTest::test10 ");
EnvironmentConfig envc = new EnvironmentConfig();
envc.setAllowCreate(true);
envc.setInitializeCache(true);
options.db_env = new Environment(TestUtils.BASETEST_DBFILE, envc);
rundb(itemcount++, options);
assertNull(options.db_env.getConfig().getMetadataDir());
options.db_env.close();
String mddir = "metadataDir";
envc.setMetadataDir(new java.io.File(mddir));
options.db_env = new Environment(TestUtils.BASETEST_DBFILE, envc);
rundb(itemcount++, options);
assertEquals(options.db_env.getConfig().getMetadataDir().getPath(), mddir);
options.db_env.close();
}
/*
* Test setting heap region size
*/
@Test public void test11()
throws DatabaseException, FileNotFoundException
{
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR, TestUtils.getDBFileName(DATABASETEST_DBNAME));
TestOptions options = new TestOptions();
options.db_config.setErrorPrefix("DatabaseTest::test11 ");
options.db_config.setAllowCreate(true);
options.db_config.setType(DatabaseType.HEAP);
options.db_config.setHeapRegionSize(4);
Database db = new Database(TestUtils.getDBFileName(DATABASETEST_DBNAME), null, options.db_config);
assertEquals(db.getConfig().getHeapRegionSize(), 4);
HeapStats stats = (HeapStats)db.getStats(null, null);
assertEquals(stats.getHeapRegionSize(), 4);
db.close();
}
/*
* Test creating partition database by keys
*/
@Test public void test12()
throws DatabaseException, Exception, FileNotFoundException
{
// Test the success case
String errpfx = "DatabaseTest::test12 ";
// Create the partition key
int parts = 3;
MultipleDataEntry keyRanges = new MultipleDataEntry();
keyRanges.setData(new byte[1024]);
keyRanges.setUserBuffer(1024, true);
DatabaseEntry kdbt1 = new DatabaseEntry();
DatabaseEntry kdbt2 = new DatabaseEntry();
kdbt1.setData("d".getBytes());
kdbt2.setData("g".getBytes());
keyRanges.append(kdbt1);
keyRanges.append(kdbt2);
// Success case: set partition by key
test_partition_db(parts, keyRanges, null, 0, errpfx);
// Test the exception case: parts != (size of key array + 1)
parts++;
try {
test_partition_db(parts, keyRanges, null, 0, errpfx);
throw new Exception("Unexpected exception: setPartitionByRange" +
"should fail as parts != (size of key array + 1).");
} catch (IllegalArgumentException e) {
}
// Test the exception case: keys == null
try {
test_partition_db(parts, null, null, 0, errpfx);
throw new Exception("Unexpected exception: database open should" +
"fail as partition key array and callback are null.");
} catch (IllegalArgumentException e) {
}
// Test the exception case: there is no data in the keys
try {
test_partition_db(parts, new MultipleDataEntry(), null, 0, errpfx);
throw new Exception("Unexpected exception: database open should" +
"fail as there is no data in the partition keys which is" +
"a MultipleDataEntry. ");
} catch (IllegalArgumentException e) {
}
// Test the exception case: parts == 1
try {
test_partition_db(1, null, null, 2, errpfx);
throw new Exception("Unexpected exception: database open should" +
"fail as the number of partition is set to 1.");
} catch (IllegalArgumentException e) {
}
}
/*
* Test creating partition database by callback
*/
@Test public void test13()
throws DatabaseException, Exception, FileNotFoundException
{
String errpfx = "DatabaseTest::test13 ";
// Success case: set partition by callback
PartitionCallback part_func = new PartitionCallback();
int parts = 2;
test_partition_db(parts, null, part_func, 1, errpfx);
// Test the exception case: callback and key array are both set
MultipleDataEntry keyRanges = new MultipleDataEntry();
keyRanges.setData(new byte[1024]);
keyRanges.setUserBuffer(1024, true);
DatabaseEntry kdbt = new DatabaseEntry();
kdbt.setData("b".getBytes());
keyRanges.append(kdbt);
try {
test_partition_db(parts, keyRanges, part_func, 2, errpfx);
throw new Exception("Unexpected exception: database open should " +
"fail as partition callback and key array are both set.");
} catch (IllegalArgumentException e) {
}
}
/*
* Test setting the blob directory and threshold value.
*/
@Test public void test14()
throws DatabaseException, Exception, FileNotFoundException
{
TestOptions options = new TestOptions();
options.db_config.setErrorPrefix("DatabaseTest::test14 ");
options.save_db = true;
EnvironmentConfig envc = new EnvironmentConfig();
envc.setAllowCreate(true);
envc.setInitializeCache(true);
// Test setting the blob directory.
String dir[] = {"null", "", "BLOBDIR"};
for (int i = -1; i < dir.length; i++) {
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR,
TestUtils.getDBFileName(DATABASETEST_DBNAME));
// Set the blob directory.
if (i >= 0) {
if (dir[i].compareTo("null") == 0)
envc.setBlobDir(null);
else
envc.setBlobDir(new java.io.File(dir[i]));
}
// Set the blob threshold value.
envc.setBlobThreshold(10485760);
// Open the env.
options.db_env = new Environment(TestUtils.BASETEST_DBFILE, envc);
// Verify the blob directory and threshold value.
assertEquals(10485760,
options.db_env.getConfig().getBlobThreshold());
if (i == -1 || dir[i].compareTo("null") == 0)
assertNull(options.db_env.getConfig().getBlobDir());
else
assertEquals(0, options.db_env.getConfig().getBlobDir().
toString().compareTo(dir[i]));
options.db_env.close();
}
// Test setting the db blob threshold value and open it with no env.
test_blob_db(0, null, 3,
TestUtils.BASETEST_DBDIR + File.separator + "DBBLOB",
0, "DatabaseTest::test14 ", DatabaseType.BTREE);
// Test setting the blob directory in the database and then
// opening the db within env and verifying the setting is ignored.
test_blob_db(3, null, 0, "DBBLOB",
0, "DatabaseTest::test14 ", DatabaseType.BTREE);
test_blob_db(3, "ENVBLOB", 0, "DBBLOB",
0, "DatabaseTest::test14 ", DatabaseType.BTREE);
// Test setting the blob directory in the environment.
test_blob_db(3, "ENVBLOB", 0, null,
0, "DatabaseTest::test14 ", DatabaseType.BTREE);
// Test the db blob threshold value defaults to env blob threshold
// value.
test_blob_db(3, null, 0, null,
0, "DatabaseTest::test14 ", DatabaseType.BTREE);
// Test setting db own blob threshold and open it within the env.
test_blob_db(4, null, 3, null,
0, "DatabaseTest::test14 ", DatabaseType.HASH);
// Test putting the data items whose size does not reach the blob
// threshold but set as blob data items.
test_blob_db(3, null, 0, null,
1, "DatabaseTest::test14 ", DatabaseType.HEAP);
}
// Check that key/data for 0 - count-1 are already present,
// and write a key/data for count. The key and data are
// both "0123...N" where N == count-1.
//
// For some reason on Windows, we need to open using the full pathname
// of the file when there is no environment, thus the 'has_env'
// variable.
//
void rundb(int count, TestOptions options)
throws DatabaseException, FileNotFoundException
{
String name;
Database db;
if(options.database == null)
{
if (options.db_env != null)
name = DATABASETEST_DBNAME;
else
name = TestUtils.getDBFileName(DATABASETEST_DBNAME);
if(count == 0)
options.db_config.setAllowCreate(true);
if(options.db_env == null)
db = new Database(name, null, options.db_config);
else
db = options.db_env.openDatabase(options.txn, name, null, options.db_config);
} else {
db = options.database;
}
// The bit map of keys we've seen
long bitmap = 0;
// The bit map of keys we expect to see
long expected = (1 << (count+1)) - 1;
byte outbuf[] = new byte[count+1];
int i;
for (i=0; i<count; i++) {
outbuf[i] = (byte)('0' + i);
}
outbuf[i++] = (byte)'x';
DatabaseEntry key = new DatabaseEntry(outbuf, 0, i);
DatabaseEntry data = new DatabaseEntry(outbuf, 0, i);
TestUtils.DEBUGOUT("Put: " + (char)outbuf[0] + ": " + new String(outbuf, 0, i));
db.putNoOverwrite(options.txn, key, data);
// Acquire a cursor for the table.
Cursor dbcp = db.openCursor(options.txn, CursorConfig.DEFAULT);
// Walk through the table, checking
DatabaseEntry readkey = new DatabaseEntry();
DatabaseEntry readdata = new DatabaseEntry();
DatabaseEntry whoknows = new DatabaseEntry();
/*
* NOTE: Maybe want to change from user-buffer to DB buffer
* depending on the flag options.user_buffer (setReuseBuffer)
* The old version set MALLOC/REALLOC here - not sure if it is the same.
*/
TestUtils.DEBUGOUT("Dbc.get");
while (dbcp.getNext(readkey, readdata, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
String key_string =
new String(readkey.getData(), 0, readkey.getSize());
String data_string =
new String(readdata.getData(), 0, readkey.getSize());
TestUtils.DEBUGOUT("Got: " + key_string + ": " + data_string);
int len = key_string.length();
if (len <= 0 || key_string.charAt(len-1) != 'x') {
TestUtils.ERR("reread terminator is bad");
}
len--;
long bit = (1 << len);
if (len > count) {
TestUtils.ERR("reread length is bad: expect " + count + " got "+ len + " (" + key_string + ")" );
}
else if (!data_string.equals(key_string)) {
TestUtils.ERR("key/data don't match");
}
else if ((bitmap & bit) != 0) {
TestUtils.ERR("key already seen");
}
else if ((expected & bit) == 0) {
TestUtils.ERR("key was not expected");
}
else {
bitmap |= bit;
expected &= ~(bit);
for (i=0; i<len; i++) {
if (key_string.charAt(i) != ('0' + i)) {
System.out.print(" got " + key_string
+ " (" + (int)key_string.charAt(i)
+ "), wanted " + i
+ " (" + (int)('0' + i)
+ ") at position " + i + "\n");
TestUtils.ERR("key is corrupt");
}
}
}
}
if (expected != 0) {
System.out.print(" expected more keys, bitmap is: " + expected + "\n");
TestUtils.ERR("missing keys in database");
}
dbcp.close();
TestUtils.DEBUGOUT("options.save_db " + options.save_db + " options.database " + options.database);
if(options.save_db == false)
db.close(false);
else if (options.database == null)
options.database = db;
}
// Test if setPartitionByRange and setPartitionByCallback work by the
// following steps: 1) config the partition by keys and/or callback;
// 2) open the database; 3) insert some records; 4) verify the partition
// configs; 5) close the database.
//
// The parameter "apicall" indicates which API is tested. If it is 0,
// test setPartitionByRange. If it is 1, test setPartitionByCallback.
// Otherwise test both of them.
void test_partition_db(int nparts, MultipleDataEntry keys,
PartitionHandler funcp, int apicall, String errpfx)
throws DatabaseException, FileNotFoundException,
IllegalArgumentException
{
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR,
TestUtils.getDBFileName(DATABASETEST_DBNAME));
TestOptions options = new TestOptions();
options.db_config.setErrorPrefix(errpfx);
options.db_config.setAllowCreate(true);
options.db_config.setType(DatabaseType.BTREE);
// Config the partition.
// Parameter apicall:
// If 0 then call setPartitionByRange;
// If 1 then call setPartitionByCallback;
// Otherwise call both.
if (apicall == 0)
options.db_config.setPartitionByRange(nparts, keys);
else if (apicall == 1)
options.db_config.setPartitionByCallback(nparts, funcp);
else {
options.db_config.setPartitionByRange(nparts, keys);
options.db_config.setPartitionByCallback(nparts, funcp);
}
// Open the database.
Database db = new Database(
TestUtils.getDBFileName(DATABASETEST_DBNAME),
null, options.db_config);
// Insert some records.
String[] records = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w",
"x", "y", "z"};
DatabaseEntry ddbt, kdbt;
for (int i = 0; i < records.length; i++) {
kdbt = new DatabaseEntry();
ddbt = new DatabaseEntry();
kdbt.setData(records[i].getBytes());
ddbt.setData(records[i].getBytes());
db.putNoOverwrite(null, kdbt, ddbt);
}
// Verify the number of partitions.
assertEquals(nparts, db.getConfig().getPartitionParts());
// Verify the number of partitioned files.
File testdir = new File(TestUtils.BASETEST_DBDIR);
File[] flist = testdir.listFiles();
int cnt = 0;
for (int i = 0; i < Array.getLength(flist); i++) {
if (flist[i].getName().substring(0, 6).compareTo("__dbp.") == 0)
cnt++;
}
assertEquals(nparts, cnt);
// Verify the keys.
if (keys != null) {
MultipleDataEntry orig_key = new MultipleDataEntry(keys.getData());
MultipleDataEntry get_key =new MultipleDataEntry(
db.getConfig().getPartitionKeys().getData());
String s1, s2;
for (kdbt = new DatabaseEntry(), ddbt = new DatabaseEntry();
orig_key.next(kdbt) == true;
kdbt = new DatabaseEntry(), ddbt = new DatabaseEntry()) {
assertEquals(true, get_key.next(ddbt));
s1 = new String(kdbt.getData(), kdbt.getOffset(), kdbt.getSize());
s2 = new String(ddbt.getData(), ddbt.getOffset(), ddbt.getSize());
assertEquals(0, s1.compareTo(s2));
}
assertEquals(false, get_key.next(ddbt));
}
// Verify the callback.
assertEquals(funcp, db.getConfig().getPartitionHandler());
// Close the database.
db.close();
}
// Test if the BLOB basic APIs work by the following steps:
// 1) configure the blob threshold value and blob directory;
// 2) open the database with/without the environment;
// 3) insert and verify the blob data by database methods;
// 4) insert blob data by cursor, update the blob data and verify
// the update by database stream;
// 5) verify the blob configs, whether the blobs are created in
// expected location and the stats;
// 6) close the database and environment.
//
// The parameter "env_threshold" indicates the blob threshold value
// set in the environment and whether the database is opened within
// the enviornment. If it is <= 0, open the database without the
// enviornment. Otherwise open the database within the enviornment.
// The parameter "blobdbt" indicates whether DatabaseEntry.setBlob()
// is called on the data items to put. If it is not 0, set the data
// items as blob data and make its size < the blob threshold. Otherwise
// make the size of the data item reach the threshold and do not set
// the data item as blob data.
void test_blob_db(int env_threshold, String env_blob_dir,
int db_threshold, String db_blob_dir, int blobdbt,
String errpfx, DatabaseType dbtype)
throws DatabaseException, Exception, FileNotFoundException
{
// The blob threshold is set at least once either in the environment
// or in the database.
if (env_threshold <= 0 && db_threshold <= 0)
return;
TestUtils.removeall(true, true, TestUtils.BASETEST_DBDIR,
TestUtils.getDBFileName(DATABASETEST_DBNAME));
TestOptions options = new TestOptions();
options.db_config.setErrorPrefix(errpfx);
options.db_config.setAllowCreate(true);
options.db_config.setType(dbtype);
// Configure and open the environment.
EnvironmentConfig envc = new EnvironmentConfig();
if (env_threshold <= 0)
options.db_env = null;
else {
envc.setAllowCreate(true);
envc.setErrorStream(TestUtils.getErrorStream());
envc.setInitializeCache(true);
envc.setBlobThreshold(env_threshold);
if (env_blob_dir != null)
envc.setBlobDir(new java.io.File(env_blob_dir));
options.db_env = new Environment(TestUtils.BASETEST_DBFILE, envc);
}
// Configure and open the database.
if (db_threshold > 0)
options.db_config.setBlobThreshold(db_threshold);
if (db_blob_dir != null)
options.db_config.setBlobDir(new java.io.File(db_blob_dir));
if (options.db_env == null)
options.database =
new Database(TestUtils.getDBFileName(DATABASETEST_DBNAME),
null, options.db_config);
else {
options.database = options.db_env.openDatabase(null,
DATABASETEST_DBNAME, null, options.db_config);
}
// Insert and verify some blob data by database method, and then
// update the blob data by database stream and verify the update.
Cursor cursor = options.database.openCursor(null, null);
DatabaseStream dbs;
DatabaseStreamConfig dbs_config = new DatabaseStreamConfig();
dbs_config.setSyncPerWrite(true);
assertEquals(true, dbs_config.getSyncPerWrite());
assertEquals(false, dbs_config.getReadOnly());
String[] records = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w",
"x", "y", "z"};
DatabaseEntry ddbt, kdbt, sdbt;
String data;
for (int i = 0; i < records.length; i++) {
kdbt = new DatabaseEntry();
ddbt = new DatabaseEntry();
kdbt.setData(records[i].getBytes());
data = records[i];
if (blobdbt != 0) {
ddbt.setBlob(true);
assertTrue(ddbt.getBlob());
} else {
for (int j = 1;
j < options.database.getConfig().getBlobThreshold(); j++)
data = data + records[i];
}
ddbt.setData(data.getBytes());
if (dbtype == DatabaseType.HEAP)
options.database.append(null, kdbt, ddbt);
else
options.database.put(null, kdbt, ddbt);
// Verify the blob data by database get method.
assertEquals(OperationStatus.SUCCESS,
options.database.get(null, kdbt, ddbt, null));
assertArrayEquals(data.getBytes(), ddbt.getData());
// Update the blob data by database stream and verify the update.
assertEquals(OperationStatus.SUCCESS,
cursor.getSearchKey(kdbt, ddbt, null));
dbs = cursor.openDatabaseStream(dbs_config);
assertEquals(data.length(), dbs.size());
sdbt = new DatabaseEntry("abc".getBytes());
assertEquals(OperationStatus.SUCCESS, dbs.write(sdbt, dbs.size()));
assertEquals(OperationStatus.SUCCESS,
dbs.read(sdbt, 0, (int)dbs.size()));
assertArrayEquals((data + "abc").getBytes(), sdbt.getData());
dbs.close();
}
cursor.close();
// Insert the blob data by cursor, update the blob data by database
// stream and verify the update.
if (dbtype != DatabaseType.HEAP) {
cursor = options.database.openCursor(null, null);
kdbt = new DatabaseEntry("abc".getBytes());
ddbt = new DatabaseEntry("abc".getBytes());
ddbt.setBlob(true);
assertEquals(true, ddbt.getBlob());
assertEquals(OperationStatus.SUCCESS,
cursor.putKeyFirst(kdbt, ddbt));
dbs = cursor.openDatabaseStream(dbs_config);
assertEquals(3, dbs.size());
sdbt = new DatabaseEntry("defg".getBytes());
assertEquals(OperationStatus.SUCCESS, dbs.write(sdbt, dbs.size()));
// Verify datbase stream writing/reading with
// partial DatabaseEntry will fail.
try {
kdbt.setPartial(true);
assertEquals(true, kdbt.getPartial());
dbs.write(kdbt, 0);
throw new Exception("database stream write/read"
+ "with partial DatabaseEntry should fail");
} catch (IllegalArgumentException e) {
}
try {
dbs.read(kdbt, 0, (int)dbs.size());
throw new Exception("database stream read"
+ "with partial DatabaseEntry should fail");
} catch (IllegalArgumentException e) {
}
dbs.close();
// Verify the update and that database stream can not write when it
// is configured to be read-only.
dbs_config.setReadOnly(true);
assertEquals(true, dbs_config.getReadOnly());
dbs = cursor.openDatabaseStream(dbs_config);
assertEquals(7, dbs.size());
assertEquals(OperationStatus.SUCCESS,
dbs.read(sdbt, 0, (int)dbs.size()));
assertArrayEquals("abcdefg".getBytes(), sdbt.getData());
try {
dbs.write(sdbt, 7);
throw new Exception("database stream write should fail"
+ "as it is configured to be read-only");
} catch (IllegalArgumentException e) {
}
dbs.close();
cursor.close();
}
// Verify the blob config of the enviornment.
if (options.db_env != null && env_threshold > 0) {
assertEquals(env_threshold,
options.db_env.getConfig().getBlobThreshold());
if (env_blob_dir == null)
assertNull(options.db_env.getConfig().getBlobDir());
else
assertEquals(0, options.db_env.getConfig().
getBlobDir().toString().compareTo(env_blob_dir));
}
// Verify the blob config of the database.
assertEquals(db_threshold > 0 ? db_threshold : env_threshold,
options.database.getConfig().getBlobThreshold());
String blrootdir;
if (options.db_env != null) {
if (env_blob_dir == null)
blrootdir = "__db_bl";
else
blrootdir = env_blob_dir;
} else if (db_blob_dir == null) {
blrootdir = "__db_bl";
} else {
blrootdir = db_blob_dir;
}
assertEquals(0, options.database.getConfig().
getBlobDir().toString().compareTo(blrootdir));
// Verify the blobs are created in the expected location.
// This part of test is disabled since the Database.getBlobSubDir()
// is not expsed to users.
//if (options.db_env != null)
// blrootdir = options.db_env.getHome().toString() + "/" + blrootdir;
//assertNotNull(options.database.getBlobSubDir().toString());
//File blobdir = new File(blrootdir + "/" +
// options.database.getBlobSubDir().toString());
//assertTrue(blobdir.listFiles().length > records.length);
// Verify the stats.
if (dbtype == DatabaseType.HASH) {
HashStats stats = (HashStats)options.database.getStats(null, null);
assertEquals(records.length + 1, stats.getNumBlobs());
} else if (dbtype == DatabaseType.HEAP) {
HeapStats stats = (HeapStats)options.database.getStats(null, null);
assertEquals(records.length, stats.getHeapNumBlobs());
} else {
BtreeStats stats =
(BtreeStats)options.database.getStats(null, null);
assertEquals(records.length + 1, stats.getNumBlobs());
}
// Close the database and set up the blob directory configuration
// used in removing the database.
options.database.close();
if (options.db_env != null)
blrootdir = TestUtils.BASETEST_DBDIR + File.separator + blrootdir;
options.db_config.setBlobDir(new File(blrootdir));
// TestUtils.removeall does not work on the blob database since it
// removes the database with the default database configuration. So
// remove the blob database with blob configuration here.
Database.remove(TestUtils.getDBFileName(DATABASETEST_DBNAME),
null, options.db_config);
// All blobs are deleted but the blob directory remains after db
// remove. Verify it and delete the blob directory.
File[] files = options.db_config.getBlobDir().listFiles();
assertTrue(files.length > 0);
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory())
assertEquals(0, files[i].listFiles().length);
}
TestUtils.removeDir(blrootdir);
// Close the environment.
if (options.db_env != null)
options.db_env.close();
}
}
class TestOptions
{
int testmask = 0; // which tests to run
int user_buffer = 0; // use DB_DBT_USERMEM or DB_DBT_MALLOC
int successcounter =0;
boolean save_db = false;
Environment db_env = null;
DatabaseConfig db_config;
Database database = null; // db is saved here by rundb if save_db is true.
Transaction txn = null;
public TestOptions()
{
this.testmask = 0;
this.user_buffer = 0;
this.successcounter = 0;
this.db_env = null;
this.txn = null;
db_config = new DatabaseConfig();
db_config.setErrorStream(TestUtils.getErrorStream());
db_config.setErrorPrefix("DatabaseTest");
db_config.setType(DatabaseType.BTREE);
// We don't really care about the pagesize
db_config.setPageSize(1024);
}
}
class PartitionCallback implements PartitionHandler
{
public int partition(Database db, DatabaseEntry key)
{
String data = new String(key.getData());
if (data.compareTo("d") >= 0)
return 1;
else
return 0;
}
}