0001: /*-
0002: * See the file LICENSE for redistribution information.
0003: *
0004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
0005: *
0006: * $Id: Database.java,v 1.216.2.6 2008/01/07 15:14:07 cwl Exp $
0007: */
0008:
0009: package com.sleepycat.je;
0010:
0011: import java.util.ArrayList;
0012: import java.util.Comparator;
0013: import java.util.Iterator;
0014: import java.util.List;
0015: import java.util.logging.Level;
0016: import java.util.logging.Logger;
0017:
0018: import com.sleepycat.je.dbi.DatabaseImpl;
0019: import com.sleepycat.je.dbi.EnvironmentImpl;
0020: import com.sleepycat.je.dbi.GetMode;
0021: import com.sleepycat.je.dbi.PutMode;
0022: import com.sleepycat.je.dbi.TruncateResult;
0023: import com.sleepycat.je.dbi.CursorImpl.SearchMode;
0024: import com.sleepycat.je.txn.Locker;
0025: import com.sleepycat.je.txn.LockerFactory;
0026: import com.sleepycat.je.utilint.DatabaseUtil;
0027: import com.sleepycat.je.utilint.TinyHashSet;
0028: import com.sleepycat.je.utilint.Tracer;
0029:
0030: public class Database {
0031:
0032: /*
0033: * DbState embodies the Database handle state.
0034: */
0035: static class DbState {
0036: private String stateName;
0037:
0038: DbState(String stateName) {
0039: this .stateName = stateName;
0040: }
0041:
0042: public String toString() {
0043: return "DbState." + stateName;
0044: }
0045: }
0046:
0047: static DbState OPEN = new DbState("OPEN");
0048: static DbState CLOSED = new DbState("CLOSED");
0049: static DbState INVALID = new DbState("INVALID");
0050:
0051: /* The current state of the handle. */
0052: private DbState state;
0053:
0054: /* Handles onto the owning environment and the databaseImpl object. */
0055: Environment envHandle; // used by subclasses
0056: private DatabaseImpl databaseImpl;
0057:
0058: DatabaseConfig configuration; // properties used at execution
0059:
0060: /* True if this handle permits write operations; */
0061: private boolean isWritable;
0062:
0063: /* Transaction that owns the db lock held while the Database is open. */
0064: Locker handleLocker;
0065:
0066: /* Set of cursors open against this db handle. */
0067: private TinyHashSet cursors = new TinyHashSet();
0068:
0069: /*
0070: * DatabaseTrigger list. The list is null if empty, and is checked for
0071: * null to avoiding read locking overhead when no triggers are present.
0072: * Access to this list is protected by the shared trigger latch in
0073: * EnvironmentImpl.
0074: */
0075: private List triggerList;
0076:
0077: private Logger logger;
0078:
0079: /**
0080: * Creates a database but does not open or fully initialize it.
0081: * Is protected for use in compat package.
0082: */
0083: protected Database(Environment env) {
0084: this .envHandle = env;
0085: handleLocker = null;
0086: logger = envHandle.getEnvironmentImpl().getLogger();
0087: }
0088:
0089: /**
0090: * Create a database, called by Environment.
0091: */
0092: void initNew(Environment env, Locker locker, String databaseName,
0093: DatabaseConfig dbConfig) throws DatabaseException {
0094:
0095: dbConfig.validateForNewDb();
0096:
0097: init(env, dbConfig);
0098:
0099: /* Make the databaseImpl. */
0100: EnvironmentImpl environmentImpl = DbInternal
0101: .envGetEnvironmentImpl(envHandle);
0102: databaseImpl = environmentImpl.createDb(locker, databaseName,
0103: dbConfig, this );
0104: databaseImpl.addReferringHandle(this );
0105: }
0106:
0107: /**
0108: * Open a database, called by Environment.
0109: */
0110: void initExisting(Environment env, Locker locker,
0111: DatabaseImpl databaseImpl, DatabaseConfig dbConfig)
0112: throws DatabaseException {
0113:
0114: /*
0115: * Make sure the configuration used for the open is compatible with the
0116: * existing databaseImpl.
0117: */
0118: validateConfigAgainstExistingDb(dbConfig, databaseImpl);
0119:
0120: init(env, dbConfig);
0121: this .databaseImpl = databaseImpl;
0122: databaseImpl.addReferringHandle(this );
0123:
0124: /*
0125: * Copy the duplicates and transactional properties of the underlying
0126: * database, in case the useExistingConfig property is set.
0127: */
0128: configuration.setSortedDuplicates(databaseImpl
0129: .getSortedDuplicates());
0130: configuration.setTransactional(databaseImpl.isTransactional());
0131: }
0132:
0133: private void init(Environment env, DatabaseConfig config)
0134: throws DatabaseException {
0135:
0136: handleLocker = null;
0137:
0138: envHandle = env;
0139: configuration = config.cloneConfig();
0140: isWritable = !configuration.getReadOnly();
0141: state = OPEN;
0142: }
0143:
0144: /**
0145: * See if this new handle's configuration is compatible with the
0146: * pre-existing database.
0147: */
0148: private void validateConfigAgainstExistingDb(DatabaseConfig config,
0149: DatabaseImpl databaseImpl) throws DatabaseException {
0150:
0151: /*
0152: * The allowDuplicates property is persistent and immutable. It does
0153: * not need to be specified if the useExistingConfig property is set.
0154: */
0155: if (!config.getUseExistingConfig()) {
0156: if (databaseImpl.getSortedDuplicates() != config
0157: .getSortedDuplicates()) {
0158: throw new DatabaseException(
0159: "You can't open a Database with a duplicatesAllowed "
0160: + "configuration of "
0161: + config.getSortedDuplicates()
0162: + " if the underlying database was created with a "
0163: + "duplicatesAllowedSetting of "
0164: + databaseImpl.getSortedDuplicates()
0165: + ".");
0166: }
0167: }
0168:
0169: /*
0170: * The transactional property is kept constant while any handles are
0171: * open, and set when the first handle is opened. It does not need to
0172: * be specified if the useExistingConfig property is set.
0173: */
0174: if (databaseImpl.hasOpenHandles()) {
0175: if (!config.getUseExistingConfig()) {
0176: if (config.getTransactional() != databaseImpl
0177: .isTransactional()) {
0178: throw new DatabaseException(
0179: "You can't open a Database with a transactional "
0180: + "configuration of "
0181: + config.getTransactional()
0182: + " if the underlying database was created with a "
0183: + "transactional configuration of "
0184: + databaseImpl.isTransactional()
0185: + ".");
0186: }
0187: }
0188: } else {
0189: databaseImpl.setTransactional(config.getTransactional());
0190: }
0191:
0192: /*
0193: * The deferredWrite property is kept constant while any handles are
0194: * open, and set when the first handle is opened. It does not need to
0195: * be specified if the useExistingConfig property is set.
0196: */
0197: if (databaseImpl.hasOpenHandles()) {
0198: if (!config.getUseExistingConfig()) {
0199: if (config.getDeferredWrite() != databaseImpl
0200: .isDeferredWrite()) {
0201: throw new DatabaseException(
0202: "You can't open a Database with a deferredWrite "
0203: + "configuration of "
0204: + config.getDeferredWrite()
0205: + " if the underlying database was created with a "
0206: + "deferredWrite configuration of "
0207: + databaseImpl.isDeferredWrite()
0208: + ".");
0209: }
0210: }
0211: } else {
0212: databaseImpl.setDeferredWrite(config.getDeferredWrite());
0213: }
0214:
0215: /*
0216: * Only re-set the comparators if the override is allowed.
0217: */
0218: boolean comparatorModified = false;
0219: if (config.getOverrideBtreeComparator()) {
0220: comparatorModified |= databaseImpl.setBtreeComparator(
0221: config.getBtreeComparator(), config
0222: .getBtreeComparatorByClassName());
0223: }
0224:
0225: if (config.getOverrideDuplicateComparator()) {
0226: comparatorModified |= databaseImpl.setDuplicateComparator(
0227: config.getDuplicateComparator(), config
0228: .getDuplicateComparatorByClassName());
0229: }
0230:
0231: /* [#15743] */
0232: if (comparatorModified) {
0233: EnvironmentImpl envImpl = envHandle.getEnvironmentImpl();
0234:
0235: /* Dirty the root. */
0236: envImpl.getDbMapTree().modifyDbRoot(databaseImpl);
0237: }
0238: }
0239:
0240: public synchronized void close() throws DatabaseException {
0241:
0242: try {
0243: closeInternal();
0244: } catch (Error E) {
0245: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0246: throw E;
0247: }
0248: }
0249:
0250: private void closeInternal() throws DatabaseException {
0251:
0252: StringBuffer errors = null;
0253:
0254: checkEnv();
0255: checkProhibitedDbState(CLOSED, "Can't close Database:");
0256:
0257: trace(Level.FINEST, "Database.close: ", null, null);
0258:
0259: /* Disassociate triggers before closing. */
0260: removeAllTriggers();
0261:
0262: envHandle.removeReferringHandle(this );
0263: if (cursors.size() > 0) {
0264: errors = new StringBuffer(
0265: "There are open cursors against the database.\n");
0266: errors.append("They will be closed.\n");
0267:
0268: /*
0269: * Copy the cursors set before iterating since the dbc.close()
0270: * mutates the set.
0271: */
0272: Iterator iter = cursors.copy().iterator();
0273: while (iter.hasNext()) {
0274: Cursor dbc = (Cursor) iter.next();
0275:
0276: try {
0277: dbc.close();
0278: } catch (DatabaseException DBE) {
0279: errors.append("Exception while closing cursors:\n");
0280: errors.append(DBE.toString());
0281: }
0282: }
0283: }
0284:
0285: if (databaseImpl != null) {
0286: databaseImpl.removeReferringHandle(this );
0287: envHandle.getEnvironmentImpl().releaseDb(databaseImpl);
0288: databaseImpl = null;
0289:
0290: /*
0291: * Tell our protecting txn that we're closing. If this type
0292: * of transaction doesn't live beyond the life of the handle,
0293: * it will release the db handle lock.
0294: */
0295: handleLocker.setHandleLockOwner(true, this , true);
0296: handleLocker.operationEnd(true);
0297: state = CLOSED;
0298: }
0299:
0300: if (errors != null) {
0301: throw new DatabaseException(errors.toString());
0302: }
0303: }
0304:
0305: /**
0306: * Javadoc for this public method is generated via
0307: * the doc templates in the doc_src directory.
0308: */
0309: public void sync() throws DatabaseException {
0310:
0311: checkEnv();
0312: checkRequiredDbState(OPEN, "Can't call Database.sync:");
0313: checkWritable("sync");
0314: trace(Level.FINEST, "Database.sync", null, null, null, null);
0315:
0316: databaseImpl.sync(true);
0317: }
0318:
0319: /**
0320: * Javadoc for this public method is generated via
0321: * the doc templates in the doc_src directory.
0322: */
0323: public Sequence openSequence(Transaction txn, DatabaseEntry key,
0324: SequenceConfig config) throws DatabaseException {
0325:
0326: try {
0327: checkEnv();
0328: DatabaseUtil.checkForNullDbt(key, "key", true);
0329: checkRequiredDbState(OPEN,
0330: "Can't call Database.openSequence:");
0331: checkWritable("openSequence");
0332: trace(Level.FINEST, "Database.openSequence", txn, key,
0333: null, null);
0334:
0335: return new Sequence(this , txn, key, config);
0336: } catch (Error E) {
0337: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0338: throw E;
0339: }
0340: }
0341:
0342: /**
0343: * Javadoc for this public method is generated via
0344: * the doc templates in the doc_src directory.
0345: */
0346: public void removeSequence(Transaction txn, DatabaseEntry key)
0347: throws DatabaseException {
0348:
0349: try {
0350: delete(txn, key);
0351: } catch (Error E) {
0352: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0353: throw E;
0354: }
0355: }
0356:
0357: public synchronized Cursor openCursor(Transaction txn,
0358: CursorConfig cursorConfig) throws DatabaseException {
0359:
0360: try {
0361: checkEnv();
0362: checkRequiredDbState(OPEN, "Can't open a cursor");
0363: CursorConfig useConfig = (cursorConfig == null) ? CursorConfig.DEFAULT
0364: : cursorConfig;
0365:
0366: if (useConfig.getReadUncommitted()
0367: && useConfig.getReadCommitted()) {
0368: throw new IllegalArgumentException(
0369: "Only one may be specified: "
0370: + "ReadCommitted or ReadUncommitted");
0371: }
0372:
0373: trace(Level.FINEST, "Database.openCursor", txn,
0374: cursorConfig);
0375: Cursor ret = newDbcInstance(txn, useConfig);
0376:
0377: return ret;
0378: } catch (Error E) {
0379: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0380: throw E;
0381: }
0382: }
0383:
0384: /**
0385: * Is overridden by SecondaryDatabase.
0386: */
0387: Cursor newDbcInstance(Transaction txn, CursorConfig cursorConfig)
0388: throws DatabaseException {
0389:
0390: return new Cursor(this , txn, cursorConfig);
0391: }
0392:
0393: public OperationStatus delete(Transaction txn, DatabaseEntry key)
0394: throws DatabaseException {
0395:
0396: try {
0397: checkEnv();
0398: DatabaseUtil.checkForNullDbt(key, "key", true);
0399: checkRequiredDbState(OPEN, "Can't call Database.delete:");
0400: checkWritable("delete");
0401: trace(Level.FINEST, "Database.delete", txn, key, null, null);
0402:
0403: OperationStatus commitStatus = OperationStatus.NOTFOUND;
0404: Locker locker = null;
0405: try {
0406: locker = LockerFactory.getWritableLocker(envHandle,
0407: txn, isTransactional());
0408: commitStatus = deleteInternal(locker, key, null);
0409: return commitStatus;
0410: } finally {
0411: if (locker != null) {
0412: locker.operationEnd(commitStatus);
0413: }
0414: }
0415: } catch (Error E) {
0416: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0417: throw E;
0418: }
0419: }
0420:
0421: /*
0422: * This is commented out until we agree on whether this should even be in
0423: * the API. See [14264].
0424: private OperationStatus delete(Transaction txn,
0425: DatabaseEntry key,
0426: DatabaseEntry data)
0427: throws DatabaseException {
0428:
0429: try {
0430: checkEnv();
0431: DatabaseUtil.checkForNullDbt(key, "key", true);
0432: DatabaseUtil.checkForNullDbt(data, "data", true);
0433: checkRequiredDbState(OPEN, "Can't call Database.delete:");
0434: checkWritable("delete");
0435: trace(Level.FINEST, "Database.delete", txn, key, data, null);
0436:
0437: OperationStatus commitStatus = OperationStatus.NOTFOUND;
0438: Locker locker = null;
0439: try {
0440: locker = LockerFactory.getWritableLocker
0441: (envHandle, txn, isTransactional());
0442: commitStatus = deleteInternal(locker, key, data);
0443: return commitStatus;
0444: } finally {
0445: if (locker != null) {
0446: locker.operationEnd(commitStatus);
0447: }
0448: }
0449: } catch (Error E) {
0450: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0451: throw E;
0452: }
0453: }
0454: */
0455:
0456: /**
0457: * Internal version of delete() that does no parameter checking. Notify
0458: * triggers. Deletes all duplicates.
0459: */
0460: OperationStatus deleteInternal(Locker locker, DatabaseEntry key,
0461: DatabaseEntry data) throws DatabaseException {
0462:
0463: Cursor cursor = null;
0464: try {
0465: cursor = new Cursor(this , locker, null);
0466: cursor.setNonCloning(true);
0467: OperationStatus commitStatus = OperationStatus.NOTFOUND;
0468:
0469: /* Position a cursor at the specified data record. */
0470: DatabaseEntry oldData;
0471: OperationStatus searchStatus;
0472: if (data == null) {
0473: oldData = new DatabaseEntry();
0474: searchStatus = cursor.search(key, oldData,
0475: LockMode.RMW, SearchMode.SET);
0476: } else {
0477: oldData = data;
0478: searchStatus = cursor.search(key, oldData,
0479: LockMode.RMW, SearchMode.BOTH);
0480: }
0481:
0482: /* Delete all records with that key. */
0483: if (searchStatus == OperationStatus.SUCCESS) {
0484: do {
0485:
0486: /*
0487: * Notify triggers before the actual deletion so that a
0488: * primary record never exists while secondary keys refer
0489: * to it. This is relied on by secondary read-uncommitted.
0490: */
0491: if (hasTriggers()) {
0492: notifyTriggers(locker, key, oldData, null);
0493: }
0494: /* The actual deletion. */
0495: commitStatus = cursor.deleteNoNotify();
0496: if (commitStatus != OperationStatus.SUCCESS) {
0497: return commitStatus;
0498: }
0499:
0500: if (data != null) {
0501: /* delete(key, data) called so only delete one item. */
0502: break;
0503: }
0504:
0505: /* Get another duplicate. */
0506: if (databaseImpl.getSortedDuplicates()) {
0507: searchStatus = cursor
0508: .retrieveNext(key, oldData,
0509: LockMode.RMW, GetMode.NEXT_DUP);
0510: } else {
0511: searchStatus = OperationStatus.NOTFOUND;
0512: }
0513: } while (searchStatus == OperationStatus.SUCCESS);
0514: commitStatus = OperationStatus.SUCCESS;
0515: }
0516: return commitStatus;
0517: } finally {
0518: if (cursor != null) {
0519: cursor.close();
0520: }
0521: }
0522: }
0523:
0524: public OperationStatus get(Transaction txn, DatabaseEntry key,
0525: DatabaseEntry data, LockMode lockMode)
0526: throws DatabaseException {
0527:
0528: try {
0529: checkEnv();
0530: DatabaseUtil.checkForNullDbt(key, "key", true);
0531: DatabaseUtil.checkForNullDbt(data, "data", false);
0532: checkRequiredDbState(OPEN, "Can't call Database.get:");
0533: trace(Level.FINEST, "Database.get", txn, key, null,
0534: lockMode);
0535:
0536: CursorConfig cursorConfig = CursorConfig.DEFAULT;
0537: if (lockMode == LockMode.READ_COMMITTED) {
0538: cursorConfig = CursorConfig.READ_COMMITTED;
0539: lockMode = null;
0540: }
0541:
0542: Cursor cursor = null;
0543: try {
0544: cursor = new Cursor(this , txn, cursorConfig);
0545: cursor.setNonCloning(true);
0546: return cursor.search(key, data, lockMode,
0547: SearchMode.SET);
0548: } finally {
0549: if (cursor != null) {
0550: cursor.close();
0551: }
0552: }
0553: } catch (Error E) {
0554: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0555: throw E;
0556: }
0557: }
0558:
0559: public OperationStatus getSearchBoth(Transaction txn,
0560: DatabaseEntry key, DatabaseEntry data, LockMode lockMode)
0561: throws DatabaseException {
0562:
0563: try {
0564: checkEnv();
0565: DatabaseUtil.checkForNullDbt(key, "key", true);
0566: DatabaseUtil.checkForNullDbt(data, "data", true);
0567: checkRequiredDbState(OPEN,
0568: "Can't call Database.getSearchBoth:");
0569: trace(Level.FINEST, "Database.getSearchBoth", txn, key,
0570: data, lockMode);
0571:
0572: CursorConfig cursorConfig = CursorConfig.DEFAULT;
0573: if (lockMode == LockMode.READ_COMMITTED) {
0574: cursorConfig = CursorConfig.READ_COMMITTED;
0575: lockMode = null;
0576: }
0577:
0578: Cursor cursor = null;
0579: try {
0580: cursor = new Cursor(this , txn, cursorConfig);
0581: cursor.setNonCloning(true);
0582: return cursor.search(key, data, lockMode,
0583: SearchMode.BOTH);
0584: } finally {
0585: if (cursor != null) {
0586: cursor.close();
0587: }
0588: }
0589: } catch (Error E) {
0590: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0591: throw E;
0592: }
0593: }
0594:
0595: public OperationStatus put(Transaction txn, DatabaseEntry key,
0596: DatabaseEntry data) throws DatabaseException {
0597:
0598: checkEnv();
0599: DatabaseUtil.checkForNullDbt(key, "key", true);
0600: DatabaseUtil.checkForNullDbt(data, "data", true);
0601: DatabaseUtil.checkForPartialKey(key);
0602: checkRequiredDbState(OPEN, "Can't call Database.put");
0603: checkWritable("put");
0604: trace(Level.FINEST, "Database.put", txn, key, data, null);
0605:
0606: return putInternal(txn, key, data, PutMode.OVERWRITE);
0607: }
0608:
0609: public OperationStatus putNoOverwrite(Transaction txn,
0610: DatabaseEntry key, DatabaseEntry data)
0611: throws DatabaseException {
0612:
0613: checkEnv();
0614: DatabaseUtil.checkForNullDbt(key, "key", true);
0615: DatabaseUtil.checkForNullDbt(data, "data", true);
0616: DatabaseUtil.checkForPartialKey(key);
0617: checkRequiredDbState(OPEN, "Can't call Database.putNoOverWrite");
0618: checkWritable("putNoOverwrite");
0619: trace(Level.FINEST, "Database.putNoOverwrite", txn, key, data,
0620: null);
0621:
0622: return putInternal(txn, key, data, PutMode.NOOVERWRITE);
0623: }
0624:
0625: public OperationStatus putNoDupData(Transaction txn,
0626: DatabaseEntry key, DatabaseEntry data)
0627: throws DatabaseException {
0628:
0629: checkEnv();
0630: DatabaseUtil.checkForNullDbt(key, "key", true);
0631: DatabaseUtil.checkForNullDbt(data, "data", true);
0632: DatabaseUtil.checkForPartialKey(key);
0633: checkRequiredDbState(OPEN, "Can't call Database.putNoDupData");
0634: checkWritable("putNoDupData");
0635: trace(Level.FINEST, "Database.putNoDupData", txn, key, data,
0636: null);
0637:
0638: return putInternal(txn, key, data, PutMode.NODUP);
0639: }
0640:
0641: /**
0642: * Internal version of put() that does no parameter checking.
0643: */
0644: OperationStatus putInternal(Transaction txn, DatabaseEntry key,
0645: DatabaseEntry data, PutMode putMode)
0646: throws DatabaseException {
0647:
0648: try {
0649: Locker locker = null;
0650: Cursor cursor = null;
0651: OperationStatus commitStatus = OperationStatus.KEYEXIST;
0652: try {
0653: locker = LockerFactory.getWritableLocker(envHandle,
0654: txn, isTransactional());
0655:
0656: cursor = new Cursor(this , locker, null);
0657: cursor.setNonCloning(true);
0658: commitStatus = cursor.putInternal(key, data, putMode);
0659: return commitStatus;
0660: } finally {
0661: if (cursor != null) {
0662: cursor.close();
0663: }
0664: if (locker != null) {
0665: locker.operationEnd(commitStatus);
0666: }
0667: }
0668: } catch (Error E) {
0669: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0670: throw E;
0671: }
0672: }
0673:
0674: /**
0675: */
0676: public JoinCursor join(Cursor[] cursors, JoinConfig config)
0677: throws DatabaseException {
0678:
0679: try {
0680: checkEnv();
0681: checkRequiredDbState(OPEN, "Can't call Database.join");
0682: DatabaseUtil.checkForNullParam(cursors, "cursors");
0683: if (cursors.length == 0) {
0684: throw new IllegalArgumentException(
0685: "At least one cursor is required.");
0686: }
0687:
0688: /*
0689: * Check that all cursors use the same locker, if any cursor is
0690: * transactional. And if non-transactional, that all databases are
0691: * in the same environment.
0692: */
0693: Locker locker = cursors[0].getCursorImpl().getLocker();
0694: if (!locker.isTransactional()) {
0695: EnvironmentImpl env = envHandle.getEnvironmentImpl();
0696: for (int i = 1; i < cursors.length; i += 1) {
0697: Locker locker2 = cursors[i].getCursorImpl()
0698: .getLocker();
0699: if (locker2.isTransactional()) {
0700: throw new IllegalArgumentException(
0701: "All cursors must use the same transaction.");
0702: }
0703: EnvironmentImpl env2 = cursors[i].getDatabaseImpl()
0704: .getDbEnvironment();
0705: if (env != env2) {
0706: throw new IllegalArgumentException(
0707: "All cursors must use the same environment.");
0708: }
0709: }
0710: locker = null; /* Don't reuse a non-transactional locker. */
0711: } else {
0712: for (int i = 1; i < cursors.length; i += 1) {
0713: Locker locker2 = cursors[i].getCursorImpl()
0714: .getLocker();
0715: if (locker.getTxnLocker() != locker2.getTxnLocker()) {
0716: throw new IllegalArgumentException(
0717: "All cursors must use the same transaction.");
0718: }
0719: }
0720: }
0721:
0722: /* Create the join cursor. */
0723: return new JoinCursor(locker, this , cursors, config);
0724: } catch (Error E) {
0725: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0726: throw E;
0727: }
0728: }
0729:
0730: /**
0731: * @deprecated It has not been possible to implement this method with
0732: * correct transactional semantics without incurring a performance penalty
0733: * on all Database operations. Truncate functionality has been moved to
0734: * Environment.truncateDatabase(), which requires that all Database handles
0735: * on the database are closed before the truncate operation can execute.
0736: */
0737: public int truncate(Transaction txn, boolean countRecords)
0738: throws DatabaseException {
0739:
0740: try {
0741: checkEnv();
0742: checkRequiredDbState(OPEN, "Can't call Database.truncate");
0743: checkWritable("truncate");
0744: Tracer.trace(Level.FINEST, envHandle.getEnvironmentImpl(),
0745: "Database.truncate: txnId="
0746: + ((txn == null) ? "null" : Long
0747: .toString(txn.getId())));
0748:
0749: Locker locker = null;
0750: boolean triggerLock = false;
0751: boolean operationOk = false;
0752:
0753: try {
0754: locker = LockerFactory.getWritableLocker(envHandle,
0755: txn, isTransactional(), true /*retainLocks*/,
0756: null);
0757:
0758: /*
0759: * Pass true to always get a read lock on the triggers, so we
0760: * are sure that no secondaries are added during truncation.
0761: */
0762: acquireTriggerListReadLock();
0763: triggerLock = true;
0764:
0765: /* Truncate primary. */
0766: int count = truncateInternal(locker, countRecords);
0767:
0768: /* Truncate secondaries. */
0769: for (int i = 0; i < triggerList.size(); i += 1) {
0770: Object obj = triggerList.get(i);
0771: if (obj instanceof SecondaryTrigger) {
0772: SecondaryDatabase secDb = ((SecondaryTrigger) obj)
0773: .getDb();
0774: secDb.truncateInternal(locker, false);
0775: }
0776: }
0777:
0778: operationOk = true;
0779: return count;
0780: } finally {
0781: if (locker != null) {
0782: locker.operationEnd(operationOk);
0783: }
0784: if (triggerLock) {
0785: releaseTriggerListReadLock();
0786: }
0787: }
0788: } catch (Error E) {
0789: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0790: throw E;
0791: }
0792: }
0793:
0794: /**
0795: * Internal unchecked truncate that optionally counts records.
0796: * @deprecated
0797: */
0798: int truncateInternal(Locker locker, boolean countRecords)
0799: throws DatabaseException {
0800:
0801: if (databaseImpl == null) {
0802: throw new DatabaseException(
0803: "couldn't find database - truncate");
0804: }
0805: databaseImpl.checkIsDeleted("truncate");
0806:
0807: /*
0808: * Truncate must obtain a write lock. In order to do so, it assumes
0809: * ownership for the handle lock and transfers it from this Database
0810: * object to the txn.
0811: */
0812: if (handleLocker.isHandleLockTransferrable()) {
0813: handleLocker.transferHandleLock(this , locker, false);
0814: }
0815:
0816: boolean operationOk = false;
0817: try {
0818:
0819: /*
0820: * truncate clones the existing database and returns a new one to
0821: * replace it with. The old databaseImpl object is marked
0822: * 'deleted'.
0823: */
0824: TruncateResult result = envHandle.getEnvironmentImpl()
0825: .truncate(locker, databaseImpl);
0826: databaseImpl = result.getDatabase();
0827:
0828: operationOk = true;
0829: return countRecords ? result.getRecordCount() : -1;
0830: } finally {
0831:
0832: /*
0833: * The txn will know if it's living past the end of this operation,
0834: * and if it needs to transfer the handle lock. operationEnd()
0835: * will be called one level up by the public truncate() method.
0836: */
0837: locker.setHandleLockOwner(operationOk, this , false);
0838: }
0839: }
0840:
0841: /*
0842: * @deprecated As of JE 2.0.55, replaced by
0843: * {@link Database#preload(PreloadConfig)}.
0844: */
0845: public void preload(long maxBytes) throws DatabaseException {
0846:
0847: checkEnv();
0848: checkRequiredDbState(OPEN, "Can't call Database.preload");
0849: databaseImpl.checkIsDeleted("preload");
0850:
0851: PreloadConfig config = new PreloadConfig();
0852: config.setMaxBytes(maxBytes);
0853: databaseImpl.preload(config);
0854: }
0855:
0856: /*
0857: * @deprecated As of JE 2.1.1, replaced by
0858: * {@link Database#preload(PreloadConfig)}.
0859: */
0860: public void preload(long maxBytes, long maxMillisecs)
0861: throws DatabaseException {
0862:
0863: checkEnv();
0864: checkRequiredDbState(OPEN, "Can't call Database.preload");
0865: databaseImpl.checkIsDeleted("preload");
0866:
0867: PreloadConfig config = new PreloadConfig();
0868: config.setMaxBytes(maxBytes);
0869: config.setMaxMillisecs(maxMillisecs);
0870: databaseImpl.preload(config);
0871: }
0872:
0873: public PreloadStats preload(PreloadConfig config)
0874: throws DatabaseException {
0875:
0876: checkEnv();
0877: checkRequiredDbState(OPEN, "Can't call Database.preload");
0878: databaseImpl.checkIsDeleted("preload");
0879:
0880: return databaseImpl.preload(config);
0881: }
0882:
0883: public long count() throws DatabaseException {
0884:
0885: checkEnv();
0886: checkRequiredDbState(OPEN, "Can't call Database.count");
0887: databaseImpl.checkIsDeleted("count");
0888:
0889: return databaseImpl.count();
0890: }
0891:
0892: public DatabaseStats getStats(StatsConfig config)
0893: throws DatabaseException {
0894:
0895: checkEnv();
0896: checkRequiredDbState(OPEN, "Can't call Database.stat");
0897: StatsConfig useConfig = (config == null) ? StatsConfig.DEFAULT
0898: : config;
0899:
0900: if (databaseImpl != null) {
0901: databaseImpl.checkIsDeleted("stat");
0902: return databaseImpl.stat(useConfig);
0903: }
0904: return null;
0905: }
0906:
0907: public DatabaseStats verify(VerifyConfig config)
0908: throws DatabaseException {
0909:
0910: try {
0911: checkEnv();
0912: checkRequiredDbState(OPEN, "Can't call Database.verify");
0913: databaseImpl.checkIsDeleted("verify");
0914: VerifyConfig useConfig = (config == null) ? VerifyConfig.DEFAULT
0915: : config;
0916:
0917: DatabaseStats stats = databaseImpl.getEmptyStats();
0918: databaseImpl.verify(useConfig, stats);
0919: return stats;
0920: } catch (Error E) {
0921: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0922: throw E;
0923: }
0924: }
0925:
0926: public String getDatabaseName() throws DatabaseException {
0927:
0928: try {
0929: checkEnv();
0930: if (databaseImpl != null) {
0931: return databaseImpl.getName();
0932: } else {
0933: return null;
0934: }
0935: } catch (Error E) {
0936: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0937: throw E;
0938: }
0939: }
0940:
0941: /*
0942: * Non-transactional database name, safe to access when creating error
0943: * messages.
0944: */
0945: String getDebugName() {
0946: if (databaseImpl != null) {
0947: return databaseImpl.getDebugName();
0948: } else {
0949: return null;
0950: }
0951: }
0952:
0953: public DatabaseConfig getConfig() throws DatabaseException {
0954:
0955: try {
0956: DatabaseConfig showConfig = configuration.cloneConfig();
0957:
0958: /*
0959: * Set the comparators from the database impl, they might have
0960: * changed from another handle.
0961: */
0962: Comparator btComp = null;
0963: Comparator dupComp = null;
0964: boolean btCompByClass = false;
0965: boolean dupCompByClass = false;
0966: if (databaseImpl != null) {
0967: btComp = databaseImpl.getBtreeComparator();
0968: dupComp = databaseImpl.getDuplicateComparator();
0969: btCompByClass = databaseImpl
0970: .getBtreeComparatorByClass();
0971: dupCompByClass = databaseImpl
0972: .getDuplicateComparatorByClass();
0973: }
0974: showConfig
0975: .setBtreeComparatorInternal(btComp, btCompByClass);
0976: showConfig.setDuplicateComparatorInternal(dupComp,
0977: dupCompByClass);
0978: return showConfig;
0979: } catch (Error E) {
0980: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
0981: throw E;
0982: }
0983: }
0984:
0985: /**
0986: * Equivalent to getConfig().getTransactional() but cheaper.
0987: */
0988: boolean isTransactional() throws DatabaseException {
0989:
0990: return databaseImpl.isTransactional();
0991: }
0992:
0993: public Environment getEnvironment() throws DatabaseException {
0994:
0995: return envHandle;
0996: }
0997:
0998: public List getSecondaryDatabases() throws DatabaseException {
0999:
1000: try {
1001: List list = new ArrayList();
1002: if (hasTriggers()) {
1003: acquireTriggerListReadLock();
1004: try {
1005: for (int i = 0; i < triggerList.size(); i += 1) {
1006: Object obj = triggerList.get(i);
1007: if (obj instanceof SecondaryTrigger) {
1008: list.add(((SecondaryTrigger) obj).getDb());
1009: }
1010: }
1011: } finally {
1012: releaseTriggerListReadLock();
1013: }
1014: }
1015: return list;
1016: } catch (Error E) {
1017: DbInternal.envGetEnvironmentImpl(envHandle).invalidate(E);
1018: throw E;
1019: }
1020: }
1021:
1022: /*
1023: * Helpers, not part of the public API
1024: */
1025:
1026: /**
1027: * @return true if the Database was opened read/write.
1028: */
1029: boolean isWritable() {
1030: return isWritable;
1031: }
1032:
1033: /**
1034: * Return the databaseImpl object instance.
1035: */
1036: DatabaseImpl getDatabaseImpl() {
1037: return databaseImpl;
1038: }
1039:
1040: /**
1041: * The handleLocker is the one that holds the db handle lock.
1042: */
1043: void setHandleLocker(Locker locker) {
1044: handleLocker = locker;
1045: }
1046:
1047: synchronized void removeCursor(Cursor dbc) {
1048: cursors.remove(dbc);
1049: }
1050:
1051: synchronized void addCursor(Cursor dbc) {
1052: cursors.add(dbc);
1053: }
1054:
1055: /**
1056: * @throws DatabaseException if the Database state is not this value.
1057: */
1058: void checkRequiredDbState(DbState required, String msg)
1059: throws DatabaseException {
1060:
1061: if (state != required) {
1062: throw new DatabaseException(msg
1063: + " Database state can't be " + state + " must be "
1064: + required);
1065: }
1066: }
1067:
1068: /**
1069: * @throws DatabaseException if the Database state is this value.
1070: */
1071: void checkProhibitedDbState(DbState prohibited, String msg)
1072: throws DatabaseException {
1073:
1074: if (state == prohibited) {
1075: throw new DatabaseException(msg
1076: + " Database state must not be " + prohibited);
1077: }
1078: }
1079:
1080: /**
1081: * @throws RunRecoveryException if the underlying environment is
1082: * invalid
1083: */
1084: void checkEnv() throws RunRecoveryException {
1085:
1086: EnvironmentImpl env = envHandle.getEnvironmentImpl();
1087: if (env != null) {
1088: env.checkIfInvalid();
1089: }
1090: }
1091:
1092: /**
1093: * Invalidate the handle, called by txn.abort by way of DbInternal.
1094: */
1095: synchronized void invalidate() {
1096: state = INVALID;
1097: envHandle.removeReferringHandle(this );
1098: if (databaseImpl != null) {
1099: databaseImpl.removeReferringHandle(this );
1100: envHandle.getEnvironmentImpl().releaseDb(databaseImpl);
1101:
1102: /*
1103: * Database.close may be called after an abort. By setting the
1104: * databaseImpl field to null we ensure that close won't call
1105: * releaseDb or endOperation. [#13415]
1106: */
1107: databaseImpl = null;
1108: }
1109: }
1110:
1111: /**
1112: * Check that write operations aren't used on a readonly Database.
1113: */
1114: private void checkWritable(String operation)
1115: throws DatabaseException {
1116:
1117: if (!isWritable) {
1118: throw new DatabaseException("Database is Read Only: "
1119: + operation);
1120: }
1121: }
1122:
1123: /**
1124: * Send trace messages to the java.util.logger. Don't rely on the logger
1125: * alone to conditionalize whether we send this message, we don't even want
1126: * to construct the message if the level is not enabled.
1127: */
1128: void trace(Level level, String methodName, Transaction txn,
1129: DatabaseEntry key, DatabaseEntry data, LockMode lockMode)
1130: throws DatabaseException {
1131:
1132: if (logger.isLoggable(level)) {
1133: StringBuffer sb = new StringBuffer();
1134: sb.append(methodName);
1135: if (txn != null) {
1136: sb.append(" txnId=").append(txn.getId());
1137: }
1138: sb.append(" key=").append(key.dumpData());
1139: if (data != null) {
1140: sb.append(" data=").append(data.dumpData());
1141: }
1142: if (lockMode != null) {
1143: sb.append(" lockMode=").append(lockMode);
1144: }
1145: logger.log(level, sb.toString());
1146: }
1147: }
1148:
1149: /**
1150: * Send trace messages to the java.util.logger. Don't rely on the logger
1151: * alone to conditionalize whether we send this message, we don't even want
1152: * to construct the message if the level is not enabled.
1153: */
1154: void trace(Level level, String methodName, Transaction txn,
1155: CursorConfig config) throws DatabaseException {
1156:
1157: if (logger.isLoggable(level)) {
1158: StringBuffer sb = new StringBuffer();
1159: sb.append(methodName);
1160: sb.append(" name=" + getDebugName());
1161: if (txn != null) {
1162: sb.append(" txnId=").append(txn.getId());
1163: }
1164: if (config != null) {
1165: sb.append(" config=").append(config);
1166: }
1167: logger.log(level, sb.toString());
1168: }
1169: }
1170:
1171: /*
1172: * Manage triggers.
1173: */
1174:
1175: /**
1176: * Returns whether any triggers are currently associated with this primary.
1177: * Note that an update of the trigger list may be in progress and this
1178: * method does not wait for that update to be completed.
1179: */
1180: boolean hasTriggers() {
1181:
1182: return triggerList != null;
1183: }
1184:
1185: /**
1186: * Gets a read-lock on the list of triggers. releaseTriggerListReadLock()
1187: * must be called to release the lock. Called by all primary put and
1188: * delete operations.
1189: */
1190: private void acquireTriggerListReadLock() throws DatabaseException {
1191:
1192: EnvironmentImpl env = envHandle.getEnvironmentImpl();
1193: env.getTriggerLatch().acquireShared();
1194: if (triggerList == null) {
1195: triggerList = new ArrayList();
1196: }
1197: }
1198:
1199: /**
1200: * Releases a lock acquired by calling acquireTriggerListReadLock().
1201: */
1202: private void releaseTriggerListReadLock() throws DatabaseException {
1203:
1204: EnvironmentImpl env = envHandle.getEnvironmentImpl();
1205: env.getTriggerLatch().release();
1206: }
1207:
1208: /**
1209: * Gets a write lock on the list of triggers. An empty list is created if
1210: * necessary, so null is never returned. releaseTriggerListWriteLock()
1211: * must always be called to release the lock.
1212: */
1213: private void acquireTriggerListWriteLock() throws DatabaseException {
1214:
1215: EnvironmentImpl env = envHandle.getEnvironmentImpl();
1216: env.getTriggerLatch().acquireExclusive();
1217: if (triggerList == null) {
1218: triggerList = new ArrayList();
1219: }
1220: }
1221:
1222: /**
1223: * Releases a lock acquired by calling acquireTriggerListWriteLock(). If
1224: * the list is now empty then it is set to null, that is, hasTriggers()
1225: * will subsequently return false.
1226: */
1227: private void releaseTriggerListWriteLock() throws DatabaseException {
1228:
1229: if (triggerList.size() == 0) {
1230: triggerList = null;
1231: }
1232: EnvironmentImpl env = envHandle.getEnvironmentImpl();
1233: env.getTriggerLatch().release();
1234: }
1235:
1236: /**
1237: * Adds a given trigger to the list of triggers. Called while opening
1238: * a SecondaryDatabase.
1239: *
1240: * @param insertAtFront true to insert at the front, or false to append.
1241: */
1242: void addTrigger(DatabaseTrigger trigger, boolean insertAtFront)
1243: throws DatabaseException {
1244:
1245: acquireTriggerListWriteLock();
1246: try {
1247: if (insertAtFront) {
1248: triggerList.add(0, trigger);
1249: } else {
1250: triggerList.add(trigger);
1251: }
1252: trigger.triggerAdded(this );
1253: } finally {
1254: releaseTriggerListWriteLock();
1255: }
1256: }
1257:
1258: /**
1259: * Removes a given trigger from the list of triggers. Called by
1260: * SecondaryDatabase.close().
1261: */
1262: void removeTrigger(DatabaseTrigger trigger)
1263: throws DatabaseException {
1264:
1265: acquireTriggerListWriteLock();
1266: try {
1267: triggerList.remove(trigger);
1268: trigger.triggerRemoved(this );
1269: } finally {
1270: releaseTriggerListWriteLock();
1271: }
1272: }
1273:
1274: /**
1275: * Clears the list of triggers. Called by close(), this allows closing the
1276: * primary before its secondaries, although we document that secondaries
1277: * should be closed first.
1278: */
1279: private void removeAllTriggers() throws DatabaseException {
1280:
1281: acquireTriggerListWriteLock();
1282: try {
1283: for (int i = 0; i < triggerList.size(); i += 1) {
1284: DatabaseTrigger trigger = (DatabaseTrigger) triggerList
1285: .get(i);
1286: trigger.triggerRemoved(this );
1287: }
1288: triggerList.clear();
1289: } finally {
1290: releaseTriggerListWriteLock();
1291: }
1292: }
1293:
1294: /**
1295: * Notifies associated triggers when a put() or delete() is performed on
1296: * the primary. This method is normally called only if hasTriggers() has
1297: * returned true earlier. This avoids acquiring a shared latch for
1298: * primaries with no triggers. If a trigger is added during the update
1299: * process, there is no requirement to immediately start updating it.
1300: *
1301: * @param locker the internal locker.
1302: *
1303: * @param priKey the primary key.
1304: *
1305: * @param oldData the primary data before the change, or null if the record
1306: * did not previously exist.
1307: *
1308: * @param newData the primary data after the change, or null if the record
1309: * has been deleted.
1310: */
1311: void notifyTriggers(Locker locker, DatabaseEntry priKey,
1312: DatabaseEntry oldData, DatabaseEntry newData)
1313: throws DatabaseException {
1314:
1315: acquireTriggerListReadLock();
1316: try {
1317: for (int i = 0; i < triggerList.size(); i += 1) {
1318: DatabaseTrigger trigger = (DatabaseTrigger) triggerList
1319: .get(i);
1320:
1321: /* Notify trigger. */
1322: trigger.databaseUpdated(this, locker, priKey, oldData,
1323: newData);
1324: }
1325: } finally {
1326: releaseTriggerListReadLock();
1327: }
1328: }
1329: }
|