1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002, 2014 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 
8 package com.sleepycat.je;
9 
10 import java.util.Collection;
11 import java.util.Comparator;
12 import java.util.logging.Level;
13 import java.util.logging.Logger;
14 
15 import com.sleepycat.je.dbi.CursorImpl;
16 import com.sleepycat.je.dbi.CursorImpl.LockStanding;
17 import com.sleepycat.je.dbi.CursorImpl.SearchMode;
18 import com.sleepycat.je.dbi.DatabaseImpl;
19 import com.sleepycat.je.dbi.DupKeyData;
20 import com.sleepycat.je.dbi.GetMode;
21 import com.sleepycat.je.dbi.PutMode;
22 import com.sleepycat.je.dbi.RangeConstraint;
23 import com.sleepycat.je.dbi.RangeRestartException;
24 import com.sleepycat.je.dbi.RecordVersion;
25 import com.sleepycat.je.dbi.TriggerManager;
26 import com.sleepycat.je.latch.LatchSupport;
27 import com.sleepycat.je.log.LogUtils;
28 import com.sleepycat.je.log.ReplicationContext;
29 import com.sleepycat.je.tree.BIN;
30 import com.sleepycat.je.tree.CountEstimator;
31 import com.sleepycat.je.tree.Key;
32 import com.sleepycat.je.tree.LN;
33 import com.sleepycat.je.txn.BuddyLocker;
34 import com.sleepycat.je.txn.LockType;
35 import com.sleepycat.je.txn.Locker;
36 import com.sleepycat.je.txn.LockerFactory;
37 import com.sleepycat.je.utilint.DatabaseUtil;
38 import com.sleepycat.je.utilint.LoggerUtils;
39 import com.sleepycat.je.utilint.Pair;
40 import com.sleepycat.je.utilint.ThroughputStatGroup;
41 
42 import static com.sleepycat.je.EnvironmentFailureException.assertState;
43 
44 /**
45  * A database cursor. Cursors are used for operating on collections of records,
46  * for iterating over a database, and for saving handles to individual records,
47  * so that they can be modified after they have been read.
48  *
49  * <p>Cursors which are opened with a transaction instance are transactional
50  * cursors and may be used by multiple threads, but only serially.  That is,
51  * the application must serialize access to the handle. Non-transactional
52  * cursors, opened with a null transaction instance, may not be used by
53  * multiple threads.</p>
54  *
55  * <p>If the cursor is to be used to perform operations on behalf of a
56  * transaction, the cursor must be opened and closed within the context of that
57  * single transaction.</p>
58  *
59  * <p>Once the cursor {@link #close} method has been called, the handle may not
60  * be accessed again, regardless of the {@code close} method's success or
61  * failure, with one exception:  the {@code close} method itself may be called
62  * any number of times to simplify error handling.</p>
63  *
64  * <p>To obtain a cursor with default attributes:</p>
65  *
66  * <blockquote><pre>
67  *     Cursor cursor = myDatabase.openCursor(txn, null);
68  * </pre></blockquote>
69  *
70  * <p>To customize the attributes of a cursor, use a CursorConfig object.</p>
71  *
72  * <blockquote><pre>
73  *     CursorConfig config = new CursorConfig();
74  *     config.setReadUncommitted(true);
75  *     Cursor cursor = myDatabase.openCursor(txn, config);
76  * </pre></blockquote>
77  *
78  * <p>Modifications to the database during a sequential scan will be reflected
79  * in the scan; that is, records inserted behind a cursor will not be returned
80  * while records inserted in front of a cursor will be returned.</p>
81  *
82  * <p>By default, a cursor is "sticky", meaning that the prior position is
83  * maintained by cursor movement operations, and the cursor stays at the
84  * prior position when {@code NOTFOUND} is returned or an exception is thrown.
85  * However, it is possible to configure a cursor as non-sticky to enable
86  * certain performance benefits.  See {@link CursorConfig#setNonSticky} for
87  * details.</p>
88  *
89  * <a name="partialEntry"><h3>Using Partial DatabaseEntry Parameters</h3></a>
90  *
91  * <p>The {@link DatabaseEntry#setPartial DatabaseEntry Partial} property can
92  * be used to optimize in certain cases.  This provides varying degrees of
93  * performance benefits that depend on the specific operation and use of {@code
94  * READ_UNCOMMITTED} isolation, as described below.</p>
95  *
96  * <p>When retrieving a record with a {@link Database} or {@link Cursor}
97  * method, if only the key is needed by the application then the retrieval of
98  * the data item can be suppressed using the Partial property. If {@code
99  * setPartial(0, 0, true)} is called for the {@code DatabaseEntry} passed as
100  * the data parameter, the data item will not be returned by the {@code
101  * Database} or {@code Cursor} method.</p>
102  *
103  * <p>Suppressing the return of the data item potentially has a large
104  * performance benefit.  In this case, if the record data is not in the JE
105  * cache, it will not be read from disk.  The performance benefit is
106  * potentially large because random access disk reads may be reduced.
107  * Examples use cases are:</p>
108  * <ul>
109  * <li>Scanning all records in key order, when the data is not needed.</li>
110  * <li>Skipping over records quickly with {@code READ_UNCOMMITTED} isolation to
111  * select records for further processing by examining the key value.</li>
112  * </ul>
113  *
114  * <p>Note that by "record data" we mean both the {@code data} parameter for a
115  * regular or primary DB, and the {@code pKey} parameter for a secondary DB.
116  * Also note that the performance advantage of a key-only operation does not
117  * apply to databases configured for duplicates.  For a duplicates DB, the data
118  * is always available along with the key and does not have to be fetched
119  * separately.</p>
120  *
121  * <p>For information on specifying isolation modes, see {@link LockMode},
122  * {@link CursorConfig} and {@link TransactionConfig}.</p>
123  *
124  * <p>The Partial property may also be used to retrieve or update only a
125  * portion of a data item.  This avoids copying the entire record between the
126  * JE cache and the application data parameter. However, this feature is not
127  * currently fully optimized, since the entire record is always read or written
128  * to the database, and the entire record is cached.  A partial update may
129  * be performed only with {@link Cursor#putCurrent Cursor.putCurrent}.</p>
130  *
131  * <p>In limited cases, the Partial property may also be used to retrieve a
132  * partial key item.  For example, a {@code DatabaseEntry} with a Partial
133  * property may be passed to {@link #getNext getNext}.  However, in practice
134  * this has limited value since the entire key is usually needed by the
135  * application, and the benefit of copying a portion of the key is generally
136  * very small.  Partial key items may not be passed to methods that use the key
137  * as an input parameter, for example, {@link #getSearchKey getSearchKey}.  In
138  * general, the usefulness of partial key items is very limited.</p>
139  */
140 public class Cursor implements ForwardCursor {
141 
142     private static final DatabaseEntry EMPTY_DUP_DATA =
143         new DatabaseEntry(new byte[0]);
144 
145     static final DatabaseEntry NO_RETURN_DATA = new DatabaseEntry();
146 
147     static {
148         NO_RETURN_DATA.setPartial(0, 0, true);
149     }
150 
151     /**
152      * The CursorConfig used to configure this cursor.
153      */
154     CursorConfig config;
155 
156     /* User Transacational, or null if none. */
157     private Transaction transaction;
158 
159     /**
160      * Handle under which this cursor was created; may be null when the cursor
161      * is used internally.
162      */
163     private Database dbHandle;
164 
165     /**
166      * Database implementation.
167      */
168     private DatabaseImpl dbImpl;
169 
170     /**
171      * The underlying cursor.
172      */
173     CursorImpl cursorImpl; // Used by subclasses.
174 
175     private boolean updateOperationsProhibited;
176 
177     /* Attributes */
178     private boolean readUncommittedDefault;
179     private boolean serializableIsolationDefault;
180 
181     private boolean nonSticky = false;
182 
183     private CacheMode cacheMode;
184 
185     /*
186      * For range searches, it establishes the upper bound (K2) of the search
187      * range via a function that returns false if a key is >= K2.
188      */
189     private RangeConstraint rangeConstraint;
190 
191     /* Used to access call counters. This is null for internal cursors. */
192     private ThroughputStatGroup thrput;
193 
194     private Logger logger;
195 
196     /**
197      * Creates a cursor for a given user transaction with
198      * retainNonTxnLocks=false.
199      *
200      * <p>If txn is null, a non-transactional cursor will be created that
201      * releases locks for the prior operation when the next operation
202      * succeeds.</p>
203      */
Cursor(final Database dbHandle, final Transaction txn, CursorConfig cursorConfig)204     Cursor(final Database dbHandle,
205            final Transaction txn,
206            CursorConfig cursorConfig) {
207 
208         if (cursorConfig == null) {
209             cursorConfig = CursorConfig.DEFAULT;
210         }
211 
212         /* Check that Database is open for internal Cursor usage. */
213         if (dbHandle != null) {
214             dbHandle.checkOpen("Can't access Database:");
215         }
216 
217         /* Do not allow auto-commit when creating a user cursor. */
218         Locker locker = LockerFactory.getReadableLocker(
219             dbHandle, txn, cursorConfig.getReadCommitted());
220 
221         init(dbHandle, dbHandle.getDatabaseImpl(), locker, cursorConfig,
222              false /*retainNonTxnLocks*/);
223     }
224 
225     /**
226      * Creates a cursor for a given locker with retainNonTxnLocks=false.
227      *
228      * <p>If locker is null or is non-transactional, a non-transactional cursor
229      * will be created that releases locks for the prior operation when the
230      * next operation succeeds.</p>
231      */
Cursor(final Database dbHandle, Locker locker, CursorConfig cursorConfig)232     Cursor(final Database dbHandle, Locker locker, CursorConfig cursorConfig) {
233 
234         if (cursorConfig == null) {
235             cursorConfig = CursorConfig.DEFAULT;
236         }
237 
238         /* Check that Database is open for internal Cursor usage. */
239         if (dbHandle != null) {
240             dbHandle.checkOpen("Can't access Database:");
241         }
242 
243         locker = LockerFactory.getReadableLocker(
244             dbHandle, locker, cursorConfig.getReadCommitted());
245 
246         init(dbHandle, dbHandle.getDatabaseImpl(), locker, cursorConfig,
247              false /*retainNonTxnLocks*/);
248     }
249 
250     /**
251      * Creates a cursor for a given locker and retainNonTxnLocks parameter.
252      *
253      * <p>The locker parameter must be non-null.  With this constructor, we use
254      * the given locker and retainNonTxnLocks parameter without applying any
255      * special rules for different lockers -- the caller must supply the
256      * correct locker and retainNonTxnLocks combination.</p>
257      */
Cursor(final Database dbHandle, final Locker locker, CursorConfig cursorConfig, final boolean retainNonTxnLocks)258     Cursor(final Database dbHandle,
259            final Locker locker,
260            CursorConfig cursorConfig,
261            final boolean retainNonTxnLocks) {
262 
263         if (cursorConfig == null) {
264             cursorConfig = CursorConfig.DEFAULT;
265         }
266 
267         /* Check that Database is open for internal Cursor usage. */
268         if (dbHandle != null) {
269             dbHandle.checkOpen("Can't access Database:");
270         }
271 
272         init(dbHandle, dbHandle.getDatabaseImpl(), locker, cursorConfig,
273              retainNonTxnLocks);
274     }
275 
276     /**
277      * Creates a cursor for a given locker and retainNonTxnLocks parameter,
278      * without a Database handle.
279      *
280      * <p>The locker parameter must be non-null.  With this constructor, we use
281      * the given locker and retainNonTxnLocks parameter without applying any
282      * special rules for different lockers -- the caller must supply the
283      * correct locker and retainNonTxnLocks combination.</p>
284      */
Cursor(final DatabaseImpl databaseImpl, final Locker locker, CursorConfig cursorConfig, final boolean retainNonTxnLocks)285     Cursor(final DatabaseImpl databaseImpl,
286            final Locker locker,
287            CursorConfig cursorConfig,
288            final boolean retainNonTxnLocks) {
289 
290         if (cursorConfig == null) {
291             cursorConfig = CursorConfig.DEFAULT;
292         }
293 
294         /* Check that Database is open for internal Cursor usage. */
295         if (dbHandle != null) {
296             dbHandle.checkOpen("Can't access Database:");
297         }
298 
299         init(null /*dbHandle*/, databaseImpl, locker, cursorConfig,
300              retainNonTxnLocks);
301     }
302 
init(final Database dbHandle, final DatabaseImpl databaseImpl, final Locker locker, final CursorConfig cursorConfig, final boolean retainNonTxnLocks)303     private void init(final Database dbHandle,
304                       final DatabaseImpl databaseImpl,
305                       final Locker locker,
306                       final CursorConfig cursorConfig,
307                       final boolean retainNonTxnLocks) {
308         assert locker != null;
309 
310         /*
311          * Allow locker to perform "open cursor" actions, such as consistency
312          * checks for a non-transactional locker on a Replica.
313          */
314         try {
315             locker.openCursorHook(databaseImpl);
316         } catch (RuntimeException e) {
317             locker.operationEnd();
318             throw e;
319         }
320 
321         cursorImpl = new CursorImpl(
322             databaseImpl, locker, retainNonTxnLocks, isSecondaryCursor());
323 
324         transaction = locker.getTransaction();
325 
326         /* Perform eviction for user cursors. */
327         cursorImpl.setAllowEviction(true);
328 
329         readUncommittedDefault =
330             cursorConfig.getReadUncommitted() ||
331             locker.isReadUncommittedDefault();
332 
333         serializableIsolationDefault =
334             cursorImpl.getLocker().isSerializableIsolation();
335 
336         /* Be sure to keep this logic in sync with checkUpdatesAllowed. */
337         updateOperationsProhibited =
338             locker.isReadOnly() ||
339             (dbHandle != null && !dbHandle.isWritable()) ||
340             (databaseImpl.isTransactional() && !locker.isTransactional()) ||
341             (databaseImpl.isReplicated() == locker.isLocalWrite());
342 
343         this.dbImpl = databaseImpl;
344         if (dbHandle != null) {
345             this.dbHandle = dbHandle;
346             dbHandle.addCursor(this);
347             thrput = dbHandle.getEnvironment().
348                      getEnvironmentImpl().getThroughputStatGroup();
349         }
350 
351         this.config = cursorConfig;
352         this.logger = databaseImpl.getEnv().getLogger();
353 
354         /*
355          * The non-sticky and cache mode properties are related. setCacheMode
356          * may modify non-sticky, so initialize in the following order.
357          */
358         nonSticky = cursorConfig.getNonSticky();
359         setCacheMode(null);
360     }
361 
362     /**
363      * Copy constructor.
364      */
Cursor(final Cursor cursor, final boolean samePosition)365     Cursor(final Cursor cursor, final boolean samePosition) {
366         readUncommittedDefault = cursor.readUncommittedDefault;
367         serializableIsolationDefault = cursor.serializableIsolationDefault;
368         updateOperationsProhibited = cursor.updateOperationsProhibited;
369 
370         cursorImpl = cursor.cursorImpl.cloneCursor(samePosition);
371         dbImpl = cursor.dbImpl;
372         dbHandle = cursor.dbHandle;
373         if (dbHandle != null) {
374             dbHandle.addCursor(this);
375         }
376         config = cursor.config;
377         logger = dbImpl.getEnv().getLogger();
378         cacheMode = cursor.cacheMode;
379         nonSticky = cursor.nonSticky;
380         thrput = cursor.thrput;
381     }
382 
isSecondaryCursor()383     boolean isSecondaryCursor() {
384         return false;
385     }
386 
387     /**
388      * Sets non-sticky mode, if possible for the currently configured cache
389      * mode. See CursorImpl.beforeAdvance on cache mode restrictions.
390      *
391      * TODO: Make this private after removing DbInternal.setNonSticky.
392      *
393      * @see CursorConfig#setNonSticky
394      */
setNonSticky(final boolean nonSticky)395     void setNonSticky(final boolean nonSticky) {
396         this.nonSticky =
397             nonSticky &&
398             cacheMode != CacheMode.EVICT_BIN &&
399             cacheMode != CacheMode.MAKE_COLD;
400     }
401 
402     /**
403      * Internal entrypoint.
404      */
getCursorImpl()405     CursorImpl getCursorImpl() {
406         return cursorImpl;
407     }
408 
409     /**
410      * Returns the Database handle associated with this Cursor.
411      *
412      * @return The Database handle associated with this Cursor.
413      */
getDatabase()414     public Database getDatabase() {
415         return dbHandle;
416     }
417 
418     /**
419      * Always returns non-null, while getDatabase() returns null if no handle
420      * is associated with this cursor.
421      */
getDatabaseImpl()422     DatabaseImpl getDatabaseImpl() {
423         return dbImpl;
424     }
425 
426     /**
427      * Returns this cursor's configuration.
428      *
429      * <p>This may differ from the configuration used to open this object if
430      * the cursor existed previously.</p>
431      *
432      * @return This cursor's configuration.
433      */
getConfig()434     public CursorConfig getConfig() {
435         try {
436             return config.clone();
437         } catch (Error E) {
438             dbImpl.getEnv().invalidate(E);
439             throw E;
440         }
441     }
442 
443     /**
444      * Returns the {@code CacheMode} used for subsequent operations performed
445      * using this cursor.  If {@link #setCacheMode} has not been called with a
446      * non-null value, the configured Database or Environment default is
447      * returned.
448      *
449      * @return the {@code CacheMode} used for subsequent operations using
450      * this cursor.
451      *
452      * @see #setCacheMode
453      */
getCacheMode()454     public CacheMode getCacheMode() {
455         return cacheMode;
456     }
457 
458     /**
459      * Sets the {@code CacheMode} used for subsequent operations performed
460      * using this cursor.  This method may be used to override the defaults
461      * specified using {@link DatabaseConfig#setCacheMode} and {@link
462      * EnvironmentConfig#setCacheMode}.
463      *
464      * @param cacheMode is the {@code CacheMode} used for subsequent operations
465      * using this cursor, or null to configure the Database or Environment
466      * default.
467      *
468      * @see CacheMode for further details.
469      */
setCacheMode(final CacheMode cacheMode)470     public void setCacheMode(final CacheMode cacheMode) {
471 
472         this.cacheMode =
473             (cacheMode != null) ? cacheMode : dbImpl.getDefaultCacheMode();
474 
475         /* Reinitialize non-sticky after changing the cache mode. */
476         setNonSticky(nonSticky);
477     }
478 
479     /**
480      * @hidden
481      * For internal use only.
482      * Used by KVStore.
483      *
484      * A RangeConstraint is used by search-range and next/previous methods to
485      * prevent keys that are not inside the range from being returned.
486      *
487      * This method is not yet part of the public API because it has not been
488      * designed with future-proofing or generality in mind, and has not been
489      * reviewed.
490      */
setRangeConstraint(RangeConstraint rangeConstraint)491     public void setRangeConstraint(RangeConstraint rangeConstraint) {
492         if (dbImpl.getSortedDuplicates()) {
493             throw new UnsupportedOperationException("Not allowed with dups");
494         }
495         this.rangeConstraint = rangeConstraint;
496     }
497 
setPrefixConstraint(final Cursor c, final byte[] keyBytes2)498     private void setPrefixConstraint(final Cursor c, final byte[] keyBytes2) {
499         c.rangeConstraint = new RangeConstraint() {
500             public boolean inBounds(byte[] checkKey) {
501                 return DupKeyData.compareMainKey(
502                     checkKey, keyBytes2, dbImpl.getBtreeComparator()) == 0;
503             }
504         };
505     }
506 
setPrefixConstraint(final Cursor c, final DatabaseEntry key2)507     private void setPrefixConstraint(final Cursor c,
508                                      final DatabaseEntry key2) {
509         c.rangeConstraint = new RangeConstraint() {
510             public boolean inBounds(byte[] checkKey) {
511                 return DupKeyData.compareMainKey(
512                     checkKey, key2.getData(), key2.getOffset(),
513                     key2.getSize(), dbImpl.getBtreeComparator()) == 0;
514             }
515         };
516     }
517 
checkRangeConstraint(final DatabaseEntry key)518     private boolean checkRangeConstraint(final DatabaseEntry key) {
519         assert key.getOffset() == 0;
520         assert key.getData().length == key.getSize();
521 
522         if (rangeConstraint == null) {
523             return true;
524         }
525 
526         return rangeConstraint.inBounds(key.getData());
527     }
528 
529     /**
530      * Discards the cursor.
531      *
532      * <p>The cursor handle may not be used again after this method has been
533      * called, regardless of the method's success or failure, with one
534      * exception:  the {@code close} method itself may be called any number of
535      * times.</p>
536      *
537      * <p>WARNING: To guard against memory leaks, the application should
538      * discard all references to the closed handle.  While BDB makes an effort
539      * to discard references from closed objects to the allocated memory for an
540      * environment, this behavior is not guaranteed.  The safe course of action
541      * for an application is to discard all references to closed BDB
542      * objects.</p>
543      *
544      * @throws EnvironmentFailureException if an unexpected, internal or
545      * environment-wide failure occurs.
546      */
close()547     public void close()
548         throws DatabaseException {
549 
550         try {
551             if (cursorImpl.isClosed()) {
552                 return;
553             }
554 
555             /*
556              * Do not call checkState here, to allow closing a cursor after an
557              * operation failure.  [#17015]
558              */
559             checkEnv();
560             cursorImpl.close();
561             if (dbHandle != null) {
562                 dbHandle.removeCursor(this);
563                 dbHandle = null;
564             }
565         } catch (Error E) {
566             dbImpl.getEnv().invalidate(E);
567             throw E;
568         }
569     }
570 
571     /**
572      * Returns a new cursor with the same transaction and locker ID as the
573      * original cursor.
574      *
575      * <p>This is useful when an application is using locking and requires
576      * two or more cursors in the same thread of control.</p>
577      *
578      * @param samePosition If true, the newly created cursor is initialized
579      * to refer to the same position in the database as the original cursor
580      * (if any) and hold the same locks (if any). If false, or the original
581      * cursor does not hold a database position and locks, the returned
582      * cursor is uninitialized and will behave like a newly created cursor.
583      *
584      * @return A new cursor with the same transaction and locker ID as the
585      * original cursor.
586      *
587      * @throws com.sleepycat.je.rep.DatabasePreemptedException in a replicated
588      * environment if the master has truncated, removed or renamed the
589      * database.
590      *
591      * @throws OperationFailureException if this exception occurred earlier and
592      * caused the transaction to be invalidated.
593      *
594      * @throws EnvironmentFailureException if an unexpected, internal or
595      * environment-wide failure occurs.
596      *
597      * @throws IllegalStateException if the cursor or database has been closed.
598      */
dup(final boolean samePosition)599     public Cursor dup(final boolean samePosition)
600         throws DatabaseException {
601 
602         try {
603             checkState(false);
604             return new Cursor(this, samePosition);
605         } catch (Error E) {
606             dbImpl.getEnv().invalidate(E);
607             throw E;
608         }
609     }
610 
611     /**
612      * Deletes the key/data pair to which the cursor refers.
613      *
614      * <p>When called on a cursor opened on a database that has been made into
615      * a secondary index, this method the key/data pair from the primary
616      * database and all secondary indices.</p>
617      *
618      * <p>The cursor position is unchanged after a delete, and subsequent calls
619      * to cursor functions expecting the cursor to refer to an existing key
620      * will fail.</p>
621      *
622      * @return {@link com.sleepycat.je.OperationStatus#KEYEMPTY
623      * OperationStatus.KEYEMPTY} if the key/pair at the cursor position has
624      * been deleted; otherwise, {@link
625      * com.sleepycat.je.OperationStatus#SUCCESS OperationStatus.SUCCESS}.
626      *
627      * @throws OperationFailureException if one of the <a
628      * href="../je/OperationFailureException.html#writeFailures">Write
629      * Operation Failures</a> occurs.
630      *
631      * @throws EnvironmentFailureException if an unexpected, internal or
632      * environment-wide failure occurs.
633      *
634      * @throws UnsupportedOperationException if the database is transactional
635      * but this cursor was not opened with a non-null transaction parameter,
636      * or the database is read-only.
637      *
638      * @throws IllegalStateException if the cursor or database has been closed,
639      * or the cursor is uninitialized (not positioned on a record), or the
640      * non-transactional cursor was created in a different thread.
641      */
delete()642     public OperationStatus delete()
643         throws LockConflictException,
644                DatabaseException,
645                UnsupportedOperationException {
646 
647         checkState(true);
648         trace(Level.FINEST, "Cursor.delete: ", null);
649         if (thrput != null) {
650             thrput.increment(ThroughputStatGroup.CURSOR_DELETE_OFFSET);
651         }
652         return deleteInternal(dbImpl.getRepContext());
653     }
654 
655     /**
656      * Stores a key/data pair into the database.
657      *
658      * <p>If the put method succeeds, the cursor is always positioned to refer
659      * to the newly inserted item.</p>
660      *
661      * <p>If the key already appears in the database and duplicates are
662      * supported, the new data value is inserted at the correct sorted
663      * location, unless the new data value also appears in the database
664      * already. In the later case, although the given key/data pair compares
665      * equal to an existing key/data pair, the two records may not be identical
666      * if custom comparators are used, in which case the existing record will
667      * be replaced with the new record. If the key already appears in the
668      * database and duplicates are not supported, the data associated with
669      * the key will be replaced.</p>
670      *
671      * @param key the key {@link com.sleepycat.je.DatabaseEntry
672      * DatabaseEntry} operated on.
673      *
674      * @param data the data {@link com.sleepycat.je.DatabaseEntry
675      * DatabaseEntry} stored.
676      *
677      * @return an OperationStatus for the operation.
678      *
679      * @throws OperationFailureException if one of the <a
680      * href="../je/OperationFailureException.html#writeFailures">Write
681      * Operation Failures</a> occurs.
682      *
683      * @throws EnvironmentFailureException if an unexpected, internal or
684      * environment-wide failure occurs.
685      *
686      * @throws UnsupportedOperationException if the database is transactional
687      * but this cursor was not opened with a non-null transaction parameter,
688      * or the database is read-only.
689      *
690      * @throws IllegalStateException if the cursor or database has been closed,
691      * or the non-transactional cursor was created in a different thread.
692      *
693      * @throws IllegalArgumentException if an invalid parameter is specified.
694      */
put( final DatabaseEntry key, final DatabaseEntry data)695     public OperationStatus put(
696         final DatabaseEntry key,
697         final DatabaseEntry data)
698         throws DatabaseException, UnsupportedOperationException {
699 
700         checkState(false);
701         DatabaseUtil.checkForNullDbt(key, "key", true);
702         DatabaseUtil.checkForNullDbt(data, "data", true);
703         DatabaseUtil.checkForPartialKey(key);
704         trace(Level.FINEST, "Cursor.put: ", key, data, null);
705         if (thrput != null) {
706             thrput.increment(ThroughputStatGroup.CURSOR_PUT_OFFSET);
707         }
708         return putInternal(key, data, PutMode.OVERWRITE);
709     }
710 
711     /**
712      * Stores a key/data pair into the database.
713      *
714      * <p>If the putNoOverwrite method succeeds, the cursor is always
715      * positioned to refer to the newly inserted item.</p>
716      *
717      * <p>If the key already appears in the database, putNoOverwrite will
718      * return {@link com.sleepycat.je.OperationStatus#KEYEXIST
719      * OperationStatus.KEYEXIST}.</p>
720      *
721      * @param key the key {@link com.sleepycat.je.DatabaseEntry
722      * DatabaseEntry} operated on.
723      *
724      * @param data the data {@link com.sleepycat.je.DatabaseEntry
725      * DatabaseEntry} stored.
726      *
727      * @return an OperationStatus for the operation.
728      *
729      * @throws OperationFailureException if one of the <a
730      * href="../je/OperationFailureException.html#writeFailures">Write
731      * Operation Failures</a> occurs.
732      *
733      * @throws EnvironmentFailureException if an unexpected, internal or
734      * environment-wide failure occurs.
735      *
736      * @throws UnsupportedOperationException if the database is transactional
737      * but this cursor was not opened with a non-null transaction parameter,
738      * or the database is read-only.
739      *
740      * @throws IllegalStateException if the cursor or database has been closed,
741      * or the non-transactional cursor was created in a different thread.
742      *
743      * @throws IllegalArgumentException if an invalid parameter is specified.
744      */
putNoOverwrite( final DatabaseEntry key, final DatabaseEntry data)745     public OperationStatus putNoOverwrite(
746         final DatabaseEntry key,
747         final DatabaseEntry data)
748         throws DatabaseException, UnsupportedOperationException {
749 
750         checkState(false);
751         DatabaseUtil.checkForNullDbt(key, "key", true);
752         DatabaseUtil.checkForNullDbt(data, "data", true);
753         DatabaseUtil.checkForPartialKey(key);
754         trace(Level.FINEST, "Cursor.putNoOverwrite: ", key, data, null);
755         if (thrput != null) {
756             thrput.increment(ThroughputStatGroup.CURSOR_PUTNOOVERWRITE_OFFSET);
757         }
758         return putInternal(key, data, PutMode.NO_OVERWRITE);
759     }
760 
761     /**
762      * Stores a key/data pair into the database. The database must be
763      * configured for duplicates.
764      *
765      * <p>If the putNoDupData method succeeds, the cursor is always positioned
766      * to refer to the newly inserted item.</p>
767      *
768      * <p>Insert the specified key/data pair into the database, unless a
769      * key/data pair comparing equally to it already exists in the database.
770      * If a matching key/data pair already exists in the database, {@link
771      * com.sleepycat.je.OperationStatus#KEYEXIST OperationStatus.KEYEXIST} is
772      * returned.</p>
773      *
774      * @param key the key {@link com.sleepycat.je.DatabaseEntry DatabaseEntry}
775      * operated on.
776      *
777      * @param data the data {@link com.sleepycat.je.DatabaseEntry
778      * DatabaseEntry} stored.
779      *
780      * @return an OperationStatus for the operation.
781      *
782      * @throws OperationFailureException if one of the <a
783      * href="../je/OperationFailureException.html#writeFailures">Write
784      * Operation Failures</a> occurs.
785      *
786      * @throws EnvironmentFailureException if an unexpected, internal or
787      * environment-wide failure occurs.
788      *
789      * @throws UnsupportedOperationException if the database is transactional
790      * but this cursor was not opened with a non-null transaction parameter, or
791      * the database is read-only, or the database is not configured for
792      * duplicates.
793      *
794      * @throws IllegalStateException if the cursor or database has been closed,
795      * or the non-transactional cursor was created in a different thread.
796      *
797      * @throws IllegalArgumentException if an invalid parameter is specified.
798      */
putNoDupData( final DatabaseEntry key, final DatabaseEntry data)799     public OperationStatus putNoDupData(
800         final DatabaseEntry key,
801         final DatabaseEntry data)
802         throws DatabaseException, UnsupportedOperationException {
803 
804         checkState(false);
805         DatabaseUtil.checkForNullDbt(key, "key", true);
806         DatabaseUtil.checkForNullDbt(data, "data", true);
807         DatabaseUtil.checkForPartialKey(key);
808         trace(Level.FINEST, "Cursor.putNoDupData: ", key, data, null);
809         if (thrput != null) {
810             thrput.increment(ThroughputStatGroup.CURSOR_PUTNODUPDATA_OFFSET);
811         }
812         return putInternal(key, data, PutMode.NO_DUP_DATA);
813     }
814 
815     /**
816      * Replaces the data in the key/data pair at the current cursor position.
817      *
818      * <p>Overwrite the data of the key/data pair to which the cursor refers
819      * with the specified data item. This method will return
820      * OperationStatus.NOTFOUND if the cursor currently refers to an
821      * already-deleted key/data pair.</p>
822      *
823      * <p>For a database that does not support duplicates, the data may be
824      * changed by this method.  If duplicates are supported, the data may be
825      * changed only if a custom partial comparator is configured and the
826      * comparator considers the old and new data to be equal (that is, the
827      * comparator returns zero).  For more information on partial comparators
828      * see {@link DatabaseConfig#setDuplicateComparator}.</p>
829      *
830      * <p>If the old and new data are unequal according to the comparator, a
831      * {@link DuplicateDataException} is thrown.  Changing the data in this
832      * case would change the sort order of the record, which would change the
833      * cursor position, and this is not allowed.  To change the sort order of a
834      * record, delete it and then re-insert it.</p>
835      *
836      * @param data - the data DatabaseEntry stored.
837      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
838      * specified to optimize for partial data update.
839      *
840      * @return {@link com.sleepycat.je.OperationStatus#KEYEMPTY
841      * OperationStatus.KEYEMPTY} if the key/pair at the cursor position has
842      * been deleted; otherwise, {@link
843      * com.sleepycat.je.OperationStatus#SUCCESS OperationStatus.SUCCESS}.
844      *
845      * @throws DuplicateDataException if the old and new data are not equal
846      * according to the configured duplicate comparator or default comparator.
847      *
848      * @throws OperationFailureException if one of the <a
849      * href="../je/OperationFailureException.html#writeFailures">Write
850      * Operation Failures</a> occurs.
851      *
852      * @throws EnvironmentFailureException if an unexpected, internal or
853      * environment-wide failure occurs.
854      *
855      * @throws UnsupportedOperationException if the database is transactional
856      * but this cursor was not opened with a non-null transaction parameter,
857      * or the database is read-only.
858      *
859      * @throws IllegalStateException if the cursor or database has been closed,
860      * or the cursor is uninitialized (not positioned on a record), or the
861      * non-transactional cursor was created in a different thread.
862      *
863      * @throws IllegalArgumentException if an invalid parameter is specified.
864      */
putCurrent(final DatabaseEntry data)865     public OperationStatus putCurrent(final DatabaseEntry data)
866         throws DatabaseException, UnsupportedOperationException {
867 
868         checkState(true);
869         DatabaseUtil.checkForNullDbt(data, "data", true);
870         trace(Level.FINEST, "Cursor.putCurrent: ", null, data, null);
871         if (thrput != null) {
872             thrput.increment(ThroughputStatGroup.CURSOR_PUTCURRENT_OFFSET);
873         }
874         return putInternal(null /*key*/, data, PutMode.CURRENT);
875     }
876 
877     /**
878      * Returns the key/data pair to which the cursor refers.
879      *
880      * <p>In a replicated environment, an explicit transaction must have been
881      * specified when opening the cursor, unless read-uncommitted isolation is
882      * specified via the {@link CursorConfig} or {@link LockMode}
883      * parameter.</p>
884      *
885      * @param key the key returned as output.  Its byte array does not need to
886      * be initialized by the caller.
887      *
888      * @param data the data returned as output.  Its byte array does not need
889      * to be initialized by the caller.
890      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
891      * specified to optimize for key only or partial data retrieval.
892      *
893      * @param lockMode the locking attributes; if null, default attributes are
894      * used. {@link LockMode#READ_COMMITTED} is not allowed.
895      *
896      * @return {@link com.sleepycat.je.OperationStatus#KEYEMPTY
897      * OperationStatus.KEYEMPTY} if the key/pair at the cursor position has
898      * been deleted; otherwise, {@link
899      * com.sleepycat.je.OperationStatus#SUCCESS OperationStatus.SUCCESS}.
900      *
901      * @throws OperationFailureException if one of the <a
902      * href="OperationFailureException.html#readFailures">Read Operation
903      * Failures</a> occurs.
904      *
905      * @throws IllegalStateException if the cursor or database has been closed,
906      * or the cursor is uninitialized (not positioned on a record), or the
907      * non-transactional cursor was created in a different thread.
908      *
909      * @throws IllegalArgumentException if an invalid parameter is specified.
910      */
getCurrent( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)911     public OperationStatus getCurrent(
912         final DatabaseEntry key,
913         final DatabaseEntry data,
914         final LockMode lockMode)
915         throws DatabaseException {
916 
917         try {
918             checkState(true);
919             checkArgsNoValRequired(key, data);
920             trace(Level.FINEST, "Cursor.getCurrent: ", lockMode);
921             if (thrput != null) {
922                 thrput.increment(ThroughputStatGroup.CURSOR_GETCURRENT_OFFSET);
923             }
924 
925             return getCurrentInternal(key, data, lockMode);
926         } catch (Error E) {
927             dbImpl.getEnv().invalidate(E);
928             throw E;
929         }
930     }
931 
932     /**
933      * Moves the cursor to the first key/data pair of the database, and returns
934      * that pair.  If the first key has duplicate values, the first data item
935      * in the set of duplicates is returned.
936      *
937      * <p>In a replicated environment, an explicit transaction must have been
938      * specified when opening the cursor, unless read-uncommitted isolation is
939      * specified via the {@link CursorConfig} or {@link LockMode}
940      * parameter.</p>
941      *
942      * @param key the key returned as output.  Its byte array does not need to
943      * be initialized by the caller.
944      *
945      * @param data the data returned as output.  Its byte array does not need
946      * to be initialized by the caller.
947      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
948      * specified to optimize for key only or partial data retrieval.
949      *
950      * @param lockMode the locking attributes; if null, default attributes are
951      * used. {@link LockMode#READ_COMMITTED} is not allowed.
952      *
953      * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND
954      * OperationStatus.NOTFOUND} if no matching key/data pair is found;
955      * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS
956      * OperationStatus.SUCCESS}.
957      *
958      * @throws OperationFailureException if one of the <a
959      * href="OperationFailureException.html#readFailures">Read Operation
960      * Failures</a> occurs.
961      *
962      * @throws EnvironmentFailureException if an unexpected, internal or
963      * environment-wide failure occurs.
964      *
965      * @throws IllegalStateException if the cursor or database has been closed,
966      * or the non-transactional cursor was created in a different thread.
967      *
968      * @throws IllegalArgumentException if an invalid parameter is specified.
969      */
getFirst( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)970     public OperationStatus getFirst(
971         final DatabaseEntry key,
972         final DatabaseEntry data,
973         final LockMode lockMode)
974         throws DatabaseException {
975 
976         checkState(false);
977         checkArgsNoValRequired(key, data);
978         trace(Level.FINEST, "Cursor.getFirst: ", lockMode);
979         if (thrput != null) {
980             thrput.increment(ThroughputStatGroup.CURSOR_GETFIRST_OFFSET);
981         }
982         return position(key, data, lockMode, true);
983     }
984 
985     /**
986      * Moves the cursor to the last key/data pair of the database, and returns
987      * that pair.  If the last key has duplicate values, the last data item in
988      * the set of duplicates is returned.
989      *
990      * <p>In a replicated environment, an explicit transaction must have been
991      * specified when opening the cursor, unless read-uncommitted isolation is
992      * specified via the {@link CursorConfig} or {@link LockMode}
993      * parameter.</p>
994      *
995      * @param key the key returned as output.  Its byte array does not need to
996      * be initialized by the caller.
997      *
998      * @param data the data returned as output.  Its byte array does not need
999      * to be initialized by the caller.
1000      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
1001      * specified to optimize for key only or partial data retrieval.
1002      *
1003      * @param lockMode the locking attributes; if null, default attributes are
1004      * used. {@link LockMode#READ_COMMITTED} is not allowed.
1005      *
1006      * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND
1007      * OperationStatus.NOTFOUND} if no matching key/data pair is found;
1008      * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS
1009      * OperationStatus.SUCCESS}.
1010      *
1011      * @throws OperationFailureException if one of the <a
1012      * href="OperationFailureException.html#readFailures">Read Operation
1013      * Failures</a> occurs.
1014      *
1015      * @throws EnvironmentFailureException if an unexpected, internal or
1016      * environment-wide failure occurs.
1017      *
1018      * @throws IllegalStateException if the cursor or database has been closed,
1019      * or the non-transactional cursor was created in a different thread.
1020      *
1021      * @throws IllegalArgumentException if an invalid parameter is specified.
1022      */
getLast( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1023     public OperationStatus getLast(
1024         final DatabaseEntry key,
1025         final DatabaseEntry data,
1026         final LockMode lockMode)
1027         throws DatabaseException {
1028 
1029         checkState(false);
1030         checkArgsNoValRequired(key, data);
1031         trace(Level.FINEST, "Cursor.getLast: ", lockMode);
1032         if (thrput != null) {
1033             thrput.increment(ThroughputStatGroup.CURSOR_GETLAST_OFFSET);
1034         }
1035         return position(key, data, lockMode, false);
1036     }
1037 
1038     /**
1039      * Moves the cursor to the next key/data pair and returns that pair.
1040      *
1041      * <p>If the cursor is not yet initialized, move the cursor to the first
1042      * key/data pair of the database, and return that pair.  Otherwise, the
1043      * cursor is moved to the next key/data pair of the database, and that pair
1044      * is returned.  In the presence of duplicate key values, the value of the
1045      * key may not change.</p>
1046      *
1047      * <p>In a replicated environment, an explicit transaction must have been
1048      * specified when opening the cursor, unless read-uncommitted isolation is
1049      * specified via the {@link CursorConfig} or {@link LockMode}
1050      * parameter.</p>
1051      *
1052      * @param key the key returned as output.  Its byte array does not need to
1053      * be initialized by the caller.
1054      *
1055      * @param data the data returned as output.  Its byte array does not need
1056      * to be initialized by the caller.
1057      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
1058      * specified to optimize for key only or partial data retrieval.
1059      *
1060      * @param lockMode the locking attributes; if null, default attributes are
1061      * used. {@link LockMode#READ_COMMITTED} is not allowed.
1062      *
1063      * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND
1064      * OperationStatus.NOTFOUND} if no matching key/data pair is found;
1065      * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS
1066      * OperationStatus.SUCCESS}.
1067      *
1068      * @throws OperationFailureException if one of the <a
1069      * href="OperationFailureException.html#readFailures">Read Operation
1070      * Failures</a> occurs.
1071      *
1072      * @throws EnvironmentFailureException if an unexpected, internal or
1073      * environment-wide failure occurs.
1074      *
1075      * @throws IllegalStateException if the cursor or database has been closed,
1076      * or the non-transactional cursor was created in a different thread.
1077      *
1078      * @throws IllegalArgumentException if an invalid parameter is specified.
1079      */
getNext( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1080     public OperationStatus getNext(
1081         final DatabaseEntry key,
1082         final DatabaseEntry data,
1083         final LockMode lockMode)
1084         throws DatabaseException {
1085 
1086         checkState(false);
1087         checkArgsNoValRequired(key, data);
1088         trace(Level.FINEST, "Cursor.getNext: ", lockMode);
1089         if (thrput != null) {
1090             thrput.increment(ThroughputStatGroup.CURSOR_GETNEXT_OFFSET);
1091         }
1092         if (cursorImpl.isNotInitialized()) {
1093             return position(key, data, lockMode, true);
1094         } else {
1095             return retrieveNext(key, data, lockMode, GetMode.NEXT);
1096         }
1097     }
1098 
1099     /**
1100      * If the next key/data pair of the database is a duplicate data record for
1101      * the current key/data pair, moves the cursor to the next key/data pair of
1102      * the database and returns that pair.
1103      *
1104      * <p>In a replicated environment, an explicit transaction must have been
1105      * specified when opening the cursor, unless read-uncommitted isolation is
1106      * specified via the {@link CursorConfig} or {@link LockMode}
1107      * parameter.</p>
1108      *
1109      * @param key the key returned as output.  Its byte array does not need to
1110      * be initialized by the caller.
1111      *
1112      * @param data the data returned as output.  Its byte array does not need
1113      * to be initialized by the caller.
1114      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
1115      * specified to optimize for key only or partial data retrieval.
1116      *
1117      * @param lockMode the locking attributes; if null, default attributes are
1118      * used. {@link LockMode#READ_COMMITTED} is not allowed.
1119      *
1120      * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND
1121      * OperationStatus.NOTFOUND} if no matching key/data pair is found;
1122      * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS
1123      * OperationStatus.SUCCESS}.
1124      *
1125      * @throws OperationFailureException if one of the <a
1126      * href="OperationFailureException.html#readFailures">Read Operation
1127      * Failures</a> occurs.
1128      *
1129      * @throws EnvironmentFailureException if an unexpected, internal or
1130      * environment-wide failure occurs.
1131      *
1132      * @throws IllegalStateException if the cursor or database has been closed,
1133      * or the cursor is uninitialized (not positioned on a record), or the
1134      * non-transactional cursor was created in a different thread.
1135      *
1136      * @throws IllegalArgumentException if an invalid parameter is specified.
1137      */
getNextDup( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1138     public OperationStatus getNextDup(
1139         final DatabaseEntry key,
1140         final DatabaseEntry data,
1141         final LockMode lockMode)
1142         throws DatabaseException {
1143 
1144         checkState(true);
1145         checkArgsNoValRequired(key, data);
1146         trace(Level.FINEST, "Cursor.getNextDup: ", lockMode);
1147         if (thrput != null) {
1148             thrput.increment(ThroughputStatGroup.CURSOR_GETNEXTDUP_OFFSET);
1149         }
1150         return retrieveNext(key, data, lockMode, GetMode.NEXT_DUP);
1151     }
1152 
1153     /**
1154      * Moves the cursor to the next non-duplicate key/data pair and returns
1155      * that pair.  If the matching key has duplicate values, the first data
1156      * item in the set of duplicates is returned.
1157      *
1158      * <p>If the cursor is not yet initialized, move the cursor to the first
1159      * key/data pair of the database, and return that pair.  Otherwise, the
1160      * cursor is moved to the next non-duplicate key of the database, and that
1161      * key/data pair is returned.</p>
1162      *
1163      * <p>In a replicated environment, an explicit transaction must have been
1164      * specified when opening the cursor, unless read-uncommitted isolation is
1165      * specified via the {@link CursorConfig} or {@link LockMode}
1166      * parameter.</p>
1167      *
1168      * @param key the key returned as output.  Its byte array does not need to
1169      * be initialized by the caller.
1170      *
1171      * @param data the data returned as output.  Its byte array does not need
1172      * to be initialized by the caller.
1173      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
1174      * specified to optimize for key only or partial data retrieval.
1175      *
1176      * @param lockMode the locking attributes; if null, default attributes are
1177      * used. {@link LockMode#READ_COMMITTED} is not allowed.
1178      *
1179      * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND
1180      * OperationStatus.NOTFOUND} if no matching key/data pair is found;
1181      * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS
1182      * OperationStatus.SUCCESS}.
1183      *
1184      * @throws OperationFailureException if one of the <a
1185      * href="OperationFailureException.html#readFailures">Read Operation
1186      * Failures</a> occurs.
1187      *
1188      * @throws EnvironmentFailureException if an unexpected, internal or
1189      * environment-wide failure occurs.
1190      *
1191      * @throws IllegalStateException if the cursor or database has been closed,
1192      * or the non-transactional cursor was created in a different thread.
1193      *
1194      * @throws IllegalArgumentException if an invalid parameter is specified.
1195      */
getNextNoDup( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1196     public OperationStatus getNextNoDup(
1197         final DatabaseEntry key,
1198         final DatabaseEntry data,
1199         final LockMode lockMode)
1200         throws DatabaseException {
1201 
1202         checkState(false);
1203         checkArgsNoValRequired(key, data);
1204         trace(Level.FINEST, "Cursor.getNextNoDup: ", lockMode);
1205         if (thrput != null) {
1206             thrput.increment(ThroughputStatGroup.CURSOR_GETNEXTNODUP_OFFSET);
1207         }
1208         if (cursorImpl.isNotInitialized()) {
1209             return position(key, data, lockMode, true);
1210         } else {
1211             return retrieveNext(key, data, lockMode, GetMode.NEXT_NODUP);
1212         }
1213     }
1214 
1215     /**
1216      * Moves the cursor to the previous key/data pair and returns that pair.
1217      *
1218      * <p>If the cursor is not yet initialized, move the cursor to the last
1219      * key/data pair of the database, and return that pair.  Otherwise, the
1220      * cursor is moved to the previous key/data pair of the database, and that
1221      * pair is returned. In the presence of duplicate key values, the value of
1222      * the key may not change.</p>
1223      *
1224      * <p>In a replicated environment, an explicit transaction must have been
1225      * specified when opening the cursor, unless read-uncommitted isolation is
1226      * specified via the {@link CursorConfig} or {@link LockMode}
1227      * parameter.</p>
1228      *
1229      * @param key the key returned as output.  Its byte array does not need to
1230      * be initialized by the caller.
1231      *
1232      * @param data the data returned as output.  Its byte array does not need
1233      * to be initialized by the caller.
1234      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
1235      * specified to optimize for key only or partial data retrieval.
1236      *
1237      * @param lockMode the locking attributes; if null, default attributes are
1238      * used. {@link LockMode#READ_COMMITTED} is not allowed.
1239      *
1240      * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND
1241      * OperationStatus.NOTFOUND} if no matching key/data pair is found;
1242      * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS
1243      * OperationStatus.SUCCESS}.
1244      *
1245      * @throws OperationFailureException if one of the <a
1246      * href="OperationFailureException.html#readFailures">Read Operation
1247      * Failures</a> occurs.
1248      *
1249      * @throws EnvironmentFailureException if an unexpected, internal or
1250      * environment-wide failure occurs.
1251      *
1252      * @throws IllegalStateException if the cursor or database has been closed,
1253      * or the non-transactional cursor was created in a different thread.
1254      *
1255      * @throws IllegalArgumentException if an invalid parameter is specified.
1256      */
getPrev( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1257     public OperationStatus getPrev(
1258         final DatabaseEntry key,
1259         final DatabaseEntry data,
1260         final LockMode lockMode)
1261         throws DatabaseException {
1262 
1263         checkState(false);
1264         checkArgsNoValRequired(key, data);
1265         trace(Level.FINEST, "Cursor.getPrev: ", lockMode);
1266         if (thrput != null) {
1267             thrput.increment(ThroughputStatGroup.CURSOR_GETPREV_OFFSET);
1268         }
1269         if (cursorImpl.isNotInitialized()) {
1270             return position(key, data, lockMode, false);
1271         } else {
1272             return retrieveNext(key, data, lockMode, GetMode.PREV);
1273         }
1274     }
1275 
1276     /**
1277      * If the previous key/data pair of the database is a duplicate data record
1278      * for the current key/data pair, moves the cursor to the previous key/data
1279      * pair of the database and returns that pair.
1280      *
1281      * <p>In a replicated environment, an explicit transaction must have been
1282      * specified when opening the cursor, unless read-uncommitted isolation is
1283      * specified via the {@link CursorConfig} or {@link LockMode}
1284      * parameter.</p>
1285      *
1286      * @param key the key returned as output.  Its byte array does not need to
1287      * be initialized by the caller.
1288      *
1289      * @param data the data returned as output.  Its byte array does not need
1290      * to be initialized by the caller.
1291      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
1292      * specified to optimize for key only or partial data retrieval.
1293      *
1294      * @param lockMode the locking attributes; if null, default attributes are
1295      * used. {@link LockMode#READ_COMMITTED} is not allowed.
1296      *
1297      * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND
1298      * OperationStatus.NOTFOUND} if no matching key/data pair is found;
1299      * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS
1300      * OperationStatus.SUCCESS}.
1301      *
1302      * @throws OperationFailureException if one of the <a
1303      * href="OperationFailureException.html#readFailures">Read Operation
1304      * Failures</a> occurs.
1305      *
1306      * @throws EnvironmentFailureException if an unexpected, internal or
1307      * environment-wide failure occurs.
1308      *
1309      * @throws IllegalStateException if the cursor or database has been closed,
1310      * or the cursor is uninitialized (not positioned on a record), or the
1311      * non-transactional cursor was created in a different thread.
1312      *
1313      * @throws IllegalArgumentException if an invalid parameter is specified.
1314      */
getPrevDup( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1315     public OperationStatus getPrevDup(
1316         final DatabaseEntry key,
1317         final DatabaseEntry data,
1318         final LockMode lockMode)
1319         throws DatabaseException {
1320 
1321         checkState(true);
1322         checkArgsNoValRequired(key, data);
1323         trace(Level.FINEST, "Cursor.getPrevDup: ", lockMode);
1324         if (thrput != null) {
1325             thrput.increment(ThroughputStatGroup.CURSOR_GETPREVDUP_OFFSET);
1326         }
1327         return retrieveNext(key, data, lockMode, GetMode.PREV_DUP);
1328     }
1329 
1330     /**
1331      * Moves the cursor to the previous non-duplicate key/data pair and returns
1332      * that pair.  If the matching key has duplicate values, the last data item
1333      * in the set of duplicates is returned.
1334      *
1335      * <p>If the cursor is not yet initialized, move the cursor to the last
1336      * key/data pair of the database, and return that pair.  Otherwise, the
1337      * cursor is moved to the previous non-duplicate key of the database, and
1338      * that key/data pair is returned.</p>
1339      *
1340      * <p>In a replicated environment, an explicit transaction must have been
1341      * specified when opening the cursor, unless read-uncommitted isolation is
1342      * specified via the {@link CursorConfig} or {@link LockMode}
1343      * parameter.</p>
1344      *
1345      * @param key the key returned as output.  Its byte array does not need to
1346      * be initialized by the caller.
1347      *
1348      * @param data the data returned as output.  Its byte array does not need
1349      * to be initialized by the caller.
1350      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
1351      * specified to optimize for key only or partial data retrieval.
1352      *
1353      * @param lockMode the locking attributes; if null, default attributes are
1354      * used. {@link LockMode#READ_COMMITTED} is not allowed.
1355      *
1356      * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND
1357      * OperationStatus.NOTFOUND} if no matching key/data pair is found;
1358      * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS
1359      * OperationStatus.SUCCESS}.
1360      *
1361      * @throws OperationFailureException if one of the <a
1362      * href="OperationFailureException.html#readFailures">Read Operation
1363      * Failures</a> occurs.
1364      *
1365      * @throws EnvironmentFailureException if an unexpected, internal or
1366      * environment-wide failure occurs.
1367      *
1368      * @throws IllegalStateException if the cursor or database has been closed,
1369      * or the non-transactional cursor was created in a different thread.
1370      *
1371      * @throws IllegalArgumentException if an invalid parameter is specified.
1372      */
getPrevNoDup( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1373     public OperationStatus getPrevNoDup(
1374         final DatabaseEntry key,
1375         final DatabaseEntry data,
1376         final LockMode lockMode)
1377         throws DatabaseException {
1378 
1379         checkState(false);
1380         checkArgsNoValRequired(key, data);
1381         trace(Level.FINEST, "Cursor.getPrevNoDup: ", lockMode);
1382         if (thrput != null) {
1383             thrput.increment(ThroughputStatGroup.CURSOR_GETPREVNODUP_OFFSET);
1384         }
1385         if (cursorImpl.isNotInitialized()) {
1386             return position(key, data, lockMode, false);
1387         } else {
1388             return retrieveNext(key, data, lockMode, GetMode.PREV_NODUP);
1389         }
1390     }
1391 
1392     /**
1393      * Skips forward a given number of key/data pairs and returns the number by
1394      * which the cursor is moved.
1395      *
1396      * <p>Without regard to performance, calling this method is equivalent to
1397      * repeatedly calling {@link #getNext getNext} with {@link
1398      * LockMode#READ_UNCOMMITTED} to skip over the desired number of key/data
1399      * pairs, and then calling {@link #getCurrent getCurrent} with the {@code
1400      * lockMode} parameter to return the final key/data pair.</p>
1401      *
1402      * <p>With regard to performance, this method is optimized to skip over
1403      * key/value pairs using a smaller number of Btree operations.  When there
1404      * is no contention on the bottom internal nodes (BINs) and all BINs are in
1405      * cache, the number of Btree operations is reduced by roughly two orders
1406      * of magnitude, where the exact number depends on the {@link
1407      * EnvironmentConfig#NODE_MAX_ENTRIES} setting.  When there is contention
1408      * on BINs or fetching BINs is required, the scan is broken up into smaller
1409      * operations to avoid blocking other threads for long time periods.</p>
1410      *
1411      * <p>If the returned count is greater than zero, then the key/data pair at
1412      * the new cursor position is also returned.  If zero is returned, then
1413      * there are no key/value pairs that follow the cursor position and a
1414      * key/data pair is not returned.</p>
1415      *
1416      * <p>In a replicated environment, an explicit transaction must have been
1417      * specified when opening the cursor, unless read-uncommitted isolation is
1418      * specified via the {@link CursorConfig} or {@link LockMode}
1419      * parameter.</p>
1420      *
1421      * @param maxCount the maximum number of key/data pairs to skip, i.e., the
1422      * maximum number by which the cursor should be moved; must be greater
1423      * than zero.
1424      *
1425      * @param key the key returned as output.  Its byte array does not need to
1426      * be initialized by the caller.
1427      *
1428      * @param data the data returned as output.  Its byte array does not need
1429      * to be initialized by the caller.
1430      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
1431      * specified to optimize for key only or partial data retrieval.
1432      *
1433      * @param lockMode the locking attributes; if null, default attributes are
1434      * used. {@link LockMode#READ_COMMITTED} is not allowed.
1435      *
1436      * @return the number of key/data pairs skipped, i.e., the number by which
1437      * the cursor has moved; if zero is returned, the cursor position is
1438      * unchanged and the key/data pair is not returned.
1439      *
1440      * @throws OperationFailureException if one of the <a
1441      * href="OperationFailureException.html#readFailures">Read Operation
1442      * Failures</a> occurs.
1443      *
1444      * @throws EnvironmentFailureException if an unexpected, internal or
1445      * environment-wide failure occurs.
1446      *
1447      * @throws IllegalStateException if the cursor or database has been closed,
1448      * or the cursor is uninitialized (not positioned on a record), or the
1449      * non-transactional cursor was created in a different thread.
1450      *
1451      * @throws IllegalArgumentException if an invalid parameter is specified.
1452      */
skipNext( final long maxCount, final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1453     public long skipNext(
1454         final long maxCount,
1455         final DatabaseEntry key,
1456         final DatabaseEntry data,
1457         final LockMode lockMode)
1458         throws DatabaseException {
1459 
1460         checkState(true);
1461         if (maxCount <= 0) {
1462             throw new IllegalArgumentException("maxCount must be positive: " +
1463                                                maxCount);
1464         }
1465         trace(Level.FINEST, "Cursor.skipNext: ", lockMode);
1466 
1467         return skipInternal(maxCount, true /*forward*/, key, data, lockMode);
1468     }
1469 
1470     /**
1471      * Skips backward a given number of key/data pairs and returns the number
1472      * by which the cursor is moved.
1473      *
1474      * <p>Without regard to performance, calling this method is equivalent to
1475      * repeatedly calling {@link #getPrev getPrev} with {@link
1476      * LockMode#READ_UNCOMMITTED} to skip over the desired number of key/data
1477      * pairs, and then calling {@link #getCurrent getCurrent} with the {@code
1478      * lockMode} parameter to return the final key/data pair.</p>
1479      *
1480      * <p>With regard to performance, this method is optimized to skip over
1481      * key/value pairs using a smaller number of Btree operations.  When there
1482      * is no contention on the bottom internal nodes (BINs) and all BINs are in
1483      * cache, the number of Btree operations is reduced by roughly two orders
1484      * of magnitude, where the exact number depends on the {@link
1485      * EnvironmentConfig#NODE_MAX_ENTRIES} setting.  When there is contention
1486      * on BINs or fetching BINs is required, the scan is broken up into smaller
1487      * operations to avoid blocking other threads for long time periods.</p>
1488      *
1489      * <p>If the returned count is greater than zero, then the key/data pair at
1490      * the new cursor position is also returned.  If zero is returned, then
1491      * there are no key/value pairs that follow the cursor position and a
1492      * key/data pair is not returned.</p>
1493      *
1494      * <p>In a replicated environment, an explicit transaction must have been
1495      * specified when opening the cursor, unless read-uncommitted isolation is
1496      * specified via the {@link CursorConfig} or {@link LockMode}
1497      * parameter.</p>
1498      *
1499      * @param maxCount the maximum number of key/data pairs to skip, i.e., the
1500      * maximum number by which the cursor should be moved; must be greater
1501      * than zero.
1502      *
1503      * @param key the key returned as output.  Its byte array does not need to
1504      * be initialized by the caller.
1505      *
1506      * @param data the data returned as output.  Its byte array does not need
1507      * to be initialized by the caller.
1508      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
1509      * specified to optimize for key only or partial data retrieval.
1510      *
1511      * @param lockMode the locking attributes; if null, default attributes are
1512      * used. {@link LockMode#READ_COMMITTED} is not allowed.
1513      *
1514      * @return the number of key/data pairs skipped, i.e., the number by which
1515      * the cursor has moved; if zero is returned, the cursor position is
1516      * unchanged and the key/data pair is not returned.
1517      *
1518      * @throws OperationFailureException if one of the <a
1519      * href="OperationFailureException.html#readFailures">Read Operation
1520      * Failures</a> occurs.
1521      *
1522      * @throws EnvironmentFailureException if an unexpected, internal or
1523      * environment-wide failure occurs.
1524      *
1525      * @throws IllegalStateException if the cursor or database has been closed,
1526      * or the cursor is uninitialized (not positioned on a record), or the
1527      * non-transactional cursor was created in a different thread.
1528      *
1529      * @throws IllegalArgumentException if an invalid parameter is specified.
1530      */
skipPrev( final long maxCount, final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1531     public long skipPrev(
1532         final long maxCount,
1533         final DatabaseEntry key,
1534         final DatabaseEntry data,
1535         final LockMode lockMode)
1536         throws DatabaseException {
1537 
1538         checkState(true);
1539         if (maxCount <= 0) {
1540             throw new IllegalArgumentException("maxCount must be positive: " +
1541                                                maxCount);
1542         }
1543         trace(Level.FINEST, "Cursor.skipPrev: ", lockMode);
1544 
1545         return skipInternal(maxCount, false /*forward*/, key, data, lockMode);
1546     }
1547 
1548     /**
1549      * Moves the cursor to the given key of the database, and returns the datum
1550      * associated with the given key.  If the matching key has duplicate
1551      * values, the first data item in the set of duplicates is returned.
1552      *
1553      * <p>In a replicated environment, an explicit transaction must have been
1554      * specified when opening the cursor, unless read-uncommitted isolation is
1555      * specified via the {@link CursorConfig} or {@link LockMode}
1556      * parameter.</p>
1557      *
1558      * @param key the key used as input.  It must be initialized with a
1559      * non-null byte array by the caller.
1560      *
1561      * @param data the data returned as output.  Its byte array does not need
1562      * to be initialized by the caller.
1563      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
1564      * specified to optimize for key only or partial data retrieval.
1565      *
1566      * @param lockMode the locking attributes; if null, default attributes are
1567      * used. {@link LockMode#READ_COMMITTED} is not allowed.
1568      *
1569      * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND
1570      * OperationStatus.NOTFOUND} if no matching key/data pair is found;
1571      * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS
1572      * OperationStatus.SUCCESS}.
1573      *
1574      * @throws OperationFailureException if one of the <a
1575      * href="OperationFailureException.html#readFailures">Read Operation
1576      * Failures</a> occurs.
1577      *
1578      * @throws EnvironmentFailureException if an unexpected, internal or
1579      * environment-wide failure occurs.
1580      *
1581      * @throws IllegalStateException if the cursor or database has been closed,
1582      * or the non-transactional cursor was created in a different thread.
1583      *
1584      * @throws IllegalArgumentException if an invalid parameter is specified.
1585      */
getSearchKey( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1586     public OperationStatus getSearchKey(
1587         final DatabaseEntry key,
1588         final DatabaseEntry data,
1589         final LockMode lockMode)
1590         throws DatabaseException {
1591 
1592         checkState(false);
1593         DatabaseUtil.checkForNullDbt(key, "key", true);
1594         DatabaseUtil.checkForNullDbt(data, "data", false);
1595         trace(Level.FINEST, "Cursor.getSearchKey: ", key, null, lockMode);
1596 
1597         return search(key, data, lockMode, SearchMode.SET);
1598     }
1599 
1600     /**
1601      * Moves the cursor to the closest matching key of the database, and
1602      * returns the data item associated with the matching key.  If the matching
1603      * key has duplicate values, the first data item in the set of duplicates
1604      * is returned.
1605      *
1606      * <p>The returned key/data pair is for the smallest key greater than or
1607      * equal to the specified key (as determined by the key comparison
1608      * function), permitting partial key matches and range searches.</p>
1609      *
1610      * <p>In a replicated environment, an explicit transaction must have been
1611      * specified when opening the cursor, unless read-uncommitted isolation is
1612      * specified via the {@link CursorConfig} or {@link LockMode}
1613      * parameter.</p>
1614      *
1615      * @param key the key used as input and returned as output.  It must be
1616      * initialized with a non-null byte array by the caller.
1617      * A <a href="Cursor.html#partialEntry">partial data item</a> may be
1618      * specified to optimize for key only or partial data retrieval.
1619      *
1620      * @param data the data returned as output.  Its byte array does not need
1621      * to be initialized by the caller.
1622      *
1623      * @param lockMode the locking attributes; if null, default attributes
1624      * are used. {@link LockMode#READ_COMMITTED} is not allowed.
1625      *
1626      * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND
1627      * OperationStatus.NOTFOUND} if no matching key/data pair is found;
1628      * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS
1629      * OperationStatus.SUCCESS}.
1630      *
1631      * @throws OperationFailureException if one of the <a
1632      * href="OperationFailureException.html#readFailures">Read Operation
1633      * Failures</a> occurs.
1634      *
1635      * @throws EnvironmentFailureException if an unexpected, internal or
1636      * environment-wide failure occurs.
1637      *
1638      * @throws IllegalStateException if the cursor or database has been closed,
1639      * or the non-transactional cursor was created in a different thread.
1640      *
1641      * @throws IllegalArgumentException if an invalid parameter is specified.
1642      */
getSearchKeyRange( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1643     public OperationStatus getSearchKeyRange(
1644         final DatabaseEntry key,
1645         final DatabaseEntry data,
1646         final LockMode lockMode)
1647         throws DatabaseException {
1648 
1649         checkState(false);
1650         DatabaseUtil.checkForNullDbt(key, "key", true);
1651         DatabaseUtil.checkForNullDbt(data, "data", false);
1652         trace(Level.FINEST, "Cursor.getSearchKeyRange: ", key, null, lockMode);
1653 
1654         return search(key, data, lockMode, SearchMode.SET_RANGE);
1655     }
1656 
1657     /**
1658      * Moves the cursor to the specified key/data pair, where both the key and
1659      * data items must match.
1660      *
1661      * <p>In a replicated environment, an explicit transaction must have been
1662      * specified when opening the cursor, unless read-uncommitted isolation is
1663      * specified via the {@link CursorConfig} or {@link LockMode}
1664      * parameter.</p>
1665      *
1666      * @param key the key used as input.  It must be initialized with a
1667      * non-null byte array by the caller.
1668      *
1669      * @param data the data used as input.  It must be initialized with a
1670      * non-null byte array by the caller.
1671      *
1672      * @param lockMode the locking attributes; if null, default attributes are
1673      * used. {@link LockMode#READ_COMMITTED} is not allowed.
1674      *
1675      * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND
1676      * OperationStatus.NOTFOUND} if no matching key/data pair is found;
1677      * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS
1678      * OperationStatus.SUCCESS}.
1679      *
1680      * @throws OperationFailureException if one of the <a
1681      * href="OperationFailureException.html#readFailures">Read Operation
1682      * Failures</a> occurs.
1683      *
1684      * @throws EnvironmentFailureException if an unexpected, internal or
1685      * environment-wide failure occurs.
1686      *
1687      * @throws IllegalStateException if the cursor or database has been closed,
1688      * or the non-transactional cursor was created in a different thread.
1689      *
1690      * @throws IllegalArgumentException if an invalid parameter is specified.
1691      */
getSearchBoth( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1692     public OperationStatus getSearchBoth(
1693         final DatabaseEntry key,
1694         final DatabaseEntry data,
1695         final LockMode lockMode)
1696         throws DatabaseException {
1697 
1698         checkState(false);
1699         checkArgsValRequired(key, data);
1700         trace(Level.FINEST, "Cursor.getSearchBoth: ", key, data, lockMode);
1701 
1702         return search(key, data, lockMode, SearchMode.BOTH);
1703     }
1704 
1705     /**
1706      * Moves the cursor to the specified key and closest matching data item of
1707      * the database.
1708      *
1709      * <p>In the case of any database supporting sorted duplicate sets, the
1710      * returned key/data pair is for the smallest data item greater than or
1711      * equal to the specified data item (as determined by the duplicate
1712      * comparison function), permitting partial matches and range searches in
1713      * duplicate data sets.</p>
1714      *
1715      * <p>In the case of databases that do not support sorted duplicate sets,
1716      * this method is equivalent to getSearchBoth.</p>
1717      *
1718      * <p>In a replicated environment, an explicit transaction must have been
1719      * specified when opening the cursor, unless read-uncommitted isolation is
1720      * specified via the {@link CursorConfig} or {@link LockMode}
1721      * parameter.</p>
1722      *
1723      * @param key the key used as input.  It must be initialized with a
1724      * non-null byte array by the caller.
1725      *
1726      * @param data the data used as input and returned as output.  It must be
1727      * initialized with a non-null byte array by the caller.
1728      *
1729      * @param lockMode the locking attributes; if null, default attributes are
1730      * used. {@link LockMode#READ_COMMITTED} is not allowed.
1731      *
1732      * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND
1733      * OperationStatus.NOTFOUND} if no matching key/data pair is found;
1734      * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS
1735      * OperationStatus.SUCCESS}.
1736      *
1737      * @throws OperationFailureException if one of the <a
1738      * href="OperationFailureException.html#readFailures">Read Operation
1739      * Failures</a> occurs.
1740      *
1741      * @throws EnvironmentFailureException if an unexpected, internal or
1742      * environment-wide failure occurs.
1743      *
1744      * @throws IllegalStateException if the cursor or database has been closed,
1745      * or the non-transactional cursor was created in a different thread.
1746      *
1747      * @throws IllegalArgumentException if an invalid parameter is specified.
1748      */
getSearchBothRange( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)1749     public OperationStatus getSearchBothRange(
1750         final DatabaseEntry key,
1751         final DatabaseEntry data,
1752         final LockMode lockMode)
1753         throws DatabaseException {
1754 
1755         checkState(false);
1756         checkArgsValRequired(key, data);
1757         trace(Level.FINEST, "Cursor.getSearchBothRange: ", key, data,
1758               lockMode);
1759 
1760         if (!dbImpl.getSortedDuplicates()) {
1761             return search(key, data, lockMode, SearchMode.BOTH);
1762         }
1763 
1764         return search(key, data, lockMode, SearchMode.BOTH_RANGE);
1765     }
1766 
1767     /**
1768      * Returns a count of the number of data items for the key to which the
1769      * cursor refers.
1770      *
1771      * <p>If the database is configured for duplicates, the database is scanned
1772      * internally, without taking any record locks, to count the number of
1773      * non-deleted entries.  Although the internal scan is more efficient under
1774      * some conditions, the result is the same as if a cursor were used to
1775      * iterate over the entries using {@link LockMode#READ_UNCOMMITTED}.</p>
1776      *
1777      * <p>If the database is not configured for duplicates, the count returned
1778      * is always zero or one, depending on the record at the cursor position is
1779      * deleted or not.</p>
1780      *
1781      * <p>The cost of this method is directly proportional to the number of
1782      * records scanned.</p>
1783      *
1784      * @return A count of the number of data items for the key to which the
1785      * cursor refers.
1786      *
1787      * @throws OperationFailureException if one of the <a
1788      * href="OperationFailureException.html#readFailures">Read Operation
1789      * Failures</a> occurs.
1790      *
1791      * @throws EnvironmentFailureException if an unexpected, internal or
1792      * environment-wide failure occurs.
1793      *
1794      * @throws IllegalStateException if the cursor or database has been closed,
1795      * or the cursor is uninitialized (not positioned on a record), or the
1796      * non-transactional cursor was created in a different thread.
1797      */
count()1798     public int count()
1799         throws DatabaseException {
1800 
1801         checkState(true);
1802         trace(Level.FINEST, "Cursor.count: ", null);
1803 
1804         return countInternal();
1805     }
1806 
1807     /**
1808      * Returns a rough estimate of the count of the number of data items for
1809      * the key to which the cursor refers.
1810      *
1811      * <p>If the database is configured for duplicates, a quick estimate of the
1812      * number of records is computed using information in the Btree.  Because
1813      * the Btree is unbalanced, in some cases the estimate may be off by a
1814      * factor of two or more.  The estimate is accurate when the number of
1815      * records is less than the configured {@link
1816      * DatabaseConfig#setNodeMaxEntries NodeMaxEntries}.</p>
1817      *
1818      * <p>If the database is not configured for duplicates, the count returned
1819      * is always zero or one, depending on the record at the cursor position is
1820      * deleted or not.</p>
1821      *
1822      * <p>The cost of this method is fixed, rather than being proportional to
1823      * the number of records scanned.  Because its accuracy is variable, this
1824      * method should normally be used when accuracy is not required, such as
1825      * for query optimization, and a fixed cost operation is needed. For
1826      * example, this method is used internally for determining the index
1827      * processing order in a {@link JoinCursor}.</p>
1828      *
1829      * @return an estimate of the count of the number of data items for the key
1830      * to which the cursor refers.
1831      *
1832      * @throws OperationFailureException if one of the <a
1833      * href="OperationFailureException.html#readFailures">Read Operation
1834      * Failures</a> occurs.
1835      *
1836      * @throws EnvironmentFailureException if an unexpected, internal or
1837      * environment-wide failure occurs.
1838      *
1839      * @throws IllegalStateException if the cursor or database has been closed,
1840      * or the cursor is uninitialized (not positioned on a record), or the
1841      * non-transactional cursor was created in a different thread.
1842      */
countEstimate()1843     public long countEstimate()
1844         throws DatabaseException {
1845 
1846         checkState(true);
1847         trace(Level.FINEST, "Cursor.countEstimate: ", null);
1848 
1849         return countEstimateInternal();
1850     }
1851 
1852     /**
1853      * Internal version of delete() that does no parameter checking.  Notify
1854      * triggers, update secondaries and enforce foreign key constraints.
1855      *
1856      * Note that this algorithm is duplicated in Database and Cursor for
1857      * efficiency reasons: in Cursor delete we must separately fetch the key
1858      * and data, while in Database delete we know the key and have to search
1859      * anyway so we can get the old data when we search.  The two algorithms
1860      * need to be kept in sync.
1861      */
deleteInternal(final ReplicationContext repContext)1862     OperationStatus deleteInternal(final ReplicationContext repContext) {
1863 
1864         checkUpdatesAllowed();
1865 
1866         final boolean hasUserTriggers = (dbImpl.getTriggers() != null);
1867         final boolean hasAssociations = (dbHandle != null) &&
1868             dbHandle.hasSecondaryOrForeignKeyAssociations();
1869 
1870         if (hasAssociations) {
1871             try {
1872                 dbImpl.getEnv().getSecondaryAssociationLock().
1873                     readLock().lockInterruptibly();
1874             } catch (InterruptedException e) {
1875                 throw new ThreadInterruptedException(
1876                     dbImpl.getEnv(), e);
1877             }
1878         }
1879         try {
1880             /* The key is needed if there are secondaries or triggers. */
1881             final DatabaseEntry key;
1882             if (hasAssociations || hasUserTriggers) {
1883                 key = new DatabaseEntry();
1884                 key.setData(cursorImpl.getCurrentKey());
1885             } else {
1886                 key = null;
1887             }
1888 
1889             /*
1890              * Get secondaries from the association and determine whether the
1891              * old data is needed.
1892              */
1893             final Collection<SecondaryDatabase> secondaries;
1894             final Collection<SecondaryDatabase> fkSecondaries;
1895             final boolean needOldData;
1896             if (hasAssociations) {
1897                 secondaries = dbHandle.secAssoc.getSecondaries(key);
1898                 fkSecondaries = dbHandle.foreignKeySecondaries;
1899                 needOldData = hasUserTriggers ||
1900                     SecondaryDatabase.needOldDataForDelete(secondaries);
1901             } else {
1902                 secondaries = null;
1903                 fkSecondaries = null;
1904                 needOldData = hasUserTriggers;
1905             }
1906 
1907             /*
1908              * Get old data if needed.  Even if the old data is not needed, if
1909              * there are associations we call getCurrentInternal with a null
1910              * oldData param to lock the record and find out if it's already
1911              * deleted; this must be done before calling onForeignKeyDelete or
1912              * updateSecondary.
1913              */
1914             final DatabaseEntry oldData =
1915                 needOldData ? (new DatabaseEntry()) : null;
1916 
1917             if (needOldData || hasAssociations) {
1918                 final OperationStatus status = getCurrentInternal(
1919                     key, oldData, LockMode.RMW);
1920 
1921                 if (status != OperationStatus.SUCCESS) {
1922                     return OperationStatus.KEYEMPTY;
1923                 }
1924             }
1925 
1926             /*
1927              * Enforce foreign key constraints before secondary updates, so
1928              * that ForeignKeyDeleteAction.ABORT is applied before deleting the
1929              * secondary keys.
1930              */
1931             final Locker locker = cursorImpl.getLocker();
1932             if (fkSecondaries != null) {
1933                 for (final SecondaryDatabase secDb : fkSecondaries) {
1934                     secDb.onForeignKeyDelete(locker, key);
1935                 }
1936             }
1937 
1938             /*
1939              * Update secondaries before actual deletion, so that a primary
1940              * record always exists while secondary keys refer to it.  This is
1941              * relied on by secondary read-uncommitted.
1942              */
1943             if (secondaries != null) {
1944                 for (final SecondaryDatabase secDb : secondaries) {
1945                     secDb.updateSecondary(locker, null, key, oldData, null);
1946                 }
1947             }
1948 
1949             /*
1950              * The actual deletion.
1951              */
1952             final OperationStatus deleteStatus = deleteNoNotify(repContext);
1953 
1954             if (deleteStatus != OperationStatus.SUCCESS) {
1955                 return deleteStatus;
1956             }
1957 
1958             /* Run triggers after actual deletion. */
1959             if (hasUserTriggers) {
1960                 TriggerManager.runDeleteTriggers(locker, dbImpl, key, oldData);
1961             }
1962 
1963             return OperationStatus.SUCCESS;
1964         } catch (Error E) {
1965             dbImpl.getEnv().invalidate(E);
1966             throw E;
1967         } finally {
1968             if (hasAssociations) {
1969                 dbImpl.getEnv().getSecondaryAssociationLock().
1970                     readLock().unlock();
1971             }
1972         }
1973     }
1974 
1975     /**
1976      * Delete at current position.   Does not notify triggers (does not perform
1977      * secondary updates).
1978      */
deleteNoNotify(final ReplicationContext repContext)1979     OperationStatus deleteNoNotify(final ReplicationContext repContext) {
1980 
1981         synchronized (getTxnSynchronizer()) {
1982             checkTxnState();
1983 
1984             /*
1985              * No need to use a dup cursor, since this operation does not
1986              * change the cursor position.
1987              */
1988             beginUseExistingCursor();
1989 
1990             final OperationStatus status = cursorImpl.deleteCurrentRecord(
1991                 repContext, thrput);
1992 
1993             endUseExistingCursor();
1994             return status;
1995        }
1996     }
1997 
1998     /**
1999      * Version of putInternal that allows passing an existing LN and does not
2000      * interpret duplicates.  Used for replication stream replay.  Notifies
2001      * triggers and prevents phantoms.
2002      */
putForReplay( final DatabaseEntry key, final DatabaseEntry data, final LN ln, final PutMode putMode, final ReplicationContext repContext)2003     OperationStatus putForReplay(
2004         final DatabaseEntry key,
2005         final DatabaseEntry data,
2006         final LN ln,
2007         final PutMode putMode,
2008         final ReplicationContext repContext) {
2009 
2010         synchronized (getTxnSynchronizer()) {
2011             checkTxnState();
2012             assert putMode != PutMode.CURRENT;
2013 
2014             return putNotify(key, data, ln, putMode, repContext);
2015         }
2016     }
2017 
2018     /**
2019      * Internal version of put that does no parameter checking.  Interprets
2020      * duplicates, notifies triggers, and prevents phantoms.
2021      */
putInternal( final DatabaseEntry key, final DatabaseEntry data, final PutMode putMode)2022     OperationStatus putInternal(
2023         final DatabaseEntry key,
2024         final DatabaseEntry data,
2025         final PutMode putMode) {
2026 
2027         checkUpdatesAllowed();
2028 
2029         synchronized (getTxnSynchronizer()) {
2030             checkTxnState();
2031 
2032             if (dbImpl.getSortedDuplicates()) {
2033                 return putHandleDups(key, data, putMode);
2034             }
2035 
2036             if (putMode == PutMode.NO_DUP_DATA) {
2037                 throw new UnsupportedOperationException(
2038                     "Database is not configured for duplicate data.");
2039             }
2040 
2041             return putNoDups(key, data, putMode);
2042         }
2043     }
2044 
2045     /**
2046      * Interpret duplicates for the various 'putXXX' operations.
2047      */
putHandleDups( final DatabaseEntry key, final DatabaseEntry data, final PutMode putMode)2048     private OperationStatus putHandleDups(
2049         final DatabaseEntry key,
2050         final DatabaseEntry data,
2051         final PutMode putMode) {
2052 
2053         switch (putMode) {
2054         case OVERWRITE:
2055             return dupsPutOverwrite(key, data);
2056         case NO_OVERWRITE:
2057             return dupsPutNoOverwrite(key, data);
2058         case NO_DUP_DATA:
2059             return dupsPutNoDupData(key, data);
2060         case CURRENT:
2061             return dupsPutCurrent(data);
2062         default:
2063             throw EnvironmentFailureException.unexpectedState(
2064                 putMode.toString());
2065         }
2066     }
2067 
2068     /**
2069      * Interpret duplicates for the put() operation.
2070      */
dupsPutOverwrite( final DatabaseEntry key, final DatabaseEntry data)2071     private OperationStatus dupsPutOverwrite(
2072         final DatabaseEntry key,
2073         final DatabaseEntry data) {
2074 
2075         final DatabaseEntry twoPartKey = DupKeyData.combine(key, data);
2076 
2077         return putNoDups(twoPartKey, EMPTY_DUP_DATA, PutMode.OVERWRITE);
2078     }
2079 
2080     /**
2081      * Interpret duplicates for putNoOverwrite() operation.
2082      *
2083      * The main purpose of this method is to guarantee that when two threads
2084      * call putNoOverwrite concurrently, only one of them will succeed. In
2085      * other words, if putNoOverwrite is called for all dup insertions, there
2086      * will always be at most one dup per key.
2087      *
2088      * Next key locking must be used to prevent two insertions, since there is
2089      * no other way to block an insertion of dup Y in another thread, while
2090      * inserting dup X in the current thread.  This is tested by AtomicPutTest.
2091      *
2092      * Although this method does extra searching and locking compared to
2093      * putNoOverwrite for a non-dup DB (or to putNoDupData for a dup DB), that
2094      * is not considered a significant issue because this method is rarely, if
2095      * ever, used by applications (for dup DBs that is).  It exists primarily
2096      * for compatibility with the DB core API.
2097      */
dupsPutNoOverwrite( final DatabaseEntry key, final DatabaseEntry data)2098     private OperationStatus dupsPutNoOverwrite(
2099         final DatabaseEntry key,
2100         final DatabaseEntry data) {
2101 
2102         final DatabaseEntry key2 = new DatabaseEntry();
2103         final DatabaseEntry data2 = new DatabaseEntry();
2104 
2105         final Cursor c = dup(false /*samePosition*/);
2106 
2107         try {
2108             c.setNonSticky(true);
2109 
2110             /* Lock next key (or EOF if none) exclusively, before we insert. */
2111             setEntry(key, key2);
2112 
2113             OperationStatus status = c.dupsGetSearchKeyRange(
2114                 key2, data2, LockMode.RMW);
2115 
2116             if (status == OperationStatus.SUCCESS && key.equals(key2)) {
2117                 /* Key exists, no need for further checks. */
2118                 return OperationStatus.KEYEXIST;
2119             }
2120             if (status != OperationStatus.SUCCESS) {
2121                 /* No next key exists, lock EOF. */
2122                 c.cursorImpl.lockEof(LockType.WRITE);
2123             }
2124 
2125             /* While next key is locked, check for key existence again. */
2126             setEntry(key, key2);
2127 
2128             status = c.dupsGetSearchKey(key2, data2, LockMode.RMW);
2129 
2130             if (status == OperationStatus.SUCCESS) {
2131                 return OperationStatus.KEYEXIST;
2132             }
2133 
2134             /* Insertion can safely be done now. */
2135             status = c.dupsPutNoDupData(key, data);
2136 
2137             if (status != OperationStatus.SUCCESS) {
2138                 return status;
2139             }
2140 
2141             /* We successfully inserted the first dup for the key. */
2142             swapCursor(c);
2143             return OperationStatus.SUCCESS;
2144         } finally {
2145             c.close();
2146         }
2147     }
2148 
2149     /**
2150      * Interpret duplicates for putNoDupData operation.
2151      */
dupsPutNoDupData( final DatabaseEntry key, final DatabaseEntry data)2152     private OperationStatus dupsPutNoDupData(
2153         final DatabaseEntry key,
2154         final DatabaseEntry data) {
2155 
2156         final DatabaseEntry twoPartKey = DupKeyData.combine(key, data);
2157 
2158         return putNoDups(twoPartKey, EMPTY_DUP_DATA, PutMode.NO_OVERWRITE);
2159     }
2160 
2161     /**
2162      * Interpret duplicates for putCurrent operation.
2163      *
2164      * Get old key/data, replace data portion, and put new key/data.
2165      *
2166      * Arguably we could skip the replacement if there is no user defined
2167      * comparison function and the new data is the same.
2168      */
dupsPutCurrent(final DatabaseEntry newData)2169     private OperationStatus dupsPutCurrent(final DatabaseEntry newData) {
2170 
2171         final DatabaseEntry oldTwoPartKey = new DatabaseEntry();
2172 
2173         /*
2174          * Lock the LSN of the current slot in WRITE mode and extract the
2175          * slot key.
2176          */
2177         final OperationStatus status = getCurrentNoDups(
2178             oldTwoPartKey, NO_RETURN_DATA, LockMode.RMW);
2179 
2180         if (status != OperationStatus.SUCCESS) {
2181             return status;
2182         }
2183 
2184         final DatabaseEntry key = new DatabaseEntry();
2185         DupKeyData.split(oldTwoPartKey, key, null);
2186 
2187         final DatabaseEntry newTwoPartKey = DupKeyData.combine(key, newData);
2188 
2189         return putNoDups(newTwoPartKey, EMPTY_DUP_DATA, PutMode.CURRENT);
2190     }
2191 
2192     /**
2193      * Eventually, all insertions/updates are happenning via this method.
2194      */
putNoDups( final DatabaseEntry key, final DatabaseEntry data, final PutMode putMode)2195     private OperationStatus putNoDups(
2196         final DatabaseEntry key,
2197         final DatabaseEntry data,
2198         final PutMode putMode) {
2199 
2200         final LN ln = (putMode == PutMode.CURRENT) ?
2201             null :
2202             LN.makeLN(dbImpl.getEnv(), data);
2203 
2204         return putNotify(key, data, ln, putMode, dbImpl.getRepContext());
2205     }
2206 
2207     /**
2208      * This single method is used for all put operations in order to notify
2209      * triggers and perform secondary updates in one place.  Prevents phantoms.
2210      * Does not interpret duplicates.
2211      *
2212      * WARNING: When the cursor has no Database handle, which is true when
2213      * called from the replication replayer, this method notifies user triggers
2214      * but does not do secondary updates.  This is correct for replication
2215      * because secondary updates are part of the replication stream.  However,
2216      * it is fragile because other operations, when no Database handle is used,
2217      * will not perform secondary updates.  This isn't currently a problem
2218      * because a Database handle is present for all user operations.  But it is
2219      * fragile and needs work.
2220      *
2221      * @param putMode One of OVERWRITE, NO_OVERWITE, CURRENT. (NO_DUPS_DATA
2222      * has been converted to NO_OVERWRITE).  Note: OVERWRITE may perform an
2223      * insertion or an update, NO_OVERWRITE performs insertion only, and
2224      * CURRENT updates the slot where the cursor is currently positioned at.
2225      *
2226      * @param key The new key value for the BIN slot S to be inserted/updated.
2227      * Cannot be partial. For a no-dups DB, it is null if the putMode is
2228      * CURRENT. For dups DBs it is a 2-part key: if the putMode is CURRENT,
2229      * it combines the current primary key of slot S with the original,
2230      * user-provided data; for OVERWRITE and NO_OVERWRITE, it combines the
2231      * original, user-provided key and data. In case of update, "key" must
2232      * compare equal to S.key (otherwise DuplicateDataException is thrown),
2233      * but the 2 keys may not be identical if custom comparators are used.
2234      * So, S.key will actually be replaced by "key".
2235      *
2236      * @param data The new data for the LN associated with the BIN slot. For
2237      * dups DBs it is EMPTY_DUPS_DATA. Note: for dups DBs the original,
2238      * user-provided "data" must not be partial.
2239      *
2240      * @param ln LN to be inserted, if insertion is allowed by putMode. null
2241      * for CURRENT (since insertion is not allowed), not null for other modes.
2242      */
putNotify( DatabaseEntry key, final DatabaseEntry data, final LN ln, final PutMode putMode, final ReplicationContext repContext)2243     private OperationStatus putNotify(
2244         DatabaseEntry key,
2245         final DatabaseEntry data,
2246         final LN ln,
2247         final PutMode putMode,
2248         final ReplicationContext repContext) {
2249 
2250         final boolean hasUserTriggers = (dbImpl.getTriggers() != null);
2251         final boolean hasAssociations = (dbHandle != null) &&
2252             dbHandle.hasSecondaryOrForeignKeyAssociations();
2253 
2254         if (hasAssociations) {
2255             try {
2256                 dbImpl.getEnv().getSecondaryAssociationLock().
2257                     readLock().lockInterruptibly();
2258 
2259             } catch (InterruptedException e) {
2260                 throw new ThreadInterruptedException(
2261                     dbImpl.getEnv(), e);
2262             }
2263         }
2264 
2265         try {
2266             final OperationStatus commitStatus;
2267             final boolean inserted;
2268             DatabaseEntry replaceKey = null;
2269 
2270             if (putMode == PutMode.CURRENT) {
2271                 if (key == null) {
2272                     /*
2273                      * This is a no-dups DB. The slot key will not be affected
2274                      * by the update. However, if there are indexes/triggers,
2275                      * the value of the key is needed to update/apply the
2276                      * indexes/triggers after the update. So, it must be
2277                      * returned by the putCurrentNoNotify() call below.
2278                      * Furthermore, for indexes, the value of the key is needed
2279                      * before the update as well, to determine which indexes
2280                      * actually must be updated and whether the old data is
2281                      * also needed to do the index updates. So, we read the
2282                      * value of the key here by what is effectivelly a
2283                      * dirty-read.
2284                      */
2285                     if (hasAssociations || hasUserTriggers) {
2286                         key = new DatabaseEntry();
2287                         /*
2288                          * Latch this.bin and make "key" point to the
2289                          * slot key; then unlatch this.bin.
2290                          */
2291                         key.setData(cursorImpl.getCurrentKey());
2292                     }
2293                 } else {
2294                     /*
2295                      * This is a dups DB. The slot key must be replaced by the
2296                      * given 2-part key. We don't need the pre-update slot key.
2297                      */
2298                     replaceKey = key;
2299                 }
2300             }
2301 
2302             /*
2303              * - oldData: if needed, will be set to the LN data before the
2304              *   update.
2305              * - newData: if needed, will be set to the full LN data after
2306              *   the update; may be different than newData only if newData
2307              *   is partial.
2308              */
2309             DatabaseEntry oldData = null;
2310             DatabaseEntry newData = null;
2311 
2312             /*
2313              * Get secondaries from the association and determine whether the
2314              * old data and new data is needed.
2315              */
2316             Collection<SecondaryDatabase> secondaries = null;
2317 
2318             if (hasAssociations || hasUserTriggers) {
2319 
2320                 if (data.getPartial()) {
2321                     newData = new DatabaseEntry();
2322                 }
2323 
2324                 if (hasUserTriggers) {
2325                     oldData = new DatabaseEntry();
2326                 }
2327 
2328                 if (hasAssociations) {
2329                     secondaries = dbHandle.secAssoc.getSecondaries(key);
2330                     if (oldData == null &&
2331                         SecondaryDatabase.needOldDataForUpdate(secondaries)) {
2332                         oldData = new DatabaseEntry();
2333                     }
2334                 }
2335             }
2336 
2337             /* Perform the actual put operation. */
2338             if (putMode == PutMode.CURRENT) {
2339 
2340                 commitStatus = putCurrentNoNotify(
2341                     replaceKey, data, oldData, newData, repContext);
2342 
2343                 inserted = false;
2344             } else {
2345 
2346                 final Pair<OperationStatus, Boolean> result = putNoNotify(
2347                     key, data, ln, putMode, oldData, newData, repContext);
2348 
2349                 commitStatus = result.first();
2350                 inserted = result.second();
2351             }
2352 
2353             if (commitStatus != OperationStatus.SUCCESS) {
2354                 return commitStatus;
2355             }
2356 
2357             /* If returned data is null, this is an insertion not an update. */
2358             if (oldData != null && oldData.getData() == null) {
2359                 oldData = null;
2360             }
2361 
2362             if (newData == null) {
2363                 newData = data;
2364             }
2365 
2366             /*
2367              * Update secondaries and notify triggers.  Pass newData, not data,
2368              * since data may be partial.
2369              */
2370             final Locker locker = cursorImpl.getLocker();
2371 
2372             if (secondaries != null) {
2373 
2374                 for (final SecondaryDatabase secDb : secondaries) {
2375 
2376                     if (inserted || secDb.updateMayChangeSecondary()) {
2377                         secDb.updateSecondary(
2378                             locker, null, key, oldData, newData);
2379                     }
2380                 }
2381             }
2382 
2383             if (hasUserTriggers) {
2384                 TriggerManager.runPutTriggers(
2385                     locker, dbImpl, key, oldData, newData);
2386             }
2387 
2388             return OperationStatus.SUCCESS;
2389         } catch (Error E) {
2390             dbImpl.getEnv().invalidate(E);
2391             throw E;
2392         } finally {
2393             if (hasAssociations) {
2394                 dbImpl.getEnv().getSecondaryAssociationLock().
2395                     readLock().unlock();
2396             }
2397         }
2398     }
2399 
2400     /**
2401      * Search for the key and perform insertion or update. Does not notify
2402      * triggers or perform secondary updates.  Prevents phantoms.
2403      *
2404      * @param putMode is either OVERWRITE, NO_OEVERWRITE, or BLIND_INSERTION
2405      *
2406      * @param key The new key value for the BIN slot S to be inserted/updated.
2407      * Cannot be partial. For dups DBs it is a 2-part key combining the
2408      * original, user-provided key and data. In case of update, "key" must
2409      * compare equal to S.key (otherwise DuplicateDataException is thrown),
2410      * but the 2 keys may not be identical if custom comparators are used.
2411      * So, S.key will actually be replaced by "key".
2412      *
2413      * @param data In case of update, the new data to (perhaps partially)
2414      * replace the data of the LN associated with the BIN slot. For dups DBs
2415      * it is EMPTY_DUPS_DATA. Note: for dups DBs the original, user-provided
2416      * "data" must not be partial.
2417      *
2418      * @param ln is normally a new LN node that is created for insertion, and
2419      * will be discarded if an update occurs.  However, HA will pass an
2420      * existing node.
2421      *
2422      * @param returnOldData To receive, in case of update, the old LN data
2423      * (before the update). It is needed only by DBs with indexes/triggers;
2424      * will be null otherwise.
2425      *
2426      * @param returnNewData To receive the full data of the new or updated LN.
2427      * It is needed only by DBs with indexes/triggers and only if "data" is
2428      * partial; will be null otherwise. Note: "returnNewData" may be different
2429      * than "data" only if "data" is partial.
2430 
2431      * @return pair of status and 'inserted' boolean.
2432      */
putNoNotify( final DatabaseEntry key, final DatabaseEntry data, final LN ln, final PutMode putMode, final DatabaseEntry returnOldData, final DatabaseEntry returnNewData, final ReplicationContext repContext)2433     private Pair<OperationStatus, Boolean> putNoNotify(
2434         final DatabaseEntry key,
2435         final DatabaseEntry data,
2436         final LN ln,
2437         final PutMode putMode,
2438         final DatabaseEntry returnOldData,
2439         final DatabaseEntry returnNewData,
2440         final ReplicationContext repContext) {
2441 
2442         assert key != null;
2443         assert ln != null;
2444         assert putMode != null;
2445         assert putMode != PutMode.CURRENT;
2446 
2447         Locker nextKeyLocker = null;
2448         CursorImpl nextKeyCursor = null;
2449         CursorImpl dup = null;
2450         OperationStatus status = OperationStatus.NOTFOUND;
2451         boolean success = false;
2452 
2453         try {
2454             /*
2455              * If other transactions are serializable, lock the next key.
2456              * BUG ???? What if a serializable txn starts after the check
2457              * below returns false? At least, if this cursor is using a
2458              * serializable txn, it SHOULD do next key locking unconditionally.
2459              */
2460             Locker cursorLocker = cursorImpl.getLocker();
2461 
2462             if (dbImpl.getEnv().getTxnManager().
2463                 areOtherSerializableTransactionsActive(cursorLocker)) {
2464 
2465                 /*
2466                  * nextKeyCursor is created with retainNonTxnLocks == true,
2467                  * and as a result, releaseNonTxnLocks() will not be called
2468                  * on nextKeyLocker when nextKeyCursor is reset or closed.
2469                  * That's why in the finally clause below we explicitly call
2470                  * nextKeyLocker.operationEnd()
2471                  */
2472                 nextKeyLocker = BuddyLocker.createBuddyLocker(
2473                     dbImpl.getEnv(), cursorLocker);
2474 
2475                 nextKeyCursor = new CursorImpl(dbImpl, nextKeyLocker);
2476 
2477                 /* Perform eviction for user cursors. */
2478                 nextKeyCursor.setAllowEviction(true);
2479                 nextKeyCursor.lockNextKeyForInsert(key);
2480             }
2481 
2482             dup = beginMoveCursor(false /*samePosition*/);
2483 
2484             /* Perform operation. */
2485             Pair<OperationStatus, Boolean> result = dup.insertOrUpdateRecord(
2486                 key, data, ln, putMode,
2487                 returnOldData, returnNewData, repContext, thrput);
2488 
2489             status = result.first();
2490             /* Note that status is used in the finally. */
2491             success = true;
2492             return result;
2493 
2494         } finally {
2495 
2496             try {
2497                 if (dup != null) {
2498                     endMoveCursor(dup, status == OperationStatus.SUCCESS);
2499                 }
2500 
2501                 if (nextKeyCursor != null) {
2502                     nextKeyCursor.close();
2503                 }
2504 
2505                 /* Release the next-key lock. */
2506                 if (nextKeyLocker != null) {
2507                     nextKeyLocker.operationEnd();
2508                 }
2509             } catch (Exception e) {
2510                 if (success) {
2511                     throw e;
2512                 } else {
2513                     /*
2514                      * Log the exception thrown by the cleanup actions and
2515                      * allow the original exception to be thrown
2516                      */
2517                     LoggerUtils.traceAndLogException(
2518                         dbImpl.getEnv(), "Cursor", "putNoNotify", "", e);
2519                 }
2520             }
2521         }
2522     }
2523 
2524     /**
2525      * Update the data at the current position.  No new LN, dup cursor, or
2526      * phantom handling is needed.  Does not interpret duplicates.
2527      *
2528      * @param key The new key value for the BIN slot S to be updated. Cannot
2529      * be partial. For a no-dups DB, it is null. For dups DBs it is a 2-part
2530      * key combining the current primary key of slot S with the original,
2531      * user-provided data. "key" (if not null) must compare equal to S.key
2532      * (otherwise DuplicateDataException is thrown), but the 2 keys may not
2533      * be identical if custom comparators are used. So, S.key will actually
2534      * be replaced by "key".
2535      *
2536      * @param data The new data to (perhaps partially) replace the data of the
2537      * LN associated with the BIN slot. For dups DBs it is EMPTY_DUPS_DATA.
2538      * Note: for dups DBs the original, user-provided "data" must not be
2539      * partial.
2540      *
2541      * @param returnOldData To receive the old LN data (before the update).
2542      * It is needed only by DBs with indexes/triggers; will be null otherwise.
2543      *
2544      * @param returnNewData To receive the full data of the updated LN.
2545      * It is needed only by DBs with indexes/triggers and only if "data" is
2546      * partial; will be null otherwise. Note: "returnNewData" may be different
2547      * than "data" only if "data" is partial.
2548      */
putCurrentNoNotify( final DatabaseEntry key, final DatabaseEntry data, final DatabaseEntry returnOldData, final DatabaseEntry returnNewData, final ReplicationContext repContext)2549     private OperationStatus putCurrentNoNotify(
2550         final DatabaseEntry key,
2551         final DatabaseEntry data,
2552         final DatabaseEntry returnOldData,
2553         final DatabaseEntry returnNewData,
2554         final ReplicationContext repContext) {
2555 
2556         assert data != null;
2557 
2558         beginUseExistingCursor();
2559 
2560         final OperationStatus status = cursorImpl.updateCurrentRecord(
2561             key, data, returnOldData, returnNewData, repContext, thrput);
2562 
2563         endUseExistingCursor();
2564         return status;
2565     }
2566 
2567     /**
2568      * Returns the current key and data.  There is no need to use a dup cursor
2569      * or prevent phantoms.
2570      */
getCurrentInternal( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)2571     OperationStatus getCurrentInternal(
2572         final DatabaseEntry key,
2573         final DatabaseEntry data,
2574         final LockMode lockMode) {
2575 
2576         synchronized (getTxnSynchronizer()) {
2577 
2578             checkTxnState();
2579 
2580             if (dbImpl.getSortedDuplicates()) {
2581                 return getCurrentHandleDups(key, data, lockMode);
2582             }
2583 
2584             return getCurrentNoDups(key, data, lockMode);
2585         }
2586     }
2587 
2588     /**
2589      * Used to lock without returning key/data.  When called with
2590      * LockMode.READ_UNCOMMITTED, it simply checks for a deleted record.
2591      */
checkCurrent(final LockMode lockMode)2592     OperationStatus checkCurrent(final LockMode lockMode) {
2593 
2594         return getCurrentNoDups(null, null, lockMode);
2595     }
2596 
2597     /**
2598      * Interpret duplicates for getCurrent operation.
2599      */
getCurrentHandleDups( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)2600     private OperationStatus getCurrentHandleDups(
2601         final DatabaseEntry key,
2602         final DatabaseEntry data,
2603         final LockMode lockMode) {
2604 
2605         final DatabaseEntry twoPartKey = new DatabaseEntry();
2606 
2607         final OperationStatus status = getCurrentNoDups(
2608             twoPartKey, NO_RETURN_DATA, lockMode);
2609 
2610         if (status != OperationStatus.SUCCESS) {
2611             return status;
2612         }
2613 
2614         DupKeyData.split(twoPartKey, key, data);
2615         return OperationStatus.SUCCESS;
2616     }
2617 
getCurrentNoDups( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)2618     private OperationStatus getCurrentNoDups(
2619         final DatabaseEntry key,
2620         final DatabaseEntry data,
2621         final LockMode lockMode) {
2622 
2623         boolean success = false;
2624         OperationStatus status = OperationStatus.KEYEMPTY;
2625 
2626         beginUseExistingCursor();
2627 
2628         final LockType lockType = getLockType(lockMode, false);
2629 
2630         try {
2631             status = cursorImpl.lockAndGetCurrent(
2632                 key, data, lockType, lockMode == LockMode.READ_UNCOMMITTED_ALL,
2633                 false /*isLatched*/, false /*unlatch*/);
2634 
2635             success = true;
2636 
2637         } finally {
2638 
2639             if (success &&
2640                 thrput != null &&
2641                 cursorImpl.getBIN() != null &&
2642                 cursorImpl.getBIN().isBINDelta()) {
2643                 thrput.increment(ThroughputStatGroup.BIN_DELTA_GETS_OFFSET);
2644             }
2645 
2646             cursorImpl.releaseBIN();
2647             endUseExistingCursor();
2648         }
2649 
2650         return status;
2651     }
2652 
2653     /**
2654      * Internal version of getFirst/getLast that does no parameter checking.
2655      * Interprets duplicates.
2656      */
position( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final boolean first)2657     OperationStatus position(
2658         final DatabaseEntry key,
2659         final DatabaseEntry data,
2660         final LockMode lockMode,
2661         final boolean first) {
2662 
2663         synchronized (getTxnSynchronizer()) {
2664 
2665             checkTxnState();
2666 
2667             if (dbImpl.getSortedDuplicates()) {
2668                 return positionHandleDups(key, data, lockMode, first);
2669             }
2670 
2671             return positionNoDups(key, data, lockMode, first);
2672         }
2673     }
2674 
2675     /**
2676      * Interpret duplicates for getFirst and getLast operations.
2677      */
positionHandleDups( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final boolean first)2678     private OperationStatus positionHandleDups(
2679         final DatabaseEntry key,
2680         final DatabaseEntry data,
2681         final LockMode lockMode,
2682         final boolean first) {
2683 
2684         final DatabaseEntry twoPartKey = new DatabaseEntry();
2685 
2686         final OperationStatus status = positionNoDups(
2687             twoPartKey, NO_RETURN_DATA, lockMode, first);
2688 
2689         if (status != OperationStatus.SUCCESS) {
2690             return status;
2691         }
2692 
2693         DupKeyData.split(twoPartKey, key, data);
2694         return OperationStatus.SUCCESS;
2695     }
2696 
2697     /**
2698      * Does not interpret duplicates.  Prevents phantoms.
2699      */
positionNoDups( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final boolean first)2700     private OperationStatus positionNoDups(
2701         final DatabaseEntry key,
2702         final DatabaseEntry data,
2703         final LockMode lockMode,
2704         final boolean first) {
2705 
2706         try {
2707             if (!isSerializableIsolation(lockMode)) {
2708 
2709                 return positionAllowPhantoms(
2710                     key, data, lockMode, false /*rangeLock*/, first);
2711             }
2712 
2713             /*
2714              * Perform range locking to prevent phantoms and handle restarts.
2715              */
2716             while (true) {
2717                 try {
2718                     /* Range lock the EOF node before getLast. */
2719                     if (!first) {
2720                         cursorImpl.lockEof(LockType.RANGE_READ);
2721                     }
2722 
2723                     /* Perform operation. Use a range lock for getFirst. */
2724                     final OperationStatus status = positionAllowPhantoms(
2725                         key, data, lockMode, first /*rangeLock*/, first);
2726 
2727                     /*
2728                      * Range lock the EOF node when getFirst returns NOTFOUND.
2729                      */
2730                     if (first && status != OperationStatus.SUCCESS) {
2731                         cursorImpl.lockEof(LockType.RANGE_READ);
2732                     }
2733 
2734                     return status;
2735                 } catch (RangeRestartException e) {
2736                     continue;
2737                 }
2738             }
2739         } catch (Error E) {
2740             dbImpl.getEnv().invalidate(E);
2741             throw E;
2742         }
2743     }
2744 
2745     /**
2746      * Positions without preventing phantoms.
2747      */
positionAllowPhantoms( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final boolean rangeLock, final boolean first)2748     private OperationStatus positionAllowPhantoms(
2749         final DatabaseEntry key,
2750         final DatabaseEntry data,
2751         final LockMode lockMode,
2752         final boolean rangeLock,
2753         final boolean first) {
2754 
2755         assert (key != null && data != null);
2756 
2757         OperationStatus status = OperationStatus.NOTFOUND;
2758 
2759         final CursorImpl dup = beginMoveCursor(false /*samePosition*/);
2760 
2761         try {
2762             /* Search for first or last slot. */
2763             if (!dup.positionFirstOrLast(first)) {
2764                 /* Tree is empty. */
2765                 status = OperationStatus.NOTFOUND;
2766                 if (LatchSupport.TRACK_LATCHES) {
2767                     LatchSupport.expectBtreeLatchesHeld(0);
2768                 }
2769             } else {
2770                 /*
2771                  * Found and latched first/last BIN in this tree.
2772                  * BIN may be empty.
2773                  */
2774                 if (LatchSupport.TRACK_LATCHES) {
2775                     LatchSupport.expectBtreeLatchesHeld(1);
2776                 }
2777 
2778                 final LockType lockType = getLockType(lockMode, rangeLock);
2779 
2780                 final boolean dirtyReadAll =
2781                     lockMode == LockMode.READ_UNCOMMITTED_ALL;
2782 
2783                 status = dup.lockAndGetCurrent(
2784                     key, data, lockType, dirtyReadAll,
2785                     true /*isLatched*/, false /*unlatch*/);
2786 
2787                 if (status != OperationStatus.SUCCESS) {
2788                     /*
2789                      * The BIN may be empty or the slot we're pointing at may
2790                      * be deleted.
2791                      */
2792                     status = dup.getNext(
2793                         key, data, lockType, dirtyReadAll, first,
2794                         true /*isLatched*/, null /*rangeConstraint*/);
2795                 }
2796             }
2797         } finally {
2798             dup.releaseBIN();
2799             endMoveCursor(dup, status == OperationStatus.SUCCESS);
2800         }
2801         return status;
2802     }
2803 
2804     /**
2805      * Retrieves the next or previous record. Prevents phantoms.
2806      */
retrieveNext( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final GetMode getMode)2807     OperationStatus retrieveNext(
2808         final DatabaseEntry key,
2809         final DatabaseEntry data,
2810         final LockMode lockMode,
2811         final GetMode getMode) {
2812 
2813         if (dbImpl.getSortedDuplicates()) {
2814             return retrieveNextHandleDups(key, data, lockMode, getMode);
2815         }
2816 
2817         return retrieveNextNoDups(key, data, lockMode, getMode);
2818     }
2819 
2820     /**
2821      * Interpret duplicates for getNext/Prev/etc operations.
2822      */
retrieveNextHandleDups( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final GetMode getMode)2823     private OperationStatus retrieveNextHandleDups(
2824         final DatabaseEntry key,
2825         final DatabaseEntry data,
2826         final LockMode lockMode,
2827         final GetMode getMode) {
2828 
2829         switch (getMode) {
2830         case NEXT:
2831         case PREV:
2832             return dupsGetNextOrPrev(key, data, lockMode, getMode);
2833         case NEXT_DUP:
2834             return dupsGetNextOrPrevDup(key, data, lockMode, GetMode.NEXT);
2835         case PREV_DUP:
2836             return dupsGetNextOrPrevDup(key, data, lockMode, GetMode.PREV);
2837         case NEXT_NODUP:
2838             return dupsGetNextNoDup(key, data, lockMode);
2839         case PREV_NODUP:
2840             return dupsGetPrevNoDup(key, data, lockMode);
2841         default:
2842             throw EnvironmentFailureException.unexpectedState(
2843                 getMode.toString());
2844         }
2845     }
2846 
2847     /**
2848      * Interpret duplicates for getNext and getPrev.
2849      */
dupsGetNextOrPrev( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final GetMode getMode)2850     private OperationStatus dupsGetNextOrPrev(
2851         final DatabaseEntry key,
2852         final DatabaseEntry data,
2853         final LockMode lockMode,
2854         final GetMode getMode) {
2855 
2856         final DatabaseEntry twoPartKey = new DatabaseEntry();
2857 
2858         final OperationStatus status = retrieveNextNoDups(
2859             twoPartKey, NO_RETURN_DATA, lockMode, getMode);
2860 
2861         if (status != OperationStatus.SUCCESS) {
2862             return status;
2863         }
2864         DupKeyData.split(twoPartKey, key, data);
2865         return OperationStatus.SUCCESS;
2866     }
2867 
2868     /**
2869      * Interpret duplicates for getNextDup and getPrevDup.
2870      *
2871      * Move the cursor forward or backward by one record, and check the key
2872      * prefix to detect going out of the bounds of the duplicate set.
2873      */
dupsGetNextOrPrevDup( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final GetMode getMode)2874     private OperationStatus dupsGetNextOrPrevDup(
2875         final DatabaseEntry key,
2876         final DatabaseEntry data,
2877         final LockMode lockMode,
2878         final GetMode getMode) {
2879 
2880         final byte[] currentKey = cursorImpl.getCurrentKey();
2881         final Cursor c = dup(true /*samePosition*/);
2882         try {
2883             c.setNonSticky(true);
2884             setPrefixConstraint(c, currentKey);
2885             final DatabaseEntry twoPartKey = new DatabaseEntry();
2886 
2887             final OperationStatus status = c.retrieveNextNoDups(
2888                 twoPartKey, NO_RETURN_DATA, lockMode, getMode);
2889 
2890             if (status != OperationStatus.SUCCESS) {
2891                 return status;
2892             }
2893             DupKeyData.split(twoPartKey, key, data);
2894             swapCursor(c);
2895             return OperationStatus.SUCCESS;
2896         } finally {
2897             c.close();
2898         }
2899     }
2900 
2901     /**
2902      * Interpret duplicates for getNextNoDup.
2903      *
2904      * Using a special comparator, search for first duplicate in the duplicate
2905      * set following the one for the current key.  For details see
2906      * DupKeyData.NextNoDupComparator.
2907      */
dupsGetNextNoDup( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)2908     private OperationStatus dupsGetNextNoDup(
2909         final DatabaseEntry key,
2910         final DatabaseEntry data,
2911         final LockMode lockMode) {
2912 
2913         final byte[] currentKey = cursorImpl.getCurrentKey();
2914         final DatabaseEntry twoPartKey = DupKeyData.removeData(currentKey);
2915 
2916         final Cursor c = dup(false /*samePosition*/);
2917 
2918         try {
2919             c.setNonSticky(true);
2920 
2921             final Comparator<byte[]> searchComparator =
2922                 new DupKeyData.NextNoDupComparator(
2923                     dbImpl.getBtreeComparator());
2924 
2925             final OperationStatus status = c.searchNoDups(
2926                 twoPartKey, NO_RETURN_DATA, lockMode, SearchMode.SET_RANGE,
2927                 searchComparator);
2928 
2929             if (status != OperationStatus.SUCCESS) {
2930                 return status;
2931             }
2932 
2933             DupKeyData.split(twoPartKey, key, data);
2934 
2935             swapCursor(c);
2936             return OperationStatus.SUCCESS;
2937         } finally {
2938             c.close();
2939         }
2940     }
2941 
2942     /**
2943      * Interpret duplicates for getPrevNoDup.
2944      *
2945      * Move the cursor to the first duplicate in the duplicate set, then to the
2946      * previous record. If this fails because all dups at the current position
2947      * have been deleted, move the cursor backward to find the previous key.
2948      *
2949      * Note that we lock the first duplicate to enforce Serializable isolation.
2950      */
dupsGetPrevNoDup( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)2951     private OperationStatus dupsGetPrevNoDup(
2952         final DatabaseEntry key,
2953         final DatabaseEntry data,
2954         final LockMode lockMode) {
2955 
2956         final byte[] currentKey = cursorImpl.getCurrentKey();
2957         final DatabaseEntry twoPartKey = DupKeyData.removeData(currentKey);
2958         Cursor c = dup(false /*samePosition*/);
2959         try {
2960             c.setNonSticky(true);
2961             setPrefixConstraint(c, currentKey);
2962 
2963             OperationStatus status = c.searchNoDups(
2964                 twoPartKey, NO_RETURN_DATA, lockMode, SearchMode.SET_RANGE,
2965                 null /*comparator*/);
2966 
2967             if (status == OperationStatus.SUCCESS) {
2968                 c.rangeConstraint = null;
2969 
2970                 status = c.retrieveNextNoDups(
2971                     twoPartKey, NO_RETURN_DATA, lockMode, GetMode.PREV);
2972 
2973                 if (status != OperationStatus.SUCCESS) {
2974                     return status;
2975                 }
2976 
2977                 DupKeyData.split(twoPartKey, key, data);
2978                 swapCursor(c);
2979                 return OperationStatus.SUCCESS;
2980             }
2981         } finally {
2982             c.close();
2983         }
2984 
2985         c = dup(true /*samePosition*/);
2986 
2987         try {
2988             c.setNonSticky(true);
2989             while (true) {
2990                 final OperationStatus status =
2991                     c.retrieveNextNoDups(
2992                         twoPartKey, NO_RETURN_DATA, lockMode, GetMode.PREV);
2993 
2994                 if (status != OperationStatus.SUCCESS) {
2995                     return status;
2996                 }
2997 
2998                 if (!haveSameDupPrefix(twoPartKey, currentKey)) {
2999                     DupKeyData.split(twoPartKey, key, data);
3000                     swapCursor(c);
3001                     return OperationStatus.SUCCESS;
3002                 }
3003             }
3004         } finally {
3005             c.close();
3006         }
3007     }
3008 
3009     /**
3010      * Does not interpret duplicates.  Prevents phantoms.
3011      */
retrieveNextNoDups( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final GetMode getModeParam)3012     private OperationStatus retrieveNextNoDups(
3013         final DatabaseEntry key,
3014         final DatabaseEntry data,
3015         final LockMode lockMode,
3016         final GetMode getModeParam) {
3017 
3018         final GetMode getMode;
3019         switch (getModeParam) {
3020         case NEXT_DUP:
3021         case PREV_DUP:
3022             return OperationStatus.NOTFOUND;
3023         case NEXT_NODUP:
3024             getMode = GetMode.NEXT;
3025             break;
3026         case PREV_NODUP:
3027             getMode = GetMode.PREV;
3028             break;
3029         default:
3030             getMode = getModeParam;
3031         }
3032 
3033         try {
3034             if (!isSerializableIsolation(lockMode)) {
3035 
3036                 /*
3037                  * No need to prevent phantoms.
3038                  */
3039                 assert (getMode == GetMode.NEXT || getMode == GetMode.PREV);
3040 
3041                 final CursorImpl dup = beginMoveCursor(true /*samePosition*/);
3042 
3043                 OperationStatus status = OperationStatus.NOTFOUND;
3044                 try {
3045                     status = dup.getNext(
3046                         key, data, getLockType(lockMode, false),
3047                         lockMode == LockMode.READ_UNCOMMITTED_ALL,
3048                         getMode.isForward(), false /*isLatched*/,
3049                         rangeConstraint);
3050 
3051                     return status;
3052                 } finally {
3053                     endMoveCursor(dup, status == OperationStatus.SUCCESS);
3054                 }
3055             }
3056 
3057             /*
3058              * Perform range locking to prevent phantoms and handle restarts.
3059              */
3060             while (true) {
3061                 try {
3062                     /* Get a range lock for 'prev' operations. */
3063                     if (!getMode.isForward()) {
3064                         rangeLockCurrentPosition();
3065                     }
3066                     /* Use a range lock if performing a 'next' operation. */
3067                     final LockType lockType =
3068                         getLockType(lockMode, getMode.isForward());
3069 
3070                     /* Do not modify key/data params until SUCCESS. */
3071                     final DatabaseEntry tryKey = cloneEntry(key);
3072                     final DatabaseEntry tryData = cloneEntry(data);
3073 
3074                     /* Perform the operation with a null rangeConstraint. */
3075                     OperationStatus status = retrieveNextCheckForInsertion(
3076                         tryKey, tryData, lockType, getMode);
3077 
3078                     if (getMode.isForward() &&
3079                         status != OperationStatus.SUCCESS) {
3080                         /* NEXT: lock the EOF node. */
3081                         cursorImpl.lockEof(LockType.RANGE_READ);
3082                     }
3083 
3084                     /* Finally check rangeConstraint. */
3085                     if (status == OperationStatus.SUCCESS &&
3086                         !checkRangeConstraint(tryKey)) {
3087                         status = OperationStatus.NOTFOUND;
3088                     }
3089 
3090                     /*
3091                      * Only overwrite key/data on SUCCESS, after all locking.
3092                      */
3093                     if (status == OperationStatus.SUCCESS) {
3094                         setEntry(tryKey, key);
3095                         setEntry(tryData, data);
3096                     }
3097 
3098                     return status;
3099                 } catch (RangeRestartException e) {
3100                     continue;
3101                 }
3102             }
3103         } catch (Error E) {
3104             dbImpl.getEnv().invalidate(E);
3105             throw E;
3106         }
3107     }
3108 
3109     /**
3110      * For 'prev' operations, upgrades to a range lock at the current position.
3111      * If there are no records at the current position, get a range lock on the
3112      * next record or, if not found, on the logical EOF node.  Do not modify
3113      * the current cursor position, use a separate cursor.
3114      */
rangeLockCurrentPosition()3115     private void rangeLockCurrentPosition() {
3116 
3117         final DatabaseEntry tempKey = new DatabaseEntry();
3118         final DatabaseEntry tempData = new DatabaseEntry();
3119         tempKey.setPartial(0, 0, true);
3120         tempData.setPartial(0, 0, true);
3121 
3122         OperationStatus status;
3123 
3124         CursorImpl dup = cursorImpl.cloneCursor(true /*samePosition*/);
3125 
3126         try {
3127             status = dup.lockAndGetCurrent(
3128                 tempKey, tempData, LockType.RANGE_READ);
3129 
3130             if (status != OperationStatus.SUCCESS) {
3131 
3132                 while (true) {
3133                     if (LatchSupport.TRACK_LATCHES) {
3134                         LatchSupport.expectBtreeLatchesHeld(0);
3135                     }
3136 
3137                     status = dup.getNext(
3138                         tempKey, tempData, LockType.RANGE_READ,
3139                         false /*dirtyReadAll*/, true /*forward*/,
3140                         false /*isLatched*/, null /*rangeConstraint*/);
3141 
3142                     if (cursorImpl.checkForInsertion(GetMode.NEXT, dup)) {
3143                         dup.close(cursorImpl);
3144                         dup = cursorImpl.cloneCursor(true /*samePosition*/);
3145                         continue;
3146                     }
3147 
3148                     if (LatchSupport.TRACK_LATCHES) {
3149                         LatchSupport.expectBtreeLatchesHeld(0);
3150                     }
3151                     break;
3152                 }
3153             }
3154         } finally {
3155             dup.close(cursorImpl);
3156         }
3157 
3158         if (status != OperationStatus.SUCCESS) {
3159             cursorImpl.lockEof(LockType.RANGE_READ);
3160         }
3161     }
3162 
3163     /**
3164      * Retrieves and checks for insertions, for serializable isolation.
3165      */
retrieveNextCheckForInsertion( final DatabaseEntry key, final DatabaseEntry data, final LockType lockType, final GetMode getMode)3166     private OperationStatus retrieveNextCheckForInsertion(
3167         final DatabaseEntry key,
3168         final DatabaseEntry data,
3169         final LockType lockType,
3170         final GetMode getMode) {
3171 
3172         assert (key != null && data != null);
3173         assert (getMode == GetMode.NEXT || getMode == GetMode.PREV);
3174 
3175         while (true) {
3176 
3177             if (LatchSupport.TRACK_LATCHES) {
3178                 LatchSupport.expectBtreeLatchesHeld(0);
3179             }
3180 
3181             /*
3182              * Force cloning of the cursor because the caller may need to
3183              * restart the operation from the previous position.  In addition,
3184              * checkForInsertion depends on having two CursorImpls for
3185              * comparison, at the old and new position.
3186              */
3187             final CursorImpl dup = beginMoveCursor(
3188                 true /*samePosition*/, true /*forceClone*/);
3189 
3190             boolean doEndMoveCursor = true;
3191 
3192             try {
3193                 final OperationStatus status = dup.getNext(
3194                     key, data, lockType, false /*dirtyReadAll*/,
3195                     getMode.isForward(), false /*isLatched*/,
3196                     null /*rangeConstraint*/);
3197 
3198                 if (!cursorImpl.checkForInsertion(getMode, dup)) {
3199 
3200                     doEndMoveCursor = false;
3201                     endMoveCursor(dup, status == OperationStatus.SUCCESS);
3202 
3203                     if (LatchSupport.TRACK_LATCHES) {
3204                         LatchSupport.expectBtreeLatchesHeld(0);
3205                     }
3206 
3207                     return status;
3208                 }
3209             } finally {
3210                 if (doEndMoveCursor) {
3211                     endMoveCursor(dup, false);
3212                 }
3213             }
3214         }
3215     }
3216 
skipInternal( final long maxCount, final boolean forward, final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)3217     private long skipInternal(
3218         final long maxCount,
3219         final boolean forward,
3220         final DatabaseEntry key,
3221         final DatabaseEntry data,
3222         final LockMode lockMode) {
3223 
3224         final LockType lockType = getLockType(lockMode, false);
3225 
3226         synchronized (getTxnSynchronizer()) {
3227             checkTxnState();
3228             while (true) {
3229 
3230                 /*
3231                  * Force cloning of the cursor since we may need to restart
3232                  * the operation at the previous position.
3233                  */
3234                 final CursorImpl dup = beginMoveCursor(
3235                     true /*samePosition*/, true /*forceClone*/);
3236                 boolean success = false;
3237                 try {
3238                     final long count = dup.skip(forward, maxCount,
3239                                                 null /*rangeConstraint*/);
3240                     if (count <= 0) {
3241                         return 0;
3242                     }
3243                     final OperationStatus status =
3244                         getCurrentWithCursorImpl(dup, key, data, lockType);
3245 
3246                     if (status == OperationStatus.KEYEMPTY) {
3247                         /* Retry if deletion occurs while unlatched. */
3248                         continue;
3249                     }
3250                     success = true;
3251                     return count;
3252                 } finally {
3253                     endMoveCursor(dup, success);
3254                 }
3255             }
3256         }
3257     }
3258 
3259     /**
3260      * Convenience method that does lockAndGetCurrent, with and without dups,
3261      * using a CursorImpl.  Does no setup or save/restore of cursor state.
3262      */
getCurrentWithCursorImpl( final CursorImpl c, final DatabaseEntry key, final DatabaseEntry data, final LockType lockType)3263     private OperationStatus getCurrentWithCursorImpl(
3264         final CursorImpl c,
3265         final DatabaseEntry key,
3266         final DatabaseEntry data,
3267         final LockType lockType) {
3268 
3269         if (!dbImpl.getSortedDuplicates()) {
3270             return c.lockAndGetCurrent(key, data, lockType);
3271         }
3272 
3273         final DatabaseEntry twoPartKey = new DatabaseEntry();
3274 
3275         final OperationStatus status =
3276             c.lockAndGetCurrent(twoPartKey, NO_RETURN_DATA, lockType);
3277 
3278         if (status != OperationStatus.SUCCESS) {
3279             return status;
3280         }
3281 
3282         DupKeyData.split(twoPartKey, key, data);
3283         return OperationStatus.SUCCESS;
3284     }
3285 
3286     /**
3287      * Performs search by key, data, or both.  Prevents phantoms.
3288      */
search( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final SearchMode searchMode)3289     OperationStatus search(
3290         final DatabaseEntry key,
3291         final DatabaseEntry data,
3292         final LockMode lockMode,
3293         final SearchMode searchMode) {
3294 
3295         synchronized (getTxnSynchronizer()) {
3296 
3297             checkTxnState();
3298 
3299             if (dbImpl.getSortedDuplicates()) {
3300 
3301                 switch (searchMode) {
3302                 case SET:
3303                     return dupsGetSearchKey(key, data, lockMode);
3304                 case SET_RANGE:
3305                     return dupsGetSearchKeyRange(key, data, lockMode);
3306                 case BOTH:
3307                     return dupsGetSearchBoth(key, data, lockMode);
3308                 case BOTH_RANGE:
3309                     return dupsGetSearchBothRange(key, data, lockMode);
3310                 default:
3311                     throw EnvironmentFailureException.unexpectedState(
3312                         searchMode.toString());
3313                 }
3314             }
3315 
3316             return searchNoDups(
3317                 key, data, lockMode, searchMode, null /*comparator*/);
3318         }
3319     }
3320 
3321     /**
3322      * Version of search that does not interpret duplicates.  Used for
3323      * replication stream replay.  Prevents phantoms.
3324      */
searchForReplay( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final SearchMode searchMode)3325     OperationStatus searchForReplay(
3326         final DatabaseEntry key,
3327         final DatabaseEntry data,
3328         final LockMode lockMode,
3329         final SearchMode searchMode) {
3330 
3331         synchronized (getTxnSynchronizer()) {
3332 
3333             checkTxnState();
3334 
3335             return searchNoDups(
3336                 key, data, lockMode, searchMode, null /*comparator*/);
3337         }
3338     }
3339 
3340     /**
3341      * Interpret duplicates for getSearchKey operation.
3342      *
3343      * Use key as prefix to find first duplicate using a range search.  Compare
3344      * result to prefix to see whether we went out of the bounds of the
3345      * duplicate set, i.e., whether NOTFOUND should be returned.
3346      *
3347      * Even if the user-provided "key" exists in the DB, the twoPartKey built
3348      * here out of "key" compares < any of the BIN-slot keys that comprise the
3349      * duplicates-set of "key". So there is no way to get an exact key match
3350      * by a BTree search. Instead, we do a constrained range search: we forbid
3351      * the cursor to advance past the duplicates-set of "key" by using an
3352      * appropriate range constraint.
3353      */
dupsGetSearchKey( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)3354     private OperationStatus dupsGetSearchKey(
3355         final DatabaseEntry key,
3356         final DatabaseEntry data,
3357         final LockMode lockMode) {
3358 
3359         final DatabaseEntry twoPartKey = new DatabaseEntry(
3360             DupKeyData.makePrefixKey(key.getData(),
3361                                      key.getOffset(),
3362                                      key.getSize()));
3363 
3364         final RangeConstraint savedRangeConstraint = rangeConstraint;
3365 
3366         try {
3367             setPrefixConstraint(this, key);
3368 
3369             final OperationStatus status = searchNoDups(
3370                 twoPartKey, NO_RETURN_DATA, lockMode, SearchMode.SET_RANGE,
3371                 null /*comparator*/);
3372 
3373             if (status != OperationStatus.SUCCESS) {
3374                 return OperationStatus.NOTFOUND;
3375             }
3376 
3377             DupKeyData.split(twoPartKey, key, data);
3378 
3379             return OperationStatus.SUCCESS;
3380         } finally {
3381             rangeConstraint = savedRangeConstraint;
3382         }
3383     }
3384 
3385     /**
3386      * Interpret duplicates for getSearchKeyRange operation.
3387      *
3388      * Do range search for key prefix.
3389      */
dupsGetSearchKeyRange( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)3390     private OperationStatus dupsGetSearchKeyRange(
3391         final DatabaseEntry key,
3392         final DatabaseEntry data,
3393         final LockMode lockMode) {
3394 
3395         final DatabaseEntry twoPartKey = new DatabaseEntry(
3396             DupKeyData.makePrefixKey(key.getData(),
3397                                      key.getOffset(),
3398                                      key.getSize()));
3399 
3400         final OperationStatus status = searchNoDups(
3401             twoPartKey, NO_RETURN_DATA, lockMode, SearchMode.SET_RANGE,
3402             null /*comparator*/);
3403 
3404         if (status != OperationStatus.SUCCESS) {
3405             return status;
3406         }
3407 
3408         DupKeyData.split(twoPartKey, key, data);
3409         return OperationStatus.SUCCESS;
3410     }
3411 
3412     /**
3413      * Interpret duplicates for getSearchBoth operation.
3414      *
3415      * Do exact search for combined key.
3416      */
dupsGetSearchBoth( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)3417     private OperationStatus dupsGetSearchBoth(
3418         final DatabaseEntry key,
3419         final DatabaseEntry data,
3420         final LockMode lockMode) {
3421 
3422         final DatabaseEntry twoPartKey = DupKeyData.combine(key, data);
3423 
3424         final OperationStatus status = searchNoDups(
3425             twoPartKey, NO_RETURN_DATA, lockMode, SearchMode.BOTH,
3426             null /*comparator*/);
3427 
3428         if (status != OperationStatus.SUCCESS) {
3429             return status;
3430         }
3431 
3432         DupKeyData.split(twoPartKey, key, data);
3433         return OperationStatus.SUCCESS;
3434     }
3435 
3436     /**
3437      * Interpret duplicates for getSearchBothRange operation.
3438      *
3439      * Do range search for combined key.  Compare result to prefix to see
3440      * whether we went out of the bounds of the duplicate set, i.e., whether
3441      * NOTFOUND should be returned.
3442      */
dupsGetSearchBothRange( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)3443     private OperationStatus dupsGetSearchBothRange(
3444         final DatabaseEntry key,
3445         final DatabaseEntry data,
3446         final LockMode lockMode) {
3447 
3448         final DatabaseEntry twoPartKey = DupKeyData.combine(key, data);
3449 
3450         final RangeConstraint savedRangeConstraint = rangeConstraint;
3451 
3452         try {
3453             setPrefixConstraint(this, key);
3454 
3455             final OperationStatus status = searchNoDups(
3456                 twoPartKey, NO_RETURN_DATA, lockMode, SearchMode.SET_RANGE,
3457                 null /*comparator*/);
3458 
3459             if (status != OperationStatus.SUCCESS) {
3460                 return OperationStatus.NOTFOUND;
3461             }
3462 
3463             DupKeyData.split(twoPartKey, key, data);
3464 
3465             return OperationStatus.SUCCESS;
3466         } finally {
3467             rangeConstraint = savedRangeConstraint;
3468         }
3469     }
3470 
3471     /**
3472      * Does not interpret duplicates.  Prevents phantoms.
3473      */
searchNoDups( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final SearchMode searchMode, final Comparator<byte[]> comparator)3474     private OperationStatus searchNoDups(
3475         final DatabaseEntry key,
3476         final DatabaseEntry data,
3477         final LockMode lockMode,
3478         final SearchMode searchMode,
3479         final Comparator<byte[]> comparator) {
3480 
3481         /*
3482          * searchMode cannot be BOTH_RANGE, because for non-dups DBs BOTH_RANGE
3483          * is converted to BOTH, and for dup DBs BOTH_RANGE is converted to
3484          * SET_RANGE.
3485          */
3486         assert(searchMode != SearchMode.BOTH_RANGE);
3487 
3488         try {
3489             if (!isSerializableIsolation(lockMode)) {
3490 
3491                 if (searchMode.isExactSearch()) {
3492 
3493                     assert(comparator == null);
3494 
3495                     return searchExact(key, data, lockMode, searchMode);
3496                 }
3497 
3498                 while (true) {
3499                     try {
3500                         return searchRange(key, data, lockMode, comparator);
3501                     } catch (RangeRestartException e) {
3502                         continue;
3503                     }
3504                 }
3505             }
3506 
3507             /*
3508              * Perform range locking to prevent phantoms and handle restarts.
3509              */
3510             while (true) {
3511 
3512                 OperationStatus result;
3513 
3514                 try {
3515                     /*
3516                      * Do not use a range lock for the initial search, but
3517                      * switch to a range lock when advancing forward.
3518                      */
3519                     final LockType searchLockType;
3520                     final LockType advanceLockType;
3521                     searchLockType = getLockType(lockMode, false);
3522                     advanceLockType = getLockType(lockMode, true);
3523 
3524                     /* Do not modify key/data params until SUCCESS. */
3525                     final DatabaseEntry tryKey = cloneEntry(key);
3526                     final DatabaseEntry tryData = cloneEntry(data);
3527 
3528                     /*
3529                      * If the searchMode is SET or BOTH (i.e., we are looking
3530                      * for an exact key match) we do a artificial range search
3531                      * to range lock the next key. If an exact match for the
3532                      * search key is not found, we still want to advance to the
3533                      * next slot in order to RANGE lock it, but contrary to a
3534                      * normal range scan, we want to return NOTFOUND to the
3535                      * caller and we want to consider this as an operation
3536                      * failure so that the position of the cursor won't change,
3537                      * even though we advance to the following slot in order
3538                      * to range lock it. We achieve this by passing true for
3539                      * the checkForExactKey parameter.
3540                      */
3541                     result = searchRangeSerializable(
3542                         tryKey, tryData, searchLockType, advanceLockType,
3543                         comparator, searchMode);
3544 
3545                     if (result == OperationStatus.SUCCESS) {
3546                         setEntry(tryKey, key);
3547                         setEntry(tryData, data);
3548                     }
3549 
3550                     return result;
3551                 } catch (RangeRestartException e) {
3552                     continue;
3553                 }
3554             }
3555         } catch (Error E) {
3556             dbImpl.getEnv().invalidate(E);
3557             throw E;
3558         }
3559     }
3560 
3561     /**
3562      * Search for a "valid" BIN slot whose key is equal to the given "key".
3563      * A slot is "valid" only if after locking it, neither its PD nor it KD
3564      * flags are set. If no slot exists, return NOTFOUND. Otherwise, copy
3565      * the key and the LN of the found slot into "key" and "data" respectively
3566      * (if "key"/"data" request so) and return either NOTFOUND if searchMode
3567      * == BOTH and "data" does not match the LN of the found slot, or SUCCESS
3568      * otherwise.
3569      *
3570      * Note: On return from this method no latches are held by this cursor.
3571      *
3572      * Note: If the method returns NOTFOUND or raises an exception, any non-
3573      * transactional locks acquired by this method are released.
3574      *
3575      * Note: On SUCCESS, if this is a sticky cursor, any non-transactional
3576      * locks held by this cursor before calling this method are released.
3577      *
3578      * Note: this method is never called when the desired isolation is
3579      * "serializable", because in order to do next-slot-locking, a range
3580      * search is required.
3581      *
3582      * @param key It is used as the search key, as well as to receive the key
3583      * of the BIN slot found by this method, if any. If the DB contains
3584      * duplicates, the key is in the "two-part-key" format (see
3585      * dbi/DupKeyData.java) so that it can be compared with the two-part keys
3586      * stored in the BTree (which contain both a primary key and a data
3587      * portion). The search key itself may or may not contain a data portion.
3588      *
3589      * @param data A DatabaseEntry to compare against the LN of the slot found
3590      * by the search (if searchMode == BOTH) as well as to receive the data of
3591      * that LN. If the DB contains duplicates, it is equal to NO_RETURN_DATA,
3592      * because the LN will be emtpy (the full record is contained in the key).
3593      *
3594      * @param searchMode Either SET or BOTH.
3595      *
3596      * @return NOTFOUND if (a) no valid slot exists with a key == the search
3597      * key, or (b) searchMode == BOTH and "data" does not match the LN of the
3598      * found slot. SUCCESS otherwise.
3599      */
searchExact( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, final SearchMode searchMode)3600     private OperationStatus searchExact(
3601         final DatabaseEntry key,
3602         final DatabaseEntry data,
3603         final LockMode lockMode,
3604         final SearchMode searchMode) {
3605 
3606         assert(key != null && data != null);
3607         assert(searchMode == SearchMode.SET || searchMode == SearchMode.BOTH);
3608 
3609         boolean success = false;
3610         OperationStatus status = OperationStatus.NOTFOUND;
3611 
3612         DatabaseEntry origData = new DatabaseEntry(
3613             data.getData(), data.getOffset(), data.getSize());
3614 
3615         final boolean dataRequested =
3616             !data.getPartial() || data.getPartialLength() != 0;
3617 
3618         final LockType lockType = getLockType(lockMode, false);
3619 
3620         final boolean dirtyReadAll =
3621             lockMode == LockMode.READ_UNCOMMITTED_ALL;
3622 
3623         final CursorImpl dup = beginMoveCursor(false /*samePosition*/);
3624 
3625         try {
3626             /*
3627              * Search for a BIN slot whose key is == the search key. If such a
3628              * slot is found, lock it and check whether it is valid.
3629              */
3630             if (dup.searchExact(
3631                 key, lockType, dirtyReadAll, dataRequested) == null) {
3632                 success = true;
3633                 return status;
3634             }
3635 
3636             /*
3637              * The search found and locked a valid BIN slot whose key is
3638              * equal to the search key. Copy into "data" the LN of this
3639              * slot (if "data" requests so). Also if searchMode is BOTH,
3640              * copy into "key" the key of the found slot (it may be
3641              * different than the given key if a partial key comparator
3642              * is used). Why don't we do this for SET as well ????
3643              */
3644             dup.getCurrent((searchMode == SearchMode.SET ? null : key), data);
3645 
3646             /* Check for data match, if asked so. */
3647             if (searchMode == SearchMode.BOTH) {
3648                 if (checkDataMatch(origData, data)) {
3649                     status = OperationStatus.SUCCESS;
3650                 } else {
3651                     status = OperationStatus.NOTFOUND;
3652                 }
3653             } else {
3654                 status = OperationStatus.SUCCESS;
3655             }
3656 
3657             success = true;
3658 
3659         } finally {
3660 
3661             if (success &&
3662                 thrput != null &&
3663                 dup.getBIN() != null &&
3664                 dup.getBIN().isBINDelta()) {
3665                 thrput.increment(ThroughputStatGroup.BIN_DELTA_GETS_OFFSET);
3666             }
3667 
3668             dup.releaseBIN();
3669             endMoveCursor(dup, status == OperationStatus.SUCCESS);
3670         }
3671 
3672         return status;
3673     }
3674 
3675     /**
3676      * Search for the 1st "valid" BIN slot whose key is in the range [K1, K2),
3677      * where (a) K1 is a given key, (b) K2 is determined by
3678      * this.rangeConstraint, or is +INFINITY if this.rangeConstraint == null,
3679      * and (c) a slot is "valid" only if after locking it, neither its PD nor
3680      * its KD flags are set.
3681      *
3682      * If such a slot is found, copy its key and its associated LN into "key"
3683      * and "data" respectively (if "key"/"data" request so). Note that the
3684      * fact that the slot is valid implies that it has been locked.
3685      *
3686      * Note: On return from this method no latches are held by this cursor.
3687      *
3688      * Note: If the method returns NOTFOUND or raises an exception, any non-
3689      * transactional locks acquired by this method are released.
3690      *
3691      * Note: On SUCCESS, if this is a sticky cursor, any non-transactional
3692      * locks held by this cursor before calling this method are released.
3693      *
3694      * @param key It is used as the search key, as well as to receive the key
3695      * of the BIN slot found by this method, if any. If the DB contains
3696      * duplicates, the key is in the "two-part-key" format (see
3697      * dbi/DupKeyData.java) so that it can be compared with the two-part keys
3698      * stored in the BTree (which contain both a primary key and a data
3699      * portion). The search key itself may or may not contain a data portion.
3700      *
3701      * @param data A DatabaseEntry to receive the data of the LN associated
3702      * with the found slot, if any. If the DB contains duplicates, it is equal
3703      * to NO_RETURN_DATA, because the LN will be empty (the full record is
3704      * contained in the key).
3705      *
3706      * @param comparator Comparator to use to compare the search key against
3707      * the BTree keys.
3708      *
3709      * @return NOTFOUND if no valid slot exists in the [K1, K2) range; SUCCESS
3710      * otherwise.
3711      *
3712      * @throws RangeRestartException if the search should be restarted by the
3713      * caller.
3714      */
searchRange( final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode, Comparator<byte[]> comparator)3715     private OperationStatus searchRange(
3716         final DatabaseEntry key,
3717         final DatabaseEntry data,
3718         final LockMode lockMode,
3719         Comparator<byte[]> comparator)
3720         throws RangeRestartException {
3721 
3722         assert(key != null && data != null);
3723 
3724         boolean success = false;
3725         boolean incStats = (thrput != null);
3726         OperationStatus status = OperationStatus.NOTFOUND;
3727 
3728         final LockType lockType = getLockType(lockMode, false);
3729 
3730         final boolean dirtyReadAll =
3731             lockMode == LockMode.READ_UNCOMMITTED_ALL;
3732 
3733         final CursorImpl dup = beginMoveCursor(false /*samePosition*/);
3734 
3735         try {
3736             /* Search for a BIN slot whose key is the max key <= K1. */
3737             final int searchResult = dup.searchRange(key, comparator);
3738 
3739             if ((searchResult & CursorImpl.FOUND) == 0) {
3740                 /* The tree is completely empty (has no nodes at all) */
3741                 success = true;
3742                 return status;
3743             }
3744 
3745             /*
3746              * The search positioned dup on the BIN that should contain K1
3747              * and this BIN is now latched. If the BIN does contain K1,
3748              * dup.index points to K1's slot. Otherwise, dup.index points
3749              * to the right-most slot whose key is < K1 (or dup.index is -1
3750              * if K1 is < than all keys in the BIN). Note: if foundLast is
3751              * true, dup is positioned on the very last slot of the BTree.
3752              */
3753             final boolean exactKeyMatch =
3754                 ((searchResult & CursorImpl.EXACT_KEY) != 0);
3755             final boolean foundLast =
3756                 ((searchResult & CursorImpl.FOUND_LAST) != 0);
3757 
3758             /*
3759              * If we found K1, lock the slot and check whether it is valid.
3760              * If so, copy out its key and associated LN.
3761              */
3762             if (exactKeyMatch) {
3763                 status = dup.lockAndGetCurrent(
3764                     key, data, lockType, dirtyReadAll,
3765                     true /*isLatched*/, false /*unlatch*/);
3766             }
3767 
3768             /*
3769              * If K1 is not in the BTree or its slot is not valid, advance
3770              * dup until (a) the rangeConstraint (if any) returns false, or
3771              * (b) there are no more slots, or (c) we find a valid slot. If
3772              * (c), check whether the slot key is < K1. This can happen if
3773              * K1 was not in the BTree (so dup is now on a key K0 < K1) and
3774              * another txn inserted new keys < K1 while we were trying to
3775              * advance dup. If so, a RestartException is thrown. Otherwise,
3776              * the slot key and LN are copied into "key" and "data" (if
3777              * "key"/"data" request so).
3778              */
3779             if (!exactKeyMatch || status == OperationStatus.KEYEMPTY) {
3780                 status = OperationStatus.NOTFOUND;
3781                 if (!foundLast) {
3782                     status = searchRangeAdvanceAndCheckKey(
3783                         dup, key, data, lockType, dirtyReadAll,
3784                         comparator, rangeConstraint);
3785 
3786                     /*
3787                      * Don't inc thput stats because the bin is released by
3788                      * searchRangeAdvanceAndCheckKey(). This is ok because
3789                      * searchRangeAdvanceAndCheckKey() will cause mutation
3790                      * to full bin anyway.
3791                      */
3792                     incStats = false;
3793                 }
3794             }
3795 
3796             success = true;
3797 
3798         } finally {
3799 
3800             if (success &&
3801                 incStats &&
3802                 dup.getBIN() != null &&
3803                 dup.getBIN().isBINDelta()) {
3804                 thrput.increment(ThroughputStatGroup.BIN_DELTA_GETS_OFFSET);
3805             }
3806 
3807             dup.releaseBIN();
3808             endMoveCursor(dup, status == OperationStatus.SUCCESS);
3809         }
3810 
3811         return status;
3812     }
3813 
3814     /**
3815      * Search for the 1st "valid" BIN slot whose key is in the range [K1, K2),
3816      * where (a) K1 is a given key, (b) K2 is determined by
3817      * this.rangeConstraint, or is +INFINITY if this.rangeConstraint == null,
3818      * and (c) a slot is "valid" only if after locking it, neither its PD nor
3819      * its KD flags are set.
3820      *
3821      * If such a slot is found, copy its key and it associated LN into "key"
3822      * and "data" respectively (if "key"/"data" request so). Note that the
3823      * fact that the slot is valid implies that it has been locked. If the
3824      * key of the found slot is == K1, it is locked in a non-range lock. If
3825      * the key is > K1, the slot is locked in a range lock.
3826      *
3827      * If no slot is found, lock the EOF with a range lock.
3828      *
3829      * Note: On return from this method no latches are held by this cursor.
3830      *
3831      * Note: This Cursor's locker should be a Txn, so there are no non-
3832      * transactional locks to be released.
3833      *
3834      * @param key It is used as the search key, as well as to receive the key
3835      * of the BIN slot found by this method, if any. If the DB contains
3836      * duplicates, the key is in the "two-part-key" format (see
3837      * dbi/DupKeyData.java) so that it can be compared with the two-part keys
3838      * stored in the BTree (which contain both a primary key and a data
3839      * portion). The search key itself may or may not contain a data portion.
3840      *
3841      * @param data A DatabaseEntry to receive the data of the LN associated
3842      * with the found slot, if any. If the DB contains duplicates, it is equal
3843      * to NO_RETURN_DATA, because the LN will be emtpy (the full record is
3844      * contained in the key).
3845      *
3846      * @param searchLockType LockType to use for locking the slot if its key
3847      * is == search key. Normally, this is a READ or WRITE lock.
3848      *
3849      * @param advanceLockType LockType to use for locking the slot if its key
3850      * is > search key. Normally, this is a READ_RANGE or WRITE_RANGE lock.
3851      *
3852      * @param comparator Comparator to use to compare the search key against
3853      * the BTree keys.
3854      *
3855      * @param searchMode If SET or BOTH, we are actually looking for an exact
3856      * match on K1. If so and K1 is not in the BTree, we want the cursor to
3857      * advance temporarily to the next slot in order to range-lock it, but
3858      * then return NOTFOUND. NOTFOUND is returned also if K1 is found, but
3859      * searchMode is BOTH and the data associated with the K1 slot does not
3860      * match the given data.
3861      *
3862      * @return NOTFOUND if no valid slot exists in the [K1, K2) range, or
3863      * checkForExactKey == true and the key of the found slot is > K1; SUCCESS
3864      * otherwise.
3865      *
3866      * @throws RangeRestartException if the search should be restarted by the
3867      * caller.
3868      */
searchRangeSerializable( final DatabaseEntry key, final DatabaseEntry data, final LockType searchLockType, final LockType advanceLockType, final Comparator<byte[]> comparator, final SearchMode searchMode)3869     private OperationStatus searchRangeSerializable(
3870         final DatabaseEntry key,
3871         final DatabaseEntry data,
3872         final LockType searchLockType,
3873         final LockType advanceLockType,
3874         final Comparator<byte[]> comparator,
3875         final SearchMode searchMode)
3876         throws RangeRestartException {
3877 
3878         assert(key != null && data != null);
3879 
3880         boolean success = false;
3881         boolean incStats = (thrput != null);
3882 
3883         OperationStatus status = OperationStatus.NOTFOUND;
3884         boolean exactSearch = searchMode.isExactSearch();
3885         boolean keyChange = false;
3886         boolean mustLockEOF = false;
3887 
3888         DatabaseEntry origData = null;
3889         if (exactSearch) {
3890             origData = new DatabaseEntry(
3891                 data.getData(), data.getOffset(), data.getSize());
3892         }
3893 
3894         final CursorImpl dup = beginMoveCursor(false /*samePosition*/);
3895 
3896         try {
3897             /* Search for a BIN slot whose key is the max key <= K1. */
3898             final int searchResult = dup.searchRange(key, comparator);
3899 
3900             if ((searchResult & CursorImpl.FOUND) != 0) {
3901 
3902                 /*
3903                  * The search positioned dup on the BIN that should contain K1
3904                  * and this BIN is now latched. If the BIN does contain K1,
3905                  * dup.index points to K1's slot. Otherwise, dup.index points
3906                  * to the right-most slot whose key is < K1 (or dup.index is -1
3907                  * if K1 is < than all keys in the BIN). Note: if foundLast is
3908                  * true, dup is positioned on the very last slot of the BTree.
3909                  */
3910                 final boolean exactKeyMatch =
3911                     ((searchResult & CursorImpl.EXACT_KEY) != 0);
3912                 final boolean foundLast =
3913                     ((searchResult & CursorImpl.FOUND_LAST) != 0);
3914 
3915                 /*
3916                  * If we found K1, lock the slot and check whether it is valid.
3917                  * If so, copy out its key and associated LN.
3918                  */
3919                 if (exactKeyMatch) {
3920                     status = dup.lockAndGetCurrent(
3921                         key, data, searchLockType, false /*dirtyReadAll*/,
3922                         true /*isLatched*/, false /*unlatch*/);
3923                 }
3924 
3925                 /*
3926                  * If K1 is not in the BTree or its slot is not valid, advance
3927                  * dup until (a) there are no more slots, or (b) we find a
3928                  * valid slot. If (b), check whether the slot key is < K1. This
3929                  * can happen if K1 was not in the BTree (so dup is now on a
3930                  * key K0 < K1) and another txn inserted new keys < K1 while we
3931                  * were trying to advance dup. If so, a RestartException is
3932                  * thrown. Otherwise, the slot key and LN are copied into "key"
3933                  * and "data" (if "key"/"data" request so).
3934                  */
3935                 if (!exactKeyMatch || status == OperationStatus.KEYEMPTY) {
3936                     status = OperationStatus.NOTFOUND;
3937                     if (!foundLast) {
3938                         status = searchRangeAdvanceAndCheckKey(
3939                             dup, key, data, advanceLockType,
3940                             false /*dirtyReadAll*/, comparator,
3941                             null /*rangeConstraint*/);
3942 
3943                         keyChange = (status == OperationStatus.SUCCESS);
3944                         incStats = false;
3945                     }
3946 
3947                     mustLockEOF = (status != OperationStatus.SUCCESS);
3948                 }
3949 
3950                 /*
3951                  * Consider this search op a failure if we are actually looking
3952                  * for an exact key match and we didn't find the search key.
3953                  */
3954                 if (status == OperationStatus.SUCCESS && exactSearch) {
3955                     if (keyChange) {
3956                         status = OperationStatus.NOTFOUND;
3957                     } else if (searchMode == SearchMode.BOTH) {
3958                         if (checkDataMatch(origData, data)) {
3959                             status = OperationStatus.SUCCESS;
3960                         } else {
3961                             status = OperationStatus.NOTFOUND;
3962                         }
3963                     }
3964                 }
3965 
3966                 /* Finally check rangeConstraint. */
3967                 if (status == OperationStatus.SUCCESS &&
3968                     !exactSearch &&
3969                     !checkRangeConstraint(key)) {
3970                     status = OperationStatus.NOTFOUND;
3971                 }
3972             } else {
3973                 /* The tree is completely empty (has no nodes at all) */
3974                 mustLockEOF = true;
3975             }
3976 
3977             success = true;
3978 
3979         } finally {
3980 
3981             if (success &&
3982                 incStats &&
3983                 dup.getBIN() != null &&
3984                 dup.getBIN().isBINDelta()) {
3985                 thrput.increment(ThroughputStatGroup.BIN_DELTA_GETS_OFFSET);
3986             }
3987 
3988             dup.releaseBIN();
3989             endMoveCursor(dup, status == OperationStatus.SUCCESS);
3990         }
3991 
3992         /*
3993          * Lock the EOF node if no records follow the key.
3994          *
3995          * BUG ????? At this point no latches are held by this cursor, so
3996          * another transaction can insert new slots at the end of the DB
3997          * and then commit. I think the fix is to request the eof lock in
3998          * non-blocking mode with the BIN latched and restart the search
3999          * if the lock is denied.
4000          */
4001         if (mustLockEOF) {
4002             cursorImpl.lockEof(LockType.RANGE_READ);
4003         }
4004 
4005         return status;
4006     }
4007 
4008     /*
4009      * Helper method for searchRange and searchRangeSerializable
4010      *
4011      * @throws RangeRestartException if the search should be restarted by the
4012      * caller.
4013      */
searchRangeAdvanceAndCheckKey( final CursorImpl dup, final DatabaseEntry key, final DatabaseEntry data, final LockType lockType, final boolean dirtyReadAll, Comparator<byte[]> comparator, final RangeConstraint rangeConstraint)4014     private OperationStatus searchRangeAdvanceAndCheckKey(
4015         final CursorImpl dup,
4016         final DatabaseEntry key,
4017         final DatabaseEntry data,
4018         final LockType lockType,
4019         final boolean dirtyReadAll,
4020         Comparator<byte[]> comparator,
4021         final RangeConstraint rangeConstraint)
4022         throws RangeRestartException {
4023 
4024         if (comparator == null) {
4025             comparator = dbImpl.getKeyComparator();
4026         }
4027 
4028         DatabaseEntry origKey = new DatabaseEntry(
4029             key.getData(), key.getOffset(), key.getSize());
4030 
4031         DatabaseEntry nextKey = key;
4032         if (key.getPartial()) {
4033             nextKey = new DatabaseEntry(
4034                 key.getData(), key.getOffset(), key.getSize());
4035         }
4036 
4037         OperationStatus status = dup.getNext(
4038             nextKey, data, lockType, dirtyReadAll, true /*forward*/,
4039             true /*isLatched*/, rangeConstraint);
4040 
4041         /*
4042          * Check whether the dup.getNext() landed on slot whose key is < K1.
4043          * This can happen if K1 was not in the BTree (so before dup.getNext()
4044          * is called, dup is on a key K0 < K1) and another txn inserted new
4045          * keys < K1 while we were trying to advance dup. Such an insertion is
4046          * possible because if dup must move to the next BIN, it releases all
4047          * latches for a while, so the inserter can come in, split the current
4048          * BIN and insert its keys on the right split-sibling. Finally, dup
4049          * moves to the right split-sibling and lands on a wrong slot.
4050          */
4051         if (status == OperationStatus.SUCCESS) {
4052             int c = Key.compareKeys(nextKey, origKey, comparator);
4053             if (c < 0) {
4054                 key.setData(origKey.getData(),
4055                             origKey.getOffset(),
4056                             origKey.getSize());
4057 
4058                 throw new RangeRestartException();
4059 
4060             } else if (key.getPartial()) {
4061                 LN.setEntry(key, nextKey);
4062             }
4063         }
4064 
4065         return status;
4066     }
4067 
4068     /**
4069      * For a non-duplicates database, the data must match exactly when
4070      * getSearchBoth or getSearchBothRange is called.
4071      */
checkDataMatch( DatabaseEntry data1, DatabaseEntry data2)4072     private boolean checkDataMatch(
4073         DatabaseEntry data1,
4074         DatabaseEntry data2) {
4075 
4076         final int size1 = data1.getSize();
4077         final int size2 = data2.getSize();
4078         if (size1 != size2) {
4079             return false;
4080         }
4081         return Key.compareUnsignedBytes(
4082             data1.getData(), data1.getOffset(), size1,
4083             data2.getData(), data2.getOffset(), size2) == 0;
4084     }
4085 
4086     /**
4087      * Counts duplicates without parameter checking.  No need to dup the cursor
4088      * because we never change the position.
4089      */
countInternal()4090     int countInternal() {
4091         synchronized (getTxnSynchronizer()) {
4092             checkTxnState();
4093             if (dbImpl.getSortedDuplicates()) {
4094                 return countHandleDups();
4095             }
4096             return countNoDups();
4097         }
4098     }
4099 
4100     /**
4101      * Count duplicates by skipping over the entries in the dup set key range.
4102      */
countHandleDups()4103     private int countHandleDups() {
4104         final byte[] currentKey = cursorImpl.getCurrentKey();
4105         final DatabaseEntry twoPartKey = DupKeyData.removeData(currentKey);
4106 
4107         final Cursor c = dup(false /*samePosition*/);
4108         try {
4109             c.setNonSticky(true);
4110             setPrefixConstraint(c, currentKey);
4111 
4112             /* Move cursor to first key in this dup set. */
4113             OperationStatus status = c.searchNoDups(
4114                 twoPartKey, NO_RETURN_DATA, LockMode.READ_UNCOMMITTED,
4115                 SearchMode.SET_RANGE, null /*comparator*/);
4116 
4117             if (status != OperationStatus.SUCCESS) {
4118                 return 0;
4119             }
4120 
4121             /* Skip over entries in the dup set. */
4122             long count = 1 + c.cursorImpl.skip(
4123                 true /*forward*/, 0 /*maxCount*/, c.rangeConstraint);
4124 
4125             if (count > Integer.MAX_VALUE) {
4126                 throw new IllegalStateException(
4127                     "count exceeded integer size: " + count);
4128             }
4129 
4130             return (int) count;
4131 
4132         } finally {
4133             c.close();
4134         }
4135     }
4136 
4137     /**
4138      * When there are no duplicates, the count is either 0 or 1, and is very
4139      * cheap to determine.
4140      */
countNoDups()4141     private int countNoDups() {
4142         try {
4143             beginUseExistingCursor();
4144 
4145             final OperationStatus status = cursorImpl.lockAndGetCurrent(
4146                 null /*foundKey*/, null /*foundData*/, LockType.NONE);
4147 
4148             endUseExistingCursor();
4149 
4150             return (status == OperationStatus.SUCCESS) ? 1 : 0;
4151         } catch (Error E) {
4152             dbImpl.getEnv().invalidate(E);
4153             throw E;
4154         }
4155     }
4156 
4157     /**
4158      * Estimates duplicate count without parameter checking.  No need to dup
4159      * the cursor because we never change the position.
4160      */
countEstimateInternal()4161     long countEstimateInternal() {
4162         if (dbImpl.getSortedDuplicates()) {
4163             return countEstimateHandleDups();
4164         }
4165         return countNoDups();
4166     }
4167 
4168     /**
4169      * Estimate duplicate count using the end point positions.
4170      */
countEstimateHandleDups()4171     private long countEstimateHandleDups() {
4172         final byte[] currentKey = cursorImpl.getCurrentKey();
4173         final DatabaseEntry twoPartKey = DupKeyData.removeData(currentKey);
4174 
4175         final Cursor c1 = dup(false /*samePosition*/);
4176         try {
4177             c1.setNonSticky(true);
4178             setPrefixConstraint(c1, currentKey);
4179 
4180             /* Move cursor 1 to first key in this dup set. */
4181             OperationStatus status = c1.searchNoDups(
4182                 twoPartKey, NO_RETURN_DATA, LockMode.READ_UNCOMMITTED,
4183                 SearchMode.SET_RANGE, null /*comparator*/);
4184 
4185             if (status != OperationStatus.SUCCESS) {
4186                 return 0;
4187             }
4188 
4189             /* Move cursor 2 to first key in the following dup set. */
4190             final Cursor c2 = c1.dup(true /*samePosition*/);
4191             try {
4192                 c2.setNonSticky(true);
4193 
4194                 status = c2.dupsGetNextNoDup(
4195                     twoPartKey, NO_RETURN_DATA, LockMode.READ_UNCOMMITTED);
4196 
4197                 final boolean c2Inclusive;
4198                 if (status == OperationStatus.SUCCESS) {
4199                     c2Inclusive = false;
4200                 } else {
4201                     c2Inclusive = true;
4202 
4203                     /*
4204                      * There is no following dup set.  Go to the last record in
4205                      * the database.  If we land on a newly inserted dup set,
4206                      * go to the prev record until we find the last record in
4207                      * the original dup set.
4208                      */
4209                     status = c2.positionNoDups(
4210                         twoPartKey, NO_RETURN_DATA, LockMode.READ_UNCOMMITTED,
4211                         false /*first*/);
4212 
4213                     if (status != OperationStatus.SUCCESS) {
4214                         return 0;
4215                     }
4216 
4217                     while (!haveSameDupPrefix(twoPartKey, currentKey)) {
4218                         status = c2.retrieveNextNoDups(
4219                             twoPartKey, NO_RETURN_DATA,
4220                             LockMode.READ_UNCOMMITTED, GetMode.PREV);
4221 
4222                         if (status != OperationStatus.SUCCESS) {
4223                             return 0;
4224                         }
4225                     }
4226                 }
4227 
4228                 /* Estimate the count between the two cursor positions. */
4229                 return CountEstimator.count(
4230                     dbImpl, c1.cursorImpl, true, c2.cursorImpl, c2Inclusive);
4231 
4232             } finally {
4233                 c2.close();
4234             }
4235         } finally {
4236             c1.close();
4237         }
4238     }
4239 
4240     /**
4241      * Reads the primary data for a primary key that was retrieved from a
4242      * secondary DB via this secondary cursor ("this" may also be a regular
4243      * Cursor in the role of a secondary cursor).  This method is in the
4244      * Cursor class, rather than in SecondaryCursor, to support joins with
4245      * plain Cursors [#21258].
4246      *
4247      * When SUCCESS is returned by this method, the caller should return
4248      * SUCCESS.  When KEYEMPTY is returned, the caller should treat this as a
4249      * deleted record and either skip the record (in the case of position,
4250      * search, and retrieveNext) or return KEYEMPTY (in the case of
4251      * getCurrent).  KEYEMPTY is only returned when read-uncommitted is used.
4252      *
4253      * @param priDb primary database as input.
4254      *
4255      * @param key secondary key as input.
4256      *
4257      * @param pKey key as input.
4258      *
4259      * @param data the data returned as output.
4260      *
4261      * @param lockMode the lock mode to use for the primary read; if null, use
4262      * the default lock mode.
4263      *
4264      * @param secDirtyRead whether we used dirty-read for reading the secondary
4265      * record.  It is true if the user's configured isolation mode (or lockMode
4266      * param) is dirty-read, or we used dirty-read for the secondary read to
4267      * avoid deadlocks (this is done when the user's isolation mode is
4268      * READ_COMMITTED or REPEATABLE_READ).
4269      *
4270      * @param lockPrimaryOnly If false, then we are not using dirty-read for
4271      * secondary deadlock avoidance.  If true, this secondary cursor's
4272      * reference to the primary will be checked after the primary record has
4273      * been locked.
4274      *
4275      * @return status plus primary record version.  The status is SUCCESS if
4276      * the primary was read successfully, or KEYEMPTY if using read-uncommitted
4277      * and the primary has been deleted, or KEYEMPTY if using read-uncommitted
4278      * and the primary has been updated and no longer contains the secondary
4279      * key.
4280      *
4281      * @throws SecondaryIntegrityException to indicate a corrupt secondary
4282      * reference if the primary record is not found and read-uncommitted is not
4283      * used.
4284      */
readPrimaryAfterGet( final Database priDb, final DatabaseEntry key, final DatabaseEntry pKey, DatabaseEntry data, final LockMode lockMode, final boolean secDirtyRead, final boolean lockPrimaryOnly)4285     Pair<OperationStatus, RecordVersion> readPrimaryAfterGet(
4286         final Database priDb,
4287         final DatabaseEntry key,
4288         final DatabaseEntry pKey,
4289         DatabaseEntry data,
4290         final LockMode lockMode,
4291         final boolean secDirtyRead,
4292         final boolean lockPrimaryOnly) {
4293 
4294         final boolean priDirtyRead = isReadUncommittedMode(lockMode);
4295 
4296         /*
4297          * If we only lock the primary (and check the sec cursor), we must be
4298          * using sec dirty-read for deadlock avoidance (whether or not the user
4299          * requested dirty-read). Otherwise, we should be using sec dirty-read
4300          * iff the user requested it.
4301          */
4302         if (lockPrimaryOnly) {
4303             assert secDirtyRead;
4304         } else {
4305             assert secDirtyRead == priDirtyRead;
4306         }
4307 
4308         /*
4309          * There is no need to read the primary if no data is requested. In
4310          * this case a lock on the secondary has been acquired (if the caller
4311          * did not specify dirty-read).
4312          */
4313         if (data.getPartial() && data.getPartialLength() == 0) {
4314             data.setData(LogUtils.ZERO_LENGTH_BYTE_ARRAY);
4315             return new Pair<>(OperationStatus.SUCCESS, null);
4316         }
4317 
4318         /*
4319          * If partial data is requested along with read-uncommitted, then we
4320          * must read all data in order to call the key creator below. [#14966]
4321          */
4322         DatabaseEntry copyToPartialEntry = null;
4323 
4324         if (priDirtyRead && data.getPartial()) {
4325             copyToPartialEntry = data;
4326             data = new DatabaseEntry();
4327         }
4328 
4329         /*
4330          * Do not release non-transactional locks when reading the primary
4331          * cursor.  They are held until all locks for this operation are
4332          * released by the secondary cursor.  [#15573]
4333          */
4334         final CursorImpl priCursor = new CursorImpl(
4335             priDb.getDatabaseImpl(), cursorImpl.getLocker(),
4336             true /*retainNonTxnLocks*/, false /*isSecondaryCursor*/);
4337 
4338         try {
4339 
4340             /*
4341              * Do not rely on a default/null lock mode for dirty-read, since
4342              * the primary cursor will not have the same default lock mode.
4343              */
4344             final LockMode priLockMode;
4345             if (priDirtyRead) {
4346                 if (lockMode == LockMode.READ_UNCOMMITTED_ALL) {
4347                     priLockMode = LockMode.READ_UNCOMMITTED_ALL;
4348                 } else {
4349                     priLockMode = LockMode.READ_UNCOMMITTED;
4350                 }
4351             } else {
4352                 priLockMode = lockMode;
4353             }
4354 
4355             final LockType priLockType = getLockType(priLockMode, false);
4356 
4357             final boolean dirtyReadAll =
4358                 priLockMode == LockMode.READ_UNCOMMITTED_ALL;
4359 
4360             final boolean dataRequested =
4361                 !data.getPartial() || data.getPartialLength() != 0;
4362 
4363             LockStanding priLockStanding = priCursor.searchExact(
4364                 pKey, priLockType, dirtyReadAll, dataRequested);
4365 
4366             if (priLockStanding != null) {
4367                 priCursor.getCurrent(null, data);
4368             }
4369 
4370             priCursor.releaseBIN();
4371 
4372             if (priLockStanding != null && lockPrimaryOnly) {
4373                 if (!checkReferenceToPrimary(pKey, priLockType)) {
4374                     priCursor.revertLock(priLockStanding);
4375                     priLockStanding = null;
4376                 }
4377             }
4378 
4379             if (priLockStanding == null) {
4380 
4381                 /*
4382                  * If using read-uncommitted and the primary is deleted, the
4383                  * primary must have been deleted after reading the secondary.
4384                  * We cannot verify this by checking if the secondary is
4385                  * deleted, because it may have been reinserted.  Instead, we
4386                  * simply return KEYEMPTY to skip this record.  [#22603]
4387                  */
4388                 if (secDirtyRead) {
4389                     return new Pair<>(OperationStatus.KEYEMPTY, null);
4390                 }
4391 
4392                 /*
4393                  * When the primary is deleted, secondary keys are deleted
4394                  * first.  So if the above check fails, we know the secondary
4395                  * reference is corrupt and retries will not be productive.
4396                  */
4397                 throw dbHandle.secondaryRefersToMissingPrimaryKey(
4398                     cursorImpl.getLocker(), key, pKey);
4399             }
4400 
4401             /*
4402              * If using read-uncommitted and the primary was found, check to
4403              * see if primary was updated so that it no longer contains the
4404              * secondary key.  If it has been, return KEYEMPTY.
4405              */
4406             if (priDirtyRead && checkForPrimaryUpdate(key, pKey, data)) {
4407                 return new Pair<>(OperationStatus.KEYEMPTY, null);
4408             }
4409 
4410             /*
4411              * When a partial entry was requested but we read all the data,
4412              * copy the requested partial data to the caller's entry. [#14966]
4413              */
4414             if (copyToPartialEntry != null) {
4415                 LN.setEntry(copyToPartialEntry, data.getData());
4416             }
4417 
4418             return new Pair<>(
4419                 OperationStatus.SUCCESS,
4420                 priCursor.getCachedRecordVersion());
4421         } finally {
4422             priCursor.close();
4423         }
4424     }
4425 
4426     /**
4427      * Checks whether this secondary cursor still refers to the primary key.
4428      *
4429      * This is used for deadlock avoidance with secondary DBs.  The initial
4430      * secondary index read is done without locking.  After the primary has
4431      * been locked, we check here to insure that the primary/secondary
4432      * relationship is still in place. If the secondary DB has duplicates, the
4433      * key contains the sec/pri relationship and the presence of the record is
4434      * sufficient to insure the sec/pri relationship. However, if the
4435      * secondary DB does not allow duplicates, then the primary key (the data
4436      * of the secondary record) must be compared to the original search key.
4437      */
checkReferenceToPrimary( final DatabaseEntry matchKey, final LockType lockType)4438     private boolean checkReferenceToPrimary(
4439         final DatabaseEntry matchKey,
4440         final LockType lockType) {
4441 
4442         assert lockType != LockType.NONE;
4443 
4444         boolean refersToPrimary = true;
4445 
4446         if (!cursorImpl.hasDuplicates()) {
4447             final DatabaseEntry priData = new DatabaseEntry();
4448 
4449             /* get the primary key value without taking locks. */
4450             if (cursorImpl.lockAndGetCurrent(null, priData, LockType.NONE) !=
4451                 OperationStatus.SUCCESS) {
4452                 refersToPrimary = false;
4453             } else {
4454                 if (!priData.equals(matchKey)) {
4455                     refersToPrimary = false;
4456                 }
4457             }
4458         }
4459 
4460         if (refersToPrimary) {
4461 
4462             /*
4463              * To check whether the reference is still valid, because the
4464              * primary is locked and the secondary can only be deleted after
4465              * locking the primary, it is sufficient to check whether the
4466              * secondary PD and KD flags are set. There is no need to lock the
4467              * secondary, because it is protected from changes by the lock on
4468              * the primary.
4469              *
4470              * If this technique were used with serialization isolation then
4471              * checking the PD/KD flags wouldn't be sufficient -- locking the
4472              * secondary would be necessary to prevent phantoms.  With
4473              * serializable isolation, a lock on the secondary record is
4474              * acquired up front by SecondaryCursor.
4475              */
4476             cursorImpl.latchBIN();
4477             try {
4478                 final BIN bin = cursorImpl.getBIN();
4479                 final int index = cursorImpl.getIndex();
4480                 if (bin.isEntryPendingDeleted(index) ||
4481                     bin.isEntryKnownDeleted(index)) {
4482                     refersToPrimary = false;
4483                 }
4484             } finally {
4485                 cursorImpl.releaseBIN();
4486             }
4487         }
4488         return refersToPrimary;
4489     }
4490 
4491     /**
4492      * Checks for a secondary corruption caused by a primary record update
4493      * during a read-uncommitted read.  Checking in this method is not possible
4494      * because there is no secondary key creator available.  It is overridden
4495      * by SecondaryCursor.
4496      *
4497      * This method is in the Cursor class, rather than in SecondaryCursor, to
4498      * support joins with plain Cursors [#21258].
4499      */
checkForPrimaryUpdate( final DatabaseEntry key, final DatabaseEntry pKey, final DatabaseEntry data)4500     boolean checkForPrimaryUpdate(
4501         final DatabaseEntry key,
4502         final DatabaseEntry pKey,
4503         final DatabaseEntry data) {
4504         return false;
4505     }
4506 
4507     /**
4508      * Returns whether the two keys have the same prefix.
4509      *
4510      * @param twoPartKey1 combined key with zero offset and size equal to the
4511      * data array length.
4512      *
4513      * @param keyBytes2 combined key byte array.
4514      */
haveSameDupPrefix( final DatabaseEntry twoPartKey1, final byte[] keyBytes2)4515     private boolean haveSameDupPrefix(
4516         final DatabaseEntry twoPartKey1,
4517         final byte[] keyBytes2) {
4518 
4519         assert twoPartKey1.getOffset() == 0;
4520         assert twoPartKey1.getData().length == twoPartKey1.getSize();
4521 
4522         return DupKeyData.compareMainKey(
4523             twoPartKey1.getData(), keyBytes2,
4524             dbImpl.getBtreeComparator()) == 0;
4525     }
4526 
4527     /**
4528      * Called to start an operation that potentially moves the cursor.
4529      *
4530      * If the cursor is not initialized already, the method simply returns
4531      * this.cursorImpl. This avoids the overhead of cloning this.cursorImpl
4532      * when this is a sticky cursor or forceClone is true.
4533      *
4534      * If the cursor is initialized, the actions taken here depend on whether
4535      * cloning is required (either because this is a sticky cursor or
4536      * because forceClone is true).
4537      *
4538      * (a) No cloning:
4539      * - If same position is true, (1) the current LN (if any) is evicted, if
4540      *   the cachemode is EVICT_LN, and (2) non-txn locks are released, if
4541      *   retainNonTxnLocks is false. this.cursorImpl remains registered at its
4542      *   current BIN.
4543      * - If same position is false, this.cursorImpl is "reset", i.e., (1) it is
4544      *   deregistered from its current position, (2) cachemode eviction is
4545      *   performed, (3) non-txn locks are released, if retainNonTxnLocks is
4546      *   false, and (4) this.cursorImpl is marked unintialized.
4547      * - this.cursorImpl is returned.
4548      *
4549      * Note: In cases where only non-transactional locks are held, releasing
4550      * them before the move prevents more than one lock from being held during
4551      * a cursor move, which helps to avoid deadlocks.
4552      *
4553      * (b) Cloning:
4554      * - this.cursorImpl is cloned.
4555      * - If same position is true, the clone is registered at the same position
4556      *   as this.cursorImpl.
4557      * - If same position is false, the clone is marked unitialized.
4558      * - If this.cursorImpl uses a locker that may acquire non-txn locks and
4559      *   retainNonTxnLocks is false, the clone cursorImpl gets a new locker
4560      *   of the same kind as this.cursorImpl. This allows for the non-txn locks
4561      *   acquired by the clone to be released independently from the non-txn
4562      *   locks of this.cursorImpl.
4563      * - The clone cursorImpl is returned.
4564      *
4565      * In all cases, critical eviction is performed, if necessary, before the
4566      * method returns. This is done by CursorImpl.cloneCursor()/reset(), or is
4567      * done here explicitly when the cursor is not cloned or reset.
4568      *
4569      * In all cases, the cursor returned must be passed to endMoveCursor() to
4570      * close the correct cursor.
4571      *
4572      * @param samePosition If true, this cursor's position is used for the new
4573      * cursor and addCursor is called on the new cursor; if non-sticky, this
4574      * cursor's position is unchanged.  If false, the new cursor will be
4575      * uninitialized; if non-sticky, this cursor is reset.
4576      *
4577      * @param forceClone is true to clone an initialized cursor even if
4578      * non-sticky is configured.  Used when cloning is needed to support
4579      * internal algorithms, namely when the algorithm may restart the operation
4580      * and samePosition is true.
4581      *
4582      * @see CursorImpl#performCacheEviction for a description of how the
4583      * cacheMode field is used.  This method ensures that the correct cache
4584      * mode is used before each operation.
4585      */
beginMoveCursor( final boolean samePosition, final boolean forceClone)4586     private CursorImpl beginMoveCursor(
4587         final boolean samePosition,
4588         final boolean forceClone) {
4589 
4590         /*
4591          * It don't make sense to force cloning if the new cursor will be
4592          * uninitialized.
4593          */
4594         assert !(forceClone && !samePosition);
4595 
4596         /* Must set cache mode before calling criticalEviction or reset. */
4597         cursorImpl.setCacheMode(cacheMode);
4598 
4599         if (cursorImpl.isNotInitialized()) {
4600             cursorImpl.criticalEviction();
4601             return cursorImpl;
4602         }
4603 
4604         if (nonSticky && !forceClone) {
4605             if (samePosition) {
4606                 cursorImpl.beforeAdvance();
4607             } else {
4608                 cursorImpl.reset();
4609             }
4610             return cursorImpl;
4611         }
4612 
4613         final CursorImpl dup = cursorImpl.cloneCursor(samePosition);
4614         dup.setClosingLocker(cursorImpl);
4615         return dup;
4616     }
4617 
beginMoveCursor(final boolean samePosition)4618     private CursorImpl beginMoveCursor(final boolean samePosition) {
4619         return beginMoveCursor(samePosition, false /*forceClone*/);
4620     }
4621 
4622     /**
4623      * Called to end an operation that potentially moves the cursor.
4624      *
4625      * The actions taken here depend on whether cloning was done in
4626      * beginMoveCursor() or not:
4627      *
4628      * (a) No cloning:
4629      * - If the op is successfull, only critical eviction is done.
4630      * - If the op is not successfull, this.cursorImpl is "reset", i.e.,
4631      *   (1) it is deregistered from its current position, (2) cachemode
4632      *   eviction is performed, (3) non-txn locks are released, if
4633      *   retainNonTxnLocks is false, and (4) this.cursorImpl is marked
4634      *   unintialized.
4635      *
4636      * (b) Cloning:
4637      * - If the op is successful, this.cursorImpl is closed and then it is
4638      *   set to the clone cursorImpl.
4639      * - If the op is not successfull, the clone cursorImpl is closed.
4640      * - In either case, closing a cursorImpl involves deregistering it from
4641      *   its current position, performing cachemode eviction, releasing its
4642      *   non-transactional locks and closing its locker, if retainNonTxnLocks
4643      *   is false and the locker is not a Txn, and finally marking the
4644      *   cursorImpl as closed.
4645      *
4646      * In all cases, critical eviction is performed after each cursor operation.
4647      * This is done by CursorImpl.reset() and close(), or is done here explicitly
4648      * when the cursor is not cloned.
4649      */
endMoveCursor(final CursorImpl dup, final boolean success)4650     private void endMoveCursor(final CursorImpl dup, final boolean success) {
4651 
4652         dup.clearClosingLocker();
4653 
4654         if (dup == cursorImpl) {
4655             if (success) {
4656                 cursorImpl.criticalEviction();
4657             } else {
4658                 cursorImpl.reset();
4659             }
4660         } else {
4661             if (success) {
4662                 cursorImpl.close(dup);
4663                 cursorImpl = dup;
4664             } else {
4665                 dup.close(cursorImpl);
4666             }
4667         }
4668     }
4669 
4670     /**
4671      * Called to start an operation that does not move the cursor, and
4672      * therefore does not clone the cursor.  Either beginUseExistingCursor /
4673      * endUseExistingCursor or beginMoveCursor / endMoveCursor must be used for
4674      * each operation.
4675      */
beginUseExistingCursor()4676     private void beginUseExistingCursor() {
4677         /* Must set cache mode before calling criticalEviction. */
4678         cursorImpl.setCacheMode(cacheMode);
4679         cursorImpl.criticalEviction();
4680     }
4681 
4682     /**
4683      * Called to end an operation that does not move the cursor.
4684      */
endUseExistingCursor()4685     private void endUseExistingCursor() {
4686         cursorImpl.criticalEviction();
4687     }
4688 
4689     /**
4690      * Swaps CursorImpl of this cursor and the other cursor given.
4691      */
swapCursor(Cursor other)4692     private void swapCursor(Cursor other) {
4693         final CursorImpl otherImpl = other.cursorImpl;
4694         other.cursorImpl = this.cursorImpl;
4695         this.cursorImpl = otherImpl;
4696     }
4697 
advanceCursor(final DatabaseEntry key, final DatabaseEntry data)4698     boolean advanceCursor(final DatabaseEntry key, final DatabaseEntry data) {
4699         return cursorImpl.advanceCursor(key, data);
4700     }
4701 
getLockType( final LockMode lockMode, final boolean rangeLock)4702     private LockType getLockType(
4703         final LockMode lockMode,
4704         final boolean rangeLock) {
4705 
4706         if (isReadUncommittedMode(lockMode)) {
4707             return LockType.NONE;
4708         } else if (lockMode == null || lockMode == LockMode.DEFAULT) {
4709             return rangeLock ? LockType.RANGE_READ: LockType.READ;
4710         } else if (lockMode == LockMode.RMW) {
4711             return rangeLock ? LockType.RANGE_WRITE: LockType.WRITE;
4712         } else if (lockMode == LockMode.READ_COMMITTED) {
4713             throw new IllegalArgumentException(
4714                 lockMode.toString() + " not allowed with Cursor methods, " +
4715                 "use CursorConfig.setReadCommitted instead.");
4716         } else {
4717             assert false : lockMode;
4718             return LockType.NONE;
4719         }
4720     }
4721 
4722     /**
4723      * Returns whether the given lock mode will cause a read-uncommitted when
4724      * used with this cursor, taking into account the default cursor
4725      * configuration.
4726      */
isReadUncommittedMode(final LockMode lockMode)4727     boolean isReadUncommittedMode(final LockMode lockMode) {
4728 
4729         return (lockMode == LockMode.READ_UNCOMMITTED ||
4730                 lockMode == LockMode.READ_UNCOMMITTED_ALL ||
4731                 (readUncommittedDefault &&
4732                  (lockMode == null || lockMode == LockMode.DEFAULT)));
4733     }
4734 
isSerializableIsolation(final LockMode lockMode)4735     boolean isSerializableIsolation(final LockMode lockMode) {
4736 
4737         return serializableIsolationDefault &&
4738                !isReadUncommittedMode(lockMode);
4739     }
4740 
checkUpdatesAllowed()4741     void checkUpdatesAllowed() {
4742 
4743         if (!updateOperationsProhibited) {
4744             return;
4745         }
4746 
4747         final Locker locker = cursorImpl.getLocker();
4748         final StringBuilder str = new StringBuilder(200);
4749 
4750         str.append("Write operation is not allowed because ");
4751 
4752         /* Be sure to keep this logic in sync with init(). */
4753         if (locker.isReadOnly()) {
4754             str.append("the Transaction is configured as read-only.");
4755         } else if (dbHandle != null && !dbHandle.isWritable()) {
4756             str.append("the Database is configured as read-only.");
4757         } else if (dbImpl.isTransactional() && !locker.isTransactional()) {
4758             str.append("a Transaction was not supplied to openCursor ");
4759             str.append("and the Database is transactional.");
4760         } else if (dbImpl.isReplicated() && locker.isLocalWrite()) {
4761             str.append("the Database is replicated and Transaction is ");
4762             str.append("configured as local-write.");
4763         } else if (!dbImpl.isReplicated() && !locker.isLocalWrite()) {
4764             str.append("the Database is not replicated and the ");
4765             str.append("Transaction is not configured as local-write.");
4766         } else {
4767             assert false;
4768         }
4769 
4770         throw new UnsupportedOperationException(str.toString());
4771     }
4772 
4773     /**
4774      * Note that this flavor of checkArgs allows the key and data to be null.
4775      */
checkArgsNoValRequired( final DatabaseEntry key, final DatabaseEntry data)4776     static void checkArgsNoValRequired(
4777         final DatabaseEntry key,
4778         final DatabaseEntry data) {
4779 
4780         DatabaseUtil.checkForNullDbt(key, "key", false);
4781         DatabaseUtil.checkForNullDbt(data, "data", false);
4782     }
4783 
4784     /**
4785      * Note that this flavor of checkArgs requires that the key and data are
4786      * not null.
4787      */
checkArgsValRequired( final DatabaseEntry key, final DatabaseEntry data)4788     static void checkArgsValRequired(
4789         final DatabaseEntry key,
4790         final DatabaseEntry data) {
4791 
4792         DatabaseUtil.checkForNullDbt(key, "key", true);
4793         DatabaseUtil.checkForNullDbt(data, "data", true);
4794     }
4795 
4796     /**
4797      * Checks the environment and cursor state.
4798      */
checkState(final boolean mustBeInitialized)4799     void checkState(final boolean mustBeInitialized) {
4800         checkEnv();
4801         if (dbHandle != null) {
4802             dbHandle.checkOpen("Can't call Cursor method:");
4803         }
4804         cursorImpl.checkCursorState(
4805             mustBeInitialized, false /*mustNotBeInitialized*/);
4806     }
4807 
4808     /**
4809      * @throws EnvironmentFailureException if the underlying environment is
4810      * invalid.
4811      */
checkEnv()4812     void checkEnv() {
4813         cursorImpl.checkEnv();
4814     }
4815 
4816     /**
4817      * Returns an object used for synchronizing transactions that are used in
4818      * multiple threads.
4819      *
4820      * For a transactional locker, the Transaction is returned to prevent
4821      * concurrent access using this transaction from multiple threads.  The
4822      * Transaction.commit and abort methods are synchronized so they do not run
4823      * concurrently with operations using the Transaction.  Note that the Txn
4824      * cannot be used for synchronization because locking order is BIN first,
4825      * then Txn.
4826      *
4827      * For a non-transactional locker, 'this' is returned because no special
4828      * blocking is needed.  Other mechanisms are used to prevent
4829      * non-transactional usage access by multiple threads (see ThreadLocker).
4830      * In the future we may wish to use the getTxnSynchronizer for
4831      * synchronizing non-transactional access as well; however, note that a new
4832      * locker is created for each operation.
4833      */
getTxnSynchronizer()4834     private Object getTxnSynchronizer() {
4835         return (transaction != null) ? transaction : this;
4836     }
4837 
checkTxnState()4838     private void checkTxnState() {
4839         if (transaction == null) {
4840             return;
4841         }
4842         transaction.checkOpen();
4843         transaction.getTxn().checkState(false /*calledByAbort*/);
4844     }
4845 
4846     /**
4847      * Sends trace messages to the java.util.logger. Don't rely on the logger
4848      * alone to conditionalize whether we send this message, we don't even want
4849      * to construct the message if the level is not enabled.
4850      */
trace( final Level level, final String methodName, final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode)4851     void trace(
4852         final Level level,
4853         final String methodName,
4854         final DatabaseEntry key,
4855         final DatabaseEntry data,
4856         final LockMode lockMode) {
4857 
4858         if (logger.isLoggable(level)) {
4859             final StringBuilder sb = new StringBuilder();
4860             sb.append(methodName);
4861             traceCursorImpl(sb);
4862             if (key != null) {
4863                 sb.append(" key=").append(key.dumpData());
4864             }
4865             if (data != null) {
4866                 sb.append(" data=").append(data.dumpData());
4867             }
4868             if (lockMode != null) {
4869                 sb.append(" lockMode=").append(lockMode);
4870             }
4871             LoggerUtils.logMsg(
4872                 logger, dbImpl.getEnv(), level, sb.toString());
4873         }
4874     }
4875 
4876     /**
4877      * Sends trace messages to the java.util.logger. Don't rely on the logger
4878      * alone to conditionalize whether we send this message, we don't even want
4879      * to construct the message if the level is not enabled.
4880      */
trace( final Level level, final String methodName, final LockMode lockMode)4881     void trace(
4882         final Level level,
4883         final String methodName,
4884         final LockMode lockMode) {
4885 
4886         if (logger.isLoggable(level)) {
4887             final StringBuilder sb = new StringBuilder();
4888             sb.append(methodName);
4889             traceCursorImpl(sb);
4890             if (lockMode != null) {
4891                 sb.append(" lockMode=").append(lockMode);
4892             }
4893             LoggerUtils.logMsg(
4894                 logger, dbImpl.getEnv(), level, sb.toString());
4895         }
4896     }
4897 
traceCursorImpl(final StringBuilder sb)4898     private void traceCursorImpl(final StringBuilder sb) {
4899         sb.append(" locker=").append(cursorImpl.getLocker().getId());
4900         sb.append(" bin=").append(cursorImpl.getCurrentNodeId());
4901         sb.append(" idx=").append(cursorImpl.getIndex());
4902     }
4903 
4904     /**
4905      * Clone entry contents in a new returned entry.
4906      */
cloneEntry(DatabaseEntry from)4907     private static DatabaseEntry cloneEntry(DatabaseEntry from) {
4908         final DatabaseEntry to = new DatabaseEntry();
4909         setEntry(from, to);
4910         return to;
4911     }
4912 
4913     /**
4914      * Copy entry contents to another entry.
4915      */
setEntry(DatabaseEntry from, DatabaseEntry to)4916     private static void setEntry(DatabaseEntry from, DatabaseEntry to) {
4917         to.setPartial(from.getPartialOffset(), from.getPartialLength(),
4918                       from.getPartial());
4919         to.setData(from.getData(), from.getOffset(), from.getSize());
4920     }
4921 }
4922