je/test/com/sleepycat/collections/KeyRangeTest.java
2021-06-06 13:46:45 -04:00

426 lines
13 KiB
Java
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*-
* Copyright (C) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.collections;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Comparator;
import org.junit.After;
import org.junit.Test;
import com.sleepycat.bind.ByteArrayBinding;
import com.sleepycat.compat.DbCompat;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.util.keyrange.KeyRange;
import com.sleepycat.util.keyrange.KeyRangeException;
import com.sleepycat.util.test.SharedTestUtils;
import com.sleepycat.util.test.TestBase;
/**
* @author Mark Hayes
*/
public class KeyRangeTest extends TestBase {
private static boolean VERBOSE = false;
private static final byte FF = (byte) 0xFF;
private static final byte[][] KEYS = {
/* 0 */ {1},
/* 1 */ {FF},
/* 2 */ {FF, 0},
/* 3 */ {FF, 0x7F},
/* 4 */ {FF, FF},
/* 5 */ {FF, FF, 0},
/* 6 */ {FF, FF, 0x7F},
/* 7 */ {FF, FF, FF},
};
private static byte[][] EXTREME_KEY_BYTES = {
/* 0 */ {0},
/* 1 */ {FF, FF, FF, FF},
};
private Environment env;
private Database store;
private DataView view;
private DataCursor cursor;
private void openDb(Comparator<byte []> comparator)
throws Exception {
File dir = SharedTestUtils.getNewDir();
ByteArrayBinding dataBinding = new ByteArrayBinding();
EnvironmentConfig envConfig = new EnvironmentConfig();
envConfig.setAllowCreate(true);
DbCompat.setInitializeCache(envConfig, true);
env = new Environment(dir, envConfig);
DatabaseConfig dbConfig = new DatabaseConfig();
DbCompat.setTypeBtree(dbConfig);
dbConfig.setAllowCreate(true);
if (comparator != null) {
DbCompat.setBtreeComparator(dbConfig, comparator);
}
store = DbCompat.testOpenDatabase
(env, null, "test.db", null, dbConfig);
view = new DataView(store, dataBinding, dataBinding, null, true, null);
}
private void closeDb()
throws Exception {
store.close();
store = null;
env.close();
env = null;
}
@After
public void tearDown() {
try {
if (store != null) {
store.close();
}
} catch (Exception e) {
System.out.println("Exception ignored during close: " + e);
}
try {
if (env != null) {
env.close();
}
} catch (Exception e) {
System.out.println("Exception ignored during close: " + e);
}
/* Ensure that GC can cleanup. */
env = null;
store = null;
view = null;
cursor = null;
}
@Test
public void testScan() throws Exception {
openDb(null);
doScan(false);
closeDb();
}
@Test
public void testScanComparator() throws Exception {
openDb(new ReverseComparator());
doScan(true);
closeDb();
}
private void doScan(boolean reversed) throws Exception {
byte[][] keys = new byte[KEYS.length][];
final int end = KEYS.length - 1;
cursor = new DataCursor(view, true);
for (int i = 0; i <= end; i++) {
keys[i] = KEYS[i];
cursor.put(keys[i], KEYS[i], null, false);
}
cursor.close();
byte[][] extremeKeys = new byte[EXTREME_KEY_BYTES.length][];
for (int i = 0; i < extremeKeys.length; i++) {
extremeKeys[i] = EXTREME_KEY_BYTES[i];
}
// with empty range
cursor = new DataCursor(view, false);
expectRange(KEYS, 0, end, reversed);
cursor.close();
// begin key only, inclusive
for (int i = 0; i <= end; i++) {
cursor = newCursor(view, keys[i], true, null, false, reversed);
expectRange(KEYS, i, end, reversed);
cursor.close();
}
// begin key only, exclusive
for (int i = 0; i <= end; i++) {
cursor = newCursor(view, keys[i], false, null, false, reversed);
expectRange(KEYS, i + 1, end, reversed);
cursor.close();
}
// end key only, inclusive
for (int i = 0; i <= end; i++) {
cursor = newCursor(view, null, false, keys[i], true, reversed);
expectRange(KEYS, 0, i, reversed);
cursor.close();
}
// end key only, exclusive
for (int i = 0; i <= end; i++) {
cursor = newCursor(view, null, false, keys[i], false, reversed);
expectRange(KEYS, 0, i - 1, reversed);
cursor.close();
}
// begin and end keys, inclusive and exclusive
for (int i = 0; i <= end; i++) {
for (int j = i; j <= end; j++) {
// begin inclusive, end inclusive
cursor = newCursor(view, keys[i], true, keys[j],
true, reversed);
expectRange(KEYS, i, j, reversed);
cursor.close();
// begin inclusive, end exclusive
cursor = newCursor(view, keys[i], true, keys[j],
false, reversed);
expectRange(KEYS, i, j - 1, reversed);
cursor.close();
// begin exclusive, end inclusive
cursor = newCursor(view, keys[i], false, keys[j],
true, reversed);
expectRange(KEYS, i + 1, j, reversed);
cursor.close();
// begin exclusive, end exclusive
cursor = newCursor(view, keys[i], false, keys[j],
false, reversed);
expectRange(KEYS, i + 1, j - 1, reversed);
cursor.close();
}
}
// single key range
for (int i = 0; i <= end; i++) {
cursor = new DataCursor(view, false, keys[i]);
expectRange(KEYS, i, i, reversed);
cursor.close();
}
// start with lower extreme (before any existing key)
cursor = newCursor(view, extremeKeys[0], true, null, false, reversed);
expectRange(KEYS, 0, end, reversed);
cursor.close();
// start with higher extreme (after any existing key)
cursor = newCursor(view, null, false, extremeKeys[1], true, reversed);
expectRange(KEYS, 0, end, reversed);
cursor.close();
}
private DataCursor newCursor(DataView view,
Object beginKey, boolean beginInclusive,
Object endKey, boolean endInclusive,
boolean reversed)
throws Exception {
if (reversed) {
return new DataCursor(view, false,
endKey, endInclusive,
beginKey, beginInclusive);
} else {
return new DataCursor(view, false,
beginKey, beginInclusive,
endKey, endInclusive);
}
}
private void expectRange(byte[][] bytes, int first, int last,
boolean reversed)
throws DatabaseException {
int i;
boolean init;
for (init = true, i = first;; i++, init = false) {
if (checkRange(bytes, first, last, i <= last,
reversed, !reversed, init, i)) {
break;
}
}
for (init = true, i = last;; i--, init = false) {
if (checkRange(bytes, first, last, i >= first,
reversed, reversed, init, i)) {
break;
}
}
}
private boolean checkRange(byte[][] bytes, int first, int last,
boolean inRange, boolean reversed,
boolean forward, boolean init,
int i)
throws DatabaseException {
OperationStatus s;
if (forward) {
if (init) {
s = cursor.getFirst(false);
} else {
s = cursor.getNext(false);
}
} else {
if (init) {
s = cursor.getLast(false);
} else {
s = cursor.getPrev(false);
}
}
String msg = " " + (forward ? "next" : "prev") + " i=" + i +
" first=" + first + " last=" + last +
(reversed ? " reversed" : " not reversed");
// check that moving past ends doesn't move the cursor
if (s == OperationStatus.SUCCESS && i == first) {
OperationStatus s2 = reversed ? cursor.getNext(false)
: cursor.getPrev(false);
assertEquals(msg, OperationStatus.NOTFOUND, s2);
}
if (s == OperationStatus.SUCCESS && i == last) {
OperationStatus s2 = reversed ? cursor.getPrev(false)
: cursor.getNext(false);
assertEquals(msg, OperationStatus.NOTFOUND, s2);
}
byte[] val = (s == OperationStatus.SUCCESS)
? ((byte[]) cursor.getCurrentValue())
: null;
if (inRange) {
assertNotNull("RangeNotFound" + msg, val);
if (!Arrays.equals(val, bytes[i])){
printBytes(val);
printBytes(bytes[i]);
fail("RangeKeyNotEqual" + msg);
}
if (VERBOSE) {
System.out.println("GotRange" + msg);
}
return false;
} else {
assertEquals("RangeExceeded" + msg, OperationStatus.NOTFOUND, s);
return true;
}
}
private void printBytes(byte[] bytes) {
for (int i = 0; i < bytes.length; i += 1) {
System.out.print(Integer.toHexString(bytes[i] & 0xFF));
System.out.print(' ');
}
System.out.println();
}
@Test
public void testSubRanges() {
DatabaseEntry begin = new DatabaseEntry();
DatabaseEntry begin2 = new DatabaseEntry();
DatabaseEntry end = new DatabaseEntry();
DatabaseEntry end2 = new DatabaseEntry();
KeyRange range = new KeyRange(null);
KeyRange range2 = null;
/* Base range [1, 2] */
begin.setData(new byte[] { 1 });
end.setData(new byte[] { 2 });
range = range.subRange(begin, true, end, true);
/* Subrange (0, 1] is invalid **. */
begin2.setData(new byte[] { 0 });
end2.setData(new byte[] { 1 });
try {
range2 = range.subRange(begin2, false, end2, true);
fail();
} catch (KeyRangeException expected) {}
/* Subrange [1, 3) is invalid. */
begin2.setData(new byte[] { 1 });
end2.setData(new byte[] { 3 });
try {
range2 = range.subRange(begin2, true, end2, false);
fail();
} catch (KeyRangeException expected) {}
/* Subrange [2, 2] is valid. */
begin2.setData(new byte[] { 2 });
end2.setData(new byte[] { 2 });
range2 = range.subRange(begin2, true, end2, true);
/* Subrange [0, 1] is invalid. */
begin2.setData(new byte[] { 0 });
end2.setData(new byte[] { 1 });
try {
range2 = range.subRange(begin2, true, end2, true);
fail();
} catch (KeyRangeException expected) {}
/* Subrange (0, 3] is invalid. */
begin2.setData(new byte[] { 0 });
end2.setData(new byte[] { 3 });
try {
range2 = range.subRange(begin2, false, end2, true);
fail();
} catch (KeyRangeException expected) {}
/* Subrange [3, 3) is invalid. */
begin2.setData(new byte[] { 3 });
end2.setData(new byte[] { 3 });
try {
range2 = range.subRange(begin2, true, end2, false);
fail();
} catch (KeyRangeException expected) {}
}
@SuppressWarnings("serial")
public static class ReverseComparator implements Comparator<byte[]>,
Serializable {
public int compare(byte[] d1, byte[] d2) {
int cmp = KeyRange.compareBytes(d1, 0, d1.length,
d2, 0, d2.length);
if (cmp < 0) {
return 1;
} else if (cmp > 0) {
return -1;
} else {
return 0;
}
}
}
}