blob: 74ea03315384e0bc940d1b23031d8ada9d028dfa [file] [log] [blame]
/*-
* 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.dbi;
import com.sleepycat.je.utilint.TestHook;
/**
* Store information about a DatabaseImpl that will have to be deleted or
* updated at transaction commit or abort. This handles cleanup after
* operations like Environment.truncateDatabase, removeDatabase and
* renameDatabase. Cleanup like this is done outside the usual transaction
* commit or node undo processing, because the mapping tree is always
* auto-committed to avoid deadlock and is essentially non-transactional.
*/
public class DbCleanup {
public enum Action {DELETE, RENAME}
private final DatabaseImpl dbImpl;
private final Action action;
private final boolean atCommit;
private final String newName;
DbCleanup(final DatabaseImpl dbImpl,
final Action action,
final boolean atCommit) {
this(dbImpl, action, atCommit, null);
}
DbCleanup(final DatabaseImpl dbImpl,
final Action action,
final boolean atCommit,
final String newName) {
assert (action == Action.RENAME) == (newName != null);
this.dbImpl = dbImpl;
this.action = action;
this.atCommit = atCommit;
this.newName = newName;
}
public static TestHook modifyDbRootHook;
/**
* Make sure that a set of DbCleanup only has one entry per
* databaseImpl/action tuple.
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DbCleanup)) {
return false;
}
final DbCleanup other = (DbCleanup) obj;
return dbImpl.equals(other.dbImpl) &&
action == other.action &&
atCommit == other.atCommit;
}
@Override
public int hashCode() {
return dbImpl.hashCode();
}
/**
* Do any preparation needed for a cleanup task _before_ the txn's write
* locks have been released.
*/
public static void setState(final DbCleanup cleanup,
final boolean isCommit) {
final DatabaseImpl dbImpl = cleanup.dbImpl;
switch (cleanup.action) {
case RENAME:
if (cleanup.atCommit == isCommit) {
dbImpl.setNameAndType(cleanup.newName);
}
break;
case DELETE:
if (cleanup.atCommit == isCommit) {
dbImpl.setDeleteStarted();
}
break;
}
}
/**
* Perform and complete the cleanup task _after_ the txn's write locks
* have been released.
*/
public static void execute(final EnvironmentImpl envImpl,
final DbCleanup[] cleanupArray,
final boolean isCommit) {
final DbTree dbTree = envImpl.getDbTree();
for (final DbCleanup cleanup : cleanupArray) {
final DatabaseImpl dbImpl = cleanup.dbImpl;
switch (cleanup.action) {
case RENAME:
/*
* We do not even attempt to support updates (renaming) along
* with truncate or remove for the same DB in the same txn.
*/
try {
if (cleanup.atCommit == isCommit) {
if (modifyDbRootHook != null) {
modifyDbRootHook.doHook();
}
dbTree.modifyDbRoot(dbImpl);
}
} finally {
dbTree.releaseDb(dbImpl);
}
break;
case DELETE:
if (cleanup.atCommit == isCommit) {
/*
* If dbCleanupSet contains same databases with different
* atCommit, first release the database, then delete it.
* [#19636]
*/
if (checkRepeatedDeletedDB(cleanupArray, cleanup)) {
dbTree.releaseDb(dbImpl);
}
/* releaseDb will be called by startDbExtinction. */
envImpl.getExtinctionScanner().startDbExtinction(dbImpl);
} else {
/*
* If dbCleanupSet contains same databases with different
* atCommit, do nothing. [#19636]
*/
if (!checkRepeatedDeletedDB(cleanupArray, cleanup)) {
dbTree.releaseDb(dbImpl);
}
}
break;
}
}
}
private static boolean checkRepeatedDeletedDB(final DbCleanup[] array,
final DbCleanup cleanup) {
for (final DbCleanup element : array) {
if (element.action == Action.DELETE &&
element.dbImpl.getId().equals(cleanup.dbImpl.getId()) &&
element.atCommit != cleanup.atCommit){
return true;
}
}
return false;
}
/**
* Process cleanup task when a non-transactional DB operation is complete
* (meaning that the NameLN was logged).
*/
public static void setStateAndExecute(final EnvironmentImpl envImpl,
final DbCleanup cleanup) {
final DbTree dbTree = envImpl.getDbTree();
final DatabaseImpl dbImpl = cleanup.dbImpl;
switch (cleanup.action) {
case RENAME:
try {
if (cleanup.atCommit) {
dbImpl.setNameAndType(cleanup.newName);
if (modifyDbRootHook != null) {
modifyDbRootHook.doHook();
}
dbTree.modifyDbRoot(cleanup.dbImpl);
}
} finally {
dbTree.releaseDb(dbImpl);
}
break;
case DELETE:
if (cleanup.atCommit) {
/* releaseDb will be called by startDbExtinction. */
dbImpl.setDeleteStarted();
envImpl.getExtinctionScanner().startDbExtinction(dbImpl);
} else {
dbTree.releaseDb(dbImpl);
}
break;
}
}
}