Comparator Bug in Java Edition 3.1 -
10-12-2006
, 09:20 AM
Hi,
I'm not sure whether this is the right place for reporting bugs. There
seems to be no official "bug report" page or bug report email adresses.
My problem is the following: When inserting two identical keys with
identical values into a newly created database with a custom
BTreeComparator and DuplicateComparator, the BTreeComparator gets
called to compare the *values* of the two inserted entries. But
according to the documentation, the BTreeComparator should only be
called to compare *keys*.
I've attached a simple Testcase that reproduces this behaviour. The
problem does not appear if there are already other entries in the
database. As a workaround, I currently require the first two items of a
database to have different values. To me, this bug is not critical, but
I thought you might like to know about it.
I'm using Berkeley DB Java Edition 3.1, but the bug also showed up in
the previous version (3.0.?). You should enable assertions to run it.
Regards,
Luzius
The resulting stack trace:
Exception in thread "main" java.lang.AssertionError
at IntComparator.compare(BugDemonstrator.java:87)
at IntComparator.compare(BugDemonstrator.java:1)
at com.sleepycat.je.tree.Key.compareKeys(Key.java:105 )
at com.sleepycat.je.dbi.CursorImpl.putCurrent(CursorI mpl.java:1163)
at com.sleepycat.je.dbi.CursorImpl.put(CursorImpl.jav a:1003)
at com.sleepycat.je.Cursor.putAllowPhantoms(Cursor.ja va:856)
at com.sleepycat.je.Cursor.putNoNotify(Cursor.java:79 3)
at com.sleepycat.je.Cursor.putInternal(Cursor.java:75 0)
at com.sleepycat.je.Cursor.put(Cursor.java:321)
at com.sleepycat.util.keyrange.RangeCursor.put(RangeC ursor.java:839)
at com.sleepycat.collections.DataCursor.put(DataCurso r.java:760)
at
com.sleepycat.collections.StoredContainer.put(Stor edContainer.java:301)
at com.sleepycat.collections.StoredMap.put(StoredMap. java:249)
at
com.kangoo.searchserver.BugDemonstrator.run(BugDem onstrator.java:40)
at
com.kangoo.searchserver.BugDemonstrator.main(BugDe monstrator.java:74)
The testclass:
import java.io.File;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.Comparator;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.collections.StoredSortedMap;
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;
public class BugDemonstrator {
private StoredSortedMap index;
public BugDemonstrator() throws DatabaseException {
Environment env = initEnvironment();
Database db = openDatabase(env);
index = new StoredSortedMap(db, new IntBinding(), new
LongBinding(), true);
}
public void run(boolean provokeAssertionError) {
index.put(new Integer(1), new Long(7));
if (!provokeAssertionError) {
index.put(new Integer(1), new Long(5));
}
index.put(new Integer(1), new Long(7));
}
private Database openDatabase(Environment env) throws
DatabaseException {
DatabaseConfig config = new DatabaseConfig();
config.setAllowCreate(true);
config.setTransactional(true);
config.setSortedDuplicates(true);
config.setBtreeComparator(new IntComparator());
config.setDuplicateComparator(new LongComparator());
Database db = env.openDatabase(null, "testdb", config);
return db;
}
private Environment initEnvironment() throws DatabaseException {
File file = new File("testdatabase");
if (file.exists()) {
throw new RuntimeException("Please delete the old
'testdatabase' dir before running again");
}
file.mkdirs();
EnvironmentConfig config = new EnvironmentConfig();
config.setAllowCreate(true);
config.setTransactional(true);
config.setReadOnly(false);
return new Environment(file, config);
}
public static void main(String[] args) throws DatabaseException {
boolean assertionsOn = false;
assert assertionsOn = true;
if (assertionsOn) {
new BugDemonstrator().run(true);
} else {
throw new RuntimeException("Please run this class with
assertions enabled");
}
}
}
class IntBinding implements EntryBinding {
public Object entryToObject(DatabaseEntry entry) {
return new Integer(ByteBuffer.wrap(entry.getData()).getInt()) ;
}
public void objectToEntry(Object object, DatabaseEntry entry) {
Integer key = (Integer) object;
ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.putInt(key);
entry.setData(buffer.array());
}
}
class IntComparator implements Comparator<byte[]>, Serializable {
public int compare(byte[] o1, byte[] o2) {
assert o1.length == 4;
int i1 = ByteBuffer.wrap(o1).getInt();
int i2 = ByteBuffer.wrap(o2).getInt();
return i1 - i2;
}
}
class LongBinding implements EntryBinding {
public Object entryToObject(DatabaseEntry entry) {
return new Long(ByteBuffer.wrap(entry.getData()).getLong());
}
public void objectToEntry(Object object, DatabaseEntry entry) {
Long key = (Long) object;
ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.putLong(key);
entry.setData(buffer.array());
}
}
class LongComparator implements Comparator<byte[]>, Serializable {
public int compare(byte[] o1, byte[] o2) {
assert o1.length == 8;
long i1 = ByteBuffer.wrap(o1).getLong();
long i2 = ByteBuffer.wrap(o2).getLong();
return i1 > i2 ? 1 : (i1 == i2 ? 0 : -1);
}
} |