Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*-
    * See the file LICENSE for redistribution information.
    *
    * Copyright (c) 2002, 2013 Oracle and/or its affiliates.  All rights reserved.
    *
    */
   
   package com.sleepycat.je;
   
  import java.util.HashSet;
  import java.util.Set;
  
A secondary database handle.

Secondary databases are opened with Environment.openSecondaryDatabase(com.sleepycat.je.Transaction,java.lang.String,com.sleepycat.je.Database,com.sleepycat.je.SecondaryConfig) and are always associated with a single primary database. The distinguishing characteristics of a secondary database are:

  • Records are automatically added to a secondary database when records are added, modified and deleted in the primary database. Direct calls to put() methods on a secondary database are prohibited.
  • The delete method of a secondary database will delete the primary record and as well as all its associated secondary records.
  • Calls to all get() methods will return the data from the associated primary database.
  • Additional get() method signatures are provided to return the primary key in an additional pKey parameter.
  • Calls to openCursor will return a SecondaryCursor, which itself has get() methods that return the data of the primary database and additional get() method signatures for returning the primary key.

Before opening or creating a secondary database you must implement the SecondaryKeyCreator or SecondaryMultiKeyCreator interface.

For example, to create a secondary database that supports duplicates:

     Database primaryDb; // The primary database must already be open.
     SecondaryKeyCreator keyCreator; // Your key creator implementation.
     SecondaryConfig secConfig = new SecondaryConfig();
     secConfig.setAllowCreate(true);
     secConfig.setSortedDuplicates(true);
     secConfig.setKeyCreator(keyCreator);
     SecondaryDatabase newDb = env.openSecondaryDatabase(transaction,
                                                         "myDatabaseName",
                                                         primaryDb,
                                                         secConfig)
 

If a primary database is to be associated with one or more secondary databases, it may not be configured for duplicates.

WARNING: The associations between primary and secondary databases are not stored persistently. Whenever a primary database is opened for write access by the application, the appropriate associated secondary databases should also be opened by the application. This is necessary to ensure data integrity when changes are made to the primary database. If the secondary database is not opened, it will not be updated when the primary is updated, and the references between the databases will become invalid. (Note that this warning does not apply when using the com.sleepycat.persist, which does store secondary relationships persistently.)

Special considerations for using Secondary Databases with and without Transactions

Normally, during a primary database write operation (insert, update or delete), all associated secondary databases are also updated. However, when an exception occurs during the write operation, the updates may be incomplete. If the databases are transactional, this is handled by aborting the transaction to undo the incomplete operation. If an auto-commit transaction is used (null is passed for the transaction), the transaction will be aborted automatically. If an explicit transaction is used, it must be aborted by the application caller after the exception is caught.

However, if the databases are non-transactional, integrity problems can result when an exception occurs during the write operation. Because the write operation is not made atomic by a transaction, references between the databases will become invalid if the operation is incomplete. This results in a SecondaryIntegrityException when attempting to access the databases later.

A secondary integrity problem is persistent; it cannot be resolved by reopening the databases or the environment. The only way to resolve the problem is to restore the environment from a valid backup, or, if the integrity of the primary database is assumed, to remove and recreate all secondary databases.

Therefore, secondary databases and indexes should always be used in conjunction with transactional databases and stores. Without transactions, it is the responsibility of the application to handle the results of the incomplete write operation or to take steps to prevent this situation from happening in the first place.

The following exceptions may be thrown during a write operation, and may cause an integrity problem in the absence of transactions.

 
 public class SecondaryDatabase extends Database {
 
     /* For type-safe check against EMPTY_SET */
     private static final Set<DatabaseEntryEMPTY_SET =
         Collections.emptySet();
 
     private Database primaryDb;
     private SecondaryConfig secondaryConfig;
     private ForeignKeyTrigger foreignKeyTrigger;

    
Creates a secondary database but does not open or fully initialize it.

Throws:
java.lang.IllegalArgumentException via Environment.openSecondaryDatabase.
 
     SecondaryDatabase(final Environment env,
                       final SecondaryConfig secConfig,
                       final Database primaryDatabase)
         throws DatabaseException {
 
         super(env);
         DatabaseUtil.checkForNullParam(primaryDatabase"primaryDatabase");
         primaryDatabase.checkOpen("Can't use as primary:");
         if (primaryDatabase.configuration.getSortedDuplicates()) {
             throw new IllegalArgumentException
                 ("Duplicates must not be allowed for a primary database: " +
                  primaryDatabase.getDebugName());
         }
         if (env.getEnvironmentImpl() !=
                 primaryDatabase.getEnvironment().getEnvironmentImpl()) {
             throw new IllegalArgumentException
                 ("Primary and secondary databases must be in the same" +
                  " environment");
         }
         if (secConfig.getKeyCreator() != null &&
             secConfig.getMultiKeyCreator() != null) {
             throw new IllegalArgumentException
                 ("secConfig.getKeyCreator() and getMultiKeyCreator() may not" +
                  " both be non-null");
         }
         if (!primaryDatabase.configuration.getReadOnly() &&
             secConfig.getKeyCreator() == null &&
             secConfig.getMultiKeyCreator() == null) {
             throw new IllegalArgumentException
                 ("secConfig and getKeyCreator()/getMultiKeyCreator()" +
                  " may be null only if the primary database is read-only");
         }
         if (secConfig.getForeignKeyNullifier() != null &&
             secConfig.getForeignMultiKeyNullifier() != null) {
             throw new IllegalArgumentException
                 ("secConfig.getForeignKeyNullifier() and" +
                  " getForeignMultiKeyNullifier() may not both be non-null");
         }
         if (secConfig.getForeignKeyDeleteAction() ==
                          . &&
             secConfig.getForeignKeyNullifier() == null &&
             secConfig.getForeignMultiKeyNullifier() == null) {
             throw new IllegalArgumentException
                 ("ForeignKeyNullifier or ForeignMultiKeyNullifier must be" +
                  " non-null when ForeignKeyDeleteAction is NULLIFY");
         }
         if (secConfig.getForeignKeyNullifier() != null &&
             secConfig.getMultiKeyCreator() != null) {
             throw new IllegalArgumentException
                 ("ForeignKeyNullifier may not be used with" +
                  " SecondaryMultiKeyCreator -- use" +
                  " ForeignMultiKeyNullifier instead");
         }
         if (secConfig.getForeignKeyDatabase() != null) {
             Database foreignDb = secConfig.getForeignKeyDatabase();
             if (foreignDb.getDatabaseImpl().getSortedDuplicates()) {
                 throw new IllegalArgumentException
                     ("Duplicates must not be allowed for a foreign key " +
                      " database: " + foreignDb.getDebugName());
             }
         }
          = primaryDatabase;
          = new SecondaryTrigger(this);
         if (secConfig.getForeignKeyDatabase() != null) {
              = new ForeignKeyTrigger(this);
         }
     }

    
Create a database, called by Environment
 
     @Override
     DatabaseImpl initNew(final Environment env,
                          final Locker locker,
                          final String databaseName,
                          final DatabaseConfig dbConfig)
         throws DatabaseException {
 
         final DatabaseImpl dbImpl =
             super.initNew(envlockerdatabaseNamedbConfig);
         init(locker);
         return dbImpl;
     }

    
Open a database, called by Environment

Throws:
java.lang.IllegalArgumentException via Environment.openSecondaryDatabase.
 
     @Override
     void initExisting(final Environment env,
                       final Locker locker,
                       final DatabaseImpl database,
                       final String databaseName,
                       final DatabaseConfig dbConfig)
         throws DatabaseException {
 
         /* Disallow one secondary associated with two different primaries. */
         Database otherPriDb = database.findPrimaryDatabase();
         if (otherPriDb != null &&
             otherPriDb.getDatabaseImpl() != .getDatabaseImpl()) {
             throw new IllegalArgumentException
                 ("Secondary is already associated with a different primary: " +
                  otherPriDb.getDebugName());
         }
 
         super.initExisting(envlockerdatabasedatabaseNamedbConfig);
         init(locker);
     }

    
Adds secondary to primary's list, and populates the secondary if needed.

Parameters:
locker should be the locker used to open the database. If a transactional locker, the population operations will occur in the same transaction; this may result in a large number of retained locks. If a non-transactional locker, the Cursor will create a ThreadLocker (even if a BasicLocker used for handle locking is passed), and locks will not be retained.
 
     private void init(final Locker locker)
         throws DatabaseException {
 
         trace(."SecondaryDatabase open");
 
 
         /*
          * Insert foreign key triggers at the front of the list and append
          * secondary triggers at the end, so that ForeignKeyDeleteAction.ABORT
          * is applied before deleting the secondary keys.
          */
         .addTrigger(false);
 
         Database foreignDb = .getForeignKeyDatabase();
         if (foreignDb != null) {
             foreignDb.addTrigger(true);
         }
 
         /* Populate secondary if requested and secondary is empty. */
         if (.getAllowPopulate()) {
             Cursor secCursor = null;
             Cursor priCursor = null;
             try {
                 secCursor = new Cursor(thislockernull);
                 DatabaseEntry key = new DatabaseEntry();
                 DatabaseEntry data = new DatabaseEntry();
                 OperationStatus status = secCursor.position(keydata,
                                                             .,
                                                             true);
                 if (status == .) {
                     /* Is empty, so populate */
                     priCursor = new Cursor(lockernull);
                     status = priCursor.position(keydata.,
                                                 true);
                     while (status == .) {
                         updateSecondary(lockersecCursorkeynulldata);
                         status = priCursor.retrieveNext(keydata,
                                                         .,
                                                         .);
                     }
                 }
             } finally {
                 if (secCursor != null) {
                     secCursor.close();
                 }
                 if (priCursor != null) {
                     priCursor.close();
                 }
             }
         }
     }

    
Closes a secondary database and dis-associates it from its primary database. A secondary database should be closed before closing its associated primary database.
 
     @Override
     public synchronized void close()
         throws DatabaseException {
 
         if ( != null &&  != null) {
             .removeTrigger();
         }
         Database foreignDb = .getForeignKeyDatabase();
         if (foreignDb != null &&  != null) {
             foreignDb.removeTrigger();
         }
         super.close();
     }

    
Should be called by the secondaryTrigger while holding a write lock on the trigger list.
 
     void clearPrimary() {
          = null;
           = null;
     }

    
Should be called by the foreignKeyTrigger while holding a write lock on the trigger list.
 
     void clearForeignKeyTrigger() {
          = null;
     }

    
Returns the primary database associated with this secondary database.

Returns:
the primary database associated with this secondary database.
 
     public Database getPrimaryDatabase() {
         return ;
     }

    
Returns a copy of the secondary configuration of this database.

Deprecated:
As of JE 4.0.13, replaced by getConfig().
Returns:
a copy of the secondary configuration of this database.
Throws:
EnvironmentFailureException if an unexpected, internal or environment-wide failure occurs.
 
         throws DatabaseException {
 
         return (SecondaryConfiggetConfig();
     }

    
Returns a copy of the secondary configuration of this database.

Returns:
a copy of the secondary configuration of this database.
Throws:
EnvironmentFailureException if an unexpected, internal or environment-wide failure occurs.
 
     public SecondaryConfig getConfig()
         throws DatabaseException {
 
         return (SecondaryConfigsuper.getConfig();
     }

    
Returns the secondary config without cloning, for internal use.
 
         return ;
     }

    
Obtain a cursor on a database, returning a SecondaryCursor. Calling this method is the equivalent of calling Database.openCursor(com.sleepycat.je.DiskOrderedCursorConfig) and casting the result to SecondaryCursor.

Deprecated:
As of JE 4.0.13, replaced by Database.openCursor(com.sleepycat.je.DiskOrderedCursorConfig).
Parameters:
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.
cursorConfig The cursor attributes. If null, default attributes are used.
Returns:
A secondary database cursor.
Throws:
EnvironmentFailureException if an unexpected, internal or environment-wide failure occurs.
 
     public SecondaryCursor openSecondaryCursor(final Transaction txn,
                                                final CursorConfig cursorConfig)
         throws DatabaseException {
 
         return (SecondaryCursoropenCursor(txncursorConfig);
     }

    
Obtain a cursor on a database, returning a SecondaryCursor.
 
     @Override
     public SecondaryCursor openCursor(final Transaction txn,
                                       final CursorConfig cursorConfig)
         throws DatabaseException {
 
         return (SecondaryCursorsuper.openCursor(txncursorConfig);
     }

    
Overrides Database method.
 
     @Override
     Cursor newDbcInstance(final Transaction txn,
                           final CursorConfig cursorConfig)
         throws DatabaseException {
 
         return new SecondaryCursor(thistxncursorConfig);
     }

    
Deletes the primary key/data pair associated with the specified secondary key. In the presence of duplicate key values, all primary records associated with the designated secondary key will be deleted. When the primary records are deleted, their associated secondary records are deleted as if Database.delete(com.sleepycat.je.Transaction,com.sleepycat.je.DatabaseEntry) were called. This includes, but is not limited to, the secondary record referenced by the given key.

Parameters:
key the secondary key used as input. It must be initialized with a non-null byte array by the caller.
 
     @Override
     public OperationStatus delete(final Transaction txn,
                                   final DatabaseEntry key)
         throws DeleteConstraintException,
                LockConflictException,
                DatabaseException,
                UnsupportedOperationException,
                IllegalArgumentException {
 
         checkEnv();
         DatabaseUtil.checkForNullDbt(key"key"true);
         checkOpen("Can't call SecondaryDatabase.delete:");
         trace(."SecondaryDatabase.delete"txn,
               keynullnull);
 
         Locker locker = null;
         Cursor cursor = null;
 
         OperationStatus commitStatus = .;
         try {
             locker = LockerFactory.getWritableLocker
                 (,
                  txn,
                  getDatabaseImpl().isInternalDb(),
                  isTransactional(),
                  getDatabaseImpl().isReplicated()); // autoTxnIsReplicated
 
             /* Read the primary key (the data of a secondary). */
             cursor = new Cursor(thislockernull);
             DatabaseEntry pKey = new DatabaseEntry();
             OperationStatus searchStatus =
                 cursor.search(keypKey..);
 
             /*
              * For each duplicate secondary key, delete the primary record and
              * all its associated secondary records, including the one
              * referenced by this secondary cursor.
              */
             while (searchStatus == .) {
                 commitStatus = .deleteInternal(lockerpKeynull);
                 if (commitStatus != .) {
                     throw secondaryRefersToMissingPrimaryKey
                         (lockerkeypKey);
                 }
                 searchStatus = cursor.retrieveNext
                     (keypKey..);
             }
             return commitStatus;
         } catch (Error E) {
             DbInternal.getEnvironmentImpl().invalidate(E);
             throw E;
         } finally {
             if (cursor != null) {
                 cursor.close();
             }
             if (locker != null) {
                 locker.operationEnd(commitStatus);
             }
         }
     }

    

Parameters:
key the secondary key used as input. It must be initialized with a non-null byte array by the caller.
data the primary data returned as output. Its byte array does not need to be initialized by the caller.
 
     @Override
     public OperationStatus get(final Transaction txn,
                                final DatabaseEntry key,
                                final DatabaseEntry data,
                                final LockMode lockMode)
         throws DatabaseException {
 
         return get(txnkeynew DatabaseEntry(), datalockMode);
     }

    
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 Cursor operations.

Parameters:
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.
key the secondary key used as input. It must be initialized with a non-null byte array by the caller.
pKey the primary key returned as output. Its byte array does not need to be initialized by the caller.
data the primary data returned as output. Its byte array does not need to be initialized by the caller.
lockMode the locking attributes; if null, default attributes are used.
Returns:
OperationStatus.NOTFOUND if no matching key/data pair is found; otherwise, OperationStatus.SUCCESS.
Throws:
OperationFailureException if one of the Read Operation Failures occurs.
EnvironmentFailureException if an unexpected, internal or environment-wide failure occurs.
java.lang.IllegalStateException if the database has been closed.
java.lang.IllegalArgumentException if an invalid parameter is specified.
 
     public OperationStatus get(final Transaction txn,
                                final DatabaseEntry key,
                                final DatabaseEntry pKey,
                                final DatabaseEntry data,
                                LockMode lockMode)
         throws DatabaseException {
 
         checkEnv();
         DatabaseUtil.checkForNullDbt(key"key"true);
         DatabaseUtil.checkForNullDbt(pKey"pKey"false);
         DatabaseUtil.checkForNullDbt(data"data"false);
         checkOpen("Can't call SecondaryDatabase.get:");
         trace(."SecondaryDatabase.get"txnkeynulllockMode);
 
         CursorConfig cursorConfig = .;
         if (lockMode == .) {
             cursorConfig = .;
             lockMode = null;
         }
         checkLockModeWithoutTxn(txnlockMode);
 
         Locker locker = null;
         SecondaryCursor cursor = null;
         OperationStatus commitStatus = null;
         try {
             locker = LockerFactory.getReadableLocker
                 (txnisTransactional(),
                  cursorConfig.getReadCommitted());
             cursor = new SecondaryCursor(thislockercursorConfig);
             commitStatus =
                 cursor.search(keypKeydatalockMode.);
             return commitStatus;
         } catch (Error E) {
             DbInternal.getEnvironmentImpl().invalidate(E);
             throw E;
         } finally {
             if (cursor != null) {
                 cursor.close();
             }
 
             if (locker != null) {
                 locker.operationEnd(commitStatus);
             }
         }
     }

    
This operation is not allowed with this method signature. java.lang.UnsupportedOperationException will always be thrown by this method. The corresponding method with the pKey parameter should be used instead.
 
     @Override
     public OperationStatus getSearchBoth(final Transaction txn,
                                          final DatabaseEntry key,
                                          final DatabaseEntry data,
                                          final LockMode lockMode)
         throws UnsupportedOperationException {
 
         throw notAllowedException();
     }

    
Retrieves the key/data pair with the specified secondary and primary key, that is, both the primary and secondary key items must match.

Parameters:
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.
key the secondary key used as input. It must be initialized with a non-null byte array by the caller.
pKey the primary key used as input. It must be initialized with a non-null byte array by the caller.
data the primary data returned as output. Its byte array does not need to be initialized by the caller.
lockMode the locking attributes; if null, default attributes are used.
Returns:
OperationStatus.NOTFOUND if no matching key/data pair is found; otherwise, OperationStatus.SUCCESS.
Throws:
OperationFailureException if one of the Read Operation Failures occurs.
EnvironmentFailureException if an unexpected, internal or environment-wide failure occurs.
java.lang.IllegalStateException if the database has been closed.
java.lang.IllegalArgumentException if an invalid parameter is specified.
 
     public OperationStatus getSearchBoth(final Transaction txn,
                                          final DatabaseEntry key,
                                          final DatabaseEntry pKey,
                                          final DatabaseEntry data,
                                          LockMode lockMode)
         throws DatabaseException {
 
         checkEnv();
         DatabaseUtil.checkForNullDbt(key"key"true);
         DatabaseUtil.checkForNullDbt(pKey"pKey"true);
         DatabaseUtil.checkForNullDbt(data"data"false);
         checkOpen("Can't call SecondaryDatabase.getSearchBoth:");
         trace(."SecondaryDatabase.getSearchBoth"txnkeydata,
               lockMode);
 
         CursorConfig cursorConfig = .;
         if (lockMode == .) {
             cursorConfig = .;
             lockMode = null;
         }
         checkLockModeWithoutTxn(txnlockMode);
 
         Locker locker = null;
         SecondaryCursor cursor = null;
         OperationStatus commitStatus = null;
         try {
             locker = LockerFactory.getReadableLocker
                 (txnisTransactional(),
                  cursorConfig.getReadCommitted());
             cursor = new SecondaryCursor(thislockercursorConfig);
             commitStatus =
                 cursor.search(keypKeydatalockMode.);
             return commitStatus;
         } catch (Error E) {
             DbInternal.getEnvironmentImpl().invalidate(E);
             throw E;
         } finally {
             if (cursor != null) {
                 cursor.close();
             }
 
             if (locker != null) {
                 locker.operationEnd(commitStatus);
             }
         }
     }

    
This operation is not allowed on a secondary database. java.lang.UnsupportedOperationException will always be thrown by this method. The corresponding method on the primary database should be used instead.
 
     @Override
     public OperationStatus put(final Transaction txn,
                                final DatabaseEntry key,
                                final DatabaseEntry data)
         throws UnsupportedOperationException {
 
         throw notAllowedException();
     }

    
This operation is not allowed on a secondary database. java.lang.UnsupportedOperationException will always be thrown by this method. The corresponding method on the primary database should be used instead.
 
     @Override
     public OperationStatus putNoOverwrite(final Transaction txn,
                                           final DatabaseEntry key,
                                           final DatabaseEntry data)
         throws UnsupportedOperationException {
 
         throw notAllowedException();
     }

    
This operation is not allowed on a secondary database. java.lang.UnsupportedOperationException will always be thrown by this method. The corresponding method on the primary database should be used instead.
 
     @Override
     public OperationStatus putNoDupData(final Transaction txn,
                                         final DatabaseEntry key,
                                         final DatabaseEntry data)
         throws UnsupportedOperationException {
 
         throw notAllowedException();
     }

    
This operation is not allowed on a secondary database. java.lang.UnsupportedOperationException will always be thrown by this method. The corresponding method on the primary database should be used instead.
 
     @Override
     public JoinCursor join(final Cursor[] cursorsfinal JoinConfig config)
         throws UnsupportedOperationException {
 
         throw notAllowedException();
     }

    
Updates a single secondary when a put() or delete() is performed on the primary.

Parameters:
locker the internal locker.
cursor secondary cursor to use, or null if this method should open and close a cursor if one is needed.
priKey the primary key.
oldData the primary data before the change, or null if the record did not previously exist.
newData the primary data after the change, or null if the record has been deleted.
 
     void updateSecondary(final Locker locker,
                          Cursor cursor,
                          final DatabaseEntry priKey,
                          final DatabaseEntry oldData,
                          final DatabaseEntry newData)
         throws DatabaseException {
 
         /*
          * If we're updating the primary and the secondary key cannot be
          * changed, optimize for that case by doing nothing.
          */
         if (.getImmutableSecondaryKey() &&
             oldData != null && newData != null) {
             return;
         }
 
         SecondaryKeyCreator keyCreator = .getKeyCreator();
         if (keyCreator != null) {
             /* Each primary record may have a single secondary key. */
             assert .getMultiKeyCreator() == null;
 
             /* Get old and new secondary keys. */
             DatabaseEntry oldSecKey = null;
             if (oldData != null) {
                 oldSecKey = new DatabaseEntry();
                 if (!keyCreator.createSecondaryKey(thispriKeyoldData,
                                                    oldSecKey)) {
                     oldSecKey = null;
                 }
             }
             DatabaseEntry newSecKey = null;
             if (newData != null) {
                 newSecKey = new DatabaseEntry();
                 if (!keyCreator.createSecondaryKey(thispriKeynewData,
                                                    newSecKey)) {
                     newSecKey = null;
                 }
             }
 
             /* Update secondary if old and new keys are unequal. */
             if ((oldSecKey != null && !oldSecKey.equals(newSecKey)) ||
                 (newSecKey != null && !newSecKey.equals(oldSecKey))) {
 
                 boolean localCursor = (cursor == null);
                 if (localCursor) {
                     cursor = new Cursor(thislockernull);
                 }
                 try {
                     /* Delete the old key. */
                     if (oldSecKey != null) {
                         deleteKey(cursorpriKeyoldSecKey);
                     }
                     /* Insert the new key. */
                     if (newSecKey != null) {
                         insertKey(lockercursorpriKeynewSecKey);
                     }
                 } finally {
                     if (localCursor && cursor != null) {
                         cursor.close();
                     }
                 }
             }
         } else {
             /* Each primary record may have multiple secondary keys. */
             SecondaryMultiKeyCreator multiKeyCreator =
                 .getMultiKeyCreator();
             assert multiKeyCreator != null;
 
             /* Get old and new secondary keys. */
             Set<DatabaseEntryoldKeys = ;
             Set<DatabaseEntrynewKeys = ;
             if (oldData != null) {
                 oldKeys = new HashSet<DatabaseEntry>();
                 multiKeyCreator.createSecondaryKeys(thispriKey,
                                                     oldDataoldKeys);
             }
             if (newData != null) {
                 newKeys = new HashSet<DatabaseEntry>();
                 multiKeyCreator.createSecondaryKeys(thispriKey,
                                                     newDatanewKeys);
             }
 
             /* Update the secondary if there is a difference. */
             if (!oldKeys.equals(newKeys)) {
 
                 boolean localCursor = (cursor == null);
                 if (localCursor) {
                     cursor = new Cursor(thislockernull);
                 }
                 try {
                     /* Delete old keys that are no longer present. */
                     Set<DatabaseEntryoldKeysCopy = oldKeys;
                     if (oldKeys != ) {
                         oldKeysCopy = new HashSet<DatabaseEntry>(oldKeys);
                         oldKeys.removeAll(newKeys);
                         for (Iterator<DatabaseEntryi = oldKeys.iterator();
                              i.hasNext();) {
                             DatabaseEntry oldKey = i.next();
                             deleteKey(cursorpriKeyoldKey);
                         }
                     }
 
                     /* Insert new keys that were not present before. */
                     if (newKeys != ) {
                         newKeys.removeAll(oldKeysCopy);
                         for (Iterator<DatabaseEntryi = newKeys.iterator();
                              i.hasNext();) {
                             DatabaseEntry newKey = i.next();
                             insertKey(lockercursorpriKeynewKey);
                         }
                     }
                 } finally {
                     if (localCursor && cursor != null) {
                         cursor.close();
                     }
                 }
             }
         }
     }

    
Deletes an old secondary key.
 
     private void deleteKey(final Cursor cursor,
                            final DatabaseEntry priKey,
                            final DatabaseEntry oldSecKey)
         throws DatabaseException {
 
         OperationStatus status =
             cursor.search(oldSecKeypriKey,
                           .,
                           .);
         if (status == .) {
             cursor.deleteInternal(getDatabaseImpl().getRepContext());
         } else {
             throw new SecondaryIntegrityException
                 (cursor.getCursorImpl().getLocker(),
                  "Secondary is corrupt: the primary record contains a key " +
                  "that is not present in the secondary",
                 getDebugName(), oldSecKeypriKey);
         }
     }

    
Inserts a new secondary key.
 
     private void insertKey(final Locker locker,
                            final Cursor cursor,
                            final DatabaseEntry priKey,
                            final DatabaseEntry newSecKey)
         throws DatabaseException {
 
         /* Check for the existence of a foreign key. */
         Database foreignDb =
             .getForeignKeyDatabase();
         if (foreignDb != null) {
             Cursor foreignCursor = null;
             try {
                 foreignCursor = new Cursor(foreignDblocker,
                                            null);
                 DatabaseEntry tmpData = new DatabaseEntry();
                 OperationStatus status =
                     foreignCursor.search(newSecKeytmpData,
                                          .,
                                          .);
                 if (status != .) {
                     throw new ForeignConstraintException
                         (locker,
                          "Secondary " + getDebugName() +
                          " foreign key not allowed: it is not" +
                          " present in the foreign database " +
                          foreignDb.getDebugName(), getDebugName(),
                          newSecKeypriKey);
                 }
             } finally {
                 if (foreignCursor != null) {
                     foreignCursor.close();
                 }
             }
         }
 
         /* Insert the new key. */
         if (.getSortedDuplicates()) {
             OperationStatus status = cursor.putInternal(newSecKeypriKey,
                                                         .);
             if (status != .) {
                 throw new SecondaryIntegrityException
                     (locker"Secondary/primary record already present",
                      getDebugName(), newSecKeypriKey);
             }
         } else {
             OperationStatus status = cursor.putInternal(newSecKeypriKey,
                                                         .);
             if (status != .) {
                 throw new UniqueConstraintException
                     (locker"Unique secondary key is already present",
                      getDebugName(), newSecKeypriKey);
            }
        }
    }

    
Called by the ForeignKeyTrigger when a record in the foreign database is deleted.

Parameters:
secKey is the primary key of the foreign database, which is the secondary key (ordinary key) of this secondary database.
    void onForeignKeyDelete(final Locker lockerfinal DatabaseEntry secKey)
        throws DatabaseException {
        ForeignKeyDeleteAction deleteAction =
        /* Use RMW if we're going to be deleting the secondary records. */
        LockMode lockMode = (deleteAction == .) ?
            . :
            .;
        /*
         * Use the deleted foreign primary key to read the data of this
         * database, which is the associated primary's key.
         */
        DatabaseEntry priKey = new DatabaseEntry();
        Cursor cursor = null;
        OperationStatus status;
        try {
            cursor = new Cursor(thislockernull);
            status = cursor.search(secKeypriKeylockMode,
                                   .);
            while (status == .) {
                if (deleteAction == .) {
                    /*
                     * ABORT - throw an exception to cause the user to abort
                     * the transaction.
                     */
                    throw new DeleteConstraintException
                        (locker"Secondary refers to a deleted foreign key",
                         getDebugName(), secKeypriKey);
                } else if (deleteAction == .) {
                    /*
                     * CASCADE - delete the associated primary record.
                     */
                    Cursor priCursor = null;
                    try {
                        DatabaseEntry data = new DatabaseEntry();
                        priCursor = new Cursor(lockernull);
                        status = priCursor.search(priKeydata.,
                                                  .);
                        if (status == .) {
                            priCursor.delete();
                        } else {
                            throw secondaryRefersToMissingPrimaryKey
                                (lockersecKeypriKey);
                        }
                    } finally {
                        if (priCursor != null) {
                            priCursor.close();
                        }
                    }
                } else if (deleteAction == .) {
                    /*
                     * NULLIFY - set the secondary key to null in the
                     * associated primary record.
                     */
                    Cursor priCursor = null;
                    try {
                        DatabaseEntry data = new DatabaseEntry();
                        priCursor = new Cursor(lockernull);
                        status = priCursor.search(priKeydata.,
                                                  .);
                        if (status == .) {
                            ForeignMultiKeyNullifier multiNullifier =
                                .getForeignMultiKeyNullifier();
                            if (multiNullifier != null) {
                                if (multiNullifier.nullifyForeignKey
                                        (thispriKeydatasecKey)) {
                                    priCursor.putCurrent(data);
                                }
                            } else {
                                ForeignKeyNullifier nullifier =
                                    .getForeignKeyNullifier();
                                if (nullifier.nullifyForeignKey
                                        (thisdata)) {
                                    priCursor.putCurrent(data);
                                }
                            }
                        } else {
                            throw secondaryRefersToMissingPrimaryKey
                                (lockersecKeypriKey);
                        }
                    } finally {
                        if (priCursor != null) {
                            priCursor.close();
                        }
                    }
                } else {
                    /* Should never occur. */
                    throw EnvironmentFailureException.unexpectedState();
                }
                status = cursor.retrieveNext(secKeypriKey.,
                                             .);
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }
        return new UnsupportedOperationException
            ("Operation not allowed on a secondary");
    }

    
Send 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 levelfinal String methodName) {
        if (.isLoggable(level)) {
            StringBuilder sb = new StringBuilder();
            sb.append(methodName);
            sb.append(" name=").append(getDebugName());
            sb.append(" primary=").append(.getDebugName());
            LoggerUtils.logMsg
                (.getEnvironmentImpl(), levelsb.toString());
        }
    }
New to GrepCode? Check out our FAQ X