All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sleepycat.je.Database Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle Berkeley
 * DB Java Edition made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle Berkeley DB Java Edition for a copy of the
 * license and additional information.
 */

package com.sleepycat.je;

import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sleepycat.je.dbi.DatabaseImpl;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.dbi.GetMode;
import com.sleepycat.je.dbi.SearchMode;
import com.sleepycat.je.dbi.TriggerManager;
import com.sleepycat.je.evictor.OffHeapCache;
import com.sleepycat.je.tree.BIN;
import com.sleepycat.je.tree.IN;
import com.sleepycat.je.txn.HandleLocker;
import com.sleepycat.je.txn.Locker;
import com.sleepycat.je.txn.LockerFactory;
import com.sleepycat.je.utilint.DatabaseUtil;
import com.sleepycat.je.utilint.DbLsn;
import com.sleepycat.je.utilint.LoggerUtils;

/**
 * A database handle.
 *
 * 

Database attributes are specified in the {@link * com.sleepycat.je.DatabaseConfig DatabaseConfig} class. Database handles are * free-threaded and may be used concurrently by multiple threads.

* *

To open an existing database with default attributes:

* *
 *     Environment env = new Environment(home, null);
 *     Database myDatabase = env.openDatabase(null, "mydatabase", null);
 * 
* *

To create a transactional database that supports duplicates:

* *
 *     DatabaseConfig dbConfig = new DatabaseConfig();
 *     dbConfig.setTransactional(true);
 *     dbConfig.setAllowCreate(true);
 *     dbConfig.setSortedDuplicates(true);
 *     Database db = env.openDatabase(txn, "mydatabase", dbConfig);
 * 
*/ public class Database implements Closeable { static final CursorConfig DEFAULT_CURSOR_CONFIG = CursorConfig.DEFAULT.clone().setNonSticky(true); static final CursorConfig READ_COMMITTED_CURSOR_CONFIG = CursorConfig.READ_COMMITTED.clone().setNonSticky(true); /* * DbState embodies the Database handle state. */ enum DbState { OPEN, CLOSED, INVALID, PREEMPTED, CORRUPTED } /* * The current state of the handle. When the state is non-open, the * databaseImpl should not be accessed, since the databaseImpl is set to * null during close. This check does not guarantee that an NPE will not * occur, since we do not synchronize -- the state could change after * checking it and before accessing the databaseImpl. But the check makes * an NPE unlikely. */ private volatile DbState state; /* Effectively final since an open DB cannot be renamed. */ private String name; /* The DatabasePreemptedException cause when state == PREEMPTED. */ private volatile OperationFailureException preemptedCause; /* The SecondaryIntegrityException cause when state == CORRUPTED. */ private volatile SecondaryIntegrityException corruptedCause; /* * The envHandle field cannot be declared as final because it is * initialized by methods called by the ctor. However, after construction * it is non-null and should be treated as final. */ Environment envHandle; /* * The databaseImpl field is set to null during close to avoid OOME. It * should normally only be accessed via the checkOpen and getDbImpl * methods. It is guaranteed to be non-null if state == DbState.OPEN. */ private DatabaseImpl databaseImpl; /* * Used to store per-Database handle properties: allow create, * exclusive create, read only and use existing config. Other Database-wide * properties are stored in DatabaseImpl. */ DatabaseConfig configuration; /* True if this handle permits write operations; */ private boolean isWritable; /* Record how many open cursors on this database. */ private final AtomicInteger openCursors = new AtomicInteger(); /* * Locker that owns the NameLN lock held while the Database is open. * * The handleLocker field is set to null during close to avoid OOME. It * is only accessed during close, while synchronized, so checking for null * is unnecessary. */ private HandleLocker handleLocker; /* * If a user-supplied SecondaryAssociation is configured, this field * contains it. Otherwise, it contains an internal SecondaryAssociation * that uses the simpleAssocSecondaries to store associations between a * single primary and its secondaries. */ SecondaryAssociation secAssoc; Collection simpleAssocSecondaries; /* * Secondaries whose keys have values contrained to the primary keys in * this database. */ Collection foreignKeySecondaries; final Logger logger; /** * Creates a database but does not open or fully initialize it. Is * protected for use in compat package. */ Database(final Environment env) { this.envHandle = env; logger = getEnv().getLogger(); } /** * Creates a database, called by Environment. */ DatabaseImpl initNew(final Environment env, final Locker locker, final String databaseName, final DatabaseConfig dbConfig) { dbConfig.validateForNewDb(); init(env, dbConfig, databaseName); /* Make the databaseImpl. */ databaseImpl = getEnv().getDbTree().createDb( locker, databaseName, dbConfig, handleLocker); databaseImpl.addReferringHandle(this); return databaseImpl; } /** * Opens a database, called by Environment. */ void initExisting(final Environment env, final Locker locker, final DatabaseImpl dbImpl, final String databaseName, final DatabaseConfig dbConfig) { /* * Make sure the configuration used for the open is compatible with the * existing databaseImpl. */ validateConfigAgainstExistingDb(locker, databaseName, dbConfig, dbImpl); init(env, dbConfig, databaseName); this.databaseImpl = dbImpl; dbImpl.addReferringHandle(this); } private void init(final Environment env, final DatabaseConfig config, final String databaseName) { assert handleLocker != null; envHandle = env; configuration = config.clone(); isWritable = !configuration.getReadOnly(); secAssoc = makeSecondaryAssociation(); state = DbState.OPEN; name = databaseName; } SecondaryAssociation makeSecondaryAssociation() { foreignKeySecondaries = new CopyOnWriteArraySet<>(); if (configuration.getSecondaryAssociation() != null) { if (configuration.getSortedDuplicates()) { throw new IllegalArgumentException( "Duplicates not allowed for a primary database"); } simpleAssocSecondaries = Collections.emptySet(); return configuration.getSecondaryAssociation(); } simpleAssocSecondaries = new CopyOnWriteArraySet<>(); return new SecondaryAssociation() { public boolean isEmpty() { return simpleAssocSecondaries.isEmpty(); } public Database getPrimary(@SuppressWarnings("unused") DatabaseEntry primaryKey) { return Database.this; } public Collection getSecondaries(@SuppressWarnings("unused") DatabaseEntry primaryKey) { return simpleAssocSecondaries; } }; } /** * Used to remove references to this database from other objects, when this * database is closed. We don't remove references from cursors or * secondaries here, because it's an error to close a database before its * cursors and to close a primary before its secondaries. */ void removeReferringAssociations() { envHandle.removeReferringHandle(this); } /** * Sees if this new handle's configuration is compatible with the * pre-existing database. */ private void validateConfigAgainstExistingDb(Locker locker, final String databaseName, final DatabaseConfig config, final DatabaseImpl dbImpl) { /* * The sortedDuplicates, temporary, and replicated properties are * persistent and immutable. But they do not need to be specified if * the useExistingConfig property is set. */ if (!config.getUseExistingConfig()) { validatePropertyMatches( "sortedDuplicates", dbImpl.getSortedDuplicates(), config.getSortedDuplicates()); validatePropertyMatches( "temporary", dbImpl.isTemporary(), config.getTemporary()); /* Only check replicated if the environment is replicated. */ if (getEnv().isReplicated()) { validatePropertyMatches( "replicated", dbImpl.isReplicated(), config.getReplicated()); } } /* * The transactional and deferredWrite properties are kept constant * while any handles are open, and set when the first handle is opened. * But if an existing handle is open and the useExistingConfig property * is set, then they do not need to be specified. */ if (dbImpl.hasOpenHandles()) { if (!config.getUseExistingConfig()) { validatePropertyMatches( "transactional", dbImpl.isTransactional(), config.getTransactional()); validatePropertyMatches( "deferredWrite", dbImpl.isDurableDeferredWrite(), config.getDeferredWrite()); } } else { dbImpl.setTransactional(config.getTransactional()); dbImpl.setDeferredWrite(config.getDeferredWrite()); if (config.getDeferredWrite()) { mutateDeferredWriteBINDeltas(dbImpl); } } /* * If this database handle uses the existing config, we shouldn't * search for and write any changed attributes to the log. */ if (config.getUseExistingConfig()) { return; } /* Write any changed, persistent attributes to the log. */ boolean dbImplModified = false; /* Only re-set the comparators if the override is allowed. */ if (config.getOverrideBtreeComparator()) { dbImplModified |= dbImpl.setBtreeComparator( config.getBtreeComparator(), config.getBtreeComparatorByClassName()); } if (config.getOverrideDuplicateComparator()) { dbImplModified |= dbImpl.setDuplicateComparator( config.getDuplicateComparator(), config.getDuplicateComparatorByClassName()); } dbImplModified |= dbImpl.setTriggers(locker, databaseName, config.getTriggers(), config.getOverrideTriggers()); /* Check if KeyPrefixing property is updated. */ boolean newKeyPrefixing = config.getKeyPrefixing(); if (newKeyPrefixing != dbImpl.getKeyPrefixing()) { dbImplModified = true; if (newKeyPrefixing) { dbImpl.setKeyPrefixing(); } else { dbImpl.clearKeyPrefixing(); } } /* * Check if NodeMaxEntries properties are updated. */ int newNodeMaxEntries = config.getNodeMaxEntries(); if (newNodeMaxEntries != 0 && newNodeMaxEntries != dbImpl.getNodeMaxTreeEntries()) { dbImplModified = true; dbImpl.setNodeMaxTreeEntries(newNodeMaxEntries); } /* Do not write LNs in a read-only environment. Also see [#15743]. */ EnvironmentImpl envImpl = getEnv(); if (dbImplModified && !envImpl.isReadOnly()) { /* Write a new NameLN to the log. */ try { envImpl.getDbTree().updateNameLN(locker, dbImpl.getName(), null); } catch (LockConflictException e) { throw new IllegalStateException( "DatabaseConfig properties may not be updated when the " + "database is already open; first close other open " + "handles for this database.", e); } /* Dirty the root. */ envImpl.getDbTree().modifyDbRoot(dbImpl); } /* CacheMode is changed for all handles, but is not persistent. */ dbImpl.setCacheMode(config.getCacheMode()); } /** * Mutate BIN-deltas to full BINs for the given DW DB. [#25999] *

* Any BIN-deltas in cache must be mutated to full BINs, since * BIN-deltas are not allowed in DW mode. This can be expensive, and is * only a workaround for the underlying problem that BIN-deltas are not * supported in DW mode. The mutation is necessary when a db transitions * from non-DW to DW mode. In that case there may be BIN-deltas in * cache, even if the DB has not yet been opened since BIN-deltas may * be placed in the Btree by recovery. This method is not optimized * because ultimately BIN-deltas will be supported in DW mode or DW mode * will be discontinued. *

* At the time this method is called, the DW flag on this object has * already been set. Therefore, this workaround doesn't guarantee that * internal operations on the db (e.g., compression or eviction) won't * operation on a BIN-delta in a DW DB. Therefore, we cannot assume/assert * that a cached BIN-delta does not exist in a DW DB. Such assertions were * removed as part of this workaround. *

* Setting the DW flag before mutating the BINs is necessary to ensure * that no new BIN-deltas appear in cache during this process. BINs loaded * into cache for a DW db are mutated to full BINs in IN.postFetchInit. * * @see BIN#shouldLogDelta */ private void mutateDeferredWriteBINDeltas(DatabaseImpl dbImpl) { final OffHeapCache ohCache = getEnv().getOffHeapCache(); for (final IN in : getEnv().getInMemoryINs()) { if (in.getDatabase() != dbImpl) { continue; } in.latchNoUpdateLRU(); try { if (in.isBIN()) { in.mutateToFullBIN(false); continue; } if (ohCache == null || in.getNormalizedLevel() != 2) { continue; } for (int i = 0; i < in.getNEntries(); i += 1) { if (in.getOffHeapBINId(i) < 0) { continue; } final IN child = in.loadIN(i, CacheMode.UNCHANGED); if (child == null) { continue; } child.latchNoUpdateLRU(); try { child.mutateToFullBIN(false); } finally { child.releaseLatch(); } } } finally { in.releaseLatch(); } } } /** * @throws IllegalArgumentException via Environment.openDatabase and * openSecondaryDatabase. */ private void validatePropertyMatches(final String propName, final boolean existingValue, final boolean newValue) { if (newValue != existingValue) { throw new IllegalArgumentException( "You can't open a Database with a " + propName + " configuration of " + newValue + " if the underlying database was created with a " + propName + " setting of " + existingValue + '.'); } } /** * Discards the database handle. *

* When closing the last open handle for a deferred-write database, any * cached database information is flushed to disk as if {@link #sync} were * called. *

* The database handle should not be closed while any other handle that * refers to it is not yet closed; for example, database handles should not * be closed while cursor handles into the database remain open, or * transactions that include operations on the database have not yet been * committed or aborted. Specifically, this includes {@link * com.sleepycat.je.Cursor Cursor} and {@link com.sleepycat.je.Transaction * Transaction} handles. *

* When multiple threads are using the {@link com.sleepycat.je.Database * Database} handle concurrently, only a single thread may call this * method. *

* When called on a database that is the primary database for a secondary * index, the primary database should be closed only after all secondary * indices which reference it have been closed. *

* The database handle may not be accessed again after this method is * called, regardless of the method's success or failure, with one * exception: the {@code close} method itself may be called any number of * times.

* *

WARNING: To guard against memory leaks, the application should * discard all references to the closed handle. While BDB makes an effort * to discard references from closed objects to the allocated memory for an * environment, this behavior is not guaranteed. The safe course of action * for an application is to discard all references to closed BDB * objects.

* * @see DatabaseConfig#setDeferredWrite DatabaseConfig.setDeferredWrite * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if cursors associated with this database * are still open. */ public void close() { try { closeInternal(true /*doSyncDw*/, true /*deleteTempDb*/, DbState.CLOSED, null /*preemptedException*/); } catch (Error E) { envHandle.invalidate(E); throw E; } } /* * This method is private for now because it is incomplete. To fully * implement it we must clear all dirty nodes for the database that is * closed, since otherwise they will be flushed during the next checkpoint. */ @SuppressWarnings("unused") private void closeNoSync() { try { closeInternal(false /*doSyncDw*/, true /*deleteTempDb*/, DbState.CLOSED, null /*preemptedException*/); } catch (Error E) { envHandle.invalidate(E); throw E; } } /** * Marks the handle as preempted when the handle lock is stolen by the HA * replayer, during replay of a naming operation (remove, truncate or * rename). This causes DatabasePreemptedException to be thrown on all * subsequent use of the handle or cursors opened on this handle. [#17015] */ synchronized void setPreempted(final String dbName, final String msg) { /* * Return silently when the DB is closed, because the calling thread is * performing an DbTree operation, and a "database closed" exception * should not be thrown there. */ final DatabaseImpl dbImpl = databaseImpl; if (dbImpl == null) { return; } final OperationFailureException preemptedException = dbImpl.getEnv().createDatabasePreemptedException( msg, dbName, this); closeInternal(false /*doSyncDw*/, false /*deleteTempDb*/, DbState.PREEMPTED, preemptedException); } synchronized void setCorrupted(SecondaryIntegrityException sie) { if (state != DbState.OPEN) { return; } corruptedCause = sie; state = DbState.CORRUPTED; } boolean isCorrupted() { return corruptedCause != null; } SecondaryIntegrityException getCorruptedCause() { return corruptedCause; } /** * Invalidates the handle when the transaction used to open the database * is aborted. * * Note that this method (unlike close) does not perform sync and removal * of DW DBs. A DW DB cannot be transactional. */ synchronized void invalidate() { closeInternal(false /*doSyncDw*/, false /*deleteTempDb*/, DbState.INVALID, null /*preemptedException*/); } EnvironmentImpl getEnv() { return envHandle.getNonNullEnvImpl(); } private void closeInternal( final boolean doSyncDw, final boolean deleteTempDb, final DbState newState, final OperationFailureException preemptedException) { /* * We acquire the SecondaryAssociationLatch exclusively because * associations are changed by removeReferringAssociations, and * operations using the associations should not run concurrently with * close. */ try { final EnvironmentImpl envImpl = getEnv(); try { envImpl.getSecondaryAssociationLock(). writeLock().lockInterruptibly(); } catch (InterruptedException e) { throw new ThreadInterruptedException(envImpl, e); } try { closeInternalWork( doSyncDw, deleteTempDb, newState, preemptedException); } finally { envImpl.getSecondaryAssociationLock().writeLock().unlock(); } } finally { minimalClose(newState, preemptedException); } } /** * Nulls-out indirect references to the environment, to allow GC. It also * sets the state to the given non-open state, if the state is currently * open. We must set the state to non-open before setting references to * null. * * The app may hold the Database references longer than expected. In * particular during an Environment re-open we need to give GC a fighting * chance while handles from two environments are temporarily referenced. * * Note that this is needed even when the db or env is invalid. */ synchronized void minimalClose( final DbState newState, final OperationFailureException preemptedException) { assert newState != DbState.OPEN; if (state == DbState.OPEN) { state = newState; preemptedCause = preemptedException; } databaseImpl = null; handleLocker = null; } private void closeInternalWork( final boolean doSyncDw, final boolean deleteTempDb, final DbState newState, final OperationFailureException preemptedException) { assert newState != DbState.OPEN; final StringBuilder handleRefErrors = new StringBuilder(); RuntimeException triggerException = null; final DatabaseImpl dbImpl; synchronized (this) { /* * Do nothing if handle was previously closed. * * When the state is set to CLOSED, INVALID and PREEMPTED, the * database has been closed. So for these states, do not close the * database again. * * The CORRUPTED state is set when SecondaryIntegrityException is * thrown, but the database is not closed at that time. * This state is currently only set for a secondary database. * For this state, we want to let the user close the database. * * Besides, if the database was not opened, just return. */ if (state == DbState.CLOSED || state == DbState.INVALID || state == DbState.PREEMPTED || state == null) { return; } /* * databaseImpl and handleLocker are set to null only while * synchronized, at which time the state is also changed to * non-open. So they should not be null here. */ dbImpl = databaseImpl; assert dbImpl != null; assert handleLocker != null; /* * Check env only after checking for closed db, to mimic close() * behavior for Cursors, etc, and avoid unnecessary exception * handling. [#21264] */ final EnvironmentImpl envImpl = checkEnv(); /* * The state should be changed ASAP during close, so that * addCursor and removeCursor will see the updated state ASAP. */ state = newState; preemptedCause = preemptedException; /* * Throw an IllegalStateException if there are open cursors or * associated secondaries. */ if (newState == DbState.CLOSED) { if (openCursors.get() != 0) { handleRefErrors.append(" "). append(openCursors.get()). append(" open cursors."); } if (simpleAssocSecondaries != null && simpleAssocSecondaries.size() > 0) { handleRefErrors.append(" "). append(simpleAssocSecondaries.size()). append(" associated SecondaryDatabases."); } if (foreignKeySecondaries != null && foreignKeySecondaries.size() > 0) { handleRefErrors.append(" "). append(foreignKeySecondaries.size()). append( " associated foreign key SecondaryDatabases."); } } trace(Level.FINEST, "Database.close: ", null, null); removeReferringAssociations(); dbImpl.removeReferringHandle(this); envImpl.getDbTree().releaseDb(dbImpl); /* * If the handle was preempted, we mark the locker as * only-abortable with the DatabasePreemptedException. If * the handle locker is a user txn, this causes the * DatabasePreemptedException to be thrown if the user * attempts to commit, or continue to use, the txn, rather * than throwing a LockConflictException. [#17015] */ if (newState == DbState.PREEMPTED) { handleLocker.setOnlyAbortable(preemptedException); } /* * Tell our protecting txn that we're closing. If this type * of transaction doesn't live beyond the life of the * handle, it will release the db handle lock. */ if (newState == DbState.CLOSED) { if (isWritable() && (dbImpl.noteWriteHandleClose() == 0)) { try { TriggerManager.runCloseTriggers(handleLocker, dbImpl); } catch (RuntimeException e) { triggerException = e; } } handleLocker.operationEnd(true); } else { handleLocker.operationEnd(false); } } /* * Notify the database when a handle is closed. This should not be * done while synchronized since it may perform database removal or * sync. Statements above are synchronized but this section must not * be. * * Note that handleClosed may throw an exception, so any following code * may not be executed. */ dbImpl.handleClosed(doSyncDw, deleteTempDb); /* Throw exceptions for previously encountered problems. */ if (handleRefErrors.length() > 0) { throw new IllegalStateException( "Database closed while still referenced by other handles." + handleRefErrors.toString()); } if (triggerException != null) { throw triggerException; } } /** * Flushes any cached information for this database to disk; only * applicable for deferred-write databases. *

Note that deferred-write databases are automatically flushed to disk * when the {@link #close} method is called. * * @see DatabaseConfig#setDeferredWrite DatabaseConfig.setDeferredWrite * * @throws com.sleepycat.je.rep.DatabasePreemptedException in a replicated * environment if the master has truncated, removed or renamed the * database. * * @throws OperationFailureException if this exception occurred earlier and * caused the transaction to be invalidated. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws UnsupportedOperationException if this is not a deferred-write * database, or this database is read-only. * * @throws IllegalStateException if the database has been closed. */ public void sync() { checkEnv(); final DatabaseImpl dbImpl = checkOpen(); trace(Level.FINEST, "Database.sync", null, null, null, null); dbImpl.sync(true); } /** * Opens a sequence in the database. * * @param txn For a transactional database, an explicit transaction may * be specified, or null may be specified to use auto-commit. For a * non-transactional database, null must be specified. * * @param key The key {@link DatabaseEntry} of the sequence. * * @param config The sequence attributes. If null, default attributes are * used. * * @return a new Sequence handle. * * @throws SequenceExistsException if the sequence record already exists * and the {@code SequenceConfig ExclusiveCreate} parameter is true. * * @throws SequenceNotFoundException if the sequence record does not exist * and the {@code SequenceConfig AllowCreate} parameter is false. * * @throws OperationFailureException if one of the Read Operation * Failures occurs. If the sequence does not exist and the {@link * SequenceConfig#setAllowCreate AllowCreate} parameter is true, then one * of the Write * Operation Failures may also occur. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws UnsupportedOperationException if this database is read-only, or * this database is configured for duplicates. * * @throws IllegalStateException if the Sequence record is deleted by * another thread during this method invocation, or the database has been * closed. * * @throws IllegalArgumentException if an invalid parameter is specified, * for example, an invalid {@code SequenceConfig} parameter. */ public Sequence openSequence(final Transaction txn, final DatabaseEntry key, final SequenceConfig config) { try { checkEnv(); DatabaseUtil.checkForNullDbt(key, "key", true); checkOpen(); trace(Level.FINEST, "Database.openSequence", txn, key, null, null); return new Sequence(this, txn, key, config); } catch (Error E) { envHandle.invalidate(E); throw E; } } /** * Removes the sequence from the database. This method should not be * called if there are open handles on this sequence. * * @param txn For a transactional database, an explicit transaction may be * specified, or null may be specified to use auto-commit. For a * non-transactional database, null must be specified. * * @param key The key {@link com.sleepycat.je.DatabaseEntry * DatabaseEntry} of the sequence. * * @throws OperationFailureException if one of the Write * Operation Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws UnsupportedOperationException if this database is read-only. */ public void removeSequence(final Transaction txn, final DatabaseEntry key) { try { delete(txn, key); } catch (Error E) { envHandle.invalidate(E); throw E; } } /** * Returns a cursor into the database. * * @param txn the transaction used to protect all operations performed with * the cursor, or null if the operations should not be transaction * protected. If the database is non-transactional, null must be * specified. For a transactional database, the transaction is optional * for read-only access and required for read-write access. * * @param cursorConfig The cursor attributes. If null, default attributes * are used. * * @return A database cursor. * * @throws com.sleepycat.je.rep.DatabasePreemptedException in a replicated * environment if the master has truncated, removed or renamed the * database. * * @throws OperationFailureException if this exception occurred earlier and * caused the transaction to be invalidated. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if an invalid parameter is specified, * for example, an invalid {@code CursorConfig} parameter. */ public Cursor openCursor(final Transaction txn, CursorConfig cursorConfig) { try { checkEnv(); checkOpen(); if (cursorConfig == null) { cursorConfig = CursorConfig.DEFAULT; } if (cursorConfig.getReadUncommitted() && cursorConfig.getReadCommitted()) { throw new IllegalArgumentException( "Only one may be specified: " + "ReadCommitted or ReadUncommitted"); } trace(Level.FINEST, "Database.openCursor", txn, cursorConfig); return newDbcInstance(txn, cursorConfig); } catch (Error E) { envHandle.invalidate(E); throw E; } } /** * Create a DiskOrderedCursor to iterate over the records in 'this' * Database. Because the retrieval is based on Log Sequence Number (LSN) * order rather than key order, records are returned in unsorted order in * exchange for generally faster retrieval. LSN order approximates disk * sector order. *

* See {@link DiskOrderedCursor} for more details and a description of the * consistency guarantees provided by the scan. *

* WARNING: After calling this method, deletion of log files by * the JE log cleaner will be disabled until {@link * DiskOrderedCursor#close()} is called. To prevent unbounded growth of * disk usage, be sure to call {@link DiskOrderedCursor#close()} to * re-enable log file deletion. */ public DiskOrderedCursor openCursor(DiskOrderedCursorConfig cursorConfig) { try { checkEnv(); checkOpen(); if (cursorConfig == null) { cursorConfig = DiskOrderedCursorConfig.DEFAULT; } trace(Level.FINEST, "Database.openForwardCursor", null, cursorConfig); Database[] dbs = new Database[1]; dbs[0] = this; return new DiskOrderedCursor(dbs, cursorConfig); } catch (Error E) { envHandle.invalidate(E); throw E; } } /** * Is overridden by SecondaryDatabase. */ Cursor newDbcInstance(final Transaction txn, final CursorConfig cursorConfig) { return new Cursor(this, txn, cursorConfig); } /** * @hidden * For internal use only. * * @deprecated in favor of {@link #populateSecondaries(Transaction, * DatabaseEntry, DatabaseEntry, long, CacheMode)}. */ public void populateSecondaries(final Transaction txn, final DatabaseEntry key, final DatabaseEntry data) { populateSecondaries(txn, key, data, 0 /*expirationTime*/, null); } /** * @hidden * For internal use only. * * Given the {@code key}, {@code data} and {@code expirationTime} for a * locked primary DB record, update the corresponding secondary database * (index) records, for secondaries enabled for incremental population. *

* The secondaries associated the primary record are determined by calling * {@link SecondaryAssociation#getSecondaries}. For each of these * secondaries, {@link SecondaryDatabase#isIncrementalPopulationEnabled} is * called to determine whether incremental population is enabled. If so, * appropriate secondary records are inserted and deleted so that the * index accurately reflects the current state of the primary record. *

* Note that for a given primary record, this method will not modify the * secondary database if the secondary has already been updated for the * primary record, due to concurrent primary write operations. Due to this * behavior, certain integrity checks are not performed as documented in * {@link SecondaryDatabase#startIncrementalPopulation}. *

* The primary record must be locked (read or write locked) when this * method is called. Therefore, the caller should not use dirty-read to * read the primary record. The simplest way to ensure that the primary * record is locked is to use a cursor to read primary records, and call * this method while the cursor is still positioned on the primary record. *

* It is the caller's responsibility to pass all primary records to this * method that contain index keys for a secondary DB being incrementally * populated, before calling {@link * SecondaryDatabase#endIncrementalPopulation} on that secondary DB. * * @param txn is the transaction to be used to write secondary records. If * null and the secondary DBs are transactional, auto-commit will be used. * @param key is the key of the locked primary record. * * @param data is the data of the locked primary record. * * @param expirationTime the expiration time of the locked primary record. * This can be obtained from {@link OperationResult#getExpirationTime()} * when reading the primary record. * * @param cacheMode the CacheMode to use, or null for the Database default. * * @throws OperationFailureException if one of the Write * Operation Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws UnsupportedOperationException if this database is read-only. * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if an invalid parameter is specified. * This includes passing a null input key parameter, an input key parameter * with a null data array, a null input data parameter, or an input data * parameter with a null data array. */ public void populateSecondaries(final Transaction txn, final DatabaseEntry key, final DatabaseEntry data, final long expirationTime, CacheMode cacheMode) { try { checkEnv(); DatabaseUtil.checkForNullDbt(key, "key", true); DatabaseUtil.checkForNullDbt(data, "true", true); final DatabaseImpl dbImpl = checkOpen(); trace(Level.FINEST, "populateSecondaries", null, key, data, null); final Collection secondaries = secAssoc.getSecondaries(key); final Locker locker = LockerFactory.getWritableLocker( envHandle, txn, dbImpl.isInternalDb(), isTransactional(), dbImpl.isReplicated()); // autoTxnIsReplicated boolean success = false; if (cacheMode == null) { cacheMode = dbImpl.getDefaultCacheMode(); } try { for (final SecondaryDatabase secDb : secondaries) { if (secDb.isIncrementalPopulationEnabled()) { secDb.updateSecondary( locker, null /*cursor*/, dbImpl /*priDb*/, null /*priCursor*/, key /*priKey*/, null /*oldData*/, data /*newData*/, cacheMode, expirationTime, false /*expirationUpdated*/, expirationTime); } } success = true; } finally { locker.operationEnd(success); } } catch (Error E) { envHandle.invalidate(E); throw E; } } /** * Removes records with a given key from the database. In the presence of * duplicate keys, all records associated with the given key will be * removed. When the database has associated secondary databases, this * method also deletes the associated index records. * * @param txn For a transactional database, an explicit transaction may * be specified, or null may be specified to use auto-commit. For a * non-transactional database, null must be specified. * * @param key the key used as * input. * * @param options the WriteOptions, or null to use default options. * * @return the OperationResult if the record is deleted, else null if the * given key was not found in the database. * * @throws OperationFailureException if one of the Write * Operation Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws UnsupportedOperationException if this database is read-only. * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if an invalid parameter is specified. * This includes passing a null input key parameter, an input key parameter * with a null data array, or a partial key input parameter. * * @since 7.0 */ public OperationResult delete(final Transaction txn, final DatabaseEntry key, final WriteOptions options) { try { checkEnv(); final DatabaseImpl dbImpl = checkOpen(); trace(Level.FINEST, "Database.delete", txn, key, null, null); final CacheMode cacheMode = options != null ? options.getCacheMode() : null; OperationResult result = null; final Locker locker = LockerFactory.getWritableLocker( envHandle, txn, dbImpl.isInternalDb(), isTransactional(), dbImpl.isReplicated()); // autoTxnIsReplicated try { result = deleteInternal(locker, key, cacheMode); } finally { if (locker != null) { locker.operationEnd(result != null); } } return result; } catch (Error E) { envHandle.invalidate(E); throw E; } } /** * Internal version of delete() that does no parameter checking. Notify * triggers, update secondaries and enforce foreign key constraints. * Deletes all duplicates. */ OperationResult deleteInternal(final Locker locker, final DatabaseEntry key, final CacheMode cacheMode) { final DatabaseEntry noData = new DatabaseEntry(); noData.setPartial(0, 0, true); try (final Cursor cursor = new Cursor(this, locker, null)) { cursor.setNonSticky(true); /* Do not count NEXT_DUP ops. */ cursor.excludeFromOpStats(); final LockMode lockMode = cursor.isSerializableIsolation(LockMode.RMW) ? LockMode.RMW : LockMode.READ_UNCOMMITTED_ALL; OperationResult searchResult = cursor.search( key, noData, lockMode, cacheMode, SearchMode.SET, false); final DatabaseImpl dbImpl = getDbImpl(); OperationResult anyResult = null; while (searchResult != null) { final OperationResult deleteResult = cursor.deleteInternal( dbImpl.getRepContext(), cacheMode); if (deleteResult != null) { dbImpl.getEnv().incDeleteOps(dbImpl); anyResult = deleteResult; } if (!dbImpl.getSortedDuplicates()) { break; } searchResult = cursor.retrieveNext( key, noData, lockMode, cacheMode, GetMode.NEXT_DUP); } if (anyResult == null) { dbImpl.getEnv().incDeleteFailOps(dbImpl); } return anyResult; } } /** * Removes records with a given key from the database. In the presence of * duplicate keys, all records associated with the given key will be * removed. When the database has associated secondary databases, this * method also deletes the associated index records. * *

Calling this method is equivalent to calling {@link * #delete(Transaction, DatabaseEntry, WriteOptions)}.

* * @param txn For a transactional database, an explicit transaction may * be specified, or null may be specified to use auto-commit. For a * non-transactional database, null must be specified. * * @param key the key used as * input. * * @return The method will return {@link * com.sleepycat.je.OperationStatus#NOTFOUND OperationStatus.NOTFOUND} if * the given key is not found in the database; otherwise {@link * com.sleepycat.je.OperationStatus#SUCCESS OperationStatus.SUCCESS}. * * @throws OperationFailureException if one of the Write * Operation Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws UnsupportedOperationException if this database is read-only. * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if an invalid parameter is specified. * This includes passing a null input key parameter, an input key parameter * with a null data array, or a partial key input parameter. */ public OperationStatus delete(final Transaction txn, final DatabaseEntry key) { final OperationResult result = delete(txn, key, null); return result == null ? OperationStatus.NOTFOUND : OperationStatus.SUCCESS; } /** * Retrieves a record according to the specified {@link Get} type. * *

If the operation succeeds, the record will be locked according to the * {@link ReadOptions#getLockMode() lock mode} specified, the key and/or * data will be returned via the (non-null) DatabaseEntry parameters, and a * non-null OperationResult will be returned. If the operation fails * because the record requested is not found, null is returned.

* *

The following table lists each allowed operation and whether the key * and data parameters are input or * output parameters. See the individual {@link Get} operations for * more information.

* *
* * * * * * * * * * * * * * * * * * *
Get operationDescription'key' parameter'data' parameter
{@link Get#SEARCH}Searches using an exact match by key.inputoutput
{@link Get#SEARCH_BOTH}Searches using an exact match by key and data.inputinput
* * @param txn For a transactional database, an explicit transaction may be * specified to transaction-protect the operation, or null may be specified * to perform the operation without transaction protection. For a * non-transactional database, null must be specified. * * @param key the key input parameter. * * @param data the data input or output parameter, depending on getType. * * @param getType the Get operation type. May not be null. * * @param options the ReadOptions, or null to use default options. * * @return the OperationResult if the record requested is found, else null. * * @throws OperationFailureException if one of the Read Operation * Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if an invalid parameter is specified. * This includes passing a null getType, a null input key/data parameter, * an input key/data parameter with a null data array, and a partial * key/data input parameter. * * @since 7.0 */ public OperationResult get( final Transaction txn, final DatabaseEntry key, final DatabaseEntry data, final Get getType, ReadOptions options) { try { checkEnv(); checkOpen(); if (options == null) { options = Cursor.DEFAULT_READ_OPTIONS; } LockMode lockMode = options.getLockMode(); trace( Level.FINEST, "Database.get", String.valueOf(getType), txn, key, null, lockMode); checkLockModeWithoutTxn(txn, lockMode); final CursorConfig cursorConfig; if (lockMode == LockMode.READ_COMMITTED) { cursorConfig = READ_COMMITTED_CURSOR_CONFIG; lockMode = null; } else { cursorConfig = DEFAULT_CURSOR_CONFIG; } OperationResult result = null; final Locker locker = LockerFactory.getReadableLocker( this, txn, cursorConfig.getReadCommitted()); try { try (final Cursor cursor = new Cursor(this, locker, cursorConfig)) { result = cursor.getInternal( key, data, getType, options, lockMode); } } finally { locker.operationEnd(result != null); } return result; } catch (Error E) { envHandle.invalidate(E); throw E; } } /** * Retrieves the key/data pair with the given key. If the matching key has * duplicate values, the first data item in the set of duplicates is * returned. Retrieval of duplicates requires the use of {@link Cursor} * operations. * *

Calling this method is equivalent to calling {@link * #get(Transaction, DatabaseEntry, DatabaseEntry, Get, ReadOptions)} with * {@link Get#SEARCH}.

* * @param txn For a transactional database, an explicit transaction may be * specified to transaction-protect the operation, or null may be specified * to perform the operation without transaction protection. For a * non-transactional database, null must be specified. * * @param key the key used as * input. * * @param data the data returned as * output. * * @param lockMode the locking attributes; if null, default attributes are * used. * * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND * OperationStatus.NOTFOUND} if no matching key/data pair is found; * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS * OperationStatus.SUCCESS}. * * @throws OperationFailureException if one of the Read Operation * Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if an invalid parameter is specified. */ public OperationStatus get(final Transaction txn, final DatabaseEntry key, final DatabaseEntry data, LockMode lockMode) { final OperationResult result = get( txn, key, data, Get.SEARCH, DbInternal.getReadOptions(lockMode)); return result == null ? OperationStatus.NOTFOUND : OperationStatus.SUCCESS; } /** * Retrieves the key/data pair with the given key and data value, that is, * both the key and data items must match. * *

Calling this method is equivalent to calling {@link * #get(Transaction, DatabaseEntry, DatabaseEntry, Get, ReadOptions)} with * {@link Get#SEARCH_BOTH}.

* * @param txn For a transactional database, an explicit transaction may be * specified to transaction-protect the operation, or null may be specified * to perform the operation without transaction protection. For a * non-transactional database, null must be specified. * * @param key the key used as * input. * * @param data the data used as * input. * * @param lockMode the locking attributes; if null, default attributes are * used. * * @return {@link com.sleepycat.je.OperationStatus#NOTFOUND * OperationStatus.NOTFOUND} if no matching key/data pair is found; * otherwise, {@link com.sleepycat.je.OperationStatus#SUCCESS * OperationStatus.SUCCESS}. * * @throws OperationFailureException if one of the Read Operation * Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if an invalid parameter is specified. */ public OperationStatus getSearchBoth(final Transaction txn, final DatabaseEntry key, final DatabaseEntry data, LockMode lockMode) { final OperationResult result = get( txn, key, data, Get.SEARCH_BOTH, DbInternal.getReadOptions(lockMode)); return result == null ? OperationStatus.NOTFOUND : OperationStatus.SUCCESS; } /** * Inserts or updates a record according to the specified {@link Put} * type. * *

If the operation succeeds, the record will be locked according to the * {@link ReadOptions#getLockMode() lock mode} specified, the cursor will * be positioned on the record, and a non-null OperationResult will be * returned. If the operation fails because the record already exists (or * does not exist, depending on the putType), null is returned.

* *

When the database has associated secondary databases, this method * also inserts or deletes associated index records as necessary.

* *

The following table lists each allowed operation. See the individual * {@link Put} operations for more information.

* *
* * * * * * * * * * * * * * * * * * * * * * * * *
Put operationDescriptionReturns null when?Other special rules
{@link Put#OVERWRITE}Inserts or updates a record depending on whether a matching * record is already present.Never returns null.Without duplicates, a matching record is one with the same key; * with duplicates, it is one with the same key and data.
{@link Put#NO_OVERWRITE}Inserts a record if a record with a matching key is not already * present.When an existing record matches.If the database has duplicate keys, a record is inserted only if * there are no records with a matching key.
{@link Put#NO_DUP_DATA}Inserts a record in a database with duplicate keys if a record * with a matching key and data is not already present.When an existing record matches.Without duplicates, this operation is not allowed.
* * @param txn For a transactional database, an explicit transaction may be * specified, or null may be specified to use auto-commit. For a * non-transactional database, null must be specified. * * @param key the key used as * input. * * @param data the data used as * input. * * @param putType the Put operation type. May not be null. * * @param options the WriteOptions, or null to use default options. * * @return the OperationResult if the record is written, else null. * * @throws OperationFailureException if one of the Write * Operation Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws UnsupportedOperationException if the database is read-only, or * putType is Put.NO_DUP_DATA and the database is not configured for * duplicates. * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if an invalid parameter is specified. * This includes passing a null putType, a null input key/data parameter, * an input key/data parameter with a null data array, a partial key/data * input parameter, or when putType is Put.CURRENT. * * @since 7.0 */ public OperationResult put( final Transaction txn, final DatabaseEntry key, final DatabaseEntry data, final Put putType, final WriteOptions options) { try { checkEnv(); final DatabaseImpl dbImpl = checkOpen(); if (putType == Put.CURRENT) { throw new IllegalArgumentException( "putType may not be Put.CURRENT"); } OperationResult result = null; trace( Level.FINEST, "Database.put", String.valueOf(putType), txn, key, data, null); final Locker locker = LockerFactory.getWritableLocker( envHandle, txn, dbImpl.isInternalDb(), isTransactional(), dbImpl.isReplicated()); // autoTxnIsReplicated try { try (final Cursor cursor = new Cursor(this, locker, DEFAULT_CURSOR_CONFIG)) { result = cursor.putInternal(key, data, putType, options); } } finally { locker.operationEnd(result != null); } return result; } catch (Error E) { envHandle.invalidate(E); throw E; } } /** * Stores the key/data pair into the database. * *

Calling this method is equivalent to calling {@link * #put(Transaction, DatabaseEntry, DatabaseEntry, Put, WriteOptions)} with * {@link Put#OVERWRITE}.

* *

If the key already appears in the database and duplicates are not * configured, the data associated with the key will be replaced. If the * key already appears in the database and sorted duplicates are * configured, the new data value is inserted at the correct sorted * location.

* * @param txn For a transactional database, an explicit transaction may be * specified, or null may be specified to use auto-commit. For a * non-transactional database, null must be specified. * * @param key the key used as * input.. * * @param data the data used as * input. * * @return {@link com.sleepycat.je.OperationStatus#SUCCESS * OperationStatus.SUCCESS}. * * @throws OperationFailureException if one of the Write * Operation Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws UnsupportedOperationException if this database is read-only. * * @throws IllegalStateException if the database has been closed. */ public OperationStatus put(final Transaction txn, final DatabaseEntry key, final DatabaseEntry data) { final OperationResult result = put( txn, key, data, Put.OVERWRITE, null); EnvironmentFailureException.assertState(result != null); return OperationStatus.SUCCESS; } /** * Stores the key/data pair into the database if the key does not already * appear in the database. * *

Calling this method is equivalent to calling {@link * #put(Transaction, DatabaseEntry, DatabaseEntry, Put, WriteOptions)} with * {@link Put#NO_OVERWRITE}.

* *

This method will return {@link * com.sleepycat.je.OperationStatus#KEYEXIST OpeationStatus.KEYEXIST} if * the key already exists in the database, even if the database supports * duplicates.

* * @param txn For a transactional database, an explicit transaction may be * specified, or null may be specified to use auto-commit. For a * non-transactional database, null must be specified. * * @param key the key used as * input.. * * @param data the data used as * input. * * @return {@link com.sleepycat.je.OperationStatus#KEYEXIST * OperationStatus.KEYEXIST} if the key already appears in the database, * else {@link com.sleepycat.je.OperationStatus#SUCCESS * OperationStatus.SUCCESS} * * @throws OperationFailureException if one of the Write * Operation Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws UnsupportedOperationException if this database is read-only. * * @throws IllegalStateException if the database has been closed. */ public OperationStatus putNoOverwrite(final Transaction txn, final DatabaseEntry key, final DatabaseEntry data) { final OperationResult result = put( txn, key, data, Put.NO_OVERWRITE, null); return result == null ? OperationStatus.KEYEXIST : OperationStatus.SUCCESS; } /** * Stores the key/data pair into the database if it does not already appear * in the database. * *

Calling this method is equivalent to calling {@link * #put(Transaction, DatabaseEntry, DatabaseEntry, Put, WriteOptions)} with * {@link Put#NO_DUP_DATA}.

* *

This method may only be called if the underlying database has been * configured to support sorted duplicates.

* * @param txn For a transactional database, an explicit transaction may be * specified, or null may be specified to use auto-commit. For a * non-transactional database, null must be specified. * * @param key the key used as * input.. * * @param data the data used as * input. * * @return {@link com.sleepycat.je.OperationStatus#KEYEXIST * OperationStatus.KEYEXIST} if the key/data pair already appears in the * database, else {@link com.sleepycat.je.OperationStatus#SUCCESS * OperationStatus.SUCCESS} * * @throws OperationFailureException if one of the Write * Operation Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws UnsupportedOperationException if this database is not configured * for duplicates, or this database is read-only. * * @throws IllegalStateException if the database has been closed. */ public OperationStatus putNoDupData(final Transaction txn, final DatabaseEntry key, final DatabaseEntry data) { final OperationResult result = put( txn, key, data, Put.NO_DUP_DATA, null); return result == null ? OperationStatus.KEYEXIST : OperationStatus.SUCCESS; } /** * Creates a specialized join cursor for use in performing equality or * natural joins on secondary indices. * *

Each cursor in the cursors array must have been * initialized to refer to the key on which the underlying database should * be joined. Typically, this initialization is done by calling {@link * Cursor#getSearchKey Cursor.getSearchKey}.

* *

Once the cursors have been passed to this method, they should not be * accessed or modified until the newly created join cursor has been * closed, or else inconsistent results may be returned. However, the * position of the cursors will not be changed by this method or by the * methods of the join cursor.

* * @param cursors an array of cursors associated with this primary * database. * * @param config The join attributes. If null, default attributes are * used. * * @return a specialized cursor that returns the results of the equality * join operation. * * @throws OperationFailureException if one of the Read Operation * Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if an invalid parameter is specified, * for example, an invalid {@code JoinConfig} parameter. * * @see JoinCursor */ public JoinCursor join(final Cursor[] cursors, final JoinConfig config) { try { EnvironmentImpl env = checkEnv(); checkOpen(); DatabaseUtil.checkForNullParam(cursors, "cursors"); if (cursors.length == 0) { throw new IllegalArgumentException( "At least one cursor is required."); } /* * Check that all cursors use the same locker, if any cursor is * transactional. And if non-transactional, that all databases are * in the same environment. */ Locker locker = cursors[0].getCursorImpl().getLocker(); if (!locker.isTransactional()) { for (int i = 1; i < cursors.length; i += 1) { Locker locker2 = cursors[i].getCursorImpl().getLocker(); if (locker2.isTransactional()) { throw new IllegalArgumentException( "All cursors must use the same transaction."); } EnvironmentImpl env2 = cursors[i].getDatabaseImpl().getEnv(); if (env != env2) { throw new IllegalArgumentException( "All cursors must use the same environment."); } } } else { for (int i = 1; i < cursors.length; i += 1) { Locker locker2 = cursors[i].getCursorImpl().getLocker(); if (locker.getTxnLocker() != locker2.getTxnLocker()) { throw new IllegalArgumentException( "All cursors must use the same transaction."); } } } /* Create the join cursor. */ return new JoinCursor(this, cursors, config); } catch (Error E) { envHandle.invalidate(E); throw E; } } /** * Preloads the cache. This method should only be called when there are no * operations being performed on the database in other threads. Executing * preload during concurrent updates may result in some or all of the tree * being loaded into the JE cache. Executing preload during any other * types of operations may result in JE exceeding its allocated cache * size. preload() effectively locks the entire database and therefore will * lock out the checkpointer, cleaner, and compressor, as well as not allow * eviction to occur. * * @deprecated As of JE 2.0.83, replaced by {@link * Database#preload(PreloadConfig)}. * * @param maxBytes The maximum number of bytes to load. If maxBytes is 0, * je.evictor.maxMemory is used. * * @throws OperationFailureException if one of the Read Operation * Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if the database has been closed. */ public void preload(final long maxBytes) { checkEnv(); final DatabaseImpl dbImpl = checkOpen(); PreloadConfig config = new PreloadConfig(); config.setMaxBytes(maxBytes); dbImpl.preload(config); } /** * Preloads the cache. This method should only be called when there are no * operations being performed on the database in other threads. Executing * preload during concurrent updates may result in some or all of the tree * being loaded into the JE cache. Executing preload during any other * types of operations may result in JE exceeding its allocated cache * size. preload() effectively locks the entire database and therefore will * lock out the checkpointer, cleaner, and compressor, as well as not allow * eviction to occur. * * @deprecated As of JE 2.0.101, replaced by {@link * Database#preload(PreloadConfig)}. * * @param maxBytes The maximum number of bytes to load. If maxBytes is 0, * je.evictor.maxMemory is used. * * @param maxMillisecs The maximum time in milliseconds to use when * preloading. Preloading stops once this limit has been reached. If * maxMillisecs is 0, preloading can go on indefinitely or until maxBytes * (if non-0) is reached. * * @throws OperationFailureException if one of the Read Operation * Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if the database has been closed. */ public void preload(final long maxBytes, final long maxMillisecs) { checkEnv(); final DatabaseImpl dbImpl = checkOpen(); PreloadConfig config = new PreloadConfig(); config.setMaxBytes(maxBytes); config.setMaxMillisecs(maxMillisecs); dbImpl.preload(config); } /** * Preloads the cache. This method should only be called when there are no * operations being performed on the database in other threads. Executing * preload during concurrent updates may result in some or all of the tree * being loaded into the JE cache. Executing preload during any other * types of operations may result in JE exceeding its allocated cache * size. preload() effectively locks the entire database and therefore will * lock out the checkpointer, cleaner, and compressor, as well as not allow * eviction to occur. If the database is replicated and the environment is * in the replica state, then the replica may become temporarily * disconnected from the master if the replica needs to replay changes * against the database and is locked out because the time taken by the * preload operation exceeds {@link * com.sleepycat.je.rep.ReplicationConfig#FEEDER_TIMEOUT}. *

* While this method preloads a single database, {@link * Environment#preload} lets you preload multiple databases. * * @param config The PreloadConfig object that specifies the parameters * of the preload. * * @return A PreloadStats object with various statistics about the * preload() operation. * * @throws OperationFailureException if one of the Read Operation * Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if {@code PreloadConfig.getMaxBytes} is * greater than size of the JE cache. */ public PreloadStats preload(final PreloadConfig config) { checkEnv(); final DatabaseImpl dbImpl = checkOpen(); PreloadConfig useConfig = (config == null) ? new PreloadConfig() : config; return dbImpl.preload(useConfig); } /** * Counts the key/data pairs in the database. This operation is faster than * obtaining a count from a cursor based scan of the database, and will not * perturb the current contents of the cache. However, the count is not * guaranteed to be accurate if there are concurrent updates. Note that * this method does scan a significant portion of the database and should * be considered a fairly expensive operation. *

* This operation uses the an internal infrastructure and algorithm that is * similar to the one used for the {@link DiskOrderedCursor}. Specifically, * it will disable deletion of log files by the JE log cleaner during its * execution and will consume a certain amount of memory (but without * affecting the memory that is available for the JE cache). To avoid * excessive memory consumption (and a potential {@code OutOfMemoryError}) * this method places an internal limit on its memory consumption. If this * limit is reached, the method will still work properly, but its * performance will degrade. To specify a different memory limit than the * one used by this method, use the * {@link Database#count(long memoryLimit)} method. *

* Currently, the internal memory limit is calculated as 10% of the * difference between the max JVM memory (the value returned by * Runtime.getRuntime().maxMemory()) and the configured JE cache size. * * @return The count of key/data pairs in the database. * * @throws OperationFailureException if one of the Read Operation * Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if the database has been closed. * * @see Cache * Statistics: Unexpected Sizes */ public long count() { checkEnv(); final DatabaseImpl dbImpl = checkOpen(); return dbImpl.count(0); } /** * Counts the key/data pairs in the database. This operation is faster than * obtaining a count from a cursor based scan of the database, and will not * perturb the current contents of the cache. However, the count is not * guaranteed to be accurate if there are concurrent updates. Note that * this method does scan a significant portion of the database and should * be considered a fairly expensive operation. *

* This operation uses the an internal infrastructure and algorithm that is * similar to the one used for the {@link DiskOrderedCursor}. Specifically, * it will disable deletion of log files by the JE log cleaner during its * execution and will consume a certain amount of memory (but without * affecting the memory that is available for the JE cache). To avoid * excessive memory consumption (and a potential {@code OutOfMemoryError}) * this method takes as input an upper bound on the memory it may consume. * If this limit is reached, the method will still work properly, but its * performance will degrade. * * @param memoryLimit The maximum memory (in bytes) that may be consumed * by this method. * * @return The count of key/data pairs in the database. * * @throws OperationFailureException if one of the Read Operation * Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if the database has been closed. * * @see Cache * Statistics: Unexpected Sizes */ public long count(long memoryLimit) { checkEnv(); final DatabaseImpl dbImpl = checkOpen(); return dbImpl.count(memoryLimit); } /** * Returns Btree node counts. * *

The {@code DatabaseStats} return type is abstract to allow for * different types of database structures. However, currently only the * Btree structure is used and the return type can be unconditionally * cast to {@link BtreeStats}.

* *

This method will access some of or all the pages in the database, * incurring a severe performance penalty as well as possibly flushing * the underlying cache.

* *

In the presence of multiple threads or processes accessing an active * database, the information returned by this method may be * out-of-date.

* * @param config the stats config; if null, default statistics are * returned. NOTE: If {@link com.sleepycat.je.StatsConfig#setFast fast * stats} (StatsConfig.setFast(true)) is configured when calling this * method, no statistics will be returned. The use of fast stats for this * method is deprecated. * * @return the {@code DatabaseStats} object, which is currently always a * {@link BtreeStats} object. * * @throws OperationFailureException if one of the Read Operation * Failures occurs. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if the database has been closed. */ public DatabaseStats getStats(StatsConfig config) { checkEnv(); final DatabaseImpl dbImpl = checkOpen(); if (config == null) { config = StatsConfig.DEFAULT; } return dbImpl.stat(config); } /** * Verifies the integrity of the database. * *

Verification is an expensive operation that should normally only be * used for troubleshooting and debugging.

* * @param config Configures the verify operation; if null, the default * operation is performed. * * @return Database statistics. * * @throws OperationFailureException if one of the Read Operation * Failures occurs. * * @throws EnvironmentFailureException if an environment corruption is * detected, or if an unexpected, internal or environment-wide failure * occurs. If a persistent corruption is detected, * {@link EnvironmentFailureException#isCorrupted()} will return true. * * @throws SecondaryIntegrityException if an environment corruption is * not detected, but this {@link SecondaryDatabase}s is corrupt. * {@code SecondaryIntegrityException} will be thrown when attempting to * access the database and it should be closed. * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if an invalid parameter is specified. */ public DatabaseStats verify(VerifyConfig config) { try { checkEnv(); final DatabaseImpl dbImpl = checkOpen(); if (config == null) { config = VerifyConfig.DEFAULT; } return dbImpl.verify(config); } catch (Error E) { envHandle.invalidate(E); throw E; } } /** * Returns the database name. Can be called under all conditions, including * when the database or environment is invalid, and after the database has * been closed. * * @return the database name. */ public String getDatabaseName() { return name; } /** * Returns this Database object's configuration. * *

This may differ from the configuration used to open this object if * the database existed previously.

* *

Unlike most Database methods, this method may be called after the * database is closed.

* * @return This Database object's configuration. * * @throws EnvironmentFailureException if an unexpected, internal or * environment-wide failure occurs. * * @throws IllegalStateException if the database has been closed. */ public DatabaseConfig getConfig() { checkEnv(); final DatabaseImpl dbImpl = checkOpen(); try { return DatabaseConfig.combineConfig(dbImpl, configuration); } catch (Error E) { envHandle.invalidate(E); throw E; } } /** * Equivalent to getConfig().getTransactional() but cheaper. */ boolean isTransactional() { final DatabaseImpl dbImpl = checkOpen(); return dbImpl.isTransactional(); } /** * Returns the {@link com.sleepycat.je.Environment Environment} handle for * the database environment underlying the {@link * com.sleepycat.je.Database Database}. * *

This method may be called at any time during the life of the * application.

* * @return The {@link com.sleepycat.je.Environment Environment} handle * for the database environment underlying the {@link * com.sleepycat.je.Database Database}. */ public Environment getEnvironment() { return envHandle; } /** * Returns a list of all {@link com.sleepycat.je.SecondaryDatabase * SecondaryDatabase} objects associated with a primary database. * *

If no secondaries are associated with this database, an empty list is * returned.

*/ /* * Replacement for above paragraph when SecondaryAssociation is published: *

If no secondaries are associated with this database, or a {@link * SecondaryAssociation} is {@link DatabaseConfig#setSecondaryAssociation * configured}, an empty list is returned.

*/ public List getSecondaryDatabases() { return new ArrayList<>(simpleAssocSecondaries); } /** * Compares two keys using either the default comparator if no BTree * comparator has been set or the BTree comparator if one has been set. * * @return -1 if entry1 compares less than entry2, * 0 if entry1 compares equal to entry2, * 1 if entry1 compares greater than entry2 * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if either entry is a partial * DatabaseEntry, or is null. */ public int compareKeys(final DatabaseEntry entry1, final DatabaseEntry entry2) { return doCompareKeys(entry1, entry2, false/*duplicates*/); } /** * Compares two data elements using either the default comparator if no * duplicate comparator has been set or the duplicate comparator if one has * been set. * * @return -1 if entry1 compares less than entry2, * 0 if entry1 compares equal to entry2, * 1 if entry1 compares greater than entry2 * * @throws IllegalStateException if the database has been closed. * * @throws IllegalArgumentException if either entry is a partial * DatabaseEntry, or is null. */ public int compareDuplicates(final DatabaseEntry entry1, final DatabaseEntry entry2) { return doCompareKeys(entry1, entry2, true/*duplicates*/); } private int doCompareKeys(final DatabaseEntry entry1, final DatabaseEntry entry2, final boolean duplicates) { try { checkEnv(); final DatabaseImpl dbImpl = checkOpen(); DatabaseUtil.checkForNullDbt(entry1, "entry1", true); DatabaseUtil.checkForNullDbt(entry2, "entry2", true); DatabaseUtil.checkForPartial(entry1, "entry1"); DatabaseUtil.checkForPartial(entry2, "entry2"); return dbImpl.compareEntries(entry1, entry2, duplicates); } catch (Error E) { envHandle.invalidate(E); throw E; } } /* * Helpers, not part of the public API */ /** * Returns true if the Database was opened read/write. * * @return true if the Database was opened read/write. */ boolean isWritable() { return isWritable; } /** * Returns the non-null, underlying getDbImpl. * * This method should always be called to access the databaseImpl, to guard * against NPE when the database has been closed after the initial checks. * * However, callers should additionally call checkOpen at API entry points * to reject the operation as soon as possible. Plus, if the database has * been closed, this method may return non-null because the databaseImpl * field is not volatile. * * @throws IllegalStateException if the database has been closed since * checkOpen was last called. */ DatabaseImpl getDbImpl() { final DatabaseImpl dbImpl = databaseImpl; if (dbImpl != null) { return dbImpl; } checkOpen(); /* * checkOpen should have thrown an exception, but we'll throw again * here just in case. */ throw new IllegalStateException("Database is closed. State=" + state); } /** * Called during database open to set the handleLocker field. * @see HandleLocker */ HandleLocker initHandleLocker(EnvironmentImpl envImpl, Locker openDbLocker) { handleLocker = HandleLocker.createHandleLocker(envImpl, openDbLocker); return handleLocker; } @SuppressWarnings("unused") void removeCursor(final ForwardCursor ignore) throws DatabaseException { /* * Do not call checkOpen if the handle was preempted or corrupted, to * allow closing a cursor after an operation failure. [#17015] */ if (state != DbState.PREEMPTED && state != DbState.CORRUPTED) { checkOpen(); } openCursors.getAndDecrement(); } @SuppressWarnings("unused") void addCursor(final ForwardCursor ignore) { checkOpen(); openCursors.getAndIncrement(); } DatabaseImpl checkOpen() { switch (state) { case OPEN: return databaseImpl; case CLOSED: throw new IllegalStateException("Database was closed."); case INVALID: throw new IllegalStateException( "The Transaction used to open the Database was aborted."); case PREEMPTED: throw preemptedCause.wrapSelf(preemptedCause.getMessage()); case CORRUPTED: throw corruptedCause.wrapSelf(corruptedCause.getMessage()); default: assert false : state; return null; } } /** * @throws EnvironmentFailureException if the underlying environment is * invalid. * @throws IllegalStateException if the environment is not open. */ EnvironmentImpl checkEnv() { return envHandle.checkOpen(); } void checkLockModeWithoutTxn(final Transaction userTxn, final LockMode lockMode) { if (userTxn == null && LockMode.RMW.equals(lockMode)) { throw new IllegalArgumentException( lockMode + " is meaningless and can not be specified " + "when a null (autocommit) transaction is used. There " + "will never be a follow on operation which will promote " + "the lock to WRITE."); } } /** * Sends trace messages to the java.util.logger. Don't rely on the logger * alone to conditionalize whether we send this message, we don't even want * to construct the message if the level is not enabled. */ void trace(final Level level, final String methodName, final String getOrPutType, final Transaction txn, final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode) { if (logger.isLoggable(level)) { StringBuilder sb = new StringBuilder(); sb.append(methodName).append(" "); sb.append(getOrPutType); if (txn != null) { sb.append(" txnId=").append(txn.getId()); } sb.append(" key=").append(key.dumpData()); if (data != null) { sb.append(" data=").append(data.dumpData()); } if (lockMode != null) { sb.append(" lockMode=").append(lockMode); } LoggerUtils.logMsg( logger, getEnv(), level, sb.toString()); } } /** * Sends trace messages to the java.util.logger. Don't rely on the logger * alone to conditionalize whether we send this message, we don't even want * to construct the message if the level is not enabled. */ void trace(final Level level, final String methodName, final Transaction txn, final DatabaseEntry key, final DatabaseEntry data, final LockMode lockMode) { if (logger.isLoggable(level)) { StringBuilder sb = new StringBuilder(); sb.append(methodName); if (txn != null) { sb.append(" txnId=").append(txn.getId()); } sb.append(" key=").append(key.dumpData()); if (data != null) { sb.append(" data=").append(data.dumpData()); } if (lockMode != null) { sb.append(" lockMode=").append(lockMode); } LoggerUtils.logMsg( logger, getEnv(), level, sb.toString()); } } /** * Sends trace messages to the java.util.logger. Don't rely on the logger * alone to conditionalize whether we send this message, we don't even want * to construct the message if the level is not enabled. */ void trace(final Level level, final String methodName, final Transaction txn, final Object config) { if (logger.isLoggable(level)) { StringBuilder sb = new StringBuilder(); sb.append(methodName); sb.append(" name=").append(getDatabaseName()); if (txn != null) { sb.append(" txnId=").append(txn.getId()); } if (config != null) { sb.append(" config=").append(config); } LoggerUtils.logMsg( logger, getEnv(), level, sb.toString()); } } boolean hasSecondaryOrForeignKeyAssociations() { return (!secAssoc.isEmpty() || !foreignKeySecondaries.isEmpty()); } SecondaryAssociation getSecondaryAssociation() { return secAssoc; } /** * Creates a SecondaryIntegrityException using the information given. * * This method is in the Database class, rather than in SecondaryDatabase, * to support joins with plain Cursors [#21258]. */ SecondaryIntegrityException secondaryRefersToMissingPrimaryKey( final Locker locker, final Database priDb, final DatabaseEntry secKey, final DatabaseEntry priKey, final long expirationTime) { return new SecondaryIntegrityException( this, locker, "Secondary refers to a missing key in the primary database", getDatabaseName(), priDb.getDatabaseName(), secKey, priKey, DbLsn.NULL_LSN, expirationTime, null); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy