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