0001: /*-
0002: * See the file LICENSE for redistribution information.
0003: *
0004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
0005: *
0006: * $Id: Cursor.java,v 1.202.2.4 2008/01/07 15:14:07 cwl Exp $
0007: */
0008:
0009: package com.sleepycat.je;
0010:
0011: import java.util.logging.Level;
0012: import java.util.logging.Logger;
0013:
0014: import com.sleepycat.je.dbi.CursorImpl;
0015: import com.sleepycat.je.dbi.DatabaseImpl;
0016: import com.sleepycat.je.dbi.GetMode;
0017: import com.sleepycat.je.dbi.PutMode;
0018: import com.sleepycat.je.dbi.RangeRestartException;
0019: import com.sleepycat.je.dbi.CursorImpl.KeyChangeStatus;
0020: import com.sleepycat.je.dbi.CursorImpl.SearchMode;
0021: import com.sleepycat.je.latch.LatchSupport;
0022: import com.sleepycat.je.tree.BIN;
0023: import com.sleepycat.je.tree.DBIN;
0024: import com.sleepycat.je.tree.Key;
0025: import com.sleepycat.je.tree.LN;
0026: import com.sleepycat.je.tree.Node;
0027: import com.sleepycat.je.txn.BuddyLocker;
0028: import com.sleepycat.je.txn.LockType;
0029: import com.sleepycat.je.txn.Locker;
0030: import com.sleepycat.je.txn.LockerFactory;
0031: import com.sleepycat.je.utilint.DatabaseUtil;
0032: import com.sleepycat.je.utilint.InternalException;
0033:
0034: /**
0035: * Javadoc for this public class is generated
0036: * via the doc templates in the doc_src directory.
0037: */
0038: public class Cursor {
0039:
0040: /**
0041: * The underlying cursor.
0042: */
0043: CursorImpl cursorImpl; // Used by subclasses.
0044:
0045: /**
0046: * The CursorConfig used to configure this cursor.
0047: */
0048: CursorConfig config;
0049:
0050: /**
0051: * True if update operations are prohibited through this cursor. Update
0052: * operations are prohibited if the database is read-only or:
0053: *
0054: * (1) The database is transactional,
0055: *
0056: * and
0057: *
0058: * (2) The user did not supply a txn to the cursor ctor (meaning, the
0059: * locker is non-transactional).
0060: */
0061: private boolean updateOperationsProhibited;
0062:
0063: /**
0064: * Handle under which this cursor was created; may be null.
0065: */
0066: private Database dbHandle;
0067:
0068: /**
0069: * Database implementation.
0070: */
0071: private DatabaseImpl dbImpl;
0072:
0073: /* Attributes */
0074: private boolean readUncommittedDefault;
0075: private boolean serializableIsolationDefault;
0076:
0077: private Logger logger;
0078:
0079: /**
0080: * Creates a cursor for a given user transaction.
0081: *
0082: * <p>If txn is null, a non-transactional cursor will be created that
0083: * releases locks for the prior operation when the next operation
0084: * suceeds.</p>
0085: */
0086: Cursor(Database dbHandle, Transaction txn, CursorConfig cursorConfig)
0087: throws DatabaseException {
0088:
0089: if (cursorConfig == null) {
0090: cursorConfig = CursorConfig.DEFAULT;
0091: }
0092:
0093: Locker locker = LockerFactory.getReadableLocker(dbHandle
0094: .getEnvironment(), txn, dbHandle.isTransactional(),
0095: false, // retainNonTxnLocks
0096: cursorConfig.getReadCommitted());
0097:
0098: init(dbHandle, dbHandle.getDatabaseImpl(), locker, dbHandle
0099: .isWritable(), cursorConfig);
0100: }
0101:
0102: /**
0103: * Creates a cursor for a given locker.
0104: *
0105: * <p>If locker is null or is non-transactional, a non-transactional cursor
0106: * will be created that releases locks for the prior operation when the
0107: * next operation suceeds.</p>
0108: */
0109: Cursor(Database dbHandle, Locker locker, CursorConfig cursorConfig)
0110: throws DatabaseException {
0111:
0112: if (cursorConfig == null) {
0113: cursorConfig = CursorConfig.DEFAULT;
0114: }
0115:
0116: locker = LockerFactory.getReadableLocker(dbHandle
0117: .getEnvironment(), dbHandle, locker, false, // retainNonTxnLocks
0118: cursorConfig.getReadCommitted());
0119:
0120: init(dbHandle, dbHandle.getDatabaseImpl(), locker, dbHandle
0121: .isWritable(), cursorConfig);
0122: }
0123:
0124: /**
0125: * Creates a cursor for a given locker and no db handle.
0126: *
0127: * <p>The locker parameter must be non-null. With this constructor, we use
0128: * the given locker without applying any special rules for different
0129: * isolation levels -- the caller must supply the correct locker.</p>
0130: */
0131: Cursor(DatabaseImpl dbImpl, Locker locker, CursorConfig cursorConfig)
0132: throws DatabaseException {
0133:
0134: if (cursorConfig == null) {
0135: cursorConfig = CursorConfig.DEFAULT;
0136: }
0137:
0138: init(null, dbImpl, locker, true, cursorConfig);
0139: }
0140:
0141: private void init(Database dbHandle, DatabaseImpl dbImpl,
0142: Locker locker, boolean isWritable, CursorConfig cursorConfig)
0143: throws DatabaseException {
0144:
0145: assert locker != null;
0146: assert dbImpl != null;
0147:
0148: cursorImpl = new CursorImpl(dbImpl, locker, false /*retainNonTxnLocks*/);
0149:
0150: /* Perform eviction for user cursors. */
0151: cursorImpl.setAllowEviction(true);
0152:
0153: readUncommittedDefault = cursorConfig.getReadUncommitted()
0154: || locker.isReadUncommittedDefault();
0155:
0156: serializableIsolationDefault = cursorImpl.getLocker()
0157: .isSerializableIsolation();
0158:
0159: updateOperationsProhibited = (dbImpl.isTransactional() && !locker
0160: .isTransactional())
0161: || !isWritable;
0162:
0163: this .dbImpl = dbImpl;
0164: this .dbHandle = dbHandle;
0165: if (dbHandle != null) {
0166: dbHandle.addCursor(this );
0167: }
0168: this .config = cursorConfig;
0169: this .logger = dbImpl.getDbEnvironment().getLogger();
0170: }
0171:
0172: /**
0173: * Copy constructor.
0174: */
0175: Cursor(Cursor cursor, boolean samePosition)
0176: throws DatabaseException {
0177:
0178: readUncommittedDefault = cursor.readUncommittedDefault;
0179: serializableIsolationDefault = cursor.serializableIsolationDefault;
0180: updateOperationsProhibited = cursor.updateOperationsProhibited;
0181:
0182: cursorImpl = cursor.cursorImpl.dup(samePosition);
0183: dbImpl = cursor.dbImpl;
0184: dbHandle = cursor.dbHandle;
0185: if (dbHandle != null) {
0186: dbHandle.addCursor(this );
0187: }
0188: config = cursor.config;
0189: logger = dbImpl.getDbEnvironment().getLogger();
0190: }
0191:
0192: /**
0193: * Internal entrypoint.
0194: */
0195: CursorImpl getCursorImpl() {
0196: return cursorImpl;
0197: }
0198:
0199: /**
0200: * Javadoc for this public method is generated via
0201: * the doc templates in the doc_src directory.
0202: */
0203: public Database getDatabase() {
0204: return dbHandle;
0205: }
0206:
0207: /**
0208: * Always returns non-null, while getDatabase() returns null if no handle
0209: * is associated with this cursor.
0210: */
0211: DatabaseImpl getDatabaseImpl() {
0212: return dbImpl;
0213: }
0214:
0215: /**
0216: * Javadoc for this public method is generated via
0217: * the doc templates in the doc_src directory.
0218: */
0219: public CursorConfig getConfig() {
0220: try {
0221: return config.cloneConfig();
0222: } catch (Error E) {
0223: dbImpl.getDbEnvironment().invalidate(E);
0224: throw E;
0225: }
0226: }
0227:
0228: void setNonCloning(boolean nonCloning) {
0229: cursorImpl.setNonCloning(nonCloning);
0230: }
0231:
0232: /**
0233: * Javadoc for this public method is generated via
0234: * the doc templates in the doc_src directory.
0235: */
0236: public void close() throws DatabaseException {
0237:
0238: close(true /*releaseNonTxnLocks*/);
0239: }
0240:
0241: /**
0242: * @param releaseNonTxnLocks should normally be true. See
0243: * CursorImpl.close(boolean) [#15573]
0244: */
0245: synchronized void close(boolean releaseNonTxnLocks)
0246: throws DatabaseException {
0247:
0248: try {
0249: checkState(false);
0250: cursorImpl.close(releaseNonTxnLocks);
0251: if (dbHandle != null) {
0252: dbHandle.removeCursor(this );
0253: }
0254: } catch (Error E) {
0255: dbImpl.getDbEnvironment().invalidate(E);
0256: throw E;
0257: }
0258: }
0259:
0260: /**
0261: * Javadoc for this public method is generated via
0262: * the doc templates in the doc_src directory.
0263: */
0264: public int count() throws DatabaseException {
0265:
0266: checkState(true);
0267: trace(Level.FINEST, "Cursor.count: ", null);
0268:
0269: /*
0270: * Specify a null LockMode to use default locking. The API doesn't
0271: * allow specifying a lock mode, but we should at least honor the
0272: * configured default.
0273: */
0274: return countInternal(null);
0275: }
0276:
0277: /**
0278: * Javadoc for this public method is generated via
0279: * the doc templates in the doc_src directory.
0280: */
0281: public Cursor dup(boolean samePosition) throws DatabaseException {
0282:
0283: try {
0284: checkState(false);
0285: return new Cursor(this , samePosition);
0286: } catch (Error E) {
0287: dbImpl.getDbEnvironment().invalidate(E);
0288: throw E;
0289: }
0290: }
0291:
0292: /**
0293: * Javadoc for this public method is generated via
0294: * the doc templates in the doc_src directory.
0295: */
0296: public OperationStatus delete() throws DatabaseException {
0297:
0298: checkState(true);
0299: checkUpdatesAllowed("delete");
0300: trace(Level.FINEST, "Cursor.delete: ", null);
0301:
0302: return deleteInternal();
0303: }
0304:
0305: /**
0306: * Javadoc for this public method is generated via
0307: * the doc templates in the doc_src directory.
0308: */
0309: public OperationStatus put(DatabaseEntry key, DatabaseEntry data)
0310: throws DatabaseException {
0311:
0312: checkState(false);
0313: DatabaseUtil.checkForNullDbt(key, "key", true);
0314: DatabaseUtil.checkForNullDbt(data, "data", true);
0315: DatabaseUtil.checkForPartialKey(key);
0316: checkUpdatesAllowed("put");
0317: trace(Level.FINEST, "Cursor.put: ", key, data, null);
0318:
0319: return putInternal(key, data, PutMode.OVERWRITE);
0320: }
0321:
0322: /**
0323: * Javadoc for this public method is generated via
0324: * the doc templates in the doc_src directory.
0325: */
0326: public OperationStatus putNoOverwrite(DatabaseEntry key,
0327: DatabaseEntry data) throws DatabaseException {
0328:
0329: checkState(false);
0330: DatabaseUtil.checkForNullDbt(key, "key", true);
0331: DatabaseUtil.checkForNullDbt(data, "data", true);
0332: DatabaseUtil.checkForPartialKey(key);
0333: checkUpdatesAllowed("putNoOverwrite");
0334: trace(Level.FINEST, "Cursor.putNoOverwrite: ", key, data, null);
0335:
0336: return putInternal(key, data, PutMode.NOOVERWRITE);
0337: }
0338:
0339: /**
0340: * Javadoc for this public method is generated via
0341: * the doc templates in the doc_src directory.
0342: */
0343: public OperationStatus putNoDupData(DatabaseEntry key,
0344: DatabaseEntry data) throws DatabaseException {
0345:
0346: checkState(false);
0347: DatabaseUtil.checkForNullDbt(key, "key", true);
0348: DatabaseUtil.checkForNullDbt(data, "data", true);
0349: DatabaseUtil.checkForPartialKey(key);
0350: checkUpdatesAllowed("putNoDupData");
0351: trace(Level.FINEST, "Cursor.putNoDupData: ", key, data, null);
0352:
0353: return putInternal(key, data, PutMode.NODUP);
0354: }
0355:
0356: /**
0357: * Javadoc for this public method is generated via
0358: * the doc templates in the doc_src directory.
0359: */
0360: public OperationStatus putCurrent(DatabaseEntry data)
0361: throws DatabaseException {
0362:
0363: checkState(true);
0364: DatabaseUtil.checkForNullDbt(data, "data", true);
0365: checkUpdatesAllowed("putCurrent");
0366: trace(Level.FINEST, "Cursor.putCurrent: ", null, data, null);
0367:
0368: return putInternal(null, data, PutMode.CURRENT);
0369: }
0370:
0371: /**
0372: * Javadoc for this public method is generated via
0373: * the doc templates in the doc_src directory.
0374: */
0375: public OperationStatus getCurrent(DatabaseEntry key,
0376: DatabaseEntry data, LockMode lockMode)
0377: throws DatabaseException {
0378:
0379: try {
0380: checkState(true);
0381: checkArgsNoValRequired(key, data);
0382: trace(Level.FINEST, "Cursor.getCurrent: ", lockMode);
0383:
0384: return getCurrentInternal(key, data, lockMode);
0385: } catch (Error E) {
0386: dbImpl.getDbEnvironment().invalidate(E);
0387: throw E;
0388: }
0389: }
0390:
0391: /**
0392: * Javadoc for this public method is generated via
0393: * the doc templates in the doc_src directory.
0394: */
0395: public OperationStatus getFirst(DatabaseEntry key,
0396: DatabaseEntry data, LockMode lockMode)
0397: throws DatabaseException {
0398:
0399: checkState(false);
0400: checkArgsNoValRequired(key, data);
0401: trace(Level.FINEST, "Cursor.getFirst: ", lockMode);
0402:
0403: return position(key, data, lockMode, true);
0404: }
0405:
0406: /**
0407: * Javadoc for this public method is generated via
0408: * the doc templates in the doc_src directory.
0409: */
0410: public OperationStatus getLast(DatabaseEntry key,
0411: DatabaseEntry data, LockMode lockMode)
0412: throws DatabaseException {
0413:
0414: checkState(false);
0415: checkArgsNoValRequired(key, data);
0416: trace(Level.FINEST, "Cursor.getLast: ", lockMode);
0417:
0418: return position(key, data, lockMode, false);
0419: }
0420:
0421: /**
0422: * Javadoc for this public method is generated via
0423: * the doc templates in the doc_src directory.
0424: */
0425: public OperationStatus getNext(DatabaseEntry key,
0426: DatabaseEntry data, LockMode lockMode)
0427: throws DatabaseException {
0428:
0429: checkState(false);
0430: checkArgsNoValRequired(key, data);
0431: trace(Level.FINEST, "Cursor.getNext: ", lockMode);
0432:
0433: if (cursorImpl.isNotInitialized()) {
0434: return position(key, data, lockMode, true);
0435: } else {
0436: return retrieveNext(key, data, lockMode, GetMode.NEXT);
0437: }
0438: }
0439:
0440: /**
0441: * Javadoc for this public method is generated via
0442: * the doc templates in the doc_src directory.
0443: */
0444: public OperationStatus getNextDup(DatabaseEntry key,
0445: DatabaseEntry data, LockMode lockMode)
0446: throws DatabaseException {
0447:
0448: checkState(true);
0449: checkArgsNoValRequired(key, data);
0450: trace(Level.FINEST, "Cursor.getNextDup: ", lockMode);
0451:
0452: return retrieveNext(key, data, lockMode, GetMode.NEXT_DUP);
0453: }
0454:
0455: /**
0456: * Javadoc for this public method is generated via
0457: * the doc templates in the doc_src directory.
0458: */
0459: public OperationStatus getNextNoDup(DatabaseEntry key,
0460: DatabaseEntry data, LockMode lockMode)
0461: throws DatabaseException {
0462:
0463: checkState(false);
0464: checkArgsNoValRequired(key, data);
0465: trace(Level.FINEST, "Cursor.getNextNoDup: ", lockMode);
0466:
0467: if (cursorImpl.isNotInitialized()) {
0468: return position(key, data, lockMode, true);
0469: } else {
0470: return retrieveNext(key, data, lockMode, GetMode.NEXT_NODUP);
0471: }
0472: }
0473:
0474: /**
0475: * Javadoc for this public method is generated via
0476: * the doc templates in the doc_src directory.
0477: */
0478: public OperationStatus getPrev(DatabaseEntry key,
0479: DatabaseEntry data, LockMode lockMode)
0480: throws DatabaseException {
0481:
0482: checkState(false);
0483: checkArgsNoValRequired(key, data);
0484: trace(Level.FINEST, "Cursor.getPrev: ", lockMode);
0485:
0486: if (cursorImpl.isNotInitialized()) {
0487: return position(key, data, lockMode, false);
0488: } else {
0489: return retrieveNext(key, data, lockMode, GetMode.PREV);
0490: }
0491: }
0492:
0493: /**
0494: * Javadoc for this public method is generated via
0495: * the doc templates in the doc_src directory.
0496: */
0497: public OperationStatus getPrevDup(DatabaseEntry key,
0498: DatabaseEntry data, LockMode lockMode)
0499: throws DatabaseException {
0500:
0501: checkState(true);
0502: checkArgsNoValRequired(key, data);
0503: trace(Level.FINEST, "Cursor.getPrevDup: ", lockMode);
0504:
0505: return retrieveNext(key, data, lockMode, GetMode.PREV_DUP);
0506: }
0507:
0508: /**
0509: * Javadoc for this public method is generated via
0510: * the doc templates in the doc_src directory.
0511: */
0512: public OperationStatus getPrevNoDup(DatabaseEntry key,
0513: DatabaseEntry data, LockMode lockMode)
0514: throws DatabaseException {
0515:
0516: checkState(false);
0517: checkArgsNoValRequired(key, data);
0518: trace(Level.FINEST, "Cursor.getPrevNoDup: ", lockMode);
0519:
0520: if (cursorImpl.isNotInitialized()) {
0521: return position(key, data, lockMode, false);
0522: } else {
0523: return retrieveNext(key, data, lockMode, GetMode.PREV_NODUP);
0524: }
0525: }
0526:
0527: /**
0528: * Javadoc for this public method is generated via
0529: * the doc templates in the doc_src directory.
0530: */
0531: public OperationStatus getSearchKey(DatabaseEntry key,
0532: DatabaseEntry data, LockMode lockMode)
0533: throws DatabaseException {
0534:
0535: checkState(false);
0536: DatabaseUtil.checkForNullDbt(key, "key", true);
0537: DatabaseUtil.checkForNullDbt(data, "data", false);
0538: trace(Level.FINEST, "Cursor.getSearchKey: ", key, null,
0539: lockMode);
0540:
0541: return search(key, data, lockMode, SearchMode.SET);
0542: }
0543:
0544: /**
0545: * Javadoc for this public method is generated via
0546: * the doc templates in the doc_src directory.
0547: */
0548: public OperationStatus getSearchKeyRange(DatabaseEntry key,
0549: DatabaseEntry data, LockMode lockMode)
0550: throws DatabaseException {
0551:
0552: checkState(false);
0553: DatabaseUtil.checkForNullDbt(key, "key", true);
0554: DatabaseUtil.checkForNullDbt(data, "data", false);
0555: trace(Level.FINEST, "Cursor.getSearchKeyRange: ", key, null,
0556: lockMode);
0557:
0558: return search(key, data, lockMode, SearchMode.SET_RANGE);
0559: }
0560:
0561: /**
0562: * Javadoc for this public method is generated via
0563: * the doc templates in the doc_src directory.
0564: */
0565: public OperationStatus getSearchBoth(DatabaseEntry key,
0566: DatabaseEntry data, LockMode lockMode)
0567: throws DatabaseException {
0568:
0569: checkState(false);
0570: checkArgsValRequired(key, data);
0571: trace(Level.FINEST, "Cursor.getSearchBoth: ", key, data,
0572: lockMode);
0573:
0574: return search(key, data, lockMode, SearchMode.BOTH);
0575: }
0576:
0577: /**
0578: * Javadoc for this public method is generated via
0579: * the doc templates in the doc_src directory.
0580: */
0581: public OperationStatus getSearchBothRange(DatabaseEntry key,
0582: DatabaseEntry data, LockMode lockMode)
0583: throws DatabaseException {
0584:
0585: checkState(false);
0586: checkArgsValRequired(key, data);
0587: trace(Level.FINEST, "Cursor.getSearchBothRange: ", key, data,
0588: lockMode);
0589:
0590: return search(key, data, lockMode, SearchMode.BOTH_RANGE);
0591: }
0592:
0593: /**
0594: * Counts duplicates without parameter checking.
0595: */
0596: int countInternal(LockMode lockMode) throws DatabaseException {
0597:
0598: try {
0599: CursorImpl original = null;
0600: CursorImpl dup = null;
0601:
0602: /*
0603: * We depart from the usual beginRead/endRead sequence because
0604: * count() should not retain locks unless transactions are used.
0605: * Therefore we always close the dup cursor after using it.
0606: */
0607: try {
0608: original = cursorImpl;
0609: dup = original.cloneCursor(true);
0610: return dup.count(getLockType(lockMode, false));
0611: } finally {
0612: if (dup != original && dup != null) {
0613: dup.close();
0614: }
0615: }
0616: } catch (Error E) {
0617: dbImpl.getDbEnvironment().invalidate(E);
0618: throw E;
0619: }
0620: }
0621:
0622: /**
0623: * Internal version of delete() that does no parameter checking. Calls
0624: * deleteNoNotify() and notifies triggers (performs secondary updates).
0625: */
0626: OperationStatus deleteInternal() throws DatabaseException {
0627:
0628: try {
0629: /* Get existing data if updating secondaries. */
0630: DatabaseEntry oldKey = null;
0631: DatabaseEntry oldData = null;
0632: boolean doNotifyTriggers = dbHandle != null
0633: && dbHandle.hasTriggers();
0634: if (doNotifyTriggers) {
0635: oldKey = new DatabaseEntry();
0636: oldData = new DatabaseEntry();
0637: OperationStatus status = getCurrentInternal(oldKey,
0638: oldData, LockMode.RMW);
0639: if (status != OperationStatus.SUCCESS) {
0640: return OperationStatus.KEYEMPTY;
0641: }
0642: }
0643:
0644: /*
0645: * Notify triggers before the actual deletion so that a primary
0646: * record never exists while secondary keys refer to it. This is
0647: * relied on by secondary read-uncommitted.
0648: */
0649: if (doNotifyTriggers) {
0650: dbHandle.notifyTriggers(cursorImpl.getLocker(), oldKey,
0651: oldData, null);
0652: }
0653:
0654: /* The actual deletion. */
0655: OperationStatus status = deleteNoNotify();
0656: return status;
0657: } catch (Error E) {
0658: dbImpl.getDbEnvironment().invalidate(E);
0659: throw E;
0660: }
0661: }
0662:
0663: /**
0664: * Clone the cursor, delete at current position, and if successful, swap
0665: * cursors. Does not notify triggers (does not perform secondary updates).
0666: */
0667: OperationStatus deleteNoNotify() throws DatabaseException {
0668:
0669: CursorImpl original = null;
0670: CursorImpl dup = null;
0671: OperationStatus status = OperationStatus.KEYEMPTY;
0672: try {
0673: /* Clone, add dup to cursor. */
0674: original = cursorImpl;
0675: dup = original.cloneCursor(true);
0676:
0677: /* Latch the bins and do the delete with the dup. */
0678: dup.latchBINs();
0679: status = dup.delete();
0680:
0681: return status;
0682: } finally {
0683: if (original != null) {
0684: original.releaseBINs();
0685: }
0686: if (dup != null) {
0687: dup.releaseBINs();
0688: }
0689:
0690: /* Swap if it was a success. */
0691: boolean success = (status == OperationStatus.SUCCESS);
0692: if (cursorImpl == dup) {
0693: if (!success) {
0694: cursorImpl.reset();
0695: }
0696: } else {
0697: if (success) {
0698: original.close();
0699: cursorImpl = dup;
0700: } else {
0701: dup.close();
0702: }
0703: }
0704: }
0705: }
0706:
0707: /**
0708: * Internal version of put() that does no parameter checking. Calls
0709: * putNoNotify() and notifies triggers (performs secondary updates).
0710: * Prevents phantoms.
0711: */
0712: OperationStatus putInternal(DatabaseEntry key, DatabaseEntry data,
0713: PutMode putMode) throws DatabaseException {
0714:
0715: try {
0716: /* Need to get existing data if updating secondaries. */
0717: DatabaseEntry oldData = null;
0718: boolean doNotifyTriggers = dbHandle != null
0719: && dbHandle.hasTriggers();
0720: if (doNotifyTriggers
0721: && (putMode == PutMode.CURRENT || putMode == PutMode.OVERWRITE)) {
0722: oldData = new DatabaseEntry();
0723: if (key == null && putMode == PutMode.CURRENT) {
0724: /* Key is returned by CursorImpl.putCurrent as foundKey. */
0725: key = new DatabaseEntry();
0726: }
0727: }
0728:
0729: /* Perform put. */
0730: OperationStatus commitStatus = putNoNotify(key, data,
0731: putMode, oldData);
0732:
0733: /* Notify triggers (update secondaries). */
0734: if (doNotifyTriggers
0735: && commitStatus == OperationStatus.SUCCESS) {
0736: if (oldData != null && oldData.getData() == null) {
0737: oldData = null;
0738: }
0739: dbHandle.notifyTriggers(cursorImpl.getLocker(), key,
0740: oldData, data);
0741: }
0742: return commitStatus;
0743: } catch (Error E) {
0744: dbImpl.getDbEnvironment().invalidate(E);
0745: throw E;
0746: }
0747: }
0748:
0749: /**
0750: * Performs the put operation but does not notify triggers (does not
0751: * perform secondary updates). Prevents phantoms.
0752: */
0753: OperationStatus putNoNotify(DatabaseEntry key, DatabaseEntry data,
0754: PutMode putMode, DatabaseEntry returnOldData)
0755: throws DatabaseException {
0756:
0757: Locker nextKeyLocker = null;
0758: CursorImpl nextKeyCursor = null;
0759: try {
0760: /* If other transactions are serializable, lock the next key. */
0761: Locker cursorLocker = cursorImpl.getLocker();
0762: if (putMode != PutMode.CURRENT
0763: && dbImpl.getDbEnvironment().getTxnManager()
0764: .areOtherSerializableTransactionsActive(
0765: cursorLocker)) {
0766: nextKeyLocker = new BuddyLocker(dbImpl
0767: .getDbEnvironment(), cursorLocker);
0768: nextKeyCursor = new CursorImpl(dbImpl, nextKeyLocker);
0769: /* Perform eviction for user cursors. */
0770: nextKeyCursor.setAllowEviction(true);
0771: nextKeyCursor.lockNextKeyForInsert(key, data);
0772: }
0773:
0774: /* Perform the put operation. */
0775: return putAllowPhantoms(key, data, putMode, returnOldData,
0776: nextKeyCursor);
0777: } finally {
0778: /* Release the next-key lock. */
0779: if (nextKeyCursor != null) {
0780: nextKeyCursor.close();
0781: }
0782: if (nextKeyLocker != null) {
0783: nextKeyLocker.operationEnd();
0784: }
0785: }
0786: }
0787:
0788: /**
0789: * Clone the cursor, put key/data according to PutMode, and if successful,
0790: * swap cursors. Does not notify triggers (does not perform secondary
0791: * updates). Does not prevent phantoms.
0792: *
0793: * @param nextKeyCursor is the cursor used to lock the next key during
0794: * phantom prevention. If this cursor is non-null and initialized, it's
0795: * BIN will be used to initialize the dup cursor used to perform insertion.
0796: * This enables an optimization that skips the search for the BIN.
0797: */
0798: private OperationStatus putAllowPhantoms(DatabaseEntry key,
0799: DatabaseEntry data, PutMode putMode,
0800: DatabaseEntry returnOldData, CursorImpl nextKeyCursor)
0801: throws DatabaseException {
0802:
0803: if (data == null) {
0804: throw new NullPointerException(
0805: "put passed a null DatabaseEntry arg");
0806: }
0807:
0808: if (putMode != PutMode.CURRENT && key == null) {
0809: throw new IllegalArgumentException(
0810: "put passed a null DatabaseEntry arg");
0811: }
0812:
0813: CursorImpl original = null;
0814: OperationStatus status = OperationStatus.NOTFOUND;
0815: CursorImpl dup = null;
0816: try {
0817: /* Latch and clone. */
0818: original = cursorImpl;
0819:
0820: if (putMode == PutMode.CURRENT) {
0821: /* Call addCursor for putCurrent. */
0822: dup = original.cloneCursor(true);
0823: } else {
0824:
0825: /*
0826: * Do not call addCursor when inserting. Copy the position of
0827: * nextKeyCursor if available.
0828: */
0829: dup = original.cloneCursor(false, nextKeyCursor);
0830: }
0831:
0832: /* Perform operation. */
0833: if (putMode == PutMode.CURRENT) {
0834: status = dup.putCurrent(data, key, returnOldData);
0835: } else if (putMode == PutMode.OVERWRITE) {
0836: status = dup.put(key, data, returnOldData);
0837: } else if (putMode == PutMode.NOOVERWRITE) {
0838: status = dup.putNoOverwrite(key, data);
0839: } else if (putMode == PutMode.NODUP) {
0840: status = dup.putNoDupData(key, data);
0841: } else {
0842: throw new InternalException("unknown PutMode");
0843: }
0844:
0845: return status;
0846: } finally {
0847: if (original != null) {
0848: original.releaseBINs();
0849: }
0850:
0851: boolean success = (status == OperationStatus.SUCCESS);
0852: if (cursorImpl == dup) {
0853: if (!success) {
0854: cursorImpl.reset();
0855: }
0856: } else {
0857: if (success) {
0858: original.close();
0859: cursorImpl = dup;
0860: } else {
0861: if (dup != null) {
0862: dup.close();
0863: }
0864: }
0865: }
0866: }
0867: }
0868:
0869: /**
0870: * Position the cursor at the first or last record of the database.
0871: * Prevents phantoms.
0872: */
0873: OperationStatus position(DatabaseEntry key, DatabaseEntry data,
0874: LockMode lockMode, boolean first) throws DatabaseException {
0875:
0876: try {
0877: if (!isSerializableIsolation(lockMode)) {
0878: return positionAllowPhantoms(key, data, getLockType(
0879: lockMode, false), first);
0880: }
0881:
0882: /*
0883: * Perform range locking to prevent phantoms and handle restarts.
0884: */
0885: while (true) {
0886: try {
0887: /* Range lock the EOF node before getLast. */
0888: if (!first) {
0889: cursorImpl.lockEofNode(LockType.RANGE_READ);
0890: }
0891:
0892: /* Use a range lock for getFirst. */
0893: LockType lockType = getLockType(lockMode, first);
0894:
0895: /* Perform operation. */
0896: OperationStatus status = positionAllowPhantoms(key,
0897: data, lockType, first);
0898:
0899: /*
0900: * Range lock the EOF node when getFirst returns NOTFOUND.
0901: */
0902: if (first && status != OperationStatus.SUCCESS) {
0903: cursorImpl.lockEofNode(LockType.RANGE_READ);
0904: }
0905:
0906: return status;
0907: } catch (RangeRestartException e) {
0908: continue;
0909: }
0910: }
0911: } catch (Error E) {
0912: dbImpl.getDbEnvironment().invalidate(E);
0913: throw E;
0914: }
0915: }
0916:
0917: /**
0918: * Position without preventing phantoms.
0919: */
0920: private OperationStatus positionAllowPhantoms(DatabaseEntry key,
0921: DatabaseEntry data, LockType lockType, boolean first)
0922: throws DatabaseException {
0923:
0924: assert (key != null && data != null);
0925:
0926: OperationStatus status = OperationStatus.NOTFOUND;
0927: CursorImpl dup = null;
0928: try {
0929:
0930: /*
0931: * Pass false: no need to call addCursor here because
0932: * CursorImpl.position will be adding it after it finds the bin.
0933: */
0934: dup = beginRead(false);
0935:
0936: /* Search for first or last. */
0937: if (!dup.positionFirstOrLast(first, null)) {
0938: /* Tree is empty. */
0939: status = OperationStatus.NOTFOUND;
0940: assert LatchSupport.countLatchesHeld() == 0 : LatchSupport
0941: .latchesHeldToString();
0942:
0943: } else {
0944: /* Found something in this tree. */
0945: assert LatchSupport.countLatchesHeld() == 1 : LatchSupport
0946: .latchesHeldToString();
0947: status = dup.getCurrentAlreadyLatched(key, data,
0948: lockType, first);
0949:
0950: if (status == OperationStatus.SUCCESS) {
0951: if (dup.getDupBIN() != null) {
0952: dup.incrementLNCount();
0953: }
0954: } else {
0955: /* The record we're pointing at may be deleted. */
0956: status = dup.getNext(key, data, lockType, first,
0957: false);
0958: }
0959: }
0960: } finally {
0961:
0962: /*
0963: * positionFirstOrLast returns with the target BIN latched, so it
0964: * is the responsibility of this method to make sure the latches
0965: * are released.
0966: */
0967: cursorImpl.releaseBINs();
0968: endRead(dup, status == OperationStatus.SUCCESS);
0969: }
0970: return status;
0971: }
0972:
0973: /**
0974: * Perform search by key, data, or both. Prevents phantoms.
0975: */
0976: OperationStatus search(DatabaseEntry key, DatabaseEntry data,
0977: LockMode lockMode, SearchMode searchMode)
0978: throws DatabaseException {
0979:
0980: try {
0981: if (!isSerializableIsolation(lockMode)) {
0982: LockType lockType = getLockType(lockMode, false);
0983: KeyChangeStatus result = searchAllowPhantoms(key, data,
0984: lockType, lockType, searchMode);
0985: return result.status;
0986: }
0987:
0988: /*
0989: * Perform range locking to prevent phantoms and handle restarts.
0990: */
0991: while (true) {
0992: try {
0993: /* Do not use a range lock for the initial search. */
0994: LockType searchLockType = getLockType(lockMode,
0995: false);
0996:
0997: /* Switch to a range lock when advancing forward. */
0998: LockType advanceLockType = getLockType(lockMode,
0999: true);
1000:
1001: /* Do not modify key/data params until SUCCESS. */
1002: DatabaseEntry tryKey = new DatabaseEntry(key
1003: .getData(), key.getOffset(), key.getSize());
1004: DatabaseEntry tryData = new DatabaseEntry(data
1005: .getData(), data.getOffset(), data
1006: .getSize());
1007: KeyChangeStatus result;
1008:
1009: if (searchMode.isExactSearch()) {
1010:
1011: /*
1012: * Artificial range search to range lock the next key.
1013: */
1014: result = searchExactAndRangeLock(tryKey,
1015: tryData, searchLockType,
1016: advanceLockType, searchMode);
1017: } else {
1018: /* Normal range search. */
1019: result = searchAllowPhantoms(tryKey, tryData,
1020: searchLockType, advanceLockType,
1021: searchMode);
1022:
1023: /* Lock the EOF node if no records follow the key. */
1024: if (result.status != OperationStatus.SUCCESS) {
1025: cursorImpl.lockEofNode(LockType.RANGE_READ);
1026: }
1027: }
1028:
1029: /*
1030: * Only overwrite key/data on SUCCESS, after all locking.
1031: */
1032: if (result.status == OperationStatus.SUCCESS) {
1033: key.setData(tryKey.getData(), 0, tryKey
1034: .getSize());
1035: data.setData(tryData.getData(), 0, tryData
1036: .getSize());
1037: }
1038:
1039: return result.status;
1040: } catch (RangeRestartException e) {
1041: continue;
1042: }
1043: }
1044: } catch (Error E) {
1045: dbImpl.getDbEnvironment().invalidate(E);
1046: throw E;
1047: }
1048: }
1049:
1050: /**
1051: * For an exact search, perform a range search and return NOTFOUND if the
1052: * key changes (or if the data changes for BOTH) during the search.
1053: * If no exact match is found the range search will range lock the
1054: * following key for phantom prevention. Importantly, the cursor position
1055: * is not changed if an exact match is not found, even though we advance to
1056: * the following key in order to range lock it.
1057: */
1058: private KeyChangeStatus searchExactAndRangeLock(DatabaseEntry key,
1059: DatabaseEntry data, LockType searchLockType,
1060: LockType advanceLockType, SearchMode searchMode)
1061: throws DatabaseException {
1062:
1063: /* Convert exact search to range search. */
1064: searchMode = (searchMode == SearchMode.SET) ? SearchMode.SET_RANGE
1065: : SearchMode.BOTH_RANGE;
1066:
1067: KeyChangeStatus result = null;
1068: boolean noNextKeyFound;
1069:
1070: CursorImpl dup = beginRead(false /* searchAndPosition will add cursor */);
1071:
1072: try {
1073:
1074: /*
1075: * Perform a range search and return NOTFOUND if an exact match is
1076: * not found. Pass advanceAfterRangeSearch=true to advance even if
1077: * the key is not matched, to lock the following key.
1078: */
1079: result = searchInternal(dup, key, data, searchLockType,
1080: advanceLockType, searchMode, true /*advanceAfterRangeSearch*/);
1081:
1082: /* The keyChange value is independent of the status value. */
1083: noNextKeyFound = !result.keyChange;
1084:
1085: /* If the key changed, then we do not have an exact match. */
1086: if (result.keyChange
1087: && result.status == OperationStatus.SUCCESS) {
1088: result.status = OperationStatus.NOTFOUND;
1089: }
1090: } finally {
1091: endRead(dup, result != null
1092: && result.status == OperationStatus.SUCCESS);
1093: }
1094:
1095: /* Lock the EOF node if no more records, whether or not more dups. */
1096: if (noNextKeyFound) {
1097: cursorImpl.lockEofNode(LockType.RANGE_READ);
1098: }
1099:
1100: return result;
1101: }
1102:
1103: /**
1104: * Perform search without preventing phantoms.
1105: */
1106: private KeyChangeStatus searchAllowPhantoms(DatabaseEntry key,
1107: DatabaseEntry data, LockType searchLockType,
1108: LockType advanceLockType, SearchMode searchMode)
1109: throws DatabaseException {
1110:
1111: OperationStatus status = OperationStatus.NOTFOUND;
1112:
1113: CursorImpl dup = beginRead(false /* searchAndPosition will add cursor */);
1114:
1115: try {
1116: KeyChangeStatus result = searchInternal(dup, key, data,
1117: searchLockType, advanceLockType, searchMode, false /*advanceAfterRangeSearch*/);
1118:
1119: status = result.status;
1120: return result;
1121: } finally {
1122: endRead(dup, status == OperationStatus.SUCCESS);
1123: }
1124: }
1125:
1126: /**
1127: * Perform search for a given CursorImpl.
1128: */
1129: private KeyChangeStatus searchInternal(CursorImpl dup,
1130: DatabaseEntry key, DatabaseEntry data,
1131: LockType searchLockType, LockType advanceLockType,
1132: SearchMode searchMode, boolean advanceAfterRangeSearch)
1133: throws DatabaseException {
1134:
1135: assert key != null && data != null;
1136:
1137: OperationStatus status = OperationStatus.NOTFOUND;
1138: boolean keyChange = false;
1139:
1140: try {
1141: /* search */
1142: int searchResult = dup.searchAndPosition(key, data,
1143: searchMode, searchLockType);
1144: if ((searchResult & CursorImpl.FOUND) != 0) {
1145:
1146: /*
1147: * The search found a possibly valid record.
1148: * CursorImpl.searchAndPosition's job is to settle the cursor
1149: * at a particular location on a BIN. In some cases, the
1150: * current position may not actually hold a valid record, so
1151: * it's this layer's responsiblity to judge if it might need to
1152: * bump the cursor along and search more. For example, we might
1153: * have to do so if the position holds a deleted record.
1154: *
1155: * Advance the cursor if:
1156: *
1157: * 1. This is a range type search and there was no match on the
1158: * search criteria (the key or key and data depending on the
1159: * type of search). Then we search forward until there's a
1160: * match.
1161: *
1162: * 2. If this is not a range type search, check the record at
1163: * the current position. If this is not a duplicate set,
1164: * CursorImpl.searchAndPosition gave us an exact answer.
1165: * However since it doesn't peer into the duplicate set, we may
1166: * need to probe further in if there are deleted records in the
1167: * duplicate set. i.e, we have to be able to find k1/d2 even if
1168: * there's k1/d1(deleted), k1/d2, k1/d3, etc in a duplicate
1169: * set.
1170: *
1171: * Note that searchResult has four bits possibly set:
1172: *
1173: * FOUND has already been checked above.
1174: *
1175: * EXACT_KEY means an exact match on the key portion was made.
1176: *
1177: * EXACT_DATA means that if searchMode was BOTH or BOTH_RANGE
1178: * then an exact match was made on the data (in addition to the
1179: * key).
1180: *
1181: * FOUND_LAST means that the cursor is positioned at the last
1182: * record in the database.
1183: */
1184: boolean exactKeyMatch = ((searchResult & CursorImpl.EXACT_KEY) != 0);
1185: boolean exactDataMatch = ((searchResult & CursorImpl.EXACT_DATA) != 0);
1186: boolean foundLast = ((searchResult & CursorImpl.FOUND_LAST) != 0);
1187:
1188: /*
1189: * rangeMatch means that a range match of some sort (either
1190: * SET_RANGE or BOTH_RANGE) was specified and there wasn't a
1191: * complete match. If SET_RANGE was spec'd and EXACT_KEY was
1192: * not returned as set, then the key didn't match exactly. If
1193: * BOTH_RANGE was spec'd and EXACT_DATA was not returned as
1194: * set, then the data didn't match exactly.
1195: */
1196: boolean rangeMatch = false;
1197: if (searchMode == SearchMode.SET_RANGE
1198: && !exactKeyMatch) {
1199: rangeMatch = true;
1200: }
1201:
1202: if (searchMode == SearchMode.BOTH_RANGE
1203: && (!exactKeyMatch || !exactDataMatch)) {
1204: rangeMatch = true;
1205: }
1206:
1207: /*
1208: * Pass null for key to getCurrentAlreadyLatched if searchMode
1209: * is SET since the key is not supposed to be set in that case.
1210: */
1211: DatabaseEntry useKey = (searchMode == SearchMode.SET) ? null
1212: : key;
1213:
1214: /*
1215: * rangeMatch => an exact match was not found so we need to
1216: * advance the cursor to a real item using getNextXXX. If
1217: * rangeMatch is true, then cursor is currently on some entry,
1218: * but that entry is either deleted or is prior to the target
1219: * key/data. It is also possible that rangeMatch is false (we
1220: * have an exact match) but the entry is deleted. So we test
1221: * for rangeMatch or a deleted entry, and if either is true
1222: * then we advance to the next non-deleted entry.
1223: */
1224: if (rangeMatch
1225: || (status = dup.getCurrentAlreadyLatched(
1226: useKey, data, searchLockType, true)) == OperationStatus.KEYEMPTY) {
1227:
1228: if (foundLast) {
1229: status = OperationStatus.NOTFOUND;
1230: } else if (searchMode == SearchMode.SET) {
1231:
1232: /*
1233: * SET is an exact operation, so this isn't a
1234: * rangeMatch, it's a deleted record. We should
1235: * advance, but only to duplicates for the same key.
1236: */
1237: status = dup.getNextDuplicate(key, data,
1238: advanceLockType, true, rangeMatch);
1239: } else if (searchMode == SearchMode.BOTH) {
1240:
1241: /*
1242: * BOTH is also an exact operation, but we should not
1243: * advance past a deleted record because the data match
1244: * is exact. However, this API should return NOTFOUND
1245: * instead of KEYEMPTY (which may be been set above).
1246: */
1247: if (status == OperationStatus.KEYEMPTY) {
1248: status = OperationStatus.NOTFOUND;
1249: }
1250: } else {
1251: assert !searchMode.isExactSearch();
1252:
1253: /* Save the search key for a BOTH_RANGE search. */
1254: byte[] searchKey = null;
1255: if (searchMode.isDataSearch()) {
1256: searchKey = Key.makeKey(key);
1257: }
1258:
1259: /*
1260: * This may be a deleted record or a rangeMatch, and in
1261: * either case we should advance. We must determine
1262: * whether the key changes when we advance.
1263: */
1264: if (exactKeyMatch) {
1265: KeyChangeStatus result = dup
1266: .getNextWithKeyChangeStatus(key,
1267: data, advanceLockType,
1268: true, rangeMatch);
1269: status = result.status;
1270:
1271: /*
1272: * For BOTH_RANGE, advancing always causes a data
1273: * change, which is considered a key change. For
1274: * SET_RANGE, getNextWithKeyChangeStatus determined
1275: * the key change status.
1276: */
1277: keyChange = searchMode.isDataSearch() ? (status == OperationStatus.SUCCESS)
1278: : result.keyChange;
1279:
1280: } else if (searchMode.isDataSearch()
1281: && !advanceAfterRangeSearch) {
1282:
1283: /*
1284: * If we did not match the key (exactly) for
1285: * BOTH_RANGE, and advanceAfterSearchRangeBoth is
1286: * false, then return NOTFOUND.
1287: */
1288: status = OperationStatus.NOTFOUND;
1289: } else {
1290:
1291: /*
1292: * If we didn't match the key, skip over duplicates
1293: * to the next key with getNextNoDup.
1294: */
1295: status = dup.getNextNoDup(key, data,
1296: advanceLockType, true, rangeMatch);
1297:
1298: /* getNextNoDup always causes a key change. */
1299: keyChange = (status == OperationStatus.SUCCESS);
1300: }
1301:
1302: /*
1303: * If we moved past the search key after a BOTH_RANGE
1304: * search, return NOTFOUND. Leave the keyChange value
1305: * intact, since we want to return this accurately
1306: * regardless of the status return.
1307: */
1308: if (status == OperationStatus.SUCCESS
1309: && searchMode.isDataSearch()) {
1310: if (Key.compareKeys(key.getData(),
1311: searchKey, dbImpl
1312: .getBtreeComparator()) != 0) {
1313: status = OperationStatus.NOTFOUND;
1314: }
1315: }
1316: }
1317: }
1318: }
1319: } finally {
1320:
1321: /*
1322: * searchAndPosition returns with the target BIN latched, so it is
1323: * the responsibility of this method to make sure the latches are
1324: * released.
1325: */
1326: cursorImpl.releaseBINs();
1327: if (status != OperationStatus.SUCCESS && dup != cursorImpl) {
1328: dup.releaseBINs();
1329: }
1330: }
1331:
1332: return new KeyChangeStatus(status, keyChange);
1333: }
1334:
1335: /**
1336: * Retrieve the next or previous record. Prevents phantoms.
1337: */
1338: OperationStatus retrieveNext(DatabaseEntry key, DatabaseEntry data,
1339: LockMode lockMode, GetMode getMode)
1340: throws DatabaseException {
1341:
1342: try {
1343: if (!isSerializableIsolation(lockMode)) {
1344: return retrieveNextAllowPhantoms(key, data,
1345: getLockType(lockMode, false), getMode);
1346: }
1347:
1348: /*
1349: * Perform range locking to prevent phantoms and handle restarts.
1350: */
1351: while (true) {
1352: try {
1353: OperationStatus status;
1354: if (getMode == GetMode.NEXT_DUP) {
1355:
1356: /*
1357: * Special case to lock the next key if no more dups.
1358: */
1359: status = getNextDupAndRangeLock(key, data,
1360: lockMode);
1361: } else {
1362:
1363: /* Get a range lock for 'prev' operations. */
1364: if (!getMode.isForward()) {
1365: rangeLockCurrentPosition(getMode);
1366: }
1367:
1368: /*
1369: * Use a range lock if performing a 'next' operation.
1370: */
1371: LockType lockType = getLockType(lockMode,
1372: getMode.isForward());
1373:
1374: /* Perform the operation. */
1375: status = retrieveNextAllowPhantoms(key, data,
1376: lockType, getMode);
1377:
1378: if (getMode.isForward()
1379: && status != OperationStatus.SUCCESS) {
1380: /* NEXT, NEXT_NODUP: lock the EOF node. */
1381: cursorImpl.lockEofNode(LockType.RANGE_READ);
1382: }
1383: }
1384:
1385: return status;
1386: } catch (RangeRestartException e) {
1387: continue;
1388: }
1389: }
1390: } catch (Error E) {
1391: dbImpl.getDbEnvironment().invalidate(E);
1392: throw E;
1393: }
1394: }
1395:
1396: /**
1397: * Retrieve the next dup; if no next dup is found then range lock the
1398: * following key for phantom prevention. Importantly, the cursor position
1399: * is not changed if there are no more dups, even though we advance to the
1400: * following key in order to range lock it.
1401: */
1402: private OperationStatus getNextDupAndRangeLock(DatabaseEntry key,
1403: DatabaseEntry data, LockMode lockMode)
1404: throws DatabaseException {
1405:
1406: /* Do not modify key/data params until SUCCESS. */
1407: DatabaseEntry tryKey = new DatabaseEntry();
1408: DatabaseEntry tryData = new DatabaseEntry();
1409:
1410: /* Get a range lock. */
1411: LockType lockType = getLockType(lockMode, true);
1412: OperationStatus status;
1413: boolean noNextKeyFound;
1414:
1415: /*
1416: * Perform a NEXT and return NOTFOUND if the key changes
1417: * during the search.
1418: */
1419: while (true) {
1420: assert LatchSupport.countLatchesHeld() == 0;
1421: CursorImpl dup = beginRead(true);
1422:
1423: try {
1424: KeyChangeStatus result = dup
1425: .getNextWithKeyChangeStatus(tryKey, tryData,
1426: lockType, true, false);
1427: status = result.status;
1428: noNextKeyFound = (status != OperationStatus.SUCCESS);
1429: if (result.keyChange
1430: && status == OperationStatus.SUCCESS) {
1431: status = OperationStatus.NOTFOUND;
1432: }
1433: } catch (DatabaseException DBE) {
1434: endRead(dup, false);
1435: throw DBE;
1436: }
1437:
1438: if (checkForInsertion(GetMode.NEXT, cursorImpl, dup)) {
1439: endRead(dup, false);
1440: continue;
1441: } else {
1442: endRead(dup, status == OperationStatus.SUCCESS);
1443: assert LatchSupport.countLatchesHeld() == 0;
1444: break;
1445: }
1446: }
1447:
1448: /* Lock the EOF node if no more records, whether or not more dups. */
1449: if (noNextKeyFound) {
1450: cursorImpl.lockEofNode(LockType.RANGE_READ);
1451: }
1452:
1453: /* Only overwrite key/data on SUCCESS. */
1454: if (status == OperationStatus.SUCCESS) {
1455: key.setData(tryKey.getData(), 0, tryKey.getSize());
1456: data.setData(tryData.getData(), 0, tryData.getSize());
1457: }
1458:
1459: return status;
1460: }
1461:
1462: /**
1463: * For 'prev' operations, upgrade to a range lock at the current position.
1464: * For PREV_NODUP, range lock the first duplicate instead. If there are no
1465: * records at the current position, get a range lock on the next record or,
1466: * if not found, on the logical EOF node. Do not modify the current
1467: * cursor position, use a separate cursor.
1468: */
1469: private void rangeLockCurrentPosition(GetMode getMode)
1470: throws DatabaseException {
1471:
1472: DatabaseEntry tempKey = new DatabaseEntry();
1473: DatabaseEntry tempData = new DatabaseEntry();
1474: tempKey.setPartial(0, 0, true);
1475: tempData.setPartial(0, 0, true);
1476:
1477: OperationStatus status;
1478: CursorImpl dup = cursorImpl.cloneCursor(true);
1479: try {
1480: if (getMode == GetMode.PREV_NODUP) {
1481: status = dup.getFirstDuplicate(tempKey, tempData,
1482: LockType.RANGE_READ);
1483: } else {
1484: status = dup.getCurrent(tempKey, tempData,
1485: LockType.RANGE_READ);
1486: }
1487: if (status != OperationStatus.SUCCESS) {
1488: while (true) {
1489: assert LatchSupport.countLatchesHeld() == 0;
1490:
1491: status = dup.getNext(tempKey, tempData,
1492: LockType.RANGE_READ, true, false);
1493:
1494: if (checkForInsertion(GetMode.NEXT, cursorImpl, dup)) {
1495: dup.close();
1496: dup = cursorImpl.cloneCursor(true);
1497: continue;
1498: } else {
1499: assert LatchSupport.countLatchesHeld() == 0;
1500: break;
1501: }
1502: }
1503: }
1504: } finally {
1505: if (cursorImpl == dup) {
1506: dup.reset();
1507: } else {
1508: dup.close();
1509: }
1510: }
1511:
1512: if (status != OperationStatus.SUCCESS) {
1513: cursorImpl.lockEofNode(LockType.RANGE_READ);
1514: }
1515: }
1516:
1517: /**
1518: * Retrieve without preventing phantoms.
1519: */
1520: private OperationStatus retrieveNextAllowPhantoms(
1521: DatabaseEntry key, DatabaseEntry data, LockType lockType,
1522: GetMode getMode) throws DatabaseException {
1523:
1524: assert (key != null && data != null);
1525:
1526: OperationStatus status;
1527:
1528: while (true) {
1529: assert LatchSupport.countLatchesHeld() == 0;
1530: CursorImpl dup = beginRead(true);
1531:
1532: try {
1533: if (getMode == GetMode.NEXT) {
1534: status = dup.getNext(key, data, lockType, true,
1535: false);
1536: } else if (getMode == GetMode.PREV) {
1537: status = dup.getNext(key, data, lockType, false,
1538: false);
1539: } else if (getMode == GetMode.NEXT_DUP) {
1540: status = dup.getNextDuplicate(key, data, lockType,
1541: true, false);
1542: } else if (getMode == GetMode.PREV_DUP) {
1543: status = dup.getNextDuplicate(key, data, lockType,
1544: false, false);
1545: } else if (getMode == GetMode.NEXT_NODUP) {
1546: status = dup.getNextNoDup(key, data, lockType,
1547: true, false);
1548: } else if (getMode == GetMode.PREV_NODUP) {
1549: status = dup.getNextNoDup(key, data, lockType,
1550: false, false);
1551: } else {
1552: throw new InternalException("unknown GetMode");
1553: }
1554: } catch (DatabaseException DBE) {
1555: endRead(dup, false);
1556: throw DBE;
1557: }
1558:
1559: if (checkForInsertion(getMode, cursorImpl, dup)) {
1560: endRead(dup, false);
1561: continue;
1562: } else {
1563: endRead(dup, status == OperationStatus.SUCCESS);
1564: assert LatchSupport.countLatchesHeld() == 0;
1565: break;
1566: }
1567: }
1568: return status;
1569: }
1570:
1571: /**
1572: * Returns the current key and data. There is no need to prevent phantoms.
1573: */
1574: OperationStatus getCurrentInternal(DatabaseEntry key,
1575: DatabaseEntry data, LockMode lockMode)
1576: throws DatabaseException {
1577:
1578: /* Do not use a range lock. */
1579: LockType lockType = getLockType(lockMode, false);
1580:
1581: return cursorImpl.getCurrent(key, data, lockType);
1582: }
1583:
1584: /*
1585: * Something may have been added to the original cursor (cursorImpl) while
1586: * we were getting the next BIN. cursorImpl would have been adjusted
1587: * properly but we would have skipped a BIN in the process.
1588: *
1589: * Note that when we call LN.isDeleted(), we do not need to lock the LN.
1590: * If we see a non-committed deleted entry, we'll just iterate around in
1591: * the caller. So a false positive is ok.
1592: *
1593: * @return true if an unaccounted for insertion happened.
1594: */
1595: private boolean checkForInsertion(GetMode getMode,
1596: CursorImpl origCursor, CursorImpl dupCursor)
1597: throws DatabaseException {
1598:
1599: BIN origBIN = origCursor.getBIN();
1600: BIN dupBIN = dupCursor.getBIN();
1601: DBIN origDBIN = origCursor.getDupBIN();
1602:
1603: /* If fetchTarget returns null below, a deleted LN was cleaned. */
1604:
1605: boolean forward = true;
1606: if (getMode == GetMode.PREV || getMode == GetMode.PREV_DUP
1607: || getMode == GetMode.PREV_NODUP) {
1608: forward = false;
1609: }
1610: boolean ret = false;
1611: if (origBIN != dupBIN) {
1612: /* We jumped to the next BIN during getNext(). */
1613: origCursor.latchBINs();
1614:
1615: try {
1616: if (origDBIN == null) {
1617: if (forward) {
1618: if (origBIN.getNEntries() - 1 > origCursor
1619: .getIndex()) {
1620:
1621: /*
1622: * We were adjusted to something other than the
1623: * last entry so some insertion happened.
1624: */
1625: for (int i = origCursor.getIndex() + 1; i < origBIN
1626: .getNEntries(); i++) {
1627: if (!origBIN.isEntryKnownDeleted(i)) {
1628: Node n = origBIN.fetchTarget(i);
1629: if (n != null
1630: && !n.containsDuplicates()) {
1631: LN ln = (LN) n;
1632: /* See comment above about locking. */
1633: if (!ln.isDeleted()) {
1634: ret = true;
1635: break;
1636: }
1637: }
1638: } else {
1639: /* Need to check the DupCountLN. */
1640: }
1641: }
1642: }
1643: } else {
1644: if (origCursor.getIndex() > 0) {
1645:
1646: /*
1647: * We were adjusted to something other than the
1648: * first entry so some insertion happened.
1649: */
1650: for (int i = 0; i < origCursor.getIndex(); i++) {
1651: if (!origBIN.isEntryKnownDeleted(i)) {
1652: Node n = origBIN.fetchTarget(i);
1653: if (n != null
1654: && !n.containsDuplicates()) {
1655: LN ln = (LN) n;
1656: /* See comment above about locking. */
1657: if (!ln.isDeleted()) {
1658: ret = true;
1659: break;
1660: }
1661: } else {
1662: /* Need to check the DupCountLN. */
1663: }
1664: }
1665: }
1666: }
1667: }
1668: }
1669: } finally {
1670: origCursor.releaseBINs();
1671: }
1672: return ret;
1673: }
1674:
1675: if (origDBIN != dupCursor.getDupBIN()
1676: && origCursor.getIndex() == dupCursor.getIndex()
1677: && getMode != GetMode.NEXT_NODUP
1678: && getMode != GetMode.PREV_NODUP) {
1679: /* Same as above, only for the dupBIN. */
1680: origCursor.latchBINs();
1681: try {
1682: if (forward) {
1683: if (origDBIN.getNEntries() - 1 > origCursor
1684: .getDupIndex()) {
1685:
1686: /*
1687: * We were adjusted to something other than the last
1688: * entry so some insertion happened.
1689: */
1690: for (int i = origCursor.getDupIndex() + 1; i < origDBIN
1691: .getNEntries(); i++) {
1692: if (!origDBIN.isEntryKnownDeleted(i)) {
1693: Node n = origDBIN.fetchTarget(i);
1694: LN ln = (LN) n;
1695: /* See comment above about locking. */
1696: if (n != null && !ln.isDeleted()) {
1697: ret = true;
1698: break;
1699: }
1700: }
1701: }
1702: }
1703: } else {
1704: if (origCursor.getDupIndex() > 0) {
1705:
1706: /*
1707: * We were adjusted to something other than the first
1708: * entry so some insertion happened.
1709: */
1710: for (int i = 0; i < origCursor.getDupIndex(); i++) {
1711: if (!origDBIN.isEntryKnownDeleted(i)) {
1712: Node n = origDBIN.fetchTarget(i);
1713: LN ln = (LN) n;
1714: /* See comment above about locking. */
1715: if (n != null && !ln.isDeleted()) {
1716: ret = true;
1717: break;
1718: }
1719: }
1720: }
1721: }
1722: }
1723: } finally {
1724: origCursor.releaseBINs();
1725: }
1726: return ret;
1727: }
1728: return false;
1729: }
1730:
1731: /**
1732: * If the cursor is initialized, dup it and return the dup; otherwise,
1733: * return the original. This avoids the overhead of duping when the
1734: * original is uninitialized. The cursor returned must be passed to
1735: * endRead() to close the correct cursor.
1736: */
1737: private CursorImpl beginRead(boolean addCursor)
1738: throws DatabaseException {
1739:
1740: CursorImpl dup;
1741: if (cursorImpl.isNotInitialized()) {
1742: dup = cursorImpl;
1743: } else {
1744: dup = cursorImpl.cloneCursor(addCursor);
1745: }
1746: return dup;
1747: }
1748:
1749: /**
1750: * If the operation is successful, swaps cursors and closes the original
1751: * cursor; otherwise, closes the duped cursor. In the case where the
1752: * original cursor was not duped by beginRead because it was uninitialized,
1753: * just resets the original cursor if the operation did not succeed.
1754: */
1755: private void endRead(CursorImpl dup, boolean success)
1756: throws DatabaseException {
1757:
1758: if (dup == cursorImpl) {
1759: if (!success) {
1760: cursorImpl.reset();
1761: }
1762: } else {
1763: if (success) {
1764: cursorImpl.close();
1765: cursorImpl = dup;
1766: } else {
1767: dup.close();
1768: }
1769: }
1770: }
1771:
1772: boolean advanceCursor(DatabaseEntry key, DatabaseEntry data) {
1773: return cursorImpl.advanceCursor(key, data);
1774: }
1775:
1776: private LockType getLockType(LockMode lockMode, boolean rangeLock) {
1777:
1778: if (isReadUncommittedMode(lockMode)) {
1779: return LockType.NONE;
1780: } else if (lockMode == null || lockMode == LockMode.DEFAULT) {
1781: return rangeLock ? LockType.RANGE_READ : LockType.READ;
1782: } else if (lockMode == LockMode.RMW) {
1783: return rangeLock ? LockType.RANGE_WRITE : LockType.WRITE;
1784: } else if (lockMode == LockMode.READ_COMMITTED) {
1785: throw new IllegalArgumentException(lockMode.toString()
1786: + " not allowed with Cursor methods");
1787: } else {
1788: assert false : lockMode;
1789: return LockType.NONE;
1790: }
1791: }
1792:
1793: /**
1794: * Returns whether the given lock mode will cause a read-uncommitted when
1795: * used with this cursor, taking into account the default cursor
1796: * configuration.
1797: */
1798: boolean isReadUncommittedMode(LockMode lockMode) {
1799:
1800: return (lockMode == LockMode.READ_UNCOMMITTED || (readUncommittedDefault && (lockMode == null || lockMode == LockMode.DEFAULT)));
1801: }
1802:
1803: private boolean isSerializableIsolation(LockMode lockMode) {
1804:
1805: return serializableIsolationDefault
1806: && !isReadUncommittedMode(lockMode);
1807: }
1808:
1809: protected void checkUpdatesAllowed(String operation)
1810: throws DatabaseException {
1811:
1812: if (updateOperationsProhibited) {
1813: throw new DatabaseException(
1814: "A transaction was not supplied when opening this cursor: "
1815: + operation);
1816: }
1817: }
1818:
1819: /**
1820: * Note that this flavor of checkArgs doesn't require that the dbt data is
1821: * set.
1822: */
1823: private void checkArgsNoValRequired(DatabaseEntry key,
1824: DatabaseEntry data) {
1825: DatabaseUtil.checkForNullDbt(key, "key", false);
1826: DatabaseUtil.checkForNullDbt(data, "data", false);
1827: }
1828:
1829: /**
1830: * Note that this flavor of checkArgs requires that the dbt data is set.
1831: */
1832: private void checkArgsValRequired(DatabaseEntry key,
1833: DatabaseEntry data) {
1834: DatabaseUtil.checkForNullDbt(key, "key", true);
1835: DatabaseUtil.checkForNullDbt(data, "data", true);
1836: }
1837:
1838: /**
1839: * Check the environment and cursor state.
1840: */
1841: void checkState(boolean mustBeInitialized) throws DatabaseException {
1842:
1843: checkEnv();
1844: cursorImpl.checkCursorState(mustBeInitialized);
1845: }
1846:
1847: /**
1848: * @throws RunRecoveryException if the underlying environment is invalid.
1849: */
1850: void checkEnv() throws RunRecoveryException {
1851:
1852: cursorImpl.checkEnv();
1853: }
1854:
1855: /**
1856: * Send trace messages to the java.util.logger. Don't rely on the logger
1857: * alone to conditionalize whether we send this message, we don't even want
1858: * to construct the message if the level is not enabled.
1859: */
1860: void trace(Level level, String methodName, DatabaseEntry key,
1861: DatabaseEntry data, LockMode lockMode) {
1862: if (logger.isLoggable(level)) {
1863: StringBuffer sb = new StringBuffer();
1864: sb.append(methodName);
1865: traceCursorImpl(sb);
1866: if (key != null) {
1867: sb.append(" key=").append(key.dumpData());
1868: }
1869: if (data != null) {
1870: sb.append(" data=").append(data.dumpData());
1871: }
1872: if (lockMode != null) {
1873: sb.append(" lockMode=").append(lockMode);
1874: }
1875: logger.log(level, sb.toString());
1876: }
1877: }
1878:
1879: /**
1880: * Send trace messages to the java.util.logger. Don't rely on the logger
1881: * alone to conditionalize whether we send this message, we don't even want
1882: * to construct the message if the level is not enabled.
1883: */
1884: void trace(Level level, String methodName, LockMode lockMode) {
1885: if (logger.isLoggable(level)) {
1886: StringBuffer sb = new StringBuffer();
1887: sb.append(methodName);
1888: traceCursorImpl(sb);
1889: if (lockMode != null) {
1890: sb.append(" lockMode=").append(lockMode);
1891: }
1892: logger.log(level, sb.toString());
1893: }
1894: }
1895:
1896: private void traceCursorImpl(StringBuffer sb) {
1897: sb.append(" locker=").append(cursorImpl.getLocker().getId());
1898: if (cursorImpl.getBIN() != null) {
1899: sb.append(" bin=").append(cursorImpl.getBIN().getNodeId());
1900: }
1901: sb.append(" idx=").append(cursorImpl.getIndex());
1902:
1903: if (cursorImpl.getDupBIN() != null) {
1904: sb.append(" Dbin=").append(
1905: cursorImpl.getDupBIN().getNodeId());
1906: }
1907: sb.append(" dupIdx=").append(cursorImpl.getDupIndex());
1908: }
1909: }
|