
com.sleepycat.util.keyrange.RangeCursor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of je Show documentation
Show all versions of je Show documentation
Berkeley DB Java Edition is a open source, transactional storage solution for Java applications. The Direct Persistence Layer (DPL) API is faster and easier to develop, deploy, and manage than serialized object files or ORM-based Java persistence solutions. The Collections API enhances the standard java.util.collections classes allowing them to be persisted to a local file system and accessed concurrently while protected by ACID transactions. Data is stored by serializing objects and managing class and instance data separately so as not to waste space. Berkeley DB Java Edition is the reliable drop-in solution for complex, fast, and scalable storage. Source for this release is in 'je-4.0.92-sources.jar', the Javadoc is located at 'http://download.oracle.com/berkeley-db/docs/je/4.0.92/'.
/*-
* Copyright (C) 2002, 2018, 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.util.keyrange;
import com.sleepycat.compat.DbCompat;
import com.sleepycat.compat.DbCompat.OpReadOptions;
import com.sleepycat.compat.DbCompat.OpResult;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
/* */
import com.sleepycat.je.Get;
/* */
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.SecondaryCursor;
/**
* A cursor-like interface that enforces a key range. The method signatures
* are actually those of SecondaryCursor, but the pKey parameter may be null.
* It was done this way to avoid doubling the number of methods.
*
* This is not a fully general implementation of a range cursor and should
* not be used directly by applications; however, it may evolve into a
* generally useful range cursor some day.
*
* @author Mark Hayes
*/
public class RangeCursor implements Cloneable {
/**
* The cursor and secondary cursor are the same object. The secCursor is
* null if the database is not a secondary database.
*/
private Cursor cursor;
private SecondaryCursor secCursor;
/**
* The range is always non-null, but may be unbounded meaning that it is
* open and not used.
*/
private KeyRange range;
/**
* The pkRange may be non-null only if the range is a single-key range
* and the cursor is a secondary cursor. It further restricts the range of
* primary keys in a secondary database.
*/
private KeyRange pkRange;
/**
* If the DB supported sorted duplicates, then calling
* Cursor.getSearchBothRange is allowed.
*/
private boolean sortedDups;
/**
* The privXxx entries are used only when the range is bounded. We read
* into these private entries to avoid modifying the caller's entry
* parameters in the case where we read successfully but the key is out of
* range. In that case we return NOTFOUND and we want to leave the entry
* parameters unchanged.
*/
private DatabaseEntry privKey;
private DatabaseEntry privPKey;
private DatabaseEntry privData;
/**
* The initialized flag is set to true whenever we successfully position
* the cursor. It is used to implement the getNext/Prev logic for doing a
* getFirst/Last when the cursor is not initialized. We can't rely on
* Cursor to do that for us, since if we position the underlying cursor
* successfully but the key is out of range, we have no way to set the
* underlying cursor to uninitialized. A range cursor always starts in the
* uninitialized state.
*/
private boolean initialized;
/**
* Creates a range cursor with a duplicate range.
*/
public RangeCursor(KeyRange range,
KeyRange pkRange,
boolean sortedDups,
Cursor cursor) {
if (pkRange != null && !range.singleKey) {
throw new IllegalArgumentException();
}
this.range = range;
this.pkRange = pkRange;
this.sortedDups = sortedDups;
this.cursor = cursor;
init();
if (pkRange != null && secCursor == null) {
throw new IllegalArgumentException();
}
}
/**
* Create a cloned range cursor. The caller must clone the underlying
* cursor before using this constructor, because cursor open/close is
* handled specially for CDS cursors outside this class.
*/
public RangeCursor dup(boolean samePosition)
throws DatabaseException {
try {
RangeCursor c = (RangeCursor) super.clone();
c.cursor = dupCursor(cursor, samePosition);
c.init();
return c;
} catch (CloneNotSupportedException neverHappens) {
return null;
}
}
/**
* Used for opening and duping (cloning).
*/
private void init() {
if (cursor instanceof SecondaryCursor) {
secCursor = (SecondaryCursor) cursor;
} else {
secCursor = null;
}
if (range.hasBound()) {
privKey = new DatabaseEntry();
privPKey = new DatabaseEntry();
privData = new DatabaseEntry();
} else {
privKey = null;
privPKey = null;
privData = null;
}
}
/**
* Returns whether the cursor is initialized at a valid position.
*/
public boolean isInitialized() {
return initialized;
}
/**
* Returns the underlying cursor. Used for cloning.
*/
public Cursor getCursor() {
return cursor;
}
/**
* When an unbounded range is used, this method is called to use the
* callers entry parameters directly, to avoid the extra step of copying
* between the private entries and the caller's entries.
*/
private void setParams(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data) {
privKey = key;
privPKey = pKey;
privData = data;
}
/**
* Dups the cursor, sets the cursor and secCursor fields to the duped
* cursor, and returns the old cursor. Always call endOperation in a
* finally clause after calling beginOperation.
*
* If the returned cursor == the cursor field, the cursor is
* uninitialized and was not duped; this case is handled correctly by
* endOperation.
*/
private Cursor beginOperation()
throws DatabaseException {
Cursor oldCursor = cursor;
if (initialized) {
cursor = dupCursor(cursor, true);
if (secCursor != null) {
secCursor = (SecondaryCursor) cursor;
}
} else {
return cursor;
}
return oldCursor;
}
/**
* If the operation succeeded, leaves the duped cursor in place and closes
* the oldCursor. If the operation failed, moves the oldCursor back in
* place and closes the duped cursor. oldCursor may be null if
* beginOperation was not called, in cases where we don't need to dup
* the cursor. Always call endOperation when a successful operation ends,
* in order to set the initialized field.
*/
private void endOperation(Cursor oldCursor,
OpResult result,
DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data)
throws DatabaseException {
if (result.isSuccess()) {
if (oldCursor != null && oldCursor != cursor) {
closeCursor(oldCursor);
}
if (key != null) {
swapData(key, privKey);
}
if (pKey != null && secCursor != null) {
swapData(pKey, privPKey);
}
if (data != null) {
swapData(data, privData);
}
initialized = true;
} else {
if (oldCursor != null && oldCursor != cursor) {
closeCursor(cursor);
cursor = oldCursor;
if (secCursor != null) {
secCursor = (SecondaryCursor) cursor;
}
}
}
}
/**
* Swaps the contents of the two entries. Used to return entry data to
* the caller when the operation was successful.
*/
private static void swapData(DatabaseEntry e1, DatabaseEntry e2) {
byte[] d1 = e1.getData();
int o1 = e1.getOffset();
int s1 = e1.getSize();
e1.setData(e2.getData(), e2.getOffset(), e2.getSize());
e2.setData(d1, o1, s1);
}
/**
* Shares the same byte array, offset and size between two entries.
* Used when copying the entry data is not necessary because it is known
* that the underlying operation will not modify the entry, for example,
* with getSearchKey.
*/
private static void shareData(DatabaseEntry from, DatabaseEntry to) {
if (from != null) {
to.setData(from.getData(), from.getOffset(), from.getSize());
}
}
public OpResult getFirst(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
OpResult result;
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetFirst(options);
endOperation(null, result, null, null, null);
return result;
}
if (pkRange != null && pkRange.isSingleKey()) {
KeyRange.copy(range.beginKey, privKey);
KeyRange.copy(pkRange.beginKey, privPKey);
result = doGetSearchBoth(options);
endOperation(null, result, key, pKey, data);
return result;
}
if (pkRange != null) {
KeyRange.copy(range.beginKey, privKey);
result = OpResult.FAILURE;
Cursor oldCursor = beginOperation();
try {
if (pkRange.beginKey == null || !sortedDups) {
result = doGetSearchKey(options);
} else {
KeyRange.copy(pkRange.beginKey, privPKey);
result = doGetSearchBothRange(options);
if (result.isSuccess() &&
!pkRange.beginInclusive &&
pkRange.compare(privPKey, pkRange.beginKey) == 0) {
result = doGetNextDup(options);
}
}
if (result.isSuccess() &&
!pkRange.check(privPKey)) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
} else if (range.singleKey) {
KeyRange.copy(range.beginKey, privKey);
result = doGetSearchKey(options);
endOperation(null, result, key, pKey, data);
} else {
result = OpResult.FAILURE;
Cursor oldCursor = beginOperation();
try {
if (range.beginKey == null) {
result = doGetFirst(options);
} else {
KeyRange.copy(range.beginKey, privKey);
result = doGetSearchKeyRange(options);
if (result.isSuccess() &&
!range.beginInclusive &&
range.compare(privKey, range.beginKey) == 0) {
result = doGetNextNoDup(options);
}
}
if (result.isSuccess() &&
!range.check(privKey)) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
}
return result;
}
/**
* This method will restart the operation when a key range is used and an
* insertion at the end of the key range is performed in another thread.
* The restarts are needed because a sequence of cursor movements is
* performed, and serializable isolation cannot be relied on to prevent
* insertions in other threads. Without the restarts, getLast could return
* NOTFOUND when keys in the range exist. This may only be an issue for JE
* since it uses record locking, while DB core uses page locking.
*/
public OpResult getLast(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
OpResult result = OpResult.FAILURE;
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetLast(options);
endOperation(null, result, null, null, null);
return result;
}
Cursor oldCursor = beginOperation();
try {
if (pkRange != null) {
result = getLastInPKeyRange(options);
/* Final check on candidate key and pKey value. */
if (result.isSuccess() &&
!(range.check(privKey) && pkRange.check(privPKey))) {
result = OpResult.FAILURE;
}
} else {
result = getLastInKeyRange(options);
/* Final check on candidate key value. */
if (result.isSuccess() &&
!range.check(privKey)) {
result = OpResult.FAILURE;
}
}
return result;
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
}
/**
* Performs getLast operation when a main key range is specified but
* pkRange is null. Does everything but the final checks for key in range,
* i.e., when SUCCESS is returned the caller should do the final check.
*/
private OpResult getLastInKeyRange(OpReadOptions options)
throws DatabaseException {
/* Without an endKey, getLast returns the candidate record. */
if (range.endKey == null) {
return doGetLast(options);
}
/*
* K stands for the main key at the cursor position in the comments
* below.
*/
while (true) {
KeyRange.copy(range.endKey, privKey);
OpResult result = doGetSearchKeyRange(options);
if (result.isSuccess()) {
/* Found K >= endKey. */
if (range.endInclusive &&
range.compare(range.endKey, privKey) == 0) {
/* K == endKey and endKey is inclusive. */
if (!sortedDups) {
/* If dups are not configured, we're done. */
return result;
}
/*
* If there are dups, we're positioned at endKey's first
* dup and we want to move to its last dup. Move to the
* first dup for the next main key (getNextNoDup) and then
* the prev record. In the absence of insertions by other
* threads, the prev record is the last dup for endKey.
*/
result = doGetNextNoDup(options);
if (result.isSuccess()) {
/*
* K > endKey. Move backward to the last dup for
* endKey.
*/
result = doGetPrev(options);
} else {
/*
* endKey is the last main key in the DB. Its last dup
* is the last key in the DB.
*/
result = doGetLast(options);
}
} else {
/*
* K > endKey or endKey is exclusive (and K >= endKey). In
* both cases, moving to the prev key finds the last key in
* the range, whether or not there are dups.
*/
result = doGetPrev(options);
}
} else {
/*
* There are no keys >= endKey in the DB. The last key in the
* range is the last key in the DB.
*/
result = doGetLast(options);
}
if (!result.isSuccess()) {
return result;
}
if (!range.checkEnd(privKey, true)) {
/*
* The last call above (getPrev or getLast) returned a key
* outside the endKey range. Another thread must have inserted
* this key. Start over.
*/
continue;
}
return result;
}
}
/**
* Performs getLast operation when both a main key range (which must be a
* single key range) and a pkRange are specified. Does everything but the
* final checks for key and pKey in range, i.e., when SUCCESS is returned
* the caller should do the final two checks.
*/
private OpResult getLastInPKeyRange(OpReadOptions options)
throws DatabaseException {
/* We can do an exact search when range and pkRange are single keys. */
if (pkRange.isSingleKey()) {
KeyRange.copy(range.beginKey, privKey);
KeyRange.copy(pkRange.beginKey, privPKey);
return doGetSearchBoth(options);
}
/*
* When dups are not configured, getSearchKey for the main key returns
* the only possible candidate record.
*/
if (!sortedDups) {
KeyRange.copy(range.beginKey, privKey);
return doGetSearchKey(options);
}
/*
* K stands for the main key and D for the duplicate (data item) at the
* cursor position in the comments below
*/
while (true) {
if (pkRange.endKey != null) {
KeyRange.copy(range.beginKey, privKey);
KeyRange.copy(pkRange.endKey, privPKey);
OpResult result = doGetSearchBothRange(options);
if (result.isSuccess()) {
/* Found D >= endKey. */
if (!pkRange.endInclusive ||
pkRange.compare(pkRange.endKey, privPKey) != 0) {
/*
* D > endKey or endKey is exclusive (and D >= endKey).
* In both cases, moving to the prev dup finds the last
* key in the range.
*/
result = doGetPrevDup(options);
if (!result.isSuccess()) {
return result;
}
if (!pkRange.checkEnd(privPKey, true)) {
/*
* getPrevDup returned a key outside the endKey
* range. Another thread must have inserted this
* key. Start over.
*/
continue;
}
}
/* Else D == endKey and endKey is inclusive. */
return result;
}
/* Else there are no dups >= endKey. Fall through. */
}
/*
* We're here for one of two reasons:
* 1. pkRange.endKey == null.
* 2. There are no dups >= endKey for the main key (status
* returned by getSearchBothRange above was not SUCCESS).
* In both cases, the last dup in the range is the last dup for the
* main key.
*/
KeyRange.copy(range.beginKey, privKey);
OpResult result = doGetSearchKey(options);
if (!result.isSuccess()) {
return result;
}
/*
* K == the main key and D is its first dup. We want to move to its
* last dup. Move to the first dup for the next main key;
* (getNextNoDup) and then the prev record. In the absence of
* insertions by other threads, the prev record is the last dup for
* the main key.
*/
result = doGetNextNoDup(options);
if (result.isSuccess()) {
/*
* K > main key and D is its first dup. Move to the prev record
* which should be the last dup for the main key.
*/
result = doGetPrev(options);
} else {
/*
* The main key specified is the last main key in the DB. Its
* last dup is the last record in the DB.
*/
result = doGetLast(options);
}
if (!result.isSuccess()) {
return result;
}
if (!range.checkEnd(privKey, true)) {
/*
* The last call above (getPrev or getLast) returned a key
* outside the endKey range. Another thread must have inserted
* this key. Start over.
*/
continue;
}
return result;
}
}
public OpResult getNext(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
OpResult result;
if (!initialized) {
return getFirst(key, pKey, data, options);
}
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetNext(options);
endOperation(null, result, null, null, null);
return result;
}
if (pkRange != null) {
if (pkRange.endKey == null) {
result = doGetNextDup(options);
endOperation(null, result, key, pKey, data);
} else {
result = OpResult.FAILURE;
Cursor oldCursor = beginOperation();
try {
result = doGetNextDup(options);
if (result.isSuccess() &&
!pkRange.checkEnd(privPKey, true)) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
}
} else if (range.singleKey) {
result = doGetNextDup(options);
endOperation(null, result, key, pKey, data);
} else {
result = OpResult.FAILURE;
Cursor oldCursor = beginOperation();
try {
result = doGetNext(options);
if (result.isSuccess() &&
!range.check(privKey)) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
}
return result;
}
public OpResult getNextNoDup(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
OpResult result;
if (!initialized) {
return getFirst(key, pKey, data, options);
}
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetNextNoDup(options);
endOperation(null, result, null, null, null);
return result;
}
if (range.singleKey) {
result = OpResult.FAILURE;
} else {
result = OpResult.FAILURE;
Cursor oldCursor = beginOperation();
try {
result = doGetNextNoDup(options);
if (result.isSuccess() &&
!range.check(privKey)) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
}
return result;
}
public OpResult getPrev(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
OpResult result;
if (!initialized) {
return getLast(key, pKey, data, options);
}
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetPrev(options);
endOperation(null, result, null, null, null);
return result;
}
if (pkRange != null) {
if (pkRange.beginKey == null) {
result = doGetPrevDup(options);
endOperation(null, result, key, pKey, data);
} else {
result = OpResult.FAILURE;
Cursor oldCursor = beginOperation();
try {
result = doGetPrevDup(options);
if (result.isSuccess() &&
!pkRange.checkBegin(privPKey, true)) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
}
} else if (range.singleKey) {
result = doGetPrevDup(options);
endOperation(null, result, key, pKey, data);
} else {
result = OpResult.FAILURE;
Cursor oldCursor = beginOperation();
try {
result = doGetPrev(options);
if (result.isSuccess() &&
!range.check(privKey)) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
}
return result;
}
public OpResult getPrevNoDup(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
OpResult result;
if (!initialized) {
return getLast(key, pKey, data, options);
}
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetPrevNoDup(options);
endOperation(null, result, null, null, null);
return result;
}
if (range.singleKey) {
result = OpResult.FAILURE;
} else {
result = OpResult.FAILURE;
Cursor oldCursor = beginOperation();
try {
result = doGetPrevNoDup(options);
if (result.isSuccess() &&
!range.check(privKey)) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
}
return result;
}
public OpResult getSearchKey(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
OpResult result;
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetSearchKey(options);
endOperation(null, result, null, null, null);
return result;
}
if (!range.check(key)) {
result = OpResult.FAILURE;
} else if (pkRange != null) {
result = OpResult.FAILURE;
Cursor oldCursor = beginOperation();
try {
shareData(key, privKey);
result = doGetSearchKey(options);
if (result.isSuccess() &&
!pkRange.check(privPKey)) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
} else {
shareData(key, privKey);
result = doGetSearchKey(options);
endOperation(null, result, key, pKey, data);
}
return result;
}
public OpResult getSearchBoth(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
OpResult result;
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetSearchBoth(options);
endOperation(null, result, null, null, null);
return result;
}
if (!range.check(key) ||
(pkRange != null && !pkRange.check(pKey))) {
result = OpResult.FAILURE;
} else {
shareData(key, privKey);
if (secCursor != null) {
shareData(pKey, privPKey);
} else {
shareData(data, privData);
}
result = doGetSearchBoth(options);
endOperation(null, result, key, pKey, data);
}
return result;
}
public OpResult getSearchKeyRange(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
OpResult result = OpResult.FAILURE;
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetSearchKeyRange(options);
endOperation(null, result, null, null, null);
return result;
}
Cursor oldCursor = beginOperation();
try {
shareData(key, privKey);
result = doGetSearchKeyRange(options);
if (result.isSuccess() &&
(!range.check(privKey) ||
(pkRange != null && !pkRange.check(pKey)))) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
return result;
}
public OpResult getSearchBothRange(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
OpResult result = OpResult.FAILURE;
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetSearchBothRange(options);
endOperation(null, result, null, null, null);
return result;
}
Cursor oldCursor = beginOperation();
try {
shareData(key, privKey);
if (secCursor != null) {
shareData(pKey, privPKey);
} else {
shareData(data, privData);
}
result = doGetSearchBothRange(options);
if (result.isSuccess() &&
(!range.check(privKey) ||
(pkRange != null && !pkRange.check(pKey)))) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
return result;
}
public OpResult getSearchRecordNumber(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
OpResult result;
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetSearchRecordNumber(options);
endOperation(null, result, null, null, null);
return result;
}
if (!range.check(key)) {
result = OpResult.FAILURE;
} else {
shareData(key, privKey);
result = doGetSearchRecordNumber(options);
endOperation(null, result, key, pKey, data);
}
return result;
}
public OpResult getNextDup(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
if (!initialized) {
throw new IllegalStateException("Cursor not initialized");
}
OpResult result;
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetNextDup(options);
endOperation(null, result, null, null, null);
} else if (pkRange != null && pkRange.endKey != null) {
result = OpResult.FAILURE;
Cursor oldCursor = beginOperation();
try {
result = doGetNextDup(options);
if (result.isSuccess() &&
!pkRange.checkEnd(privPKey, true)) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
} else {
result = doGetNextDup(options);
endOperation(null, result, key, pKey, data);
}
return result;
}
public OpResult getPrevDup(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
if (!initialized) {
throw new IllegalStateException("Cursor not initialized");
}
OpResult result;
if (!range.hasBound()) {
setParams(key, pKey, data);
result = doGetPrevDup(options);
endOperation(null, result, null, null, null);
} else if (pkRange != null && pkRange.beginKey != null) {
result = OpResult.FAILURE;
Cursor oldCursor = beginOperation();
try {
result = doGetPrevDup(options);
if (result.isSuccess() &&
!pkRange.checkBegin(privPKey, true)) {
result = OpResult.FAILURE;
}
} finally {
endOperation(oldCursor, result, key, pKey, data);
}
} else {
result = doGetPrevDup(options);
endOperation(null, result, key, pKey, data);
}
return result;
}
public OpResult getCurrent(DatabaseEntry key,
DatabaseEntry pKey,
DatabaseEntry data,
OpReadOptions options)
throws DatabaseException {
if (!initialized) {
throw new IllegalStateException("Cursor not initialized");
}
if (secCursor != null && pKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
key, pKey, data, Get.CURRENT, options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getCurrent(key, pKey, data, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(key, data, Get.CURRENT, options.jeOptions));
}
/* */
return OpResult.make(
cursor.getCurrent(key, data, options.getLockMode()));
}
}
/*
* Pass-thru methods.
*/
public void close()
throws DatabaseException {
closeCursor(cursor);
}
public int count()
throws DatabaseException {
return cursor.count();
}
public OperationStatus delete()
throws DatabaseException {
return cursor.delete();
}
public OperationStatus put(DatabaseEntry key, DatabaseEntry data)
throws DatabaseException {
return cursor.put(key, data);
}
public OperationStatus putNoOverwrite(DatabaseEntry key,
DatabaseEntry data)
throws DatabaseException {
return cursor.putNoOverwrite(key, data);
}
public OperationStatus putNoDupData(DatabaseEntry key, DatabaseEntry data)
throws DatabaseException {
return cursor.putNoDupData(key, data);
}
public OperationStatus putCurrent(DatabaseEntry data)
throws DatabaseException {
return cursor.putCurrent(data);
}
public OperationStatus putAfter(DatabaseEntry key, DatabaseEntry data)
throws DatabaseException {
return DbCompat.putAfter(cursor, key, data);
}
public OperationStatus putBefore(DatabaseEntry key, DatabaseEntry data)
throws DatabaseException {
return DbCompat.putBefore(cursor, key, data);
}
private OpResult doGetFirst(OpReadOptions options)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
privKey, privPKey, privData, Get.FIRST,
options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getFirst(
privKey, privPKey, privData, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(
privKey, privData, Get.FIRST, options.jeOptions));
}
/* */
return OpResult.make(
cursor.getFirst(privKey, privData, options.getLockMode()));
}
}
private OpResult doGetLast(OpReadOptions options)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
privKey, privPKey, privData, Get.LAST,
options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getLast(
privKey, privPKey, privData, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(
privKey, privData, Get.LAST, options.jeOptions));
}
/* */
return OpResult.make(
cursor.getLast(privKey, privData, options.getLockMode()));
}
}
private OpResult doGetNext(OpReadOptions options)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
privKey, privPKey, privData, Get.NEXT,
options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getNext(
privKey, privPKey, privData, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(
privKey, privData, Get.NEXT, options.jeOptions));
}
/* */
return OpResult.make(
cursor.getNext(privKey, privData, options.getLockMode()));
}
}
private OpResult doGetNextDup(OpReadOptions options)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
privKey, privPKey, privData, Get.NEXT_DUP,
options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getNextDup(
privKey, privPKey, privData, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(
privKey, privData, Get.NEXT_DUP, options.jeOptions));
}
/* */
return OpResult.make(
cursor.getNextDup(privKey, privData, options.getLockMode()));
}
}
private OpResult doGetNextNoDup(OpReadOptions options)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
privKey, privPKey, privData, Get.NEXT_NO_DUP,
options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getNextNoDup(
privKey, privPKey, privData, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(
privKey, privData, Get.NEXT_NO_DUP,
options.jeOptions));
}
/* */
return OpResult.make(
cursor.getNextNoDup(privKey, privData, options.getLockMode()));
}
}
private OpResult doGetPrev(OpReadOptions options)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
privKey, privPKey, privData, Get.PREV,
options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getPrev(
privKey, privPKey, privData, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(
privKey, privData, Get.PREV, options.jeOptions));
}
/* */
return OpResult.make(
cursor.getPrev(privKey, privData, options.getLockMode()));
}
}
private OpResult doGetPrevDup(OpReadOptions options)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
privKey, privPKey, privData, Get.PREV_DUP,
options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getPrevDup(
privKey, privPKey, privData, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(
privKey, privData, Get.PREV_DUP, options.jeOptions));
}
/* */
return OpResult.make(
cursor.getPrevDup(privKey, privData, options.getLockMode()));
}
}
private OpResult doGetPrevNoDup(OpReadOptions options)
throws DatabaseException {
if (secCursor != null && privPKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
privKey, privPKey, privData, Get.PREV_NO_DUP,
options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getPrevNoDup(
privKey, privPKey, privData, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(
privKey, privData, Get.PREV_NO_DUP,
options.jeOptions));
}
/* */
return OpResult.make(
cursor.getPrevNoDup(privKey, privData, options.getLockMode()));
}
}
private OpResult doGetSearchKey(OpReadOptions options)
throws DatabaseException {
if (checkRecordNumber() && DbCompat.getRecordNumber(privKey) <= 0) {
return OpResult.FAILURE;
}
if (secCursor != null && privPKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
privKey, privPKey, privData, Get.SEARCH,
options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getSearchKey(
privKey, privPKey, privData, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(
privKey, privData, Get.SEARCH, options.jeOptions));
}
/* */
return OpResult.make(
cursor.getSearchKey(privKey, privData, options.getLockMode()));
}
}
private OpResult doGetSearchKeyRange(OpReadOptions options)
throws DatabaseException {
if (checkRecordNumber() && DbCompat.getRecordNumber(privKey) <= 0) {
return OpResult.FAILURE;
}
if (secCursor != null && privPKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
privKey, privPKey, privData, Get.SEARCH_GTE,
options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getSearchKeyRange(
privKey, privPKey, privData, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(
privKey, privData, Get.SEARCH_GTE, options.jeOptions));
}
/* */
return OpResult.make(
cursor.getSearchKeyRange(
privKey, privData, options.getLockMode()));
}
}
private OpResult doGetSearchBoth(OpReadOptions options)
throws DatabaseException {
if (checkRecordNumber() && DbCompat.getRecordNumber(privKey) <= 0) {
return OpResult.FAILURE;
}
if (secCursor != null && privPKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
privKey, privPKey, privData, Get.SEARCH_BOTH,
options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getSearchBoth(
privKey, privPKey, privData, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(
privKey, privData, Get.SEARCH_BOTH,
options.jeOptions));
}
/* */
return OpResult.make(
cursor.getSearchBoth(
privKey, privData, options.getLockMode()));
}
}
private OpResult doGetSearchBothRange(OpReadOptions options)
throws DatabaseException {
if (checkRecordNumber() && DbCompat.getRecordNumber(privKey) <= 0) {
return OpResult.FAILURE;
}
if (secCursor != null && privPKey != null) {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
secCursor.get(
privKey, privPKey, privData, Get.SEARCH_BOTH_GTE,
options.jeOptions));
}
/* */
return OpResult.make(
secCursor.getSearchBothRange(
privKey, privPKey, privData, options.getLockMode()));
} else {
/* */
if (DbCompat.IS_JE) {
return OpResult.make(
cursor.get(
privKey, privData, Get.SEARCH_BOTH_GTE,
options.jeOptions));
}
/* */
return OpResult.make(
cursor.getSearchBothRange(
privKey, privData, options.getLockMode()));
}
}
private OpResult doGetSearchRecordNumber(OpReadOptions options)
throws DatabaseException {
if (DbCompat.getRecordNumber(privKey) <= 0) {
return OpResult.FAILURE;
}
if (secCursor != null && privPKey != null) {
return OpResult.make(
DbCompat.getSearchRecordNumber(
secCursor, privKey, privPKey, privData,
options.getLockMode()));
} else {
return OpResult.make(
DbCompat.getSearchRecordNumber(
cursor, privKey, privData, options.getLockMode()));
}
}
/*
* Protected methods for duping and closing cursors. These are overridden
* by the collections API to implement cursor pooling for CDS.
*/
/**
* Dups the given cursor.
*/
protected Cursor dupCursor(Cursor cursor, boolean samePosition)
throws DatabaseException {
return cursor.dup(samePosition);
}
/**
* Closes the given cursor.
*/
protected void closeCursor(Cursor cursor)
throws DatabaseException {
cursor.close();
}
/**
* If the database is a RECNO or QUEUE database, we know its keys are
* record numbers. We treat a non-positive record number as out of bounds,
* that is, we return NOTFOUND rather than throwing
* IllegalArgumentException as would happen if we passed a non-positive
* record number into the DB cursor. This behavior is required by the
* collections interface.
*/
protected boolean checkRecordNumber() {
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy