package net.notdot.bdbdatastore.server;
import java.util.Arrays;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.SecondaryCursor;
import com.sleepycat.je.SecondaryDatabase;
public class DatastoreIndexResultSet extends AbstractDatastoreResultSet {
protected SecondaryCursor cursor = null;
protected SecondaryDatabase db;
protected boolean exclusiveMin;
protected boolean positioned;
protected MessagePredicate predicate;
protected Message startKey;
protected DatabaseEntry currentKey;
public DatastoreIndexResultSet(AppDatastore ds, SecondaryDatabase db, Message startKey, boolean exclusiveMin, QuerySpec query, MessagePredicate predicate) throws DatabaseException {
super(ds, query);
this.db = db;
this.startKey = startKey;
this.currentKey = new DatabaseEntry(startKey.toByteArray());
this.exclusiveMin = exclusiveMin;
this.predicate = predicate;
}
@Override
protected void closeCursor() throws DatabaseException {
this.cursor.close();
this.cursor = null;
}
@Override
protected void openCursor() throws DatabaseException {
this.cursor = db.openSecondaryCursor(null, ds.getCursorConfig());
this.positioned = false;
}
@Override
protected boolean readInternal() throws DatabaseException, InvalidProtocolBufferException {
OperationStatus status;
if(positioned) {
// Cursor has already been used; just advance
status = cursor.getNext(currentKey, currentPKey, currentValue, null);
} else {
if(currentValue == null) {
// Cursor has never been used before
this.currentValue = new DatabaseEntry();
byte[] startKeyBytes = currentKey.getData();
status = cursor.getSearchKeyRange(currentKey, currentPKey, currentValue, null);
if(status == OperationStatus.SUCCESS && exclusiveMin && Arrays.equals(startKeyBytes, currentKey.getData())) {
// First key and the minimum is exclusive - fetch the next one
status = cursor.getNextNoDup(currentKey, currentPKey, currentValue, null);
}
} else {
// Cursor has been reopened
status = cursor.getSearchBothRange(currentKey, currentPKey, currentValue, null);
if(status == OperationStatus.SUCCESS) {
status = cursor.getNext(currentKey, currentPKey, currentValue, null);
}
}
positioned = true;
}
if(status == OperationStatus.SUCCESS) {
if(this.predicate != null) {
Message keyent;
keyent = this.startKey.newBuilderForType().mergeFrom(currentKey.getData()).build();
return this.predicate.evaluate(keyent);
}
return true;
} else if(status == OperationStatus.NOTFOUND) {
return false;
} else {
throw new DatabaseException(String.format("Failed to advance query cursor: returned %s", status));
}
}
}