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 static com.sleepycat.je.dbi.DbiStatDefinition.THROUGHPUT_DB_DELETE; 11 import static com.sleepycat.je.dbi.DbiStatDefinition.THROUGHPUT_DB_GET; 12 import static com.sleepycat.je.dbi.DbiStatDefinition.THROUGHPUT_DB_GETSEARCHBOTH; 13 import static com.sleepycat.je.dbi.DbiStatDefinition.THROUGHPUT_DB_PUT; 14 import static com.sleepycat.je.dbi.DbiStatDefinition.THROUGHPUT_DB_PUTNODUPDATA; 15 import static com.sleepycat.je.dbi.DbiStatDefinition.THROUGHPUT_DB_PUTNOOVERWRITE; 16 import static com.sleepycat.je.dbi.DbiStatDefinition.THROUGHPUT_DB_REMOVESEQUENCE; 17 18 import java.io.Closeable; 19 import java.util.ArrayList; 20 import java.util.Collection; 21 import java.util.Collections; 22 import java.util.List; 23 import java.util.concurrent.CopyOnWriteArraySet; 24 import java.util.concurrent.atomic.AtomicInteger; 25 import java.util.logging.Level; 26 import java.util.logging.Logger; 27 28 import com.sleepycat.je.dbi.CursorImpl.SearchMode; 29 import com.sleepycat.je.dbi.DatabaseImpl; 30 import com.sleepycat.je.dbi.EnvironmentImpl; 31 import com.sleepycat.je.dbi.GetMode; 32 import com.sleepycat.je.dbi.PutMode; 33 import com.sleepycat.je.dbi.TriggerManager; 34 import com.sleepycat.je.txn.HandleLocker; 35 import com.sleepycat.je.txn.Locker; 36 import com.sleepycat.je.txn.LockerFactory; 37 import com.sleepycat.je.utilint.AtomicLongStat; 38 import com.sleepycat.je.utilint.DatabaseUtil; 39 import com.sleepycat.je.utilint.LoggerUtils; 40 41 /** 42 * A database handle. 43 * 44 * <p>Database attributes are specified in the {@link 45 * com.sleepycat.je.DatabaseConfig DatabaseConfig} class. Database handles are 46 * free-threaded and may be used concurrently by multiple threads.</p> 47 * 48 * <p>To open an existing database with default attributes:</p> 49 * 50 * <blockquote><pre> 51 * Environment env = new Environment(home, null); 52 * Database myDatabase = env.openDatabase(null, "mydatabase", null); 53 * </pre></blockquote> 54 * 55 * <p>To create a transactional database that supports duplicates:</p> 56 * 57 * <blockquote><pre> 58 * DatabaseConfig dbConfig = new DatabaseConfig(); 59 * dbConfig.setTransactional(true); 60 * dbConfig.setAllowCreate(true); 61 * dbConfig.setSortedDuplicates(true); 62 * Database db = env.openDatabase(txn, "mydatabase", dbConfig); 63 * </pre></blockquote> 64 */ 65 public class Database implements Closeable { 66 67 /* 68 * DbState embodies the Database handle state. 69 */ 70 enum DbState { 71 OPEN, CLOSED, INVALID, PREEMPTED 72 } 73 74 /* The current state of the handle. */ 75 private volatile DbState state; 76 /* The DatabasePreemptedException cause when state == PREEMPTED. */ 77 private OperationFailureException preemptedCause; 78 79 /* Handles onto the owning environment and the databaseImpl object. */ 80 Environment envHandle; // used by subclasses 81 private DatabaseImpl databaseImpl; 82 83 /* 84 * Used to store per-Database handle properties: allow create, 85 * exclusive create, read only and use existing config. Other Database-wide 86 * properties are stored in DatabaseImpl. 87 */ 88 DatabaseConfig configuration; 89 90 /* True if this handle permits write operations; */ 91 private boolean isWritable; 92 93 /* Record how many open cursors on this database. */ 94 private final AtomicInteger openCursors = new AtomicInteger(); 95 96 /* Locker that owns the NameLN lock held while the Database is open. */ 97 private HandleLocker handleLocker; 98 99 /* 100 * If a user-supplied SecondaryAssociation is configured, this field 101 * contains it. Otherwise, it contains an internal SecondaryAssociation 102 * that uses the simpleAssocSecondaries to store associations between a 103 * single primary and its secondaries. 104 */ 105 SecondaryAssociation secAssoc; 106 Collection<SecondaryDatabase> simpleAssocSecondaries; 107 108 /* 109 * Secondaries whose keys have values contrained to the primary keys in 110 * this database. 111 */ 112 Collection<SecondaryDatabase> foreignKeySecondaries; 113 114 private AtomicLongStat deleteStat; 115 private AtomicLongStat getStat; 116 private AtomicLongStat getSearchBothStat; 117 private AtomicLongStat putStat; 118 private AtomicLongStat putNoDupDataStat; 119 private AtomicLongStat putNoOverwriteStat; 120 private AtomicLongStat removeSequenceStat; 121 122 final Logger logger; 123 124 /** 125 * Creates a database but does not open or fully initialize it. Is 126 * protected for use in compat package. 127 * @param env 128 */ Database(final Environment env)129 Database(final Environment env) { 130 this.envHandle = env; 131 handleLocker = null; 132 logger = envHandle.getEnvironmentImpl().getLogger(); 133 } 134 135 /** 136 * Creates a database, called by Environment. 137 */ initNew(final Environment env, final Locker locker, final String databaseName, final DatabaseConfig dbConfig)138 DatabaseImpl initNew(final Environment env, 139 final Locker locker, 140 final String databaseName, 141 final DatabaseConfig dbConfig) 142 throws DatabaseException { 143 144 dbConfig.validateForNewDb(); 145 146 init(env, dbConfig); 147 148 /* Make the databaseImpl. */ 149 EnvironmentImpl environmentImpl = 150 DbInternal.getEnvironmentImpl(envHandle); 151 databaseImpl = environmentImpl.getDbTree().createDb( 152 locker, databaseName, dbConfig, handleLocker); 153 databaseImpl.addReferringHandle(this); 154 return databaseImpl; 155 } 156 157 /** 158 * Opens a database, called by Environment. 159 */ initExisting(final Environment env, final Locker locker, final DatabaseImpl dbImpl, final String databaseName, final DatabaseConfig dbConfig)160 void initExisting(final Environment env, 161 final Locker locker, 162 final DatabaseImpl dbImpl, 163 final String databaseName, 164 final DatabaseConfig dbConfig) 165 throws DatabaseException { 166 167 /* 168 * Make sure the configuration used for the open is compatible with the 169 * existing databaseImpl. 170 */ 171 validateConfigAgainstExistingDb(locker, databaseName, dbConfig, 172 dbImpl); 173 174 init(env, dbConfig); 175 this.databaseImpl = dbImpl; 176 dbImpl.addReferringHandle(this); 177 } 178 init(final Environment env, final DatabaseConfig config)179 private void init(final Environment env, final DatabaseConfig config) { 180 assert handleLocker != null; 181 envHandle = env; 182 configuration = config.cloneConfig(); 183 isWritable = !configuration.getReadOnly(); 184 setupThroughputStats(env.getEnvironmentImpl()); 185 secAssoc = makeSecondaryAssociation(); 186 state = DbState.OPEN; 187 } 188 setupThroughputStats(EnvironmentImpl envImpl)189 private void setupThroughputStats(EnvironmentImpl envImpl) { 190 deleteStat = envImpl.getThroughputStat(THROUGHPUT_DB_DELETE); 191 getStat = envImpl.getThroughputStat(THROUGHPUT_DB_GET); 192 getSearchBothStat = 193 envImpl.getThroughputStat(THROUGHPUT_DB_GETSEARCHBOTH); 194 putStat = envImpl.getThroughputStat(THROUGHPUT_DB_PUT); 195 putNoDupDataStat = 196 envImpl.getThroughputStat(THROUGHPUT_DB_PUTNODUPDATA); 197 putNoOverwriteStat = 198 envImpl.getThroughputStat(THROUGHPUT_DB_PUTNOOVERWRITE); 199 removeSequenceStat = 200 envImpl.getThroughputStat(THROUGHPUT_DB_REMOVESEQUENCE); 201 } 202 makeSecondaryAssociation()203 SecondaryAssociation makeSecondaryAssociation() { 204 foreignKeySecondaries = new CopyOnWriteArraySet<SecondaryDatabase>(); 205 206 if (configuration.getSecondaryAssociation() != null) { 207 if (configuration.getSortedDuplicates()) { 208 throw new IllegalArgumentException( 209 "Duplicates not allowed for a primary database"); 210 } 211 simpleAssocSecondaries = Collections.emptySet(); 212 return configuration.getSecondaryAssociation(); 213 } 214 215 simpleAssocSecondaries = new CopyOnWriteArraySet<SecondaryDatabase>(); 216 217 return new SecondaryAssociation() { 218 219 public boolean isEmpty() { 220 return simpleAssocSecondaries.isEmpty(); 221 } 222 223 public Database getPrimary(@SuppressWarnings("unused") 224 DatabaseEntry primaryKey) { 225 return Database.this; 226 } 227 228 public Collection<SecondaryDatabase> 229 getSecondaries(@SuppressWarnings("unused") 230 DatabaseEntry primaryKey) { 231 return simpleAssocSecondaries; 232 } 233 }; 234 } 235 236 /** 237 * Used to remove references to this database from other objects, when this 238 * database is closed. We don't remove references from cursors or 239 * secondaries here, because it's an error to close a database before its 240 * cursors and to close a primary before its secondaries. 241 */ 242 void removeReferringAssociations() { 243 envHandle.removeReferringHandle(this); 244 } 245 246 /** 247 * Sees if this new handle's configuration is compatible with the 248 * pre-existing database. 249 */ 250 private void validateConfigAgainstExistingDb(Locker locker, 251 final String databaseName, 252 final DatabaseConfig config, 253 final DatabaseImpl dbImpl) 254 throws DatabaseException { 255 256 /* 257 * The sortedDuplicates, temporary, and replicated properties are 258 * persistent and immutable. But they do not need to be specified if 259 * the useExistingConfig property is set. 260 */ 261 if (!config.getUseExistingConfig()) { 262 validatePropertyMatches( 263 "sortedDuplicates", dbImpl.getSortedDuplicates(), 264 config.getSortedDuplicates()); 265 validatePropertyMatches( 266 "temporary", dbImpl.isTemporary(), 267 config.getTemporary()); 268 /* Only check replicated if the environment is replicated. */ 269 if (envHandle.getEnvironmentImpl().isReplicated()) { 270 validatePropertyMatches( 271 "replicated", dbImpl.isReplicated(), 272 config.getReplicated()); 273 } 274 } 275 276 /* 277 * The transactional and deferredWrite properties are kept constant 278 * while any handles are open, and set when the first handle is opened. 279 * But if an existing handle is open and the useExistingConfig property 280 * is set, then they do not need to be specified. 281 */ 282 if (dbImpl.hasOpenHandles()) { 283 if (!config.getUseExistingConfig()) { 284 validatePropertyMatches( 285 "transactional", dbImpl.isTransactional(), 286 config.getTransactional()); 287 validatePropertyMatches( 288 "deferredWrite", dbImpl.isDurableDeferredWrite(), 289 config.getDeferredWrite()); 290 } 291 } else { 292 dbImpl.setTransactional(config.getTransactional()); 293 dbImpl.setDeferredWrite(config.getDeferredWrite()); 294 } 295 296 /* 297 * If this database handle uses the existing config, we shouldn't 298 * search for and write any changed attributes to the log. 299 */ 300 if (config.getUseExistingConfig()) { 301 return; 302 } 303 304 /* Write any changed, persistent attributes to the log. */ 305 boolean dbImplModified = false; 306 307 /* Only re-set the comparators if the override is allowed. */ 308 if (config.getOverrideBtreeComparator()) { 309 dbImplModified |= dbImpl.setBtreeComparator( 310 config.getBtreeComparator(), 311 config.getBtreeComparatorByClassName()); 312 } 313 314 if (config.getOverrideDuplicateComparator()) { 315 dbImplModified |= dbImpl.setDuplicateComparator( 316 config.getDuplicateComparator(), 317 config.getDuplicateComparatorByClassName()); 318 } 319 320 dbImplModified |= dbImpl.setTriggers(locker, 321 databaseName, 322 config.getTriggers(), 323 config.getOverrideTriggers()); 324 325 /* Check if KeyPrefixing property is updated. */ 326 boolean newKeyPrefixing = config.getKeyPrefixing(); 327 if (newKeyPrefixing != dbImpl.getKeyPrefixing()) { 328 dbImplModified = true; 329 if (newKeyPrefixing) { 330 dbImpl.setKeyPrefixing(); 331 } else { 332 dbImpl.clearKeyPrefixing(); 333 } 334 } 335 336 /* 337 * Check if NodeMaxEntries properties are updated. 338 */ 339 int newNodeMaxEntries = config.getNodeMaxEntries(); 340 if (newNodeMaxEntries != 0 && 341 newNodeMaxEntries != dbImpl.getNodeMaxTreeEntries()) { 342 dbImplModified = true; 343 dbImpl.setNodeMaxTreeEntries(newNodeMaxEntries); 344 } 345 346 /* Do not write LNs in a read-only environment. Also see [#15743]. */ 347 EnvironmentImpl envImpl = envHandle.getEnvironmentImpl(); 348 if (dbImplModified && !envImpl.isReadOnly()) { 349 350 /* Write a new NameLN to the log. */ 351 try { 352 envImpl.getDbTree().updateNameLN(locker, dbImpl.getName(), 353 null); 354 } catch (LockConflictException e) { 355 throw new IllegalStateException( 356 "DatabaseConfig properties may not be updated when the " + 357 "database is already open; first close other open " + 358 "handles for this database.", e); 359 } 360 361 /* Dirty the root. */ 362 envImpl.getDbTree().modifyDbRoot(dbImpl); 363 } 364 365 /* 366 * CacheMode and Strategy are changed for all handles, but are not 367 * persistent. 368 */ 369 dbImpl.setCacheMode(config.getCacheMode()); 370 dbImpl.setCacheModeStrategy(config.getCacheModeStrategy()); 371 } 372 373 /** 374 * @throws IllegalArgumentException via Environment.openDatabase and 375 * openSecondaryDatabase. 376 */ 377 private void validatePropertyMatches(final String propName, 378 final boolean existingValue, 379 final boolean newValue) 380 throws IllegalArgumentException { 381 382 if (newValue != existingValue) { 383 throw new IllegalArgumentException( 384 "You can't open a Database with a " + propName + 385 " configuration of " + newValue + 386 " if the underlying database was created with a " + 387 propName + " setting of " + existingValue + '.'); 388 } 389 } 390 391 /** 392 * Discards the database handle. 393 * <p> 394 * When closing the last open handle for a deferred-write database, any 395 * cached database information is flushed to disk as if {@link #sync} were 396 * called. 397 * <p> 398 * The database handle should not be closed while any other handle that 399 * refers to it is not yet closed; for example, database handles should not 400 * be closed while cursor handles into the database remain open, or 401 * transactions that include operations on the database have not yet been 402 * committed or aborted. Specifically, this includes {@link 403 * com.sleepycat.je.Cursor Cursor} and {@link com.sleepycat.je.Transaction 404 * Transaction} handles. 405 * <p> 406 * When multiple threads are using the {@link com.sleepycat.je.Database 407 * Database} handle concurrently, only a single thread may call this 408 * method. 409 * <p> 410 * When called on a database that is the primary database for a secondary 411 * index, the primary database should be closed only after all secondary 412 * indices which reference it have been closed. 413 * <p> 414 * The database handle may not be accessed again after this method is 415 * called, regardless of the method's success or failure, with one 416 * exception: the {@code close} method itself may be called any number of 417 * times.</p> 418 * 419 * <p>WARNING: To guard against memory leaks, the application should 420 * discard all references to the closed handle. While BDB makes an effort 421 * to discard references from closed objects to the allocated memory for an 422 * environment, this behavior is not guaranteed. The safe course of action 423 * for an application is to discard all references to closed BDB 424 * objects.</p> 425 * 426 * @see DatabaseConfig#setDeferredWrite DatabaseConfig.setDeferredWrite 427 * 428 * @throws EnvironmentFailureException if an unexpected, internal or 429 * environment-wide failure occurs. 430 * 431 * @throws IllegalStateException if cursors associated with this database 432 * are still open. 433 */ 434 public void close() 435 throws DatabaseException { 436 437 try { 438 closeInternal(true /*doSyncDw*/, true /*deleteTempDb*/, 439 DbState.CLOSED, null /*preemptedException*/); 440 } catch (Error E) { 441 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 442 throw E; 443 } 444 } 445 446 /* 447 * This method is private for now because it is incomplete. To fully 448 * implement it we must clear all dirty nodes for the database that is 449 * closed, since otherwise they will be flushed during the next checkpoint. 450 */ 451 @SuppressWarnings("unused") 452 private void closeNoSync() 453 throws DatabaseException { 454 455 try { 456 closeInternal(false /*doSyncDw*/, true /*deleteTempDb*/, 457 DbState.CLOSED, null /*preemptedException*/); 458 } catch (Error E) { 459 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 460 throw E; 461 } 462 } 463 464 /** 465 * Marks the handle as preempted when the handle lock is stolen by the HA 466 * replayer, during replay of a naming operation (remove, truncate or 467 * rename). This causes DatabasePreemtpedException to be thrown on all 468 * subsequent use of the handle or cursors opened on this handle. [#17015] 469 */ 470 synchronized void setPreempted(final String dbName, final String msg) { 471 OperationFailureException preemptedException = null; 472 473 /* Bandaid: Avoid an NPE if DB is already closed. [#22827] */ 474 if (databaseImpl != null) { 475 preemptedException = databaseImpl.getEnv(). 476 createDatabasePreemptedException(msg, dbName, this); 477 } 478 479 closeInternal(false /*doSyncDw*/, false /*deleteTempDb*/, 480 DbState.PREEMPTED, preemptedException); 481 } 482 483 /** 484 * Invalidates the handle when the transaction used to open the database 485 * is aborted. 486 * 487 * Note that this method (unlike close) does not perform sync and removal 488 * of DW DBs. A DW DB cannot be transactional. 489 */ 490 synchronized void invalidate() { 491 closeInternal(false /*doSyncDw*/, false /*deleteTempDb*/, 492 DbState.INVALID, null /*preemptedException*/); 493 } 494 495 private void 496 closeInternal(final boolean doSyncDw, 497 final boolean deleteTempDb, 498 final DbState newState, 499 final OperationFailureException preemptedException) 500 throws DatabaseException { 501 502 /* 503 * We acquire the SecondaryAssociationLatch exclusively because 504 * associations are changed by removeReferringAssociations, and 505 * operations using the associations should not run concurrently with 506 * close. 507 */ 508 final EnvironmentImpl envImpl = envHandle.getEnvironmentImpl(); 509 if (envImpl != null) { 510 try { 511 envImpl.getSecondaryAssociationLock(). 512 writeLock().lockInterruptibly(); 513 } catch (InterruptedException e) { 514 throw new ThreadInterruptedException(envImpl, e); 515 } 516 } 517 try { 518 closeInternalWork(doSyncDw, deleteTempDb, newState, 519 preemptedException); 520 } finally { 521 if (envImpl != null) { 522 envImpl.getSecondaryAssociationLock().writeLock().unlock(); 523 } 524 } 525 } 526 527 private void 528 closeInternalWork(final boolean doSyncDw, 529 final boolean deleteTempDb, 530 final DbState newState, 531 final OperationFailureException preemptedException) 532 throws DatabaseException { 533 534 final StringBuilder handleRefErrors = new StringBuilder(); 535 RuntimeException triggerException = null; 536 DatabaseImpl dbClosed = null; 537 538 synchronized (this) { 539 540 /* Do nothing if handle was previously closed. */ 541 if (state != DbState.OPEN) { 542 return; 543 } 544 545 /* 546 * Check env only after checking for closed db, to mimic close() 547 * behavior for Cursors, etc, and avoid unnecessary exception 548 * handling. [#21264] 549 */ 550 checkEnv(); 551 552 /* 553 * The state should be changed ASAP during close, so that 554 * addCursor and removeCursor will see the updated state ASAP. 555 */ 556 state = newState; 557 preemptedCause = preemptedException; 558 559 /* 560 * Throw an IllegalStateException if there are open cursors or 561 * associated secondaries. 562 */ 563 if (newState == DbState.CLOSED) { 564 if (openCursors.get() != 0) { 565 handleRefErrors.append(" "). 566 append(openCursors.get()). 567 append(" open cursors."); 568 } 569 if (simpleAssocSecondaries != null && 570 simpleAssocSecondaries.size() > 0) { 571 handleRefErrors.append(" "). 572 append(simpleAssocSecondaries.size()). 573 append(" associated SecondaryDatabases."); 574 } 575 if (foreignKeySecondaries != null && 576 foreignKeySecondaries.size() > 0) { 577 handleRefErrors.append(" "). 578 append(foreignKeySecondaries.size()). 579 append( 580 " associated foreign key SecondaryDatabases."); 581 } 582 } 583 584 trace(Level.FINEST, "Database.close: ", null, null); 585 586 removeReferringAssociations(); 587 588 if (databaseImpl != null) { 589 dbClosed = databaseImpl; 590 databaseImpl.removeReferringHandle(this); 591 envHandle.getEnvironmentImpl(). 592 getDbTree().releaseDb(databaseImpl); 593 594 /* 595 * Database.close may be called after an abort. By setting the 596 * databaseImpl field to null we ensure that close won't call 597 * releaseDb or endOperation. [#13415] 598 */ 599 databaseImpl = null; 600 601 if (handleLocker != null) { 602 603 /* 604 * If the handle was preempted, we mark the locker as 605 * only-abortable with the DatabasePreemptedException. If 606 * the handle locker is a user txn, this causes the 607 * DatabasePreemptedException to be thrown if the user 608 * attempts to commit, or continue to use, the txn, rather 609 * than throwing a LockConflictException. [#17015] 610 */ 611 if (newState == DbState.PREEMPTED) { 612 handleLocker.setOnlyAbortable(preemptedException); 613 } 614 615 /* 616 * Tell our protecting txn that we're closing. If this type 617 * of transaction doesn't live beyond the life of the 618 * handle, it will release the db handle lock. 619 */ 620 if (newState == DbState.CLOSED) { 621 if (isWritable() && 622 (dbClosed.noteWriteHandleClose() == 0)) { 623 try { 624 TriggerManager.runCloseTriggers(handleLocker, 625 dbClosed); 626 } catch (RuntimeException e) { 627 triggerException = e; 628 } 629 } 630 handleLocker.operationEnd(true); 631 } else { 632 handleLocker.operationEnd(false); 633 } 634 635 /* Null-out indirect reference to environment. */ 636 handleLocker = null; 637 } 638 } 639 } 640 641 /* 642 * Notify the database when a handle is closed. This should not be 643 * done while synchronized since it may perform database removal or 644 * sync. Statements above are synchronized but this section must not 645 * be. 646 * 647 * Note that handleClosed may throw an exception, so any following code 648 * may not be executed. 649 */ 650 if (dbClosed != null) { 651 dbClosed.handleClosed(doSyncDw, deleteTempDb); 652 } 653 654 /* Throw exceptions for previously encountered problems. */ 655 if (handleRefErrors.length() > 0) { 656 throw new IllegalStateException( 657 "Database closed while still referenced by other handles." + 658 handleRefErrors.toString()); 659 } 660 if (triggerException != null) { 661 throw triggerException; 662 } 663 } 664 665 /** 666 * Flushes any cached information for this database to disk; only 667 * applicable for deferred-write databases. 668 * <p> Note that deferred-write databases are automatically flushed to disk 669 * when the {@link #close} method is called. 670 * 671 * @see DatabaseConfig#setDeferredWrite DatabaseConfig.setDeferredWrite 672 * 673 * @throws com.sleepycat.je.rep.DatabasePreemptedException in a replicated 674 * environment if the master has truncated, removed or renamed the 675 * database. 676 * 677 * @throws OperationFailureException if this exception occurred earlier and 678 * caused the transaction to be invalidated. 679 * 680 * @throws EnvironmentFailureException if an unexpected, internal or 681 * environment-wide failure occurs. 682 * 683 * @throws UnsupportedOperationException if this is not a deferred-write 684 * database, or this database is read-only. 685 * 686 * @throws IllegalStateException if the database has been closed. 687 */ 688 public void sync() 689 throws DatabaseException, UnsupportedOperationException { 690 691 checkEnv(); 692 checkOpen("Can't call Database.sync:"); 693 trace(Level.FINEST, "Database.sync", null, null, null, null); 694 695 databaseImpl.sync(true); 696 } 697 698 /** 699 * Opens a sequence in the database. 700 * 701 * @param txn For a transactional database, an explicit transaction may 702 * be specified, or null may be specified to use auto-commit. For a 703 * non-transactional database, null must be specified. 704 * 705 * @param key The key {@link DatabaseEntry} of the sequence. 706 * 707 * @param config The sequence attributes. If null, default attributes are 708 * used. 709 * 710 * @return a new Sequence handle. 711 * 712 * @throws SequenceExistsException if the sequence record already exists 713 * and the {@code SequenceConfig ExclusiveCreate} parameter is true. 714 * 715 * @throws SequenceNotFoundException if the sequence record does not exist 716 * and the {@code SequenceConfig AllowCreate} parameter is false. 717 * 718 * @throws OperationFailureException if one of the <a 719 * href="../je/OperationFailureException.html#readFailures">Read Operation 720 * Failures</a> occurs. If the sequence does not exist and the {@link 721 * SequenceConfig#setAllowCreate AllowCreate} parameter is true, then one 722 * of the <a 723 * href="../je/OperationFailureException.html#writeFailures">Write 724 * Operation Failures</a> may also occur. 725 * 726 * @throws EnvironmentFailureException if an unexpected, internal or 727 * environment-wide failure occurs. 728 * 729 * @throws UnsupportedOperationException if this database is read-only, or 730 * this database is configured for duplicates. 731 * 732 * @throws IllegalStateException if the Sequence record is deleted by 733 * another thread during this method invocation, or the database has been 734 * closed. 735 * 736 * @throws IllegalArgumentException if an invalid parameter is specified, 737 * for example, an invalid {@code SequenceConfig} parameter. 738 */ 739 public Sequence openSequence(final Transaction txn, 740 final DatabaseEntry key, 741 final SequenceConfig config) 742 throws SequenceNotFoundException, SequenceExistsException { 743 744 try { 745 checkEnv(); 746 DatabaseUtil.checkForNullDbt(key, "key", true); 747 checkOpen("Can't call Database.openSequence:"); 748 trace(Level.FINEST, "Database.openSequence", txn, key, null, null); 749 750 return new Sequence(this, txn, key, config); 751 } catch (Error E) { 752 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 753 throw E; 754 } 755 } 756 757 /** 758 * Removes the sequence from the database. This method should not be 759 * called if there are open handles on this sequence. 760 * 761 * @param txn For a transactional database, an explicit transaction may be 762 * specified, or null may be specified to use auto-commit. For a 763 * non-transactional database, null must be specified. 764 * 765 * @param key The key {@link com.sleepycat.je.DatabaseEntry 766 * DatabaseEntry} of the sequence. 767 * 768 * @throws OperationFailureException if one of the <a 769 * href="../je/OperationFailureException.html#writeFailures">Write 770 * Operation Failures</a> occurs. 771 * 772 * @throws EnvironmentFailureException if an unexpected, internal or 773 * environment-wide failure occurs. 774 * 775 * @throws UnsupportedOperationException if this database is read-only. 776 */ 777 public void removeSequence(final Transaction txn, final DatabaseEntry key) 778 throws DatabaseException { 779 780 removeSequenceStat.increment(); 781 try { 782 delete(txn, key); 783 } catch (Error E) { 784 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 785 throw E; 786 } 787 } 788 789 /** 790 * Returns a cursor into the database. 791 * 792 * @param txn the transaction used to protect all operations performed with 793 * the cursor, or null if the operations should not be transaction 794 * protected. If the database is non-transactional, null must be 795 * specified. For a transactional database, the transaction is optional 796 * for read-only access and required for read-write access. 797 * 798 * @param cursorConfig The cursor attributes. If null, default attributes 799 * are used. 800 * 801 * @return A database cursor. 802 * 803 * @throws com.sleepycat.je.rep.DatabasePreemptedException in a replicated 804 * environment if the master has truncated, removed or renamed the 805 * database. 806 * 807 * @throws OperationFailureException if this exception occurred earlier and 808 * caused the transaction to be invalidated. 809 * 810 * @throws EnvironmentFailureException if an unexpected, internal or 811 * environment-wide failure occurs. 812 * 813 * @throws IllegalStateException if the database has been closed. 814 * 815 * @throws IllegalArgumentException if an invalid parameter is specified, 816 * for example, an invalid {@code CursorConfig} parameter. 817 */ 818 public Cursor openCursor(final Transaction txn, 819 final CursorConfig cursorConfig) 820 throws DatabaseException, IllegalArgumentException { 821 822 try { 823 checkEnv(); 824 checkOpen("Can't open a cursor"); 825 CursorConfig useConfig = 826 (cursorConfig == null) ? CursorConfig.DEFAULT : cursorConfig; 827 828 if (useConfig.getReadUncommitted() && 829 useConfig.getReadCommitted()) { 830 throw new IllegalArgumentException( 831 "Only one may be specified: " + 832 "ReadCommitted or ReadUncommitted"); 833 } 834 835 trace(Level.FINEST, "Database.openCursor", txn, cursorConfig); 836 Cursor ret = newDbcInstance(txn, useConfig); 837 838 return ret; 839 } catch (Error E) { 840 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 841 throw E; 842 } 843 } 844 845 /** 846 * Create a DiskOrderedCursor to iterate over the records in 'this' 847 * Database. Because the retrieval is based on Log Sequence Number (LSN) 848 * order rather than key order, records are returned in unsorted order in 849 * exchange for generally faster retrieval. LSN order approximates disk 850 * sector order. 851 * <p> 852 * See {@link DiskOrderedCursor} for more details and a description of the 853 * consistency guarantees provided by the scan. 854 * <p> 855 * <em>WARNING:</em> After calling this method, deletion of log files by 856 * the JE log cleaner will be disabled until {@link 857 * DiskOrderedCursor#close()} is called. To prevent unbounded growth of 858 * disk usage, be sure to call {@link DiskOrderedCursor#close()} to 859 * re-enable log file deletion. 860 */ 861 public DiskOrderedCursor 862 openCursor(final DiskOrderedCursorConfig cursorConfig) 863 throws DatabaseException, IllegalArgumentException { 864 865 try { 866 checkEnv(); 867 checkOpen("Can't open a cursor"); 868 DiskOrderedCursorConfig useConfig = 869 (cursorConfig == null) ? 870 DiskOrderedCursorConfig.DEFAULT : 871 cursorConfig; 872 873 trace(Level.FINEST, "Database.openForwardCursor", 874 null, cursorConfig); 875 DiskOrderedCursor ret = new DiskOrderedCursor(this, useConfig); 876 877 return ret; 878 } catch (Error E) { 879 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 880 throw E; 881 } 882 } 883 884 /** 885 * Is overridden by SecondaryDatabase. 886 */ 887 Cursor newDbcInstance(final Transaction txn, 888 final CursorConfig cursorConfig) 889 throws DatabaseException { 890 891 return new Cursor(this, txn, cursorConfig); 892 } 893 894 /** 895 * @hidden 896 * For internal use only. 897 * 898 * Given the {@code key} and {@code data} for a locked primary DB record, 899 * update the corresponding secondary database (index) records, for 900 * secondaries enabled for incremental population. 901 * <p> 902 * The secondaries associated the primary record are determined by calling 903 * {@link SecondaryAssociation#getSecondaries}. For each of these 904 * secondaries, {@link SecondaryDatabase#isIncrementalPopulationEnabled} is 905 * called to determine whether incremental population is enabled. If so, 906 * appropriate secondary records are inserted and deleted so that the 907 * index accurately reflects the current state of the primary record. 908 * <p> 909 * Note that for a given primary record, this method will not modify the 910 * secondary database if the secondary has already been updated for the 911 * primary record, due to concurrent primary write operations. Due to this 912 * behavior, certain integrity checks are not performed as documented in 913 * {@link SecondaryDatabase#startIncrementalPopulation}. 914 * <p> 915 * The primary record must be locked (read or write locked) when this 916 * method is called. Therefore, the caller should not use dirty-read to 917 * read the primary record. The simplest way to ensure that the primary 918 * record is locked is to use a cursor to read primary records, and call 919 * this method while the cursor is still positioned on the primary record. 920 * <p> 921 * It is the caller's responsibility to pass all primary records to this 922 * method that contain index keys for a secondary DB being incrementally 923 * populated, before calling {@link 924 * SecondaryDatabase#endIncrementalPopulation} on that secondary DB. It 925 * will be simpler for some applications to read and process all records in 926 * this DB in batches by calling 927 * {@link #populateSecondaries(DatabaseEntry, int)} instead. 928 * 929 * @param txn is the transaction to be used to write secondary records. If 930 * null and the secondary DBs are transactional, auto-commit will be used. 931 932 * @param key is the key of the locked primary record. 933 * 934 * @param data is the data of the locked primary record. 935 */ 936 public void populateSecondaries(final Transaction txn, 937 final DatabaseEntry key, 938 final DatabaseEntry data) { 939 try { 940 checkEnv(); 941 DatabaseUtil.checkForNullDbt(key, "key", true); 942 DatabaseUtil.checkForNullDbt(data, "true", true); 943 checkOpen("Can't call populateSecondaries:"); 944 trace(Level.FINEST, "populateSecondaries", null, key, data, null); 945 946 final Collection<SecondaryDatabase> secondaries = 947 secAssoc.getSecondaries(key); 948 949 final Locker locker = LockerFactory.getWritableLocker( 950 envHandle, txn, databaseImpl.isInternalDb(), isTransactional(), 951 databaseImpl.isReplicated()); // autoTxnIsReplicated 952 953 boolean success = false; 954 955 try { 956 for (final SecondaryDatabase secDb : secondaries) { 957 if (secDb.isIncrementalPopulationEnabled()) { 958 secDb.updateSecondary( 959 locker, null, key, null, data); 960 } 961 } 962 success = true; 963 } finally { 964 locker.operationEnd(success); 965 } 966 } catch (Error E) { 967 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 968 throw E; 969 } 970 } 971 972 /** 973 * @hidden 974 * For internal use only. 975 * 976 * Reads {@code batchSize} records starting at the given {@code key}, and 977 * updates the corresponding secondary database (index) records, for 978 * secondaries enabled for incremental population. The next key to be 979 * processed is returned in the {@code key} parameter so it can be passed 980 * in to process the next batch. 981 * <p> 982 * For each primary record, its associated secondaries are determined by 983 * calling {@link SecondaryAssociation#getSecondaries}. For each of these 984 * secondaries, {@link SecondaryDatabase#isIncrementalPopulationEnabled} is 985 * called to determine whether incremental population is enabled. If so, 986 * appropriate secondary records are inserted and deleted so that the 987 * index accurately reflects the current state of the primary record. 988 * <p> 989 * Note that for a given primary record, this method will not modify the 990 * secondary database if the secondary has already been updated for the 991 * primary record, due to concurrent primary write operations. Due to this 992 * behavior, certain integrity checks are not performed as documented in 993 * {@link SecondaryDatabase#startIncrementalPopulation}. 994 * <p> 995 * It is the application's responsibility to save the key returned by this 996 * method, and then pass the saved key when the method is called again to 997 * process the next batch of records. The application may wish to save the 998 * key persistently in order to avoid restarting the processing from the 999 * beginning of the database after a crash. 1000 * 1001 * @param key contains the starting key for the batch of records to be 1002 * processed when this method is called, and contains the next key to be 1003 * processed when this method returns. If {@code key.getData() == null} 1004 * when this method is called, the batch will begin with the first record 1005 * in the database. 1006 * 1007 * @param batchSize is the maximum number of records to be read. The 1008 * maximum number of secondary inserts and deletions that will be included 1009 * in a single transaction is the batchSize times the number of secondary 1010 * databases (associated with this primary database) that are enabled for 1011 * incremental population. 1012 * 1013 * @return true if more records may need to be processed, or false if 1014 * processing is complete. 1015 */ 1016 public boolean populateSecondaries(final DatabaseEntry key, 1017 final int batchSize) { 1018 try { 1019 checkEnv(); 1020 DatabaseUtil.checkForNullDbt(key, "key", false); 1021 if (batchSize <= 0) { 1022 throw new IllegalArgumentException( 1023 "batchSize must be positive"); 1024 } 1025 checkOpen("Can't call populateSecondaries:"); 1026 trace(Level.FINEST, "populateSecondaries", null, key, null, null); 1027 1028 final Locker locker = LockerFactory.getWritableLocker( 1029 envHandle, null, databaseImpl.isInternalDb(), 1030 isTransactional(), 1031 databaseImpl.isReplicated() /*autoTxnIsReplicated*/); 1032 try { 1033 final Cursor cursor = new Cursor(this, locker, null); 1034 try { 1035 return populateSecondariesInternal(cursor, locker, key, 1036 batchSize); 1037 } finally { 1038 cursor.close(); 1039 } 1040 } finally { 1041 locker.operationEnd(true); 1042 } 1043 } catch (Error E) { 1044 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 1045 throw E; 1046 } 1047 } 1048 1049 /** 1050 * Use a key-only scan to walk through primary keys. Only if the primary 1051 * key has a secondary, with incremental population enabled, do we then 1052 * read the primary data. Then update secondaries as if this were a 1053 * primary database insertion. 1054 */ 1055 private boolean populateSecondariesInternal(final Cursor cursor, 1056 final Locker locker, 1057 final DatabaseEntry key, 1058 final int batchSize) { 1059 /* 1060 * Use dirty read so no lock is taken when the primary record has no 1061 * associated secondaries with incremental population enabled. Use 1062 * READ_UNCOMMITTED_ALL so we don't skip records to due txn aborts. 1063 */ 1064 final LockMode scanMode = LockMode.READ_UNCOMMITTED_ALL; 1065 1066 OperationStatus searchStatus; 1067 if (key.getData() == null) { 1068 /* Start at first key. */ 1069 searchStatus = cursor.position( 1070 key, Cursor.NO_RETURN_DATA, scanMode, true); 1071 } else { 1072 /* Resume at key last processed. */ 1073 searchStatus = cursor.search( 1074 key, Cursor.NO_RETURN_DATA, scanMode, SearchMode.SET_RANGE); 1075 } 1076 final DatabaseEntry data = new DatabaseEntry(); 1077 int nProcessed = 0; 1078 while (searchStatus == OperationStatus.SUCCESS) { 1079 if (nProcessed >= batchSize) { 1080 return true; 1081 } 1082 nProcessed += 1; 1083 final Collection<SecondaryDatabase> secondaries = 1084 secAssoc.getSecondaries(key); 1085 boolean anySecondaries = false; 1086 for (final SecondaryDatabase secDb : secondaries) { 1087 if (secDb.isIncrementalPopulationEnabled()) { 1088 anySecondaries = true; 1089 break; 1090 } 1091 } 1092 if (anySecondaries) { 1093 1094 /* 1095 * Note that we do not use RMW here for reading the primary. 1096 * This would be necessary if the secondaries being written 1097 * were accessible via reads, but the incremental population 1098 * mode makes them inaccessible. (During a secondary read, we 1099 * use the read lock on the primary to protect against 1100 * modifications to the secondary, which is not locked. If we 1101 * allowed accessed to the secondary during population, we 1102 * would need to use RMW here.) 1103 */ 1104 if (cursor.getCurrent(key, data, LockMode.DEFAULT) == 1105 OperationStatus.SUCCESS) { 1106 for (final SecondaryDatabase secDb : secondaries) { 1107 if (secDb.isIncrementalPopulationEnabled()) { 1108 secDb.updateSecondary( 1109 locker, null, key, null, data); 1110 } 1111 } 1112 } 1113 } 1114 searchStatus = cursor.retrieveNext( 1115 key, Cursor.NO_RETURN_DATA, scanMode, GetMode.NEXT); 1116 } 1117 return false; 1118 } 1119 1120 /** 1121 * Removes key/data pairs from the database. 1122 * 1123 * <p>The key/data pair associated with the specified key is discarded 1124 * from the database. In the presence of duplicate key values, all 1125 * records associated with the designated key will be discarded.</p> 1126 * 1127 * <p>The key/data pair is also deleted from any associated secondary 1128 * databases.</p> 1129 * 1130 * @param txn For a transactional database, an explicit transaction may 1131 * be specified, or null may be specified to use auto-commit. For a 1132 * non-transactional database, null must be specified. 1133 * 1134 * @param key the key {@link com.sleepycat.je.DatabaseEntry DatabaseEntry} 1135 * operated on. 1136 * 1137 * @return The method will return {@link 1138 * com.sleepycat.je.OperationStatus#NOTFOUND OperationStatus.NOTFOUND} if 1139 * the specified key is not found in the database; otherwise the method 1140 * will return {@link com.sleepycat.je.OperationStatus#SUCCESS 1141 * OperationStatus.SUCCESS}. 1142 * 1143 * @throws OperationFailureException if one of the <a 1144 * href="../je/OperationFailureException.html#writeFailures">Write 1145 * Operation Failures</a> occurs. 1146 * 1147 * @throws EnvironmentFailureException if an unexpected, internal or 1148 * environment-wide failure occurs. 1149 * 1150 * @throws UnsupportedOperationException if this database is read-only. 1151 * 1152 * @throws IllegalStateException if the database has been closed. 1153 * 1154 * @throws IllegalArgumentException if an invalid parameter is specified. 1155 */ 1156 public OperationStatus delete(final Transaction txn, 1157 final DatabaseEntry key) 1158 throws DeleteConstraintException, 1159 LockConflictException, 1160 DatabaseException, 1161 UnsupportedOperationException, 1162 IllegalArgumentException { 1163 1164 try { 1165 checkEnv(); 1166 DatabaseUtil.checkForNullDbt(key, "key", true); 1167 checkOpen("Can't call Database.delete:"); 1168 trace(Level.FINEST, "Database.delete", txn, key, null, null); 1169 deleteStat.increment(); 1170 1171 OperationStatus commitStatus = OperationStatus.NOTFOUND; 1172 Locker locker = null; 1173 try { 1174 locker = LockerFactory.getWritableLocker( 1175 envHandle, txn, 1176 databaseImpl.isInternalDb(), 1177 isTransactional(), 1178 databaseImpl.isReplicated()); // autoTxnIsReplicated 1179 1180 commitStatus = deleteInternal(locker, key); 1181 return commitStatus; 1182 } finally { 1183 if (locker != null) { 1184 locker.operationEnd(commitStatus); 1185 } 1186 } 1187 } catch (Error E) { 1188 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 1189 throw E; 1190 } 1191 } 1192 1193 /** 1194 * Internal version of delete() that does no parameter checking. Notify 1195 * triggers, update secondaries and enforce foreign key constraints. 1196 * Deletes all duplicates. 1197 * 1198 * Note that this algorithm is duplicated in Database and Cursor for 1199 * efficiency reasons: in Cursor delete we must separately fetch the key 1200 * and data, while in Database delete we know the key and have to search 1201 * anyway so we can get the old data when we search. The two algorithms 1202 * need to be kept in sync. 1203 */ 1204 OperationStatus deleteInternal(final Locker locker, 1205 final DatabaseEntry key) { 1206 1207 final boolean hasUserTriggers = (databaseImpl.getTriggers() != null); 1208 final boolean hasAssociations = hasSecondaryOrForeignKeyAssociations(); 1209 1210 if (hasAssociations) { 1211 try { 1212 databaseImpl.getEnv().getSecondaryAssociationLock(). 1213 readLock().lockInterruptibly(); 1214 } catch (InterruptedException e) { 1215 throw new ThreadInterruptedException( 1216 databaseImpl.getEnv(), e); 1217 } 1218 } 1219 try { 1220 1221 /* 1222 * Get secondaries from the association and determine whether the 1223 * old data is needed. 1224 */ 1225 final Collection<SecondaryDatabase> secondaries; 1226 final Collection<SecondaryDatabase> fkSecondaries; 1227 final boolean needOldData; 1228 if (hasAssociations) { 1229 secondaries = secAssoc.getSecondaries(key); 1230 fkSecondaries = foreignKeySecondaries; 1231 needOldData = hasUserTriggers || 1232 SecondaryDatabase.needOldDataForDelete(secondaries); 1233 } else { 1234 secondaries = null; 1235 fkSecondaries = null; 1236 needOldData = hasUserTriggers; 1237 } 1238 1239 final Cursor cursor = new Cursor(this, locker, null); 1240 try { 1241 cursor.checkUpdatesAllowed(); 1242 cursor.setNonSticky(true); 1243 1244 /* 1245 * Fetch old data with RMW to avoid lock upgrades. Do not 1246 * fetch old data unless needed for secondaries. And if we 1247 * don't need the data, use dirty read to enable the 1248 * uncontended lock optimization. Use dirty-read-all to ensure 1249 * that we don't skip an uncommitted deleted record for a txn 1250 * that is later aborted. 1251 */ 1252 final DatabaseEntry oldData = new DatabaseEntry(); 1253 LockMode lockMode = LockMode.RMW; 1254 if (!needOldData) { 1255 oldData.setPartial(0, 0, true); 1256 if (!cursor.isSerializableIsolation(LockMode.RMW)) { 1257 lockMode = LockMode.READ_UNCOMMITTED_ALL; 1258 } 1259 } 1260 1261 /* Position a cursor at the specified data record. */ 1262 OperationStatus searchStatus = cursor.search( 1263 key, oldData, lockMode, SearchMode.SET); 1264 1265 if (searchStatus != OperationStatus.SUCCESS) { 1266 return OperationStatus.NOTFOUND; 1267 } 1268 1269 while (searchStatus == OperationStatus.SUCCESS) { 1270 1271 /* 1272 * Enforce foreign key constraints before secondary 1273 * updates, so that ForeignKeyDeleteAction.ABORT is applied 1274 * before deleting the secondary keys. 1275 */ 1276 if (fkSecondaries != null) { 1277 for (final SecondaryDatabase secDb : fkSecondaries) { 1278 secDb.onForeignKeyDelete(locker, key); 1279 } 1280 } 1281 1282 final DatabaseEntry notifyOldData = 1283 needOldData ? oldData : null; 1284 1285 /* 1286 * Update secondaries before deletion, so that a primary 1287 * record always exists while secondary keys refer to it. 1288 * This is relied on by secondary read-uncommitted. 1289 */ 1290 if (secondaries != null) { 1291 for (final SecondaryDatabase secDb : secondaries) { 1292 secDb.updateSecondary(locker, null, key, 1293 notifyOldData, null); 1294 } 1295 } 1296 1297 /* The actual deletion. */ 1298 final OperationStatus deleteStatus = 1299 cursor.deleteNoNotify(databaseImpl.getRepContext()); 1300 if (deleteStatus != OperationStatus.SUCCESS) { 1301 return deleteStatus; 1302 } 1303 1304 /* Run triggers after actual deletion. */ 1305 if (hasUserTriggers) { 1306 TriggerManager.runDeleteTriggers(locker, databaseImpl, 1307 key, notifyOldData); 1308 } 1309 1310 /* Get next duplicate in this database. */ 1311 if (databaseImpl.getSortedDuplicates()) { 1312 searchStatus = cursor.retrieveNext( 1313 key, oldData, LockMode.RMW, GetMode.NEXT_DUP); 1314 } else { 1315 searchStatus = OperationStatus.NOTFOUND; 1316 } 1317 } 1318 return OperationStatus.SUCCESS; 1319 } finally { 1320 cursor.close(); 1321 } 1322 } finally { 1323 if (hasAssociations) { 1324 databaseImpl.getEnv().getSecondaryAssociationLock(). 1325 readLock().unlock(); 1326 } 1327 } 1328 } 1329 1330 /** 1331 * Retrieves the key/data pair with the given key. If the matching key has 1332 * duplicate values, the first data item in the set of duplicates is 1333 * returned. Retrieval of duplicates requires the use of {@link Cursor} 1334 * operations. 1335 * 1336 * @param txn For a transactional database, an explicit transaction may be 1337 * specified to transaction-protect the operation, or null may be specified 1338 * to perform the operation without transaction protection. For a 1339 * non-transactional database, null must be specified. 1340 * 1341 * @param key the key used as input. It must be initialized with a 1342 * non-null byte array by the caller. 1343 * 1344 * @param data the data returned as output. Its byte array does not need 1345 * to be initialized by the caller. 1346 * A <a href="Cursor.html#partialEntry">partial data item</a> may be 1347 * specified to optimize for key only or partial data retrieval. 1348 * 1349 * @param lockMode the locking attributes; if null, default attributes are 1350 * used. 1351 * 1352 * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND 1353 * OperationStatus.NOTFOUND} if no matching key/data pair is found; 1354 * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS 1355 * OperationStatus.SUCCESS}. 1356 * 1357 * @throws OperationFailureException if one of the <a 1358 * href="OperationFailureException.html#readFailures">Read Operation 1359 * Failures</a> occurs. 1360 * 1361 * @throws EnvironmentFailureException if an unexpected, internal or 1362 * environment-wide failure occurs. 1363 * 1364 * @throws IllegalStateException if the database has been closed. 1365 * 1366 * @throws IllegalArgumentException if an invalid parameter is specified. 1367 */ 1368 public OperationStatus get(final Transaction txn, 1369 final DatabaseEntry key, 1370 final DatabaseEntry data, 1371 LockMode lockMode) 1372 throws LockConflictException, 1373 DatabaseException, 1374 IllegalArgumentException { 1375 1376 try { 1377 checkEnv(); 1378 DatabaseUtil.checkForNullDbt(key, "key", true); 1379 DatabaseUtil.checkForNullDbt(data, "data", false); 1380 checkOpen("Can't call Database.get:"); 1381 trace(Level.FINEST, "Database.get", txn, key, null, lockMode); 1382 getStat.increment(); 1383 1384 CursorConfig cursorConfig = CursorConfig.DEFAULT; 1385 if (lockMode == LockMode.READ_COMMITTED) { 1386 cursorConfig = CursorConfig.READ_COMMITTED; 1387 lockMode = null; 1388 } 1389 checkLockModeWithoutTxn(txn, lockMode); 1390 1391 Locker locker = null; 1392 Cursor cursor = null; 1393 OperationStatus commitStatus = null; 1394 try { 1395 locker = LockerFactory.getReadableLocker( 1396 this, txn, cursorConfig.getReadCommitted()); 1397 1398 cursor = new Cursor(this, locker, cursorConfig); 1399 cursor.setNonSticky(true); 1400 commitStatus = 1401 cursor.search(key, data, lockMode, SearchMode.SET); 1402 return commitStatus; 1403 } finally { 1404 if (cursor != null) { 1405 cursor.close(); 1406 } 1407 1408 if (locker != null) { 1409 locker.operationEnd(commitStatus); 1410 } 1411 } 1412 } catch (Error E) { 1413 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 1414 throw E; 1415 } 1416 } 1417 1418 /** 1419 * Retrieves the key/data pair with the given key and data value, that is, 1420 * both the key and data items must match. 1421 * 1422 * @param txn For a transactional database, an explicit transaction may be 1423 * specified to transaction-protect the operation, or null may be specified 1424 * to perform the operation without transaction protection. For a 1425 * non-transactional database, null must be specified. 1426 * 1427 * @param key the key used as input. It must be initialized with a non-null 1428 * byte array by the caller. 1429 * 1430 * @param data the data used as input. It must be initialized with a 1431 * non-null byte array by the caller. 1432 * 1433 * @param lockMode the locking attributes; if null, default attributes are 1434 * used. 1435 * 1436 * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND 1437 * OperationStatus.NOTFOUND} if no matching key/data pair is found; 1438 * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS 1439 * OperationStatus.SUCCESS}. 1440 * 1441 * @throws OperationFailureException if one of the <a 1442 * href="OperationFailureException.html#readFailures">Read Operation 1443 * Failures</a> occurs. 1444 * 1445 * @throws EnvironmentFailureException if an unexpected, internal or 1446 * environment-wide failure occurs. 1447 * 1448 * @throws IllegalStateException if the database has been closed. 1449 * 1450 * @throws IllegalArgumentException if an invalid parameter is specified. 1451 */ 1452 public OperationStatus getSearchBoth(final Transaction txn, 1453 final DatabaseEntry key, 1454 final DatabaseEntry data, 1455 LockMode lockMode) 1456 throws LockConflictException, 1457 DatabaseException, 1458 IllegalArgumentException { 1459 1460 try { 1461 checkEnv(); 1462 DatabaseUtil.checkForNullDbt(key, "key", true); 1463 DatabaseUtil.checkForNullDbt(data, "data", true); 1464 checkOpen("Can't call Database.getSearchBoth:"); 1465 trace(Level.FINEST, "Database.getSearchBoth", txn, key, data, 1466 lockMode); 1467 getSearchBothStat.increment(); 1468 1469 CursorConfig cursorConfig = CursorConfig.DEFAULT; 1470 if (lockMode == LockMode.READ_COMMITTED) { 1471 cursorConfig = CursorConfig.READ_COMMITTED; 1472 lockMode = null; 1473 } 1474 checkLockModeWithoutTxn(txn, lockMode); 1475 1476 Locker locker = null; 1477 Cursor cursor = null; 1478 OperationStatus commitStatus = null; 1479 try { 1480 locker = LockerFactory.getReadableLocker( 1481 this, txn, cursorConfig.getReadCommitted()); 1482 1483 cursor = new Cursor(this, locker, cursorConfig); 1484 cursor.setNonSticky(true); 1485 commitStatus = 1486 cursor.search(key, data, lockMode, SearchMode.BOTH); 1487 return commitStatus; 1488 } finally { 1489 if (cursor != null) { 1490 cursor.close(); 1491 } 1492 1493 if (locker != null) { 1494 locker.operationEnd(commitStatus); 1495 } 1496 } 1497 } catch (Error E) { 1498 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 1499 throw E; 1500 } 1501 } 1502 1503 /** 1504 * Stores the key/data pair into the database. 1505 * 1506 * <p>If the key already appears in the database and duplicates are not 1507 * configured, the data associated with the key will be replaced. If the 1508 * key already appears in the database and sorted duplicates are 1509 * configured, the new data value is inserted at the correct sorted 1510 * location.</p> 1511 * 1512 * @param txn For a transactional database, an explicit transaction may be 1513 * specified, or null may be specified to use auto-commit. For a 1514 * non-transactional database, null must be specified. 1515 * 1516 * @param key the key {@link com.sleepycat.je.DatabaseEntry DatabaseEntry} 1517 * operated on. 1518 * 1519 * @param data the data {@link com.sleepycat.je.DatabaseEntry 1520 * DatabaseEntry} stored. 1521 * 1522 * @return {@link com.sleepycat.je.OperationStatus#SUCCESS 1523 * OperationStatus.SUCCESS} if the operation succeeds. 1524 * 1525 * @throws OperationFailureException if one of the <a 1526 * href="../je/OperationFailureException.html#writeFailures">Write 1527 * Operation Failures</a> occurs. 1528 * 1529 * @throws OperationFailureException if this exception occurred earlier and 1530 * caused the transaction to be invalidated. 1531 * 1532 * @throws EnvironmentFailureException if an unexpected, internal or 1533 * environment-wide failure occurs. 1534 * 1535 * @throws UnsupportedOperationException if this database is read-only. 1536 * 1537 * @throws IllegalStateException if the database has been closed. 1538 */ 1539 public OperationStatus put(final Transaction txn, 1540 final DatabaseEntry key, 1541 final DatabaseEntry data) 1542 throws DatabaseException { 1543 1544 checkEnv(); 1545 DatabaseUtil.checkForNullDbt(key, "key", true); 1546 DatabaseUtil.checkForNullDbt(data, "data", true); 1547 DatabaseUtil.checkForPartialKey(key); 1548 checkOpen("Can't call Database.put"); 1549 trace(Level.FINEST, "Database.put", txn, key, data, null); 1550 putStat.increment(); 1551 1552 return putInternal(txn, key, data, PutMode.OVERWRITE); 1553 } 1554 1555 /** 1556 * Stores the key/data pair into the database if the key does not already 1557 * appear in the database. 1558 * 1559 * <p>This method will return {@link 1560 * com.sleepycat.je.OperationStatus#KEYEXIST OpeationStatus.KEYEXIST} if 1561 * the key already exists in the database, even if the database supports 1562 * duplicates.</p> 1563 * 1564 * @param txn For a transactional database, an explicit transaction may be 1565 * specified, or null may be specified to use auto-commit. For a 1566 * non-transactional database, null must be specified. 1567 * 1568 * @param key the key {@link com.sleepycat.je.DatabaseEntry DatabaseEntry} 1569 * operated on. 1570 * 1571 * @param data the data {@link com.sleepycat.je.DatabaseEntry 1572 * DatabaseEntry} stored. 1573 * 1574 * @return {@link com.sleepycat.je.OperationStatus#KEYEXIST 1575 * OperationStatus.KEYEXIST} if the key already appears in the database, 1576 * else {@link com.sleepycat.je.OperationStatus#SUCCESS 1577 * OperationStatus.SUCCESS} 1578 * 1579 * @throws OperationFailureException if one of the <a 1580 * href="../je/OperationFailureException.html#writeFailures">Write 1581 * Operation Failures</a> occurs. 1582 * 1583 * @throws EnvironmentFailureException if an unexpected, internal or 1584 * environment-wide failure occurs. 1585 * 1586 * @throws UnsupportedOperationException if this database is read-only. 1587 * 1588 * @throws IllegalStateException if the database has been closed. 1589 */ 1590 public OperationStatus putNoOverwrite(final Transaction txn, 1591 final DatabaseEntry key, 1592 final DatabaseEntry data) 1593 throws DatabaseException { 1594 1595 checkEnv(); 1596 DatabaseUtil.checkForNullDbt(key, "key", true); 1597 DatabaseUtil.checkForNullDbt(data, "data", true); 1598 DatabaseUtil.checkForPartialKey(key); 1599 checkOpen("Can't call Database.putNoOverWrite"); 1600 trace(Level.FINEST, "Database.putNoOverwrite", txn, key, data, null); 1601 putNoOverwriteStat.increment(); 1602 1603 return putInternal(txn, key, data, PutMode.NO_OVERWRITE); 1604 } 1605 1606 /** 1607 * Stores the key/data pair into the database if it does not already appear 1608 * in the database. 1609 * 1610 * <p>This method may only be called if the underlying database has been 1611 * configured to support sorted duplicates.</p> 1612 * 1613 * @param txn For a transactional database, an explicit transaction may be 1614 * specified, or null may be specified to use auto-commit. For a 1615 * non-transactional database, null must be specified. 1616 * 1617 * @param key the key {@link com.sleepycat.je.DatabaseEntry DatabaseEntry} 1618 * operated on. 1619 * 1620 * @param data the data {@link com.sleepycat.je.DatabaseEntry 1621 * DatabaseEntry} stored. 1622 * 1623 * @return {@link com.sleepycat.je.OperationStatus#KEYEXIST 1624 * OperationStatus.KEYEXIST} if the key/data pair already appears in the 1625 * database, else {@link com.sleepycat.je.OperationStatus#SUCCESS 1626 * OperationStatus.SUCCESS} 1627 * 1628 * @throws OperationFailureException if one of the <a 1629 * href="../je/OperationFailureException.html#writeFailures">Write 1630 * Operation Failures</a> occurs. 1631 * 1632 * @throws EnvironmentFailureException if an unexpected, internal or 1633 * environment-wide failure occurs. 1634 * 1635 * @throws UnsupportedOperationException if this database is not configured 1636 * for duplicates, or this database is read-only. 1637 * 1638 * @throws IllegalStateException if the database has been closed. 1639 */ 1640 public OperationStatus putNoDupData(final Transaction txn, 1641 final DatabaseEntry key, 1642 final DatabaseEntry data) 1643 throws DatabaseException { 1644 1645 checkEnv(); 1646 DatabaseUtil.checkForNullDbt(key, "key", true); 1647 DatabaseUtil.checkForNullDbt(data, "data", true); 1648 DatabaseUtil.checkForPartialKey(key); 1649 checkOpen("Can't call Database.putNoDupData"); 1650 if (!databaseImpl.getSortedDuplicates()) { 1651 throw new UnsupportedOperationException( 1652 "Database is not configured for duplicate data."); 1653 } 1654 trace(Level.FINEST, "Database.putNoDupData", txn, key, data, null); 1655 putNoDupDataStat.increment(); 1656 1657 return putInternal(txn, key, data, PutMode.NO_DUP_DATA); 1658 } 1659 1660 /** 1661 * Internal version of put() that does no parameter checking. 1662 */ 1663 OperationStatus putInternal(final Transaction txn, 1664 final DatabaseEntry key, 1665 final DatabaseEntry data, 1666 final PutMode putMode) 1667 throws DatabaseException { 1668 1669 try { 1670 Locker locker = null; 1671 Cursor cursor = null; 1672 OperationStatus commitStatus = OperationStatus.KEYEXIST; 1673 try { 1674 locker = LockerFactory.getWritableLocker( 1675 envHandle, txn, 1676 databaseImpl.isInternalDb(), 1677 isTransactional(), 1678 databaseImpl.isReplicated()); // autoTxnIsReplicated 1679 1680 cursor = new Cursor(this, locker, null); 1681 cursor.setNonSticky(true); 1682 commitStatus = cursor.putInternal(key, data, putMode); 1683 return commitStatus; 1684 } finally { 1685 if (cursor != null) { 1686 cursor.close(); 1687 } 1688 if (locker != null) { 1689 locker.operationEnd(commitStatus); 1690 } 1691 } 1692 } catch (Error E) { 1693 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 1694 throw E; 1695 } 1696 } 1697 1698 /** 1699 * Creates a specialized join cursor for use in performing equality or 1700 * natural joins on secondary indices. 1701 * 1702 * <p>Each cursor in the <code>cursors</code> array must have been 1703 * initialized to refer to the key on which the underlying database should 1704 * be joined. Typically, this initialization is done by calling {@link 1705 * Cursor#getSearchKey Cursor.getSearchKey}.</p> 1706 * 1707 * <p>Once the cursors have been passed to this method, they should not be 1708 * accessed or modified until the newly created join cursor has been 1709 * closed, or else inconsistent results may be returned. However, the 1710 * position of the cursors will not be changed by this method or by the 1711 * methods of the join cursor.</p> 1712 * 1713 * @param cursors an array of cursors associated with this primary 1714 * database. In a replicated environment, an explicit transaction must be 1715 * specified when opening each cursor, unless read-uncommitted isolation is 1716 * isolation is specified via the {@link CursorConfig} or {@link LockMode} 1717 * parameter. 1718 * 1719 * @param config The join attributes. If null, default attributes are 1720 * used. 1721 * 1722 * @return a specialized cursor that returns the results of the equality 1723 * join operation. 1724 * 1725 * @throws OperationFailureException if one of the <a 1726 * href="OperationFailureException.html#readFailures">Read Operation 1727 * Failures</a> occurs. 1728 * 1729 * @throws EnvironmentFailureException if an unexpected, internal or 1730 * environment-wide failure occurs. 1731 * 1732 * @throws IllegalStateException if the database has been closed. 1733 * 1734 * @throws IllegalArgumentException if an invalid parameter is specified, 1735 * for example, an invalid {@code JoinConfig} parameter. 1736 * 1737 * @see JoinCursor 1738 */ 1739 public JoinCursor join(final Cursor[] cursors, final JoinConfig config) 1740 throws DatabaseException, IllegalArgumentException { 1741 1742 try { 1743 checkEnv(); 1744 checkOpen("Can't call Database.join"); 1745 DatabaseUtil.checkForNullParam(cursors, "cursors"); 1746 if (cursors.length == 0) { 1747 throw new IllegalArgumentException( 1748 "At least one cursor is required."); 1749 } 1750 1751 /* 1752 * Check that all cursors use the same locker, if any cursor is 1753 * transactional. And if non-transactional, that all databases are 1754 * in the same environment. 1755 */ 1756 Locker locker = cursors[0].getCursorImpl().getLocker(); 1757 if (!locker.isTransactional()) { 1758 EnvironmentImpl env = envHandle.getEnvironmentImpl(); 1759 for (int i = 1; i < cursors.length; i += 1) { 1760 Locker locker2 = cursors[i].getCursorImpl().getLocker(); 1761 if (locker2.isTransactional()) { 1762 throw new IllegalArgumentException( 1763 "All cursors must use the same transaction."); 1764 } 1765 EnvironmentImpl env2 = 1766 cursors[i].getDatabaseImpl().getEnv(); 1767 if (env != env2) { 1768 throw new IllegalArgumentException( 1769 "All cursors must use the same environment."); 1770 } 1771 } 1772 locker = null; /* Don't reuse a non-transactional locker. */ 1773 } else { 1774 for (int i = 1; i < cursors.length; i += 1) { 1775 Locker locker2 = cursors[i].getCursorImpl().getLocker(); 1776 if (locker.getTxnLocker() != locker2.getTxnLocker()) { 1777 throw new IllegalArgumentException( 1778 "All cursors must use the same transaction."); 1779 } 1780 } 1781 } 1782 1783 /* Create the join cursor. */ 1784 return new JoinCursor(locker, this, cursors, config); 1785 } catch (Error E) { 1786 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 1787 throw E; 1788 } 1789 } 1790 1791 /** 1792 * Preloads the cache. This method should only be called when there are no 1793 * operations being performed on the database in other threads. Executing 1794 * preload during concurrent updates may result in some or all of the tree 1795 * being loaded into the JE cache. Executing preload during any other 1796 * types of operations may result in JE exceeding its allocated cache 1797 * size. preload() effectively locks the entire database and therefore will 1798 * lock out the checkpointer, cleaner, and compressor, as well as not allow 1799 * eviction to occur. 1800 * 1801 * @deprecated As of JE 2.0.83, replaced by {@link 1802 * Database#preload(PreloadConfig)}.</p> 1803 * 1804 * @param maxBytes The maximum number of bytes to load. If maxBytes is 0, 1805 * je.evictor.maxMemory is used. 1806 * 1807 * @throws OperationFailureException if one of the <a 1808 * href="OperationFailureException.html#readFailures">Read Operation 1809 * Failures</a> occurs. 1810 * 1811 * @throws EnvironmentFailureException if an unexpected, internal or 1812 * environment-wide failure occurs. 1813 * 1814 * @throws IllegalStateException if the database has been closed. 1815 */ 1816 public void preload(final long maxBytes) 1817 throws DatabaseException { 1818 1819 checkEnv(); 1820 checkOpen("Can't call Database.preload"); 1821 1822 PreloadConfig config = new PreloadConfig(); 1823 config.setMaxBytes(maxBytes); 1824 databaseImpl.preload(config); 1825 } 1826 1827 /** 1828 * Preloads the cache. This method should only be called when there are no 1829 * operations being performed on the database in other threads. Executing 1830 * preload during concurrent updates may result in some or all of the tree 1831 * being loaded into the JE cache. Executing preload during any other 1832 * types of operations may result in JE exceeding its allocated cache 1833 * size. preload() effectively locks the entire database and therefore will 1834 * lock out the checkpointer, cleaner, and compressor, as well as not allow 1835 * eviction to occur. 1836 * 1837 * @deprecated As of JE 2.0.101, replaced by {@link 1838 * Database#preload(PreloadConfig)}.</p> 1839 * 1840 * @param maxBytes The maximum number of bytes to load. If maxBytes is 0, 1841 * je.evictor.maxMemory is used. 1842 * 1843 * @param maxMillisecs The maximum time in milliseconds to use when 1844 * preloading. Preloading stops once this limit has been reached. If 1845 * maxMillisecs is 0, preloading can go on indefinitely or until maxBytes 1846 * (if non-0) is reached. 1847 * 1848 * @throws OperationFailureException if one of the <a 1849 * href="OperationFailureException.html#readFailures">Read Operation 1850 * Failures</a> occurs. 1851 * 1852 * @throws EnvironmentFailureException if an unexpected, internal or 1853 * environment-wide failure occurs. 1854 * 1855 * @throws IllegalStateException if the database has been closed. 1856 */ 1857 public void preload(final long maxBytes, final long maxMillisecs) 1858 throws DatabaseException { 1859 1860 checkEnv(); 1861 checkOpen("Can't call Database.preload"); 1862 1863 PreloadConfig config = new PreloadConfig(); 1864 config.setMaxBytes(maxBytes); 1865 config.setMaxMillisecs(maxMillisecs); 1866 databaseImpl.preload(config); 1867 } 1868 1869 /** 1870 * Preloads the cache. This method should only be called when there are no 1871 * operations being performed on the database in other threads. Executing 1872 * preload during concurrent updates may result in some or all of the tree 1873 * being loaded into the JE cache. Executing preload during any other 1874 * types of operations may result in JE exceeding its allocated cache 1875 * size. preload() effectively locks the entire database and therefore will 1876 * lock out the checkpointer, cleaner, and compressor, as well as not allow 1877 * eviction to occur. If the database is replicated and the environment is 1878 * in the replica state, then the replica may become temporarily 1879 * disconnected from the master if the replica needs to replay changes 1880 * against the database and is locked out because the time taken by the 1881 * preload operation exceeds {@link 1882 * com.sleepycat.je.rep.ReplicationConfig#FEEDER_TIMEOUT}. 1883 * <p> 1884 * While this method preloads a single database, {@link 1885 * Environment#preload} lets you preload multiple databases. 1886 * 1887 * @param config The PreloadConfig object that specifies the parameters 1888 * of the preload. 1889 * 1890 * @return A PreloadStats object with various statistics about the 1891 * preload() operation. 1892 * 1893 * @throws OperationFailureException if one of the <a 1894 * href="OperationFailureException.html#readFailures">Read Operation 1895 * Failures</a> occurs. 1896 * 1897 * @throws EnvironmentFailureException if an unexpected, internal or 1898 * environment-wide failure occurs. 1899 * 1900 * @throws IllegalStateException if the database has been closed. 1901 * 1902 * @throws IllegalArgumentException if {@code PreloadConfig.getMaxBytes} is 1903 * greater than size of the JE cache. 1904 */ 1905 public PreloadStats preload(final PreloadConfig config) 1906 throws DatabaseException { 1907 1908 checkEnv(); 1909 checkOpen("Can't call Database.preload"); 1910 1911 PreloadConfig useConfig = 1912 (config == null) ? new PreloadConfig() : config; 1913 return databaseImpl.preload(useConfig); 1914 } 1915 1916 /** 1917 * Counts the key/data pairs in the database. This operation is faster than 1918 * obtaining a count from a cursor based scan of the database, and will not 1919 * perturb the current contents of the cache. However, the count is not 1920 * guaranteed to be accurate if there are concurrent updates. Note that 1921 * this method does scan a significant portion of the database and should 1922 * be considered a fairly expensive operation. 1923 * 1924 * This operation uses the an internal infrastructure and algorithm that is 1925 * similar to the one used for the {@link DiskOrderedCursor}. Specifically, 1926 * it will disable deletion of log files by the JE log cleaner during its 1927 * execution and will consume a certain amount of memory (but without 1928 * affecting the memory that is available for the JE cache). To avoid 1929 * excessive memory consumption (and a potential {@code OutOfMemoryError}) 1930 * this method places an internal limit on its memory consumption. If this 1931 * limit is reached, the method will still work properly, but its 1932 * performance will degrade. To specify a different memory limit than the 1933 * one used by this method, use the 1934 * {@link Database#count(long memoryLimit)} method. 1935 * 1936 * Currently, the internal memory limit is calculated as 10% of the 1937 * difference between the max JVM memory (the value returned by 1938 * Runtime.getRuntime().maxMemory()) and the configured JE cache size. 1939 * 1940 * @return The count of key/data pairs in the database. 1941 * 1942 * @throws OperationFailureException if one of the <a 1943 * href="OperationFailureException.html#readFailures">Read Operation 1944 * Failures</a> occurs. 1945 * 1946 * @throws EnvironmentFailureException if an unexpected, internal or 1947 * environment-wide failure occurs. 1948 * 1949 * @throws IllegalStateException if the database has been closed. 1950 */ 1951 public long count() 1952 throws DatabaseException { 1953 1954 checkEnv(); 1955 checkOpen("Can't call Database.count"); 1956 1957 return databaseImpl.count(0); 1958 } 1959 1960 /** 1961 * Counts the key/data pairs in the database. This operation is faster than 1962 * obtaining a count from a cursor based scan of the database, and will not 1963 * perturb the current contents of the cache. However, the count is not 1964 * guaranteed to be accurate if there are concurrent updates. Note that 1965 * this method does scan a significant portion of the database and should 1966 * be considered a fairly expensive operation. 1967 * 1968 * This operation uses the an internal infrastructure and algorithm that is 1969 * similar to the one used for the {@link DiskOrderedCursor}. Specifically, 1970 * it will disable deletion of log files by the JE log cleaner during its 1971 * execution and will consume a certain amount of memory (but without 1972 * affecting the memory that is available for the JE cache). To avoid 1973 * excessive memory consumption (and a potential {@code OutOfMemoryError}) 1974 * this method takes as input an upper bound on the memory it may consume. 1975 * If this limit is reached, the method will still work properly, but its 1976 * performance will degrade. 1977 * 1978 * @param memoryLimit The maximum memory (in bytes) that may be consumed 1979 * by this method. 1980 * 1981 * @return The count of key/data pairs in the database. 1982 * 1983 * @throws OperationFailureException if one of the <a 1984 * href="OperationFailureException.html#readFailures">Read Operation 1985 * Failures</a> occurs. 1986 * 1987 * @throws EnvironmentFailureException if an unexpected, internal or 1988 * environment-wide failure occurs. 1989 * 1990 * @throws IllegalStateException if the database has been closed. 1991 */ 1992 public long count(long memoryLimit) 1993 throws DatabaseException { 1994 1995 checkEnv(); 1996 checkOpen("Can't call Database.count"); 1997 1998 return databaseImpl.count(memoryLimit); 1999 } 2000 2001 /** 2002 * Returns database statistics. 2003 * 2004 * <p>If this method has not been configured to avoid expensive operations 2005 * (using the {@link com.sleepycat.je.StatsConfig#setFast 2006 * StatsConfig.setFast} method), it will access some of or all the pages in 2007 * the database, incurring a severe performance penalty as well as possibly 2008 * flushing the underlying cache.</p> 2009 * 2010 * <p>In the presence of multiple threads or processes accessing an active 2011 * database, the information returned by this method may be 2012 * out-of-date.</p> 2013 * 2014 * @param config The statistics returned; if null, default statistics are 2015 * returned. 2016 * 2017 * @return Database statistics. 2018 * 2019 * @throws OperationFailureException if one of the <a 2020 * href="OperationFailureException.html#readFailures">Read Operation 2021 * Failures</a> occurs. 2022 * 2023 * @throws EnvironmentFailureException if an unexpected, internal or 2024 * environment-wide failure occurs. 2025 * 2026 * @throws IllegalStateException if the database has been closed. 2027 */ 2028 public DatabaseStats getStats(final StatsConfig config) 2029 throws DatabaseException { 2030 2031 checkEnv(); 2032 checkOpen("Can't call Database.stat"); 2033 StatsConfig useConfig = 2034 (config == null) ? StatsConfig.DEFAULT : config; 2035 2036 if (databaseImpl != null) { 2037 return databaseImpl.stat(useConfig); 2038 } 2039 return null; 2040 } 2041 2042 /** 2043 * Verifies the integrity of the database. 2044 * 2045 * <p>Verification is an expensive operation that should normally only be 2046 * used for troubleshooting and debugging.</p> 2047 * 2048 * @param config Configures the verify operation; if null, the default 2049 * operation is performed. 2050 * 2051 * @return Database statistics. 2052 * 2053 * @throws OperationFailureException if one of the <a 2054 * href="OperationFailureException.html#readFailures">Read Operation 2055 * Failures</a> occurs. 2056 * 2057 * @throws EnvironmentFailureException if an unexpected, internal or 2058 * environment-wide failure occurs. 2059 * 2060 * @throws IllegalStateException if the database has been closed. 2061 * 2062 * @throws IllegalArgumentException if an invalid parameter is specified. 2063 */ 2064 public DatabaseStats verify(final VerifyConfig config) 2065 throws DatabaseException { 2066 2067 try { 2068 checkEnv(); 2069 checkOpen("Can't call Database.verify"); 2070 VerifyConfig useConfig = 2071 (config == null) ? VerifyConfig.DEFAULT : config; 2072 2073 DatabaseStats stats = databaseImpl.getEmptyStats(); 2074 databaseImpl.verify(useConfig, stats); 2075 return stats; 2076 } catch (Error E) { 2077 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 2078 throw E; 2079 } 2080 } 2081 2082 /** 2083 * Returns the database name. 2084 * 2085 * <p>This method may be called at any time during the life of the 2086 * application.</p> 2087 * 2088 * @return The database name. 2089 */ 2090 public String getDatabaseName() 2091 throws DatabaseException { 2092 2093 try { 2094 checkEnv(); 2095 if (databaseImpl != null) { 2096 return databaseImpl.getName(); 2097 } 2098 return null; 2099 } catch (Error E) { 2100 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 2101 throw E; 2102 } 2103 } 2104 2105 /* 2106 * Non-transactional database name, safe to access when creating error 2107 * messages. 2108 */ 2109 String getDebugName() { 2110 if (databaseImpl != null) { 2111 return databaseImpl.getDebugName(); 2112 } 2113 return null; 2114 } 2115 2116 /** 2117 * Returns this Database object's configuration. 2118 * 2119 * <p>This may differ from the configuration used to open this object if 2120 * the database existed previously.</p> 2121 * 2122 * @return This Database object's configuration. 2123 * 2124 * @throws EnvironmentFailureException if an unexpected, internal or 2125 * environment-wide failure occurs. 2126 */ 2127 public DatabaseConfig getConfig() { 2128 2129 try { 2130 return DatabaseConfig.combineConfig(databaseImpl, configuration); 2131 } catch (Error E) { 2132 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 2133 throw E; 2134 } 2135 } 2136 2137 /** 2138 * Equivalent to getConfig().getTransactional() but cheaper. 2139 */ 2140 boolean isTransactional() { 2141 return databaseImpl.isTransactional(); 2142 } 2143 2144 /** 2145 * Returns the {@link com.sleepycat.je.Environment Environment} handle for 2146 * the database environment underlying the {@link 2147 * com.sleepycat.je.Database Database}. 2148 * 2149 * <p>This method may be called at any time during the life of the 2150 * application.</p> 2151 * 2152 * @return The {@link com.sleepycat.je.Environment Environment} handle 2153 * for the database environment underlying the {@link 2154 * com.sleepycat.je.Database Database}. 2155 */ 2156 public Environment getEnvironment() { 2157 return envHandle; 2158 } 2159 2160 /** 2161 * Returns a list of all {@link com.sleepycat.je.SecondaryDatabase 2162 * SecondaryDatabase} objects associated with a primary database. 2163 * 2164 * <p>If no secondaries are associated with this database, an empty list is 2165 * returned.</p> 2166 */ 2167 /* 2168 * Replacement for above paragraph when SecondaryAssociation is published: 2169 * <p>If no secondaries are associated with this database, or a {@link 2170 * SecondaryAssociation} is {@link SecondaryCursor#setSecondaryAssociation 2171 * configured}, an empty list is returned.</p> 2172 */ 2173 public List<SecondaryDatabase> getSecondaryDatabases() { 2174 return new ArrayList<SecondaryDatabase>(simpleAssocSecondaries); 2175 } 2176 2177 /** 2178 * Compares two keys using either the default comparator if no BTree 2179 * comparator has been set or the BTree comparator if one has been set. 2180 * 2181 * @return -1 if entry1 compares less than entry2, 2182 * 0 if entry1 compares equal to entry2, 2183 * 1 if entry1 compares greater than entry2 2184 * 2185 * @throws IllegalStateException if the database has been closed. 2186 * 2187 * @throws IllegalArgumentException if either entry is a partial 2188 * DatabaseEntry, or is null. 2189 */ 2190 public int compareKeys(final DatabaseEntry entry1, 2191 final DatabaseEntry entry2) { 2192 return doCompareKeys(entry1, entry2, false/*duplicates*/); 2193 } 2194 2195 /** 2196 * Compares two data elements using either the default comparator if no 2197 * duplicate comparator has been set or the duplicate comparator if one has 2198 * been set. 2199 * 2200 * @return -1 if entry1 compares less than entry2, 2201 * 0 if entry1 compares equal to entry2, 2202 * 1 if entry1 compares greater than entry2 2203 * 2204 * @throws IllegalStateException if the database has been closed. 2205 * 2206 * @throws IllegalArgumentException if either entry is a partial 2207 * DatabaseEntry, or is null. 2208 */ 2209 public int compareDuplicates(final DatabaseEntry entry1, 2210 final DatabaseEntry entry2) { 2211 return doCompareKeys(entry1, entry2, true/*duplicates*/); 2212 } 2213 2214 private int doCompareKeys(final DatabaseEntry entry1, 2215 final DatabaseEntry entry2, 2216 final boolean duplicates) { 2217 try { 2218 checkEnv(); 2219 checkOpen("Can't compare keys/duplicates"); 2220 DatabaseUtil.checkForNullDbt(entry1, "entry1", true); 2221 DatabaseUtil.checkForNullDbt(entry2, "entry2", true); 2222 DatabaseUtil.checkForPartialKey(entry1); 2223 DatabaseUtil.checkForPartialKey(entry2); 2224 return databaseImpl.compareEntries(entry1, entry2, duplicates); 2225 } catch (Error E) { 2226 DbInternal.getEnvironmentImpl(envHandle).invalidate(E); 2227 throw E; 2228 } 2229 } 2230 2231 /* 2232 * Helpers, not part of the public API 2233 */ 2234 2235 /** 2236 * Returns true if the Database was opened read/write. 2237 * 2238 * @return true if the Database was opened read/write. 2239 */ 2240 boolean isWritable() { 2241 return isWritable; 2242 } 2243 2244 /** 2245 * Returns the databaseImpl object instance. 2246 */ 2247 DatabaseImpl getDatabaseImpl() { 2248 return databaseImpl; 2249 } 2250 2251 /** 2252 * Called during database open to set the handleLocker field. 2253 * @see HandleLocker 2254 */ 2255 HandleLocker initHandleLocker(EnvironmentImpl envImpl, 2256 Locker openDbLocker) { 2257 handleLocker = HandleLocker.createHandleLocker(envImpl, openDbLocker); 2258 return handleLocker; 2259 } 2260 2261 @SuppressWarnings("unused") 2262 void removeCursor(final ForwardCursor ignore) 2263 throws DatabaseException { 2264 2265 /* 2266 * Do not call checkOpen if the handle was preempted, to allow closing 2267 * a cursor after an operation failure. [#17015] 2268 */ 2269 if (state != DbState.PREEMPTED) { 2270 checkOpen("Database was closed while still in use:"); 2271 } 2272 openCursors.getAndDecrement(); 2273 } 2274 2275 @SuppressWarnings("unused") 2276 void addCursor(final ForwardCursor ignore) 2277 throws DatabaseException { 2278 2279 checkOpen("Database was closed while still in use:"); 2280 openCursors.getAndIncrement(); 2281 } 2282 2283 void checkOpen(final String msg) { 2284 switch (state) { 2285 case OPEN: 2286 break; 2287 case CLOSED: 2288 throw new IllegalStateException(msg + " Database was closed."); 2289 case INVALID: 2290 throw new IllegalStateException( 2291 msg + 2292 " The Transaction used to open the Database was aborted."); 2293 case PREEMPTED: 2294 throw preemptedCause.wrapSelf(msg); 2295 default: 2296 assert false : state; 2297 } 2298 } 2299 2300 /** 2301 * @throws EnvironmentFailureException if the underlying environment is 2302 * invalid 2303 */ 2304 void checkEnv() 2305 throws EnvironmentFailureException { 2306 2307 envHandle.checkHandleIsValid(); 2308 envHandle.checkEnv(); 2309 } 2310 2311 void checkLockModeWithoutTxn(final Transaction userTxn, 2312 final LockMode lockMode) { 2313 if (userTxn == null && LockMode.RMW.equals(lockMode)) { 2314 throw new IllegalArgumentException( 2315 lockMode + " is meaningless and can not be specified " + 2316 "when a null (autocommit) transaction is used. There " + 2317 "will never be a follow on operation which will promote " + 2318 "the lock to WRITE."); 2319 } 2320 } 2321 2322 /** 2323 * Sends trace messages to the java.util.logger. Don't rely on the logger 2324 * alone to conditionalize whether we send this message, we don't even want 2325 * to construct the message if the level is not enabled. 2326 */ 2327 void trace(final Level level, 2328 final String methodName, 2329 final Transaction txn, 2330 final DatabaseEntry key, 2331 final DatabaseEntry data, 2332 final LockMode lockMode) 2333 throws DatabaseException { 2334 2335 if (logger.isLoggable(level)) { 2336 StringBuilder sb = new StringBuilder(); 2337 sb.append(methodName); 2338 if (txn != null) { 2339 sb.append(" txnId=").append(txn.getId()); 2340 } 2341 sb.append(" key=").append(key.dumpData()); 2342 if (data != null) { 2343 sb.append(" data=").append(data.dumpData()); 2344 } 2345 if (lockMode != null) { 2346 sb.append(" lockMode=").append(lockMode); 2347 } 2348 LoggerUtils.logMsg( 2349 logger, envHandle.getEnvironmentImpl(), level, sb.toString()); 2350 } 2351 } 2352 2353 /** 2354 * Sends trace messages to the java.util.logger. Don't rely on the logger 2355 * alone to conditionalize whether we send this message, we don't even want 2356 * to construct the message if the level is not enabled. 2357 */ 2358 void trace(final Level level, 2359 final String methodName, 2360 final Transaction txn, 2361 final Object config) 2362 throws DatabaseException { 2363 2364 if (logger.isLoggable(level)) { 2365 StringBuilder sb = new StringBuilder(); 2366 sb.append(methodName); 2367 sb.append(" name=" + getDebugName()); 2368 if (txn != null) { 2369 sb.append(" txnId=").append(txn.getId()); 2370 } 2371 if (config != null) { 2372 sb.append(" config=").append(config); 2373 } 2374 LoggerUtils.logMsg( 2375 logger, envHandle.getEnvironmentImpl(), level, sb.toString()); 2376 } 2377 } 2378 2379 boolean hasSecondaryOrForeignKeyAssociations() { 2380 return (!secAssoc.isEmpty() || !foreignKeySecondaries.isEmpty()); 2381 } 2382 2383 /** 2384 * Creates a SecondaryIntegrityException using the information given. 2385 * 2386 * This method is in the Database class, rather than in SecondaryDatabase, 2387 * to support joins with plain Cursors [#21258]. 2388 */ 2389 SecondaryIntegrityException 2390 secondaryRefersToMissingPrimaryKey(final Locker locker, 2391 final DatabaseEntry secKey, 2392 final DatabaseEntry priKey) 2393 throws DatabaseException { 2394 2395 return new SecondaryIntegrityException( 2396 locker, 2397 "Secondary refers to a missing key in the primary database", 2398 getDebugName(), secKey, priKey); 2399 } 2400 } 2401