/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved.
*
*/
package com.sleepycat.persist;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.compat.DbCompat;
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.Environment;
import com.sleepycat.db.LockMode;
import com.sleepycat.db.OperationStatus;
import com.sleepycat.db.Transaction;
import com.sleepycat.util.keyrange.KeyRange;
import com.sleepycat.util.keyrange.RangeCursor;
/**
* Implements EntityIndex using a ValueAdapter. This class is abstract and
* does not implement get()/map()/sortedMap() because it doesn't have access
* to the entity binding.
*
* @author Mark Hayes
*/
abstract class BasicIndex<K, E> implements EntityIndex<K, E> {
static final DatabaseEntry NO_RETURN_ENTRY;
static {
NO_RETURN_ENTRY = new DatabaseEntry();
NO_RETURN_ENTRY.setPartial(0, 0, true);
}
Database db;
boolean transactional;
boolean sortedDups;
boolean locking;
boolean concurrentDB;
Class<K> keyClass;
EntryBinding keyBinding;
KeyRange emptyRange;
ValueAdapter<K> keyAdapter;
ValueAdapter<E> entityAdapter;
BasicIndex(Database db,
Class<K> keyClass,
EntryBinding keyBinding,
ValueAdapter<E> entityAdapter)
throws DatabaseException {
this.db = db;
DatabaseConfig config = db.getConfig();
transactional = config.getTransactional();
sortedDups = config.getSortedDuplicates();
locking =
DbCompat.getInitializeLocking(db.getEnvironment().getConfig());
Environment env = db.getEnvironment();
concurrentDB = DbCompat.getInitializeCDB(env.getConfig());
this.keyClass = keyClass;
this.keyBinding = keyBinding;
this.entityAdapter = entityAdapter;
emptyRange = new KeyRange(config.getBtreeComparator());
keyAdapter = new KeyValueAdapter(keyClass, keyBinding);
}
public Database getDatabase() {
return db;
}
/*
* Of the EntityIndex methods only get()/map()/sortedMap() are not
* implemented here and therefore must be implemented by subclasses.
*/
public boolean contains(K key)
throws DatabaseException {
return contains(null, key, null);
}
public boolean contains(Transaction txn, K key, LockMode lockMode)
throws DatabaseException {
DatabaseEntry keyEntry = new DatabaseEntry();
DatabaseEntry dataEntry = NO_RETURN_ENTRY;
keyBinding.objectToEntry(key, keyEntry);
OperationStatus status = db.get(txn, keyEntry, dataEntry, lockMode);
return (status == OperationStatus.SUCCESS);
}
public long count()
throws DatabaseException {
if (DbCompat.DATABASE_COUNT) {
return DbCompat.getDatabaseCount(db);
} else {
long count = 0;
DatabaseEntry key = NO_RETURN_ENTRY;
DatabaseEntry data = NO_RETURN_ENTRY;
CursorConfig cursorConfig = locking ?
CursorConfig.READ_UNCOMMITTED : null;
Cursor cursor = db.openCursor(null, cursorConfig);
try {
OperationStatus status = cursor.getFirst(key, data, null);
while (status == OperationStatus.SUCCESS) {
if (sortedDups) {
count += cursor.count();
} else {
count += 1;
}
status = cursor.getNextNoDup(key, data, null);
}
} finally {
cursor.close();
}
return count;
}
}
public boolean delete(K key)
throws DatabaseException {
return delete(null, key);
}
public boolean delete(Transaction txn, K key)
throws DatabaseException {
DatabaseEntry keyEntry = new DatabaseEntry();
keyBinding.objectToEntry(key, keyEntry);
OperationStatus status = db.delete(txn, keyEntry);
return (status == OperationStatus.SUCCESS);
}
public EntityCursor<K> keys()
throws DatabaseException {
return keys(null, null);
}
public EntityCursor<K> keys(Transaction txn, CursorConfig config)
throws DatabaseException {
return cursor(txn, emptyRange, keyAdapter, config);
}
public EntityCursor<E> entities()
throws DatabaseException {
return cursor(null, emptyRange, entityAdapter, null);
}
public EntityCursor<E> entities(Transaction txn,
CursorConfig config)
throws DatabaseException {
return cursor(txn, emptyRange, entityAdapter, config);
}
public EntityCursor<K> keys(K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive)
throws DatabaseException {
return cursor(null, fromKey, fromInclusive, toKey, toInclusive,
keyAdapter, null);
}
public EntityCursor<K> keys(Transaction txn,
K fromKey,
boolean fromInclusive,
K toKey,
boolean toInclusive,
CursorConfig config)
throws DatabaseException {
return cursor(txn, fromKey, fromInclusive, toKey, toInclusive,
keyAdapter, config);
}
public EntityCursor<E> entities(K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive)
throws DatabaseException {
return cursor(null, fromKey, fromInclusive, toKey, toInclusive,
entityAdapter, null);
}
public EntityCursor<E> entities(Transaction txn,
K fromKey,
boolean fromInclusive,
K toKey,
boolean toInclusive,
CursorConfig config)
throws DatabaseException {
return cursor(txn, fromKey, fromInclusive, toKey, toInclusive,
entityAdapter, config);
}
private <V> EntityCursor<V> cursor(Transaction txn,
K fromKey,
boolean fromInclusive,
K toKey,
boolean toInclusive,
ValueAdapter<V> adapter,
CursorConfig config)
throws DatabaseException {
DatabaseEntry fromEntry = null;
if (fromKey != null) {
fromEntry = new DatabaseEntry();
keyBinding.objectToEntry(fromKey, fromEntry);
}
DatabaseEntry toEntry = null;
if (toKey != null) {
toEntry = new DatabaseEntry();
keyBinding.objectToEntry(toKey, toEntry);
}
KeyRange range = emptyRange.subRange
(fromEntry, fromInclusive, toEntry, toInclusive);
return cursor(txn, range, adapter, config);
}
private <V> EntityCursor<V> cursor(Transaction txn,
KeyRange range,
ValueAdapter<V> adapter,
CursorConfig config)
throws DatabaseException {
Cursor cursor = db.openCursor(txn, config);
RangeCursor rangeCursor =
new RangeCursor(range, null/*pkRange*/, sortedDups, cursor);
return new BasicCursor<V>(rangeCursor, adapter, isUpdateAllowed());
}
abstract boolean isUpdateAllowed();
}