0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.store.raw.xact.Xact
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.store.raw.xact;
0023:
0024: import org.apache.derby.iapi.reference.SQLState;
0025:
0026: import org.apache.derby.iapi.store.raw.ContainerKey;
0027:
0028: import org.apache.derby.iapi.services.context.ContextManager;
0029: import org.apache.derby.iapi.services.daemon.Serviceable;
0030: import org.apache.derby.iapi.services.locks.LockFactory;
0031: import org.apache.derby.iapi.services.locks.Limit;
0032:
0033: import org.apache.derby.iapi.store.raw.ContainerHandle;
0034: import org.apache.derby.iapi.store.raw.Compensation;
0035: import org.apache.derby.iapi.store.raw.GlobalTransactionId;
0036: import org.apache.derby.iapi.store.raw.LockingPolicy;
0037: import org.apache.derby.iapi.store.raw.Loggable;
0038: import org.apache.derby.iapi.store.raw.RecordHandle;
0039: import org.apache.derby.iapi.store.raw.StreamContainerHandle;
0040: import org.apache.derby.iapi.store.raw.Transaction;
0041:
0042: import org.apache.derby.iapi.store.raw.data.DataFactory;
0043: import org.apache.derby.iapi.store.raw.data.RawContainerHandle;
0044:
0045: import org.apache.derby.iapi.store.raw.xact.RawTransaction;
0046: import org.apache.derby.iapi.store.raw.xact.TransactionId;
0047:
0048: import org.apache.derby.iapi.store.raw.log.LogFactory;
0049: import org.apache.derby.iapi.store.raw.log.LogInstant;
0050: import org.apache.derby.iapi.store.raw.log.Logger;
0051:
0052: import org.apache.derby.iapi.store.access.FileResource;
0053: import org.apache.derby.iapi.store.access.RowSource;
0054: import org.apache.derby.iapi.store.access.TransactionController;
0055: import org.apache.derby.iapi.error.ExceptionSeverity;
0056:
0057: import org.apache.derby.iapi.services.property.PersistentSet;
0058:
0059: import org.apache.derby.catalog.UUID;
0060:
0061: import java.util.Stack;
0062: import java.util.Enumeration;
0063: import java.util.Properties;
0064: import java.util.ArrayList;
0065: import java.util.List;
0066: import java.util.Dictionary;
0067:
0068: import org.apache.derby.iapi.error.StandardException;
0069:
0070: import org.apache.derby.iapi.services.sanity.SanityManager;
0071: import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
0072: import org.apache.derby.iapi.util.ByteArray;
0073: import org.apache.derby.iapi.services.property.PropertyUtil;
0074: import org.apache.derby.iapi.reference.Property;
0075:
0076: import org.apache.derby.impl.store.raw.log.LogToFile;
0077:
0078: import org.apache.derby.iapi.services.io.LimitObjectInput;
0079:
0080: import org.apache.derby.iapi.services.context.ContextService;
0081:
0082: /**
0083:
0084: A transaction has five states
0085: <OL>
0086: <LI> CLOSED - cannot be used
0087: <LI> IDLE - no reads have been performed by the transaction.
0088: <LI> ACTIVE - at least one read has been attempted by the transaction
0089: <LI> UPDATE - at least one update has been attempted by the transaction
0090: <LI> PREPARED - the transaction is ready to commit (FUTURE).
0091: </OL>
0092: <BR>Transaction identifiers are re-used for transactions that do not enter the
0093: UPDATE state during their lifetime.
0094:
0095: @see Transaction
0096:
0097: */
0098: public class Xact extends RawTransaction implements Limit {
0099:
0100: /*
0101: ** Static Fields
0102: */
0103:
0104: protected static final int CLOSED = 0;
0105: protected static final int IDLE = 1;
0106: protected static final int ACTIVE = 2;
0107: protected static final int UPDATE = 3;
0108: protected static final int PREPARED = 4;
0109:
0110: /*
0111: ** Transaction status stored in the beginXact and endXact
0112: */
0113:
0114: public static final int END_ABORTED = 0x00000001;
0115: public static final int END_PREPARED = 0x00000002;
0116: public static final int END_COMMITTED = 0x00000004;
0117:
0118: public static final int RECOVERY_ROLLBACK_FIRST = 0x00000010;
0119: public static final int INTERNAL_TRANSACTION = 0x00000020;
0120: public static final int NESTED_TOP_TRANSACTION = 0x00000040;
0121:
0122: /**
0123: private static -
0124:
0125: make sure these bits don't overwrite bits in Transaction.commit commitflag
0126: */
0127: private static final int COMMIT_SYNC = 0x00010000;
0128: private static final int COMMIT_NO_SYNC = 0x00020000;
0129: private static final int COMMIT_PREPARE = 0x00040000;
0130:
0131: /*
0132: ** Fields
0133: */
0134:
0135: //
0136: // set during recovery if this is the recovery transaction.
0137: //
0138: private int savedEndStatus;
0139:
0140: //
0141: // if this transaction object was committed without syncing, then in a
0142: // subsequent commit with sync, the log must be flushed even if the last
0143: // transaction is read only
0144: private boolean needSync;
0145:
0146: //
0147: // When the xact is first created, it is in an IDLE state. Since the
0148: // transaction table needs an XactId, one will be made for it. When this
0149: // transaction commits, it goes back to the IDLE state. If we then create
0150: // a new XactId for it, we will waste one XactId per transaction object
0151: // because it must first go thru the IDLE state before it gets closed.
0152: // Therefore, the first XactId is assigned in the constructor and
0153: // subsequent XactId is assigned in the setActiveState. However, the first
0154: // time it goes into setActiveState, we don't want it to create a new
0155: // XactId when the one that was assigned to it in the constructore is good
0156: // enough, so we use this justCreate field to indicate to setActiveState
0157: // whether it needs to make a new XactId (for the next transaction) for
0158: // not.
0159: private boolean justCreated = true;
0160:
0161: protected XactContext xc; // my context - set by XactContext
0162:
0163: // these fields remain fixed for the lifetime of this object
0164: protected final XactFactory xactFactory;
0165: protected final DataFactory dataFactory;
0166: protected final LogFactory logFactory;
0167: protected final Object compatibilitySpace;
0168:
0169: // these fields remain fixedfor the lifetime
0170: private LockingPolicy defaultLocking;
0171:
0172: // Global id, unique among all rawstores and all eternity
0173: private GlobalTransactionId myGlobalId;
0174:
0175: // id that is valid locally in this raw store.
0176: private volatile TransactionId myId;
0177:
0178: protected Logger logger; // the object we use to access the log.
0179:
0180: protected volatile int state; // we access this without synchronization sometimes
0181: private Integer inComplete = null; // set between preComplete() and postComplete()
0182:
0183: private boolean seenUpdates; // true if this session has written a log
0184: // record to disk. Note this is per
0185: // session and not per transaction, namely
0186: // during recovery, a transaction may have
0187: // updates but it may not have any updates
0188: // during recovery. In that case,
0189: // seenUpdates is false even though state
0190: // is UPDATE.
0191: // This value is used to decide whether
0192: // the log needs to get flushed at commit
0193: // time.
0194:
0195: private boolean inPostCommitProcessing; // true if we are processing post
0196: // commit work in the same context the
0197: // work was queued. This is used to stop
0198: // recursion only. We don't want a post
0199: // commit task to queue other post commit
0200: // task, ad infinitum. PostCommitWork
0201: // requested while processing
0202: // postCommitWork will be processed
0203: // by the daemon, which may itself
0204: // recurse once.
0205:
0206: private LogInstant logStart; // If this is a read only transaction (has
0207: // never written a log record to disk),
0208: // then null. Otherwise, set to the log
0209: // instant of the first log record.
0210:
0211: private LogInstant logLast; // the last log record written by this
0212: // transaction
0213:
0214: private Stack savePoints; // stack of SavePoint objects.
0215:
0216: protected List postCommitWorks; // a list of post commit work
0217: protected List postTerminationWorks; // work to be done after
0218: // transaction terminates,
0219: // commit or abort
0220: private boolean recoveryTransaction; // this transaction is being
0221: // used by recovery
0222:
0223: DynamicByteArrayOutputStream logBuffer;
0224:
0225: private boolean postCompleteMode; // perform most preComplete work in postComplete
0226:
0227: // Use this flag to catch the case where a global transaction was closed.
0228: // Normally a closed transaction should not be aborted again, but we need
0229: // to allow abort of a closed global transaction for error handling. Use
0230: // this flag to make sure people are not abusing this loop hole.
0231: // RESOLVE: sku to remove before GA
0232: private boolean sanityCheck_xaclosed;
0233:
0234: // Indicates the name of the transaction, and if it is set, it is displayed
0235: // by the transactiontable VTI
0236: private String transName;
0237:
0238: // The transaction is only allowed read operations, no log writes.
0239: private boolean readOnly;
0240:
0241: // true, if the transaction executed some operations(like unlogged
0242: // operations) that block the online backup to prevent inconsistent
0243: // backup copy.
0244: private boolean backupBlocked;
0245:
0246: /*
0247: ** Constructor
0248: */
0249:
0250: protected Xact(XactFactory xactFactory, LogFactory logFactory,
0251: DataFactory dataFactory, boolean readOnly,
0252: Object compatibilitySpace) {
0253:
0254: super ();
0255:
0256: this .xactFactory = xactFactory;
0257: this .logFactory = logFactory;
0258: this .dataFactory = dataFactory;
0259: this .readOnly = readOnly;
0260:
0261: this .compatibilitySpace = (compatibilitySpace == null ? this
0262: : compatibilitySpace);
0263:
0264: if (SanityManager.DEBUG) {
0265: SanityManager.ASSERT(dataFactory != null,
0266: "datafactory is null");
0267: SanityManager.ASSERT(xactFactory != null,
0268: "xactfactory is null");
0269: SanityManager.ASSERT(logFactory != null,
0270: "logfactory is null");
0271: }
0272:
0273: resetDefaultLocking();
0274:
0275: // TransactionTable needs this
0276: xactFactory.setNewTransactionId((XactId) null, this );
0277:
0278: setIdleState();
0279:
0280: backupBlocked = false;
0281:
0282: /*
0283: System.out.println("Xact.constructor: readonly = " + this.readOnly +
0284: ";this = " + this);
0285: */
0286: }
0287:
0288: /*
0289: ** Methods of RawTransaction
0290: */
0291:
0292: /**
0293: */
0294: public final LockFactory getLockFactory() {
0295: return xactFactory.getLockFactory();
0296: }
0297:
0298: public final DataFactory getDataFactory() {
0299: return dataFactory;
0300: }
0301:
0302: /**
0303: Get cache statistics for the specified cache
0304: */
0305: public long[] getCacheStats(String cacheName) {
0306: return getDataFactory().getCacheStats(cacheName);
0307: }
0308:
0309: /**
0310: Reset the cache statistics for the specified cache
0311: */
0312: public void resetCacheStats(String cacheName) {
0313: getDataFactory().resetCacheStats(cacheName);
0314: }
0315:
0316: /**
0317: Return true if any transaction is currently blocked, even if not by
0318: this transaction.
0319:
0320: */
0321: public boolean anyoneBlocked() {
0322: return getLockFactory().anyoneBlocked();
0323: }
0324:
0325: public DynamicByteArrayOutputStream getLogBuffer() {
0326:
0327: if (logBuffer == null) {
0328: logBuffer = new DynamicByteArrayOutputStream(1024);
0329: } else {
0330: logBuffer.reset();
0331: }
0332:
0333: return logBuffer;
0334: }
0335:
0336: /** Log and apply a compensation operation.
0337: Only need to write out the compensation op itself, the optional data has already
0338: been written by the rollforward operation this is attempting to undo.
0339:
0340: @see RawTransaction#logAndDo
0341:
0342: @exception StandardException Standard cloudscape exception policy
0343: */
0344: public void logAndUndo(Compensation compensation,
0345: LogInstant undoInstant, LimitObjectInput in)
0346: throws StandardException {
0347: if (SanityManager.DEBUG) {
0348: SanityManager.ASSERT(logStart != null);
0349: }
0350:
0351: setActiveState();
0352:
0353: if (state == ACTIVE)
0354: setUpdateState();
0355:
0356: seenUpdates = true;
0357:
0358: LogInstant clrInstant = logger.logAndUndo(this , compensation,
0359: undoInstant, in);
0360:
0361: setLastLogInstant(clrInstant);
0362:
0363: // set the top savepoint to rollback to this record if it doesn't yet have a point saved
0364: if ((savePoints != null) && !savePoints.empty()) {
0365:
0366: SavePoint sp = (SavePoint) savePoints.peek();
0367: if (sp.getSavePoint() == null)
0368: sp.setSavePoint(clrInstant);
0369: }
0370: }
0371:
0372: /**
0373: Add this to the xactFactory list of update transaction.
0374: */
0375: public void addUpdateTransaction(int transactionStatus) {
0376: // during runtime, rollbackFirst == recoveryRolblackFirst(), but during
0377: // recovery redo, we only use a regular transaction, so we need to get
0378: // the rollbackFirst status from the log record
0379: //
0380: // If my Id is null, I have no identity, makes no sense to add it to a
0381: // transaction table where my identity will be saved and restored
0382: if (myId != null)
0383: xactFactory.addUpdateTransaction(myId, this ,
0384: transactionStatus);
0385:
0386: }
0387:
0388: /** Remove this from the xactFactory list of update transaction. */
0389: public void removeUpdateTransaction() {
0390: if (myId != null)
0391: xactFactory.removeUpdateTransaction(myId);
0392:
0393: // If my Id is null, I have no identity, makes no sense to remove it
0394: // from transaction table
0395: }
0396:
0397: /** Remove this from the xactFactory list of update transaction. */
0398: public void prepareTransaction() {
0399: // RESOLVE - should I be changing the state to PREPARE?
0400:
0401: if (myId != null) {
0402: // If my Id is null, I have no identity, makes no sense to set
0403: // my state in the transaction table.
0404:
0405: xactFactory.prepareTransaction(myId);
0406: }
0407: }
0408:
0409: /**
0410: Set the log instant for the first log record written by this transaction.
0411: */
0412: public void setFirstLogInstant(LogInstant instant) {
0413: if (SanityManager.DEBUG) {
0414: SanityManager.ASSERT(instant != null);
0415: SanityManager.ASSERT(logStart == null);
0416: }
0417:
0418: logStart = instant;
0419: }
0420:
0421: /**
0422: Get the log instant for the first log record written by this transaction.
0423: */
0424: public LogInstant getFirstLogInstant() {
0425: return logStart;
0426: }
0427:
0428: /**
0429: Set the log instant for the last log record written by this transaction.
0430: */
0431: public void setLastLogInstant(LogInstant instant) {
0432: if (SanityManager.DEBUG) {
0433: SanityManager.ASSERT(instant != null);
0434: }
0435:
0436: logLast = instant;
0437: }
0438:
0439: /**
0440: Get the log instant for the last log record written by this transaction.
0441: */
0442: public LogInstant getLastLogInstant() {
0443: return logLast;
0444: }
0445:
0446: /**
0447: Set my transaction identifier.
0448: */
0449: public void setTransactionId(GlobalTransactionId extid,
0450: TransactionId localid) {
0451:
0452: if (SanityManager.DEBUG) {
0453:
0454: //SanityManager.ASSERT(myGlobalId == null, "my globalId is not null");
0455: if (!(state == IDLE || state == Xact.ACTIVE || (state == CLOSED && justCreated))) {
0456: SanityManager
0457: .THROWASSERT("my state is not idle nor active "
0458: + state);
0459: }
0460: }
0461:
0462: myGlobalId = extid;
0463: myId = localid;
0464:
0465: if (SanityManager.DEBUG) {
0466: if (SanityManager.DEBUG_ON("XATrace") && extid != null) {
0467: SanityManager.DEBUG("XATrace", "setting xid: " + myId
0468: + " " + myGlobalId + " state " + state + " "
0469: + this );
0470:
0471: SanityManager.showTrace(new Throwable());
0472: // Thread.dumpStack();
0473: }
0474: }
0475:
0476: }
0477:
0478: public void setTransactionId(Loggable beginXact,
0479: TransactionId localId) {
0480: if (SanityManager.DEBUG) {
0481: // SanityManager.ASSERT(myId == null);
0482: SanityManager.ASSERT((state == IDLE) || (state == ACTIVE));
0483: SanityManager.ASSERT(beginXact instanceof BeginXact);
0484: }
0485:
0486: myId = localId;
0487: myGlobalId = ((BeginXact) beginXact).getGlobalId();
0488: }
0489:
0490: /*
0491: ** Methods of Transaction
0492: */
0493:
0494: /**
0495: The default value for LOCKS_ESCALATION_THRESHOLD
0496: @exception StandardException Standard cloudscape exception policy
0497: */
0498: public void setup(PersistentSet set) throws StandardException {
0499:
0500: int escalationThreshold = PropertyUtil.getServiceInt(set,
0501: Property.LOCKS_ESCALATION_THRESHOLD,
0502: Property.MIN_LOCKS_ESCALATION_THRESHOLD,
0503: Integer.MAX_VALUE,
0504: Property.DEFAULT_LOCKS_ESCALATION_THRESHOLD);
0505:
0506: getLockFactory()
0507: .setLimit(this , this , escalationThreshold, this );
0508:
0509: }
0510:
0511: /**
0512: get the Global (external to raw store) transaction id that is unique
0513: across all raw stores
0514: */
0515: public final GlobalTransactionId getGlobalId() {
0516:
0517: return myGlobalId;
0518: }
0519:
0520: public final ContextManager getContextManager() {
0521: return (xc.getContextManager());
0522: }
0523:
0524: /**
0525: * Get the compatibility space of the transaction.
0526: * <p>
0527: * Returns an object that can be used with the lock manager to provide
0528: * the compatibility space of a transaction. 2 transactions with the
0529: * same compatibility space will not conflict in locks. The usual case
0530: * is that each transaction has it's own unique compatibility space.
0531: * <p>
0532: *
0533: * @return The compatibility space of the transaction.
0534: **/
0535: public Object getCompatibilitySpace() {
0536: if (SanityManager.DEBUG) {
0537: SanityManager.ASSERT(compatibilitySpace != null,
0538: "cannot have a null compatibilitySpace.");
0539: }
0540:
0541: return (this .compatibilitySpace);
0542: }
0543:
0544: /**
0545: get the short (internal to raw store) transaction id that is unique
0546: only for this raw store
0547: */
0548: public final TransactionId getId() {
0549:
0550: if (SanityManager.DEBUG)
0551: SanityManager.ASSERT(myId != null,
0552: "cannot have a transaction with null id");
0553:
0554: return myId;
0555: }
0556:
0557: /**
0558: Get the transaction id without sanity check, this should only be called
0559: by a cloned TransactionTableEntry
0560: */
0561: protected final TransactionId getIdNoCheck() {
0562: return myId;
0563: }
0564:
0565: /**
0566: Get my transaction context Id
0567: */
0568: public final String getContextId() {
0569: return (xc == null) ? null : xc.getIdName();
0570: }
0571:
0572: /**
0573: Get the current default locking policy for all operations within this
0574: transaction. The transaction is initially started with a default
0575: locking policy equivalent to
0576: <PRE>
0577: newLockingPolicy(
0578: LockingPolicy.MODE_RECORD, TransactionController.ISOLATION_SERIALIZABLE, true);
0579: </PRE>
0580: This default can be changed by subsequent calls to
0581: setDefaultLockingPolicy(LockingPolicy policy).
0582:
0583: @see Transaction#getDefaultLockingPolicy
0584:
0585:
0586: @return The current default locking policy in this transaction.
0587: */
0588:
0589: public LockingPolicy getDefaultLockingPolicy() {
0590: return (defaultLocking);
0591: }
0592:
0593: /** @see Transaction#newLockingPolicy */
0594: public final LockingPolicy newLockingPolicy(int mode,
0595: int isolation, boolean stricterOk) {
0596:
0597: return xactFactory
0598: .getLockingPolicy(mode, isolation, stricterOk);
0599:
0600: }
0601:
0602: /** @see Transaction#setDefaultLockingPolicy */
0603: public final void setDefaultLockingPolicy(LockingPolicy policy) {
0604:
0605: if (policy == null)
0606: policy = xactFactory.getLockingPolicy(
0607: LockingPolicy.MODE_NONE,
0608: TransactionController.ISOLATION_NOLOCK, false);
0609: defaultLocking = policy;
0610: }
0611:
0612: /**
0613: @exception StandardException Standard cloudscape exception policy
0614: */
0615: public LogInstant commit() throws StandardException {
0616: return commit(COMMIT_SYNC);
0617: }
0618:
0619: /**
0620: @exception StandardException Standard cloudscape exception policy
0621: */
0622: public LogInstant commitNoSync(int commitflag)
0623: throws StandardException {
0624: if (SanityManager.DEBUG) {
0625: int checkflag = Transaction.RELEASE_LOCKS
0626: | Transaction.KEEP_LOCKS;
0627:
0628: SanityManager
0629: .ASSERT((commitflag & checkflag) != 0,
0630: "commitNoSync must specify whether to keep or release locks");
0631:
0632: SanityManager.ASSERT((commitflag & checkflag) != checkflag,
0633: "cannot set both RELEASE and KEEP LOCKS flag");
0634:
0635: if ((commitflag & TransactionController.READONLY_TRANSACTION_INITIALIZATION) != 0) {
0636: SanityManager.ASSERT((state == IDLE)
0637: || (state == ACTIVE));
0638: }
0639: }
0640:
0641: // Short circuit commit no sync if we are still initializing the
0642: // transaction. Before a new transaction object is returned to the
0643: // user, it is "commit'ed" many times using commitNoSync with
0644: // TransactionController.READONLY_TRANSACTION_INITIALIZATION flag to
0645: // release read locks and reset the transaction state back to Idle.
0646: // If nothing has actually happened to the transaction object, return
0647: // right away and avoid the cost of going thru the commit logic.
0648: //
0649: if (state == IDLE
0650: && savePoints == null
0651: && ((commitflag & TransactionController.READONLY_TRANSACTION_INITIALIZATION) != 0))
0652: return null;
0653:
0654: return commit(COMMIT_NO_SYNC | commitflag);
0655: }
0656:
0657: /**
0658: @exception StandardException Standard cloudscape exception policy
0659: @see Transaction#commit
0660: */
0661:
0662: /**
0663: * Do work of commit that is common to xa_prepare and commit.
0664: * <p>
0665: * Do all the work necessary as part of a commit up to and including
0666: * writing the commit log record. This routine is used by both prepare
0667: * and commit. The work post commit is done by completeCommit().
0668: * <p>
0669: *
0670: * @param commitflag various flavors of commit.
0671: *
0672: * @exception StandardException Standard exception policy.
0673: * @see Transaction#commit
0674: **/
0675: private LogInstant prepareCommit(int commitflag)
0676: throws StandardException {
0677: LogInstant flushTo = null;
0678:
0679: if (state == CLOSED) {
0680: throw StandardException
0681: .newException(SQLState.XACT_PROTOCOL_VIOLATION);
0682: }
0683:
0684: if (SanityManager.DEBUG) {
0685: if ((commitflag & Transaction.KEEP_LOCKS) != 0) {
0686: // RESOLVE (mikem) - prepare actually want's to keep locks
0687: // during a prepare.
0688: SanityManager
0689: .ASSERT(
0690: (((commitflag & COMMIT_NO_SYNC) != 0) || ((commitflag & COMMIT_PREPARE) != 0)),
0691: "can keep locks around only in commitNoSync or prepare");
0692:
0693: SanityManager
0694: .ASSERT(isUserTransaction(),
0695: "KEEP_LOCKS can only be set on user transaction commits");
0696: }
0697: }
0698:
0699: try {
0700:
0701: preComplete(COMMIT);
0702:
0703: // flush the log.
0704:
0705: if (seenUpdates) {
0706:
0707: EndXact ex = new EndXact(
0708: getGlobalId(),
0709: ((commitflag & COMMIT_PREPARE) == 0 ? END_COMMITTED
0710: : END_PREPARED)
0711: | statusForEndXactLog());
0712:
0713: flushTo = logger.logAndDo(this , ex);
0714:
0715: if (xactFactory.flushLogOnCommit(xc.getIdName())) {
0716: if ((commitflag & COMMIT_SYNC) == 0) {
0717: // not flushing the log right now, subsequent commit
0718: // will need to flush the log
0719: needSync = true;
0720: } else {
0721: logger.flush(flushTo);
0722: needSync = false;
0723: }
0724: }
0725: } else if (needSync && (commitflag & COMMIT_SYNC) != 0) {
0726: // this transaction object was used to lazily commit some
0727: // previous transaction without syncing. Now that we commit
0728: // for real, make sure any outstanding log is flushed.
0729: logger.flushAll();
0730: needSync = false;
0731: }
0732: } catch (StandardException se) {
0733:
0734: // This catches any exceptions that have Transaction severity
0735: // or less (e.g. Statement exception). If we received any lesser
0736: // error then we abort the transaction anyway.
0737:
0738: if (se.getSeverity() < ExceptionSeverity.TRANSACTION_SEVERITY) {
0739: throw StandardException.newException(
0740: SQLState.XACT_COMMIT_EXCEPTION, se);
0741: }
0742:
0743: throw se;
0744:
0745: }
0746: return flushTo;
0747: }
0748:
0749: /**
0750: * Do work to complete a commit which is not just a prepare.
0751: * <p>
0752: * Releases locks, does post commit work, and moves the state of the
0753: * transaction to IDLE.
0754: * <p>
0755: *
0756: * @param commitflag various flavors of commit.
0757: *
0758: * @exception StandardException Standard exception policy.
0759: **/
0760: private void completeCommit(int commitflag)
0761: throws StandardException {
0762: // this releases our logical locks if commitflag don't have KEEP_LOCKS.
0763: postComplete(commitflag, COMMIT);
0764:
0765: // this transfer postCommitWorks to PostCommit queue
0766: if ((commitflag & Transaction.KEEP_LOCKS) == 0) {
0767: // if locks are released, start post commit processing
0768: postTermination();
0769: } else {
0770: // RESOLVE: actually, this transaction may not have outstanding
0771: // locks. It didn't release them, but that doesn't mean it has got
0772: // them. This is mostly harmless.
0773:
0774: if (SanityManager.DEBUG)
0775: SanityManager
0776: .ASSERT(myGlobalId == null,
0777: "calling commit with KEEP_LOCKS on a global transaction");
0778:
0779: // we have unreleased locks, the transaction has resource and
0780: // therefore is "active"
0781: setActiveState();
0782: }
0783:
0784: myGlobalId = null;
0785: return;
0786: }
0787:
0788: /**
0789: @exception StandardException Standard cloudscape exception policy
0790: @see Transaction#commit
0791: */
0792: private LogInstant commit(int commitflag) throws StandardException {
0793: if (SanityManager.DEBUG) {
0794: if (SanityManager.DEBUG_ON("XATrace"))
0795: SanityManager.DEBUG("XATrace", "commiting ");
0796: }
0797:
0798: LogInstant flushTo = prepareCommit(commitflag);
0799:
0800: completeCommit(commitflag);
0801:
0802: return (flushTo);
0803: }
0804:
0805: /**
0806: @exception StandardException Standard cloudscape exception policy
0807: @see Transaction#abort
0808: */
0809: public void abort() throws StandardException {
0810:
0811: if (SanityManager.DEBUG) {
0812: if (SanityManager.DEBUG_ON("XATrace"))
0813: SanityManager.DEBUG("XATrace", "aborting ");
0814: }
0815:
0816: if (state == CLOSED) {
0817: // I would have leave this in but close() nulls out myGlobalId
0818: // if (myGlobalId == null)
0819: // {
0820: // throw StandardException.newException(
0821: // SQLState.XACT_PROTOCOL_VIOLATION);
0822: // }
0823:
0824: if (SanityManager.DEBUG) {
0825: // Only global transaction is allowed to abort a closed
0826: // transaction.
0827: if (!sanityCheck_xaclosed) {
0828: throw StandardException
0829: .newException(SQLState.XACT_PROTOCOL_VIOLATION);
0830: }
0831: }
0832:
0833: // In global transaction, the xact object is closed automatically
0834: // on a transaction level rollback. This cause error handling to
0835: // fail because when upper level contexts in the context manager
0836: // unwinds, it calls abort again, which would have caused a
0837: // protocol violation.
0838: return;
0839: }
0840:
0841: /* This routine is never called by recovery redo, only by runtime and
0842: recovery undo. During recovery undo, even though no log record has
0843: been written by this session, it still need to rollback the
0844: incomplete transaction.
0845:
0846: The way to tell if this trasanction has ever written a log record is
0847: by FirstLogInstant.
0848: */
0849:
0850: try {
0851: preComplete(ABORT);
0852:
0853: // rollback the log - if logger is null, nothing I can do, crash.
0854: if (getFirstLogInstant() != null) {
0855: if (logger == null) {
0856: throw StandardException
0857: .newException(SQLState.XACT_CANNOT_ABORT_NULL_LOGGER);
0858: }
0859:
0860: logger.undo(this , getId(), getFirstLogInstant(),
0861: getLastLogInstant());
0862:
0863: EndXact ex = new EndXact(getGlobalId(), END_ABORTED
0864: | statusForEndXactLog());
0865:
0866: logger.flush(logger.logAndDo(this , ex));
0867: } else if (needSync) {
0868: // this transaction object was used to lazily commit some
0869: // previous transaction without syncing. Now that we abort
0870: // for real, make sure any outstanding log is flushed.
0871: logger.flushAll();
0872: }
0873:
0874: needSync = false;
0875:
0876: } catch (StandardException se) {
0877:
0878: // This catches any exceptions that have System severity
0879: // or less (e.g. Statement exception).
0880: //
0881: // If we have any error during an undo we just shut the system
0882: // down, this is a bit drastic but it does ensure that the database
0883: // will not become corrupted by changes that see half committed
0884: // changes.
0885: //
0886: // Note that we do not release our locks if we come thorugh this
0887: // path, if we did then another transaction could complete before
0888: // the system shuts down and make changes based upon the changes
0889: // that we couldn't back out.
0890:
0891: if (se.getSeverity() < ExceptionSeverity.SYSTEM_SEVERITY) {
0892: throw logFactory
0893: .markCorrupt(StandardException.newException(
0894: SQLState.XACT_ABORT_EXCEPTION, se));
0895: }
0896:
0897: throw se;
0898:
0899: }
0900:
0901: // this releases our locks.
0902: postComplete(0, ABORT);
0903:
0904: // get rid of all post commit work - we aborted, therefore no post
0905: // commit work
0906: if (postCommitWorks != null && !postCommitWorks.isEmpty()) {
0907: postCommitWorks.clear();
0908: }
0909:
0910: // Now do post termination work - must do this after the rollback is
0911: // complete because the rollback itself may generate postTermination
0912: // work.
0913: postTermination();
0914:
0915: myGlobalId = null;
0916: }
0917:
0918: /**
0919: * During recovery re-prepare a transaction.
0920: * <p>
0921: * After redo() and undo(), this routine is called on all outstanding
0922: * in-doubt (prepared) transactions. This routine re-acquires all
0923: * logical write locks for operations in the xact, and then modifies
0924: * the transaction table entry to make the transaction look as if it
0925: * had just been prepared following startup after recovery.
0926: * <p>
0927: * This routine is only called during Recovery.
0928: *
0929: * @exception StandardException Standard exception policy.
0930: **/
0931: public void reprepare() throws StandardException {
0932: if (state == CLOSED) {
0933: throw StandardException
0934: .newException(SQLState.XACT_PROTOCOL_VIOLATION);
0935: }
0936:
0937: // Should only be called during recovery on global transactions,
0938: // after redo and undo.
0939: if (SanityManager.DEBUG) {
0940: SanityManager.ASSERT(myGlobalId != null);
0941: SanityManager.ASSERT(state == PREPARED);
0942: }
0943:
0944: try {
0945: if (logger == null) {
0946: throw StandardException
0947: .newException(SQLState.XACT_CANNOT_ABORT_NULL_LOGGER);
0948: }
0949:
0950: // temporarily set state back to UPDATE, so that the reprepare()
0951: // call can do operations on the xact that would "normally" be
0952: // disallowed on a prepared xact - like opening a container to
0953: // lock it.
0954:
0955: state = UPDATE;
0956:
0957: // re-prepare the transaction.
0958: logger.reprepare(this , getId(), getFirstLogInstant(),
0959: getLastLogInstant());
0960:
0961: // make sure the xact is prepare state when we are done.
0962: state = PREPARED;
0963:
0964: seenUpdates = true;
0965:
0966: } catch (StandardException se) {
0967:
0968: // This catches any exceptions that have System severity
0969: // or less (e.g. Statement exception).
0970: //
0971: // If we have any error during an reprepare we just shut the system
0972: // down, this is a bit drastic but it does ensure that the database
0973: // will not become corrupted by changes that see data that is part
0974: // of a prepared transaction.
0975: //
0976: // Note that we do not release our locks if we come thorugh this
0977: // path, if we did then another transaction could complete before
0978: // the system shuts down and make changes based upon the changes
0979: // that we couldn't back out.
0980:
0981: if (se.getSeverity() < ExceptionSeverity.SYSTEM_SEVERITY) {
0982: throw logFactory
0983: .markCorrupt(StandardException.newException(
0984: SQLState.XACT_ABORT_EXCEPTION, se));
0985: }
0986:
0987: throw se;
0988: }
0989:
0990: // RESOLVE - something needs to change the state of the XACT so that
0991: // it is not recovery state anymore?
0992: }
0993:
0994: /**
0995: If this transaction is not idle, abort it. After this call close().
0996:
0997: @exception StandardException Standard Cloudscape error policy
0998: Thrown if the transaction is not idle.
0999:
1000:
1001: */
1002: public void destroy() throws StandardException {
1003: if (state != CLOSED)
1004: abort();
1005:
1006: close();
1007: }
1008:
1009: /**
1010: @exception StandardException Standard cloudscape exception policy
1011: @exception StandardException Thrown if the transaction is not idle, the
1012: transaction remains open.
1013: @see Transaction#close
1014:
1015: @exception StandardException Standard cloudscape policy
1016: */
1017: public void close() throws StandardException {
1018:
1019: /*
1020:
1021: if (((LogToFile) logFactory).inRedo)
1022: {
1023: SanityManager.showTrace(new Throwable());
1024: SanityManager.THROWASSERT("in Redo while in close");
1025: }
1026: */
1027:
1028: switch (state) {
1029: case CLOSED:
1030: return;
1031: case IDLE:
1032: break;
1033: default:
1034: throw StandardException
1035: .newException(SQLState.XACT_TRANSACTION_NOT_IDLE);
1036: }
1037:
1038: if (SanityManager.DEBUG) {
1039:
1040: SanityManager.ASSERT(xc.getTransaction() == this );
1041:
1042: SanityManager
1043: .ASSERT((postCommitWorks == null || postCommitWorks
1044: .isEmpty()),
1045: "cannot close a transaction with post commit work pending");
1046:
1047: // use this for sanity checking
1048: if (myGlobalId != null)
1049: sanityCheck_xaclosed = true;
1050: }
1051:
1052: getLockFactory().clearLimit(this , this );
1053:
1054: if (SanityManager.DEBUG) {
1055: if (SanityManager.DEBUG_ON("XATrace"))
1056: SanityManager.DEBUG("XATrace", "closing " + myId + " "
1057: + myGlobalId);
1058: // Thread.dumpStack();
1059: }
1060:
1061: // if we just finished recovery myId could be null
1062: if (myId != null)
1063: xactFactory.remove((XactId) myId);
1064:
1065: xc.popMe();
1066: xc = null;
1067:
1068: myGlobalId = null;
1069: myId = null;
1070: logStart = null;
1071: logLast = null;
1072:
1073: /* MT - no need to synchronize it, the state is current IDLE which will
1074: * return the same result to isActive() as if it is CLOSED
1075: */
1076: state = CLOSED;
1077:
1078: }
1079:
1080: /**
1081: Log the operation and do it.
1082:
1083: If this transaction has not generated any log records prior to this,
1084: then log a beginXact log record.
1085:
1086: If the passed in operation is null, then do nothing (after logging the
1087: beginXact if needed).
1088:
1089: @exception StandardException Standard cloudscape exception policy
1090: @see Transaction#logAndDo
1091: */
1092: public void logAndDo(Loggable operation) throws StandardException {
1093:
1094: LogInstant instant = null;
1095:
1096: if (logger == null)
1097: getLogger();
1098:
1099: if (logger == null) {
1100: throw StandardException
1101: .newException(SQLState.XACT_CANNOT_LOG_CHANGE);
1102: }
1103:
1104: setActiveState();
1105:
1106: if (state == ACTIVE) {
1107: instant = logger.logAndDo(this , new BeginXact(
1108: getGlobalId(), statusForBeginXactLog()));
1109:
1110: setUpdateState();
1111: }
1112: seenUpdates = true;
1113:
1114: if (operation != null) {
1115: instant = logger.logAndDo(this , operation);
1116: if (instant != null) {
1117: setLastLogInstant(instant);
1118:
1119: if ((savePoints != null) && !savePoints.empty()) {
1120: for (int i = savePoints.size() - 1; i >= 0; i--) {
1121: // set the top savepoint to rollback to this record if
1122: // it doesn't yet have a point saved
1123:
1124: SavePoint sp = (SavePoint) savePoints
1125: .elementAt(i);
1126: if (sp.getSavePoint() == null) {
1127: sp.setSavePoint(instant);
1128: } else
1129: break;
1130: }
1131: }
1132: }
1133:
1134: } else {
1135: if (instant != null)
1136: setLastLogInstant(instant);
1137: }
1138:
1139: }
1140:
1141: public void addPostCommitWork(Serviceable work) {
1142: if (recoveryTransaction)
1143: return;
1144:
1145: if (postCommitWorks == null)
1146: postCommitWorks = new ArrayList(1);
1147: postCommitWorks.add(work);
1148: }
1149:
1150: public void addPostTerminationWork(Serviceable work) {
1151: if (recoveryTransaction)
1152: return;
1153:
1154: if (postTerminationWorks == null)
1155: postTerminationWorks = new ArrayList(2);
1156: postTerminationWorks.add(work);
1157: }
1158:
1159: /**
1160: Return a record handle that is initialized to the given page number and
1161: record id.
1162:
1163: @exception StandardException Standard cloudscape exception policy.
1164:
1165: @param segmentId segment where the RecordHandle belongs.
1166: @param containerId container where the RecordHandle belongs.
1167: @param pageNumber the page number of the RecordHandle.
1168: @param recordId the record id of the RecordHandle.
1169:
1170: @see RecordHandle
1171: */
1172: // public RecordHandle makeRecordHandle(long segmentId, long containerId, long pageNumber, int recordId)
1173: // throws StandardException
1174: // {
1175: // return(this.dataFactory.makeRecordHandle(
1176: // segmentId, containerId, pageNumber, recordId));
1177: // }
1178: /**
1179: @exception StandardException Standard cloudscape exception policy
1180: @see Transaction#openContainer
1181: */
1182: public ContainerHandle openContainer(ContainerKey containerId,
1183: int mode) throws StandardException {
1184:
1185: return openContainer(containerId, defaultLockingPolicy(), mode);
1186: }
1187:
1188: /**
1189: @exception StandardException Standard cloudscape exception policy
1190: @see Transaction#openContainer
1191: */
1192: public ContainerHandle openContainer(ContainerKey containerId,
1193: LockingPolicy locking, int mode) throws StandardException {
1194:
1195: setActiveState();
1196:
1197: if (locking == null)
1198: locking = xactFactory.getLockingPolicy(
1199: LockingPolicy.MODE_NONE,
1200: TransactionController.ISOLATION_NOLOCK, false);
1201:
1202: return dataFactory.openContainer(this , containerId, locking,
1203: mode);
1204: }
1205:
1206: /**
1207: Open a container that may already have been dropped.
1208:
1209: @exception StandardException Standard cloudscape exception policy
1210: @see RawTransaction#openDroppedContainer
1211: */
1212: public RawContainerHandle openDroppedContainer(
1213: ContainerKey containerId, LockingPolicy locking)
1214: throws StandardException {
1215: setActiveState();
1216:
1217: if (locking == null)
1218: locking = xactFactory.getLockingPolicy(
1219: LockingPolicy.MODE_NONE,
1220: TransactionController.ISOLATION_NOLOCK, false);
1221:
1222: RawContainerHandle hdl = null;
1223:
1224: // first try to open it for update, if that fail, open it for read
1225: try {
1226: hdl = dataFactory.openDroppedContainer(this , containerId,
1227: locking, ContainerHandle.MODE_FORUPDATE);
1228: } catch (StandardException se) {
1229: // if this also fail, throw exception
1230: hdl = dataFactory.openDroppedContainer(this , containerId,
1231: locking, ContainerHandle.MODE_READONLY);
1232: }
1233:
1234: return hdl;
1235: }
1236:
1237: /**
1238: @exception StandardException Standard cloudscape exception policy
1239: @see Transaction#addContainer
1240: */
1241: public long addContainer(long segmentId, long containerid,
1242: int mode, Properties tableProperties, int temporaryFlag)
1243: throws StandardException {
1244:
1245: setActiveState();
1246:
1247: return dataFactory.addContainer(this , segmentId, containerid,
1248: mode, tableProperties, temporaryFlag);
1249: }
1250:
1251: /**
1252: @exception StandardException Standard cloudscape exception policy
1253: @see Transaction#addAndLoadStreamContainer
1254: */
1255: public long addAndLoadStreamContainer(long segmentId,
1256: Properties tableProperties, RowSource rowSource)
1257: throws StandardException {
1258:
1259: setActiveState();
1260:
1261: return dataFactory.addAndLoadStreamContainer(this , segmentId,
1262: tableProperties, rowSource);
1263:
1264: }
1265:
1266: /**
1267: @exception StandardException Standard cloudscape exception policy
1268: @see Transaction#openStreamContainer
1269: */
1270: public StreamContainerHandle openStreamContainer(long segmentId,
1271: long containerId, boolean hold) throws StandardException {
1272: setActiveState();
1273:
1274: return (dataFactory.openStreamContainer(this , segmentId,
1275: containerId, hold));
1276: }
1277:
1278: /**
1279: @see Transaction#dropStreamContainer
1280: @exception StandardException Standard Cloudscape error policy
1281: */
1282: public void dropStreamContainer(long segmentId, long containerId)
1283: throws StandardException {
1284:
1285: setActiveState();
1286:
1287: dataFactory.dropStreamContainer(this , segmentId, containerId);
1288: }
1289:
1290: /**
1291: Recreate a container during redo recovery.
1292:
1293: Used only during redo recovery while processing log records which
1294: are trying to create a container, and no valid container is found
1295: in the database.
1296:
1297: @exception StandardException Standard cloudscape exception policy
1298: @see RawTransaction#reCreateContainerForRedoRecovery
1299: */
1300: public void reCreateContainerForRedoRecovery(long segmentId,
1301: long containerId, ByteArray containerInfo)
1302: throws StandardException {
1303: setActiveState();
1304:
1305: dataFactory.reCreateContainerForRedoRecovery(this , segmentId,
1306: containerId, containerInfo);
1307: }
1308:
1309: /**
1310: @see Transaction#dropContainer
1311: @exception StandardException Standard Cloudscape error policy
1312: */
1313: public void dropContainer(ContainerKey containerId)
1314: throws StandardException {
1315:
1316: setActiveState();
1317:
1318: dataFactory.dropContainer(this , containerId);
1319: }
1320:
1321: /**
1322: @exception StandardException Standard cloudscape exception policy
1323: @see Transaction#setSavePoint
1324: */
1325: public int setSavePoint(String name, Object kindOfSavepoint)
1326: throws StandardException {
1327:
1328: if (kindOfSavepoint != null
1329: && kindOfSavepoint instanceof String) {
1330: //that means we are trying to set a SQL savepoint
1331:
1332: //error if this SQL savepoint is getting nested into other user
1333: // defined savepoints
1334: throwExceptionIfSQLSavepointNotAllowed(kindOfSavepoint);
1335: }
1336:
1337: // while setting a savepoint, we just want to see if there is a
1338: // savepoint with the passed name already in the system.
1339: if (getSavePointPosition(name, kindOfSavepoint, false) != -1) {
1340: throw StandardException
1341: .newException(SQLState.XACT_SAVEPOINT_EXISTS);
1342: }
1343:
1344: if (savePoints == null)
1345: savePoints = new Stack();
1346:
1347: savePoints.push(new SavePoint(name, kindOfSavepoint));
1348:
1349: if (SanityManager.DEBUG) {
1350:
1351: if (SanityManager.DEBUG_ON("memoryLeakTrace")) {
1352:
1353: if (savePoints.size() > 20)
1354: System.out
1355: .println("memoryLeakTrace:Xact:savepoints "
1356: + savePoints.size());
1357: }
1358: }
1359: return savePoints.size();
1360: }
1361:
1362: // SQL savepoint can't be nested inside other user defined savepoints. To
1363: // enforce this, we check if there are already user savepoint(SQL/JDBC)
1364: // defined in the transaction. If yes, then throw an exception
1365:
1366: /**
1367: @exception StandardException Standard cloudscape exception policy
1368: @see Transaction#setSavePoint
1369: */
1370: private void throwExceptionIfSQLSavepointNotAllowed(
1371: Object kindOfSavepoint) throws StandardException {
1372:
1373: boolean foundUserSavepoint = false;
1374:
1375: if ((savePoints != null) && !savePoints.empty()) {
1376: for (int i = savePoints.size() - 1; i >= 0; i--) {
1377: SavePoint sp = (SavePoint) savePoints.elementAt(i);
1378: if (sp.isThisUserDefinedsavepoint()) {
1379: //found a user defined savepoint
1380:
1381: foundUserSavepoint = true;
1382: break;
1383: }
1384: }
1385: }
1386:
1387: if (foundUserSavepoint)
1388: throw StandardException
1389: .newException(SQLState.XACT_MAX_SAVEPOINT_LEVEL_REACHED);
1390: }
1391:
1392: /**
1393: @exception StandardException Standard cloudscape exception policy
1394: @see Transaction#releaseSavePoint
1395: */
1396: public int releaseSavePoint(String name, Object kindOfSavepoint)
1397: throws StandardException {
1398: int position = getSavePointPosition(name, kindOfSavepoint, true);
1399:
1400: if (position == -1) {
1401: // following means this is a JDBC savepoint.
1402: // We prepend i./e. to JDBC savepoint names in JDBC layer.
1403: // Need to trim that here before giving error
1404:
1405: if (kindOfSavepoint != null
1406: && !(kindOfSavepoint instanceof String)) {
1407: // this means this is a JDBC savepoint.
1408: // We append "i."/"e." to JDBC savepoint names.
1409: // Trimming that here before giving error
1410:
1411: name = name.substring(2);
1412: }
1413: throw StandardException.newException(
1414: SQLState.XACT_SAVEPOINT_NOT_FOUND, name);
1415: }
1416:
1417: popSavePoints(position, true);
1418: return savePoints.size();
1419: }
1420:
1421: /**
1422: @exception StandardException Standard cloudscape exception policy
1423: @see Transaction#rollbackToSavePoint
1424: */
1425: public int rollbackToSavePoint(String name, Object kindOfSavepoint)
1426: throws StandardException {
1427: int position = getSavePointPosition(name, kindOfSavepoint, true);
1428:
1429: if (position == -1) {
1430: // following means this is a JDBC savepoint.
1431: // We append i./e. to JDBC savepoint names in JDBC layer.
1432: // Need to trim that here before giving error
1433: if (kindOfSavepoint != null
1434: && !(kindOfSavepoint instanceof String))
1435: name = name.substring(2);
1436: throw StandardException.newException(
1437: SQLState.XACT_SAVEPOINT_NOT_FOUND, name);
1438: }
1439:
1440: notifyObservers(SAVEPOINT_ROLLBACK);
1441:
1442: popSavePoints(position, false);
1443: return savePoints.size();
1444: }
1445:
1446: /*
1447: ** Implementation specific methods
1448: */
1449:
1450: /**
1451: Get the Logger object used to write log records to the transaction log.
1452: */
1453: private void getLogger() {
1454:
1455: logger = logFactory.getLogger();
1456: }
1457:
1458: /**
1459: Transform this identity to the one stored in transaction table entry.
1460: Used by recovery only!
1461: */
1462: protected void assumeIdentity(TransactionTableEntry ent) {
1463: if (ent != null) {
1464: if (SanityManager.DEBUG) {
1465: SanityManager.ASSERT(ent.getXid() != null,
1466: "TTE.xid is null");
1467:
1468: SanityManager.ASSERT(ent.getFirstLog() != null,
1469: "TTE.firstLog is null");
1470: }
1471:
1472: // I am the transaction that is using this TransactionTableEntry
1473: ent.setXact(this );
1474:
1475: myId = ent.getXid();
1476: logStart = ent.getFirstLog();
1477: logLast = ent.getLastLog();
1478:
1479: // This routine is only used by recovery to assume the identity
1480: // of the transaction for each log record during redo and undo.
1481: // For this purpose the transaction should act like a local
1482: // transaction, and ignore the fact that it may or may not be
1483: // an XA global transaction - this is necessary as global
1484: // transactions can only be committed or aborted once, but
1485: // recovery needs to reuse the same xact over and over again.
1486: // For this purpose set myGlobalId to null so it is treated as
1487: // a local xact - the entry in the transaction table will
1488: // track that in reality it is a global id and remember it's
1489: // value.
1490: myGlobalId = null;
1491:
1492: // I am very active
1493: if (state == IDLE)
1494: state = ACTIVE;
1495:
1496: if (SanityManager.DEBUG) {
1497: if (state != ACTIVE && state != UPDATE
1498: && state != PREPARED)
1499: SanityManager
1500: .THROWASSERT("recovery transaction have illegal state "
1501: + state + "xact = " + this );
1502: }
1503:
1504: if (logger == null)
1505: getLogger();
1506:
1507: savedEndStatus = 0;
1508: } else {
1509: myGlobalId = null;
1510: myId = null;
1511: logStart = null;
1512: logLast = null;
1513: state = IDLE;
1514: }
1515: }
1516:
1517: /**
1518: * Assume complete identity of the given Transaction Table Entry.
1519: * <p>
1520: * Used by the final phase of the recovery to create new real transactions
1521: * to take on the identity of in-doubt prepared transactions found during
1522: * redo. Need to assume the globalId.
1523: *
1524: * @param ent The original entry we are assuming the identity of.
1525: *
1526: **/
1527: protected void assumeGlobalXactIdentity(TransactionTableEntry ent) {
1528: if (SanityManager.DEBUG) {
1529: SanityManager.ASSERT(ent != null);
1530: SanityManager.ASSERT(ent.getXid() != null,
1531: "TTE.xid is null");
1532: SanityManager.ASSERT(ent.getFirstLog() != null,
1533: "TTE.firstLog is null");
1534: SanityManager.ASSERT(ent.isPrepared());
1535: }
1536:
1537: myId = ent.getXid();
1538: myGlobalId = ent.getGid();
1539: logStart = ent.getFirstLog();
1540: logLast = ent.getLastLog();
1541:
1542: // I am very active
1543: if (state == IDLE) {
1544: if (SanityManager.DEBUG) {
1545: if (SanityManager.DEBUG_ON("XATrace"))
1546: SanityManager
1547: .DEBUG("XATrace",
1548: "set active state in assume Global XactIdentity");
1549: }
1550:
1551: state = ACTIVE;
1552: }
1553:
1554: if (ent.isPrepared())
1555: state = PREPARED;
1556:
1557: // I am the transaction that is using this TransactionTableEntry
1558: ent.setXact(this );
1559:
1560: if (SanityManager.DEBUG) {
1561: if (state != ACTIVE && state != UPDATE && state != PREPARED)
1562: SanityManager
1563: .THROWASSERT("recovery transaction have illegal state "
1564: + state + "xact = " + this );
1565: }
1566:
1567: if (logger == null)
1568: getLogger();
1569:
1570: savedEndStatus = 0;
1571:
1572: if (SanityManager.DEBUG) {
1573: SanityManager.ASSERT(myGlobalId != null);
1574:
1575: // at least right now only prepared xact call this during recovery.
1576: SanityManager.ASSERT(state == PREPARED);
1577: }
1578: }
1579:
1580: /**
1581: Move the transaction into the update state.
1582: @exception StandardException problem setting a transaction id
1583: */
1584: private final void setUpdateState() throws StandardException {
1585:
1586: /*
1587: System.out.println("calling setUpdateState() - readOnly = " + readOnly +
1588: ";this = " + this);
1589: System.out.println("calling setUpdateState():");
1590: (new Throwable()).printStackTrace();
1591: */
1592:
1593: if (SanityManager.DEBUG) {
1594: SanityManager
1595: .ASSERT(state == ACTIVE,
1596: "setting update state without first going thru ACTIVE state");
1597:
1598: SanityManager
1599: .ASSERT(myId != null,
1600: "setting update state to a trasnaction with Null ID");
1601: }
1602:
1603: if (SanityManager.DEBUG) {
1604: if (SanityManager.DEBUG_ON("XATrace")) {
1605: SanityManager.DEBUG("XATrace", "set update state");
1606: SanityManager.showTrace(new Throwable());
1607: }
1608: }
1609:
1610: if (readOnly) {
1611: throw StandardException
1612: .newException(SQLState.XACT_PROTOCOL_VIOLATION);
1613: }
1614:
1615: state = UPDATE;
1616: }
1617:
1618: protected void setIdleState() {
1619:
1620: if (SanityManager.DEBUG)
1621: SanityManager.ASSERT(myId != null,
1622: "setIdleState got null ID");
1623:
1624: if (SanityManager.DEBUG) {
1625: if (SanityManager.DEBUG_ON("TranTrace")) {
1626: SanityManager.DEBUG("TranTrace",
1627: "transaction going idle " + myId);
1628:
1629: SanityManager.showTrace(new Throwable("TranTrace"));
1630: }
1631: }
1632:
1633: /* MT - single thread throught synchronizing this. Even though no other
1634: * thread can call this, they may call isActive which needs to be
1635: * synchronized with state transaction into or out of the idle state
1636: */
1637: // synchronized(this) -- int access, implicit synchronization
1638: // due to atomic action
1639: {
1640: state = IDLE;
1641: }
1642:
1643: seenUpdates = false;
1644:
1645: // these fields will NOT be accessed by the checkpoint thread at the
1646: // same time because the doMe method of EndXact removed this
1647: // transaction from the "update transaction" list, therefore when the
1648: // checkpoint writes out the transaction table, this transaction will
1649: // be skipped. OK to just change it without synchronization here.
1650:
1651: logStart = null;
1652: logLast = null;
1653:
1654: if (SanityManager.DEBUG) {
1655: if (SanityManager.DEBUG_ON("XATrace"))
1656: SanityManager.DEBUG("XATrace", "set idle state : "
1657: + state + " " + this );
1658: }
1659:
1660: }
1661:
1662: protected final void setActiveState() throws StandardException {
1663:
1664: if ((state == CLOSED) || (!inAbort() && (state == PREPARED))) {
1665: // This is where we catch attempted activity on a prepared xact.
1666: throw StandardException
1667: .newException(SQLState.XACT_PROTOCOL_VIOLATION);
1668: }
1669:
1670: if (SanityManager.DEBUG)
1671: SanityManager.ASSERT(myId != null,
1672: "setActiveState got null ID");
1673:
1674: if (state == IDLE) {
1675: synchronized (this ) {
1676: state = ACTIVE;
1677: }
1678:
1679: if (SanityManager.DEBUG) {
1680: if (SanityManager.DEBUG_ON("XATrace")) {
1681: SanityManager.DEBUG("XATrace", "set active state "
1682: + this );
1683: SanityManager.showTrace(new Throwable("XATrace"));
1684: }
1685: }
1686:
1687: if (!justCreated)
1688: xactFactory.setNewTransactionId(myId, this );
1689:
1690: justCreated = false;
1691:
1692: if (SanityManager.DEBUG) {
1693: if (SanityManager.DEBUG_ON("TranTrace")) {
1694: SanityManager.DEBUG("TranTrace",
1695: "transaction going active " + myId);
1696:
1697: SanityManager.showTrace(new Throwable("TranTrace"));
1698: }
1699: }
1700:
1701: }
1702: }
1703:
1704: /**
1705: * Move the state of the transaction from UPDATE to PREPARE.
1706: * <p>
1707: * The state transition should only be from UPDATE to PREPARE. Read-only
1708: * transactions (IDLE and ACTIVE) will never be prepared, they will be
1709: * commited when the prepare is requested. Only Update transactions will
1710: * be allowed to go to prepared state.
1711: * <p>
1712: *
1713: * @exception StandardException Standard exception policy.
1714: **/
1715: protected final void setPrepareState() throws StandardException {
1716: if (state == PREPARED || state == CLOSED) {
1717: throw StandardException
1718: .newException(SQLState.XACT_PROTOCOL_VIOLATION);
1719: }
1720:
1721: if (SanityManager.DEBUG) {
1722: SanityManager
1723: .ASSERT(state == UPDATE,
1724: "setting PREPARED state without first going thru UPDATE state");
1725: SanityManager
1726: .ASSERT(myId != null,
1727: "setting PREPARED state to a transaction with Null ID");
1728: }
1729:
1730: state = PREPARED;
1731: }
1732:
1733: public final LockingPolicy defaultLockingPolicy() {
1734: return defaultLocking;
1735: }
1736:
1737: private final void releaseAllLocks() {
1738:
1739: getLockFactory().unlockGroup(getCompatibilitySpace(), this );
1740: }
1741:
1742: void resetDefaultLocking() {
1743:
1744: setDefaultLockingPolicy(newLockingPolicy(
1745: LockingPolicy.MODE_RECORD,
1746: TransactionController.ISOLATION_SERIALIZABLE, true));
1747:
1748: if (SanityManager.DEBUG) {
1749: SanityManager.ASSERT(defaultLocking != null);
1750: }
1751:
1752: }
1753:
1754: protected void preComplete(Integer commitOrAbort)
1755: throws StandardException {
1756:
1757: /* If a transaction is in COMMIT/ABORT at this point, most probably
1758: * some thing went wrong in earlier attempt to commit or abort,
1759: * so we don't know wther the log records got written in previous
1760: * attempt. It's is better to bring down the system than make recovery
1761: * fail with a duplicate log records of COMMIT/ABORT for the same Transaction.
1762: */
1763: if (inComplete != null)
1764: if (commitOrAbort.equals(COMMIT))
1765: throw logFactory.markCorrupt(StandardException
1766: .newException(SQLState.XACT_COMMIT_EXCEPTION));
1767: else
1768: throw logFactory.markCorrupt(StandardException
1769: .newException(SQLState.XACT_ABORT_EXCEPTION));
1770:
1771: inComplete = commitOrAbort;
1772: if (!postCompleteMode)
1773: doComplete(commitOrAbort);
1774:
1775: }
1776:
1777: protected void postComplete(int commitflag, Integer commitOrAbort)
1778: throws StandardException {
1779:
1780: if (postCompleteMode)
1781: doComplete(commitOrAbort);
1782:
1783: // if we are want to commitNoSync with KEEP_LOCKS flag set, don't
1784: // release any locks
1785: if ((commitflag & Transaction.KEEP_LOCKS) == 0) {
1786: releaseAllLocks();
1787: } else {
1788: if (SanityManager.DEBUG) {
1789: SanityManager.ASSERT(commitOrAbort.equals(COMMIT),
1790: "cannot keep locks around after an ABORT");
1791: }
1792: }
1793:
1794: setIdleState();
1795:
1796: inComplete = null;
1797: }
1798:
1799: protected void doComplete(Integer commitOrAbort)
1800: throws StandardException {
1801:
1802: // throw away all our savepoints
1803: if (savePoints != null)
1804: savePoints.removeAllElements();
1805:
1806: // notify any of our observers that we are completing.
1807: notifyObservers(commitOrAbort);
1808:
1809: checkObserverException();
1810:
1811: if (SanityManager.DEBUG) {
1812: if (countObservers() != 0) {
1813: System.out
1814: .println("There should be 0 observers, but we still have "
1815: + countObservers() + " observers.");
1816: notifyObservers(null);
1817: }
1818: }
1819: }
1820:
1821: private void checkObserverException() throws StandardException {
1822: if (observerException != null) {
1823: StandardException se = observerException;
1824: observerException = null;
1825: throw se;
1826: }
1827: }
1828:
1829: /**
1830: If this is a user transaction (not an internal or nested top
1831: transaction), and this is not already taking care of post
1832: commit work, and not an XA transaction, then take care of hi prioirty
1833: work right now using this thread and this context manager.
1834: Otherwise, leave it to the post commit daemon.
1835: */
1836: protected boolean doPostCommitWorkInTran() {
1837: return (!inPostCommitProcessing && !recoveryTransaction
1838: && isUserTransaction() && (myGlobalId == null));
1839: }
1840:
1841: public boolean handlesPostTerminationWork() {
1842: // recovery transaction cannot handle post termination work
1843: return (recoveryTransaction == false);
1844: }
1845:
1846: public void recoveryTransaction() {
1847: recoveryTransaction = true;
1848:
1849: // remove myself from the transaction table because I am really a
1850: // "fake" transaction. All interaction I have with the transaction
1851: // table should happen after I have assumed the identity of one of the
1852: // recovery transaction that has its state frozen in the transaction
1853: // table.
1854: xactFactory.remove(myId);
1855:
1856: }
1857:
1858: private final void postTermination() throws StandardException {
1859: // move all the postTermination work to the postCommit queue
1860: int count = (postTerminationWorks == null) ? 0
1861: : postTerminationWorks.size();
1862:
1863: for (int i = 0; i < count; i++)
1864: addPostCommitWork((Serviceable) postTerminationWorks.get(i));
1865:
1866: if (count > 0)
1867: postTerminationWorks.clear();
1868:
1869: // if there are post commit work to be done, transfer them to the
1870: // daemon. The log is flushed, all locks released and the
1871: // transaction has ended at this point.
1872: if (postCommitWorks != null && !postCommitWorks.isEmpty()) {
1873: int pcsize = postCommitWorks.size();
1874:
1875: // do we want to do post commit work with this transaction object?
1876: if (doPostCommitWorkInTran()) {
1877: try {
1878: inPostCommitProcessing = true;
1879:
1880: // to avoid confusion, copy the post commit work to an array if this
1881: // is going to do some work now
1882: Serviceable[] work = new Serviceable[pcsize];
1883: work = (Serviceable[]) postCommitWorks
1884: .toArray(work);
1885:
1886: // clear this for post commit processing to queue its own post
1887: // commit works - when it commits, it will send all its post
1888: // commit request to the daemon instead of dealing with it here.
1889: postCommitWorks.clear();
1890:
1891: //All the post commit work that is part of the database creation
1892: //should be done on this thread immediately.
1893: boolean doWorkInThisThread = xactFactory
1894: .inDatabaseCreation();
1895:
1896: for (int i = 0; i < pcsize; i++) {
1897:
1898: //process work that should be done immediately or
1899: //when we are in still in database creattion.
1900: //All the other work should be submitted
1901: //to the post commit thread to be processed asynchronously
1902: if (doWorkInThisThread
1903: || work[i].serviceImmediately()) {
1904: try {
1905: // this may cause other post commit work to be
1906: // added. when that transaction commits, those
1907: // work will be transfered to the daemon
1908: if (work[i].performWork(xc
1909: .getContextManager()) == Serviceable.DONE)
1910: work[i] = null;
1911:
1912: // if REQUEUE, leave it on for the postcommit
1913: // daemon to handle
1914: } catch (StandardException se) {
1915: // don't try to service this again
1916: work[i] = null;
1917:
1918: // try to handle it here. If we fail, then let the error percolate.
1919: xc.cleanupOnError(se);
1920: }
1921: }
1922:
1923: // either it need not be serviedASAP or it needs
1924: // requeueing, send it off. Note that this is one case
1925: // where a REQUEUE ends up in the high priority queue.
1926: // Unfortunately, there is no easy way to tell. If the
1927: // Servicable is well mannered, it can change itself from
1928: // serviceASAP to not serviceASAP if it returns REQUEUE.
1929: if (work[i] != null) {
1930: boolean needHelp = xactFactory
1931: .submitPostCommitWork(work[i]);
1932: work[i] = null;
1933: if (needHelp)
1934: doWorkInThisThread = true;
1935: }
1936: }
1937: } finally {
1938: inPostCommitProcessing = false;
1939:
1940: // if something untoward happends, clear the queue.
1941: if (postCommitWorks != null)
1942: postCommitWorks.clear();
1943: }
1944:
1945: } else {
1946: // this is for non-user transaction or post commit work that is
1947: // submitted in PostCommitProcessing. (i.e., a post commit
1948: // work submitting other post commit work)
1949: for (int i = 0; i < pcsize; i++) {
1950: // SanityManager.DEBUG_PRINT("PostTermination",postCommitWorks.elementAt((i)).toString());
1951: xactFactory
1952: .submitPostCommitWork((Serviceable) postCommitWorks
1953: .get((i)));
1954: }
1955: }
1956:
1957: postCommitWorks.clear();
1958:
1959: }
1960:
1961: // any backup blocking operations (like unlogged ops) in this
1962: // transaction are done with post commit/abort work that needs be
1963: // done in the same trasaction, unblock the backup.
1964: unblockBackup();
1965: }
1966:
1967: /**
1968: Does a save point exist in the stack with the given name.
1969: Returns the position of the savepoint in the array
1970: */
1971: private int getSavePointPosition(String name,
1972: Object kindOfSavepoint, boolean forRollbackOrRelease) {
1973: if ((savePoints == null) || (savePoints.empty()))
1974: return -1;
1975:
1976: for (int i = savePoints.size() - 1; i >= 0; i--) {
1977: SavePoint savepoint = (SavePoint) savePoints.elementAt(i);
1978:
1979: if (savepoint.getName().equals(name)) {
1980: if (forRollbackOrRelease
1981: && savepoint.getKindOfSavepoint() != null) {
1982: if (savepoint.getKindOfSavepoint().equals(
1983: kindOfSavepoint))
1984: return (i);
1985: } else {
1986: return (i);
1987: }
1988: }
1989: }
1990: return -1;
1991: }
1992:
1993: /**
1994: Pop all savepoints upto the one with the given name and rollback
1995: all changes made since this savepoint was pushed.
1996: If release is true then this savepoint is popped as well,
1997: otherwise it is left in the stack (at the top).
1998:
1999: @return true if any work is rolled back, false if no work is rolled back
2000: @exception StandardException Standard cloudscape policy
2001: @exception StandardException Thrown if a error of severity less than TransactionException#SEVERITY
2002: is encountered during the rollback of this savepoint.
2003: */
2004: protected boolean popSavePoints(int position, boolean release)
2005: throws StandardException {
2006:
2007: if (release) {
2008: savePoints.setSize(position);
2009: return false;
2010: }
2011:
2012: LogInstant rollbackTo = null;
2013:
2014: int size = savePoints.size();
2015: for (int i = position; i < size; i++) {
2016: SavePoint rollbackSavePoint = (SavePoint) savePoints
2017: .elementAt(i);
2018:
2019: LogInstant li = rollbackSavePoint.getSavePoint();
2020: if (li != null) {
2021: rollbackTo = li;
2022: break;
2023: }
2024: }
2025:
2026: savePoints.setSize(position + 1);
2027:
2028: if (rollbackTo == null)
2029: return false;
2030:
2031: // now perform the rollback
2032: try {
2033:
2034: logger.undo(this , getId(), rollbackTo, getLastLogInstant());
2035:
2036: } catch (StandardException se) {
2037:
2038: // This catches any exceptions that have Transaction severity
2039: // or less (e.g. Statement exception). If we received any lesser
2040: // error then we abort the transaction anyway.
2041:
2042: if (se.getSeverity() < ExceptionSeverity.TRANSACTION_SEVERITY) {
2043: throw StandardException.newException(
2044: SQLState.XACT_ROLLBACK_EXCEPTION, se);
2045: }
2046:
2047: throw se;
2048: }
2049:
2050: return true;
2051: }
2052:
2053: /**
2054: @exception StandardException Cloudscape Standard error policy
2055: */
2056: public RawTransaction startNestedTopTransaction()
2057: throws StandardException {
2058:
2059: return xactFactory.startNestedTopTransaction(xc.getFactory(),
2060: xc.getContextManager());
2061: }
2062:
2063: /**
2064: * see if this transaction is a user transaction.
2065: *
2066: * @return true if this transaction is a user transaction
2067: */
2068: private boolean isUserTransaction() {
2069: String context_id = getContextId();
2070:
2071: return ((context_id == XactFactory.USER_CONTEXT_ID || context_id
2072: .equals(XactFactory.USER_CONTEXT_ID)));
2073: }
2074:
2075: /**
2076: * see if this transaction has ever done anything.
2077: *
2078: * MT - single thread through synchronizing this. This method may be
2079: * called by other thread to test the state of this transaction. That's
2080: * why we need to synchronize with all methods which enters or exits the
2081: * Idle state.
2082: *
2083: * Local method which read the state need not be synchronized because
2084: * the other thread may look at the state but it may not change it.
2085: *
2086: * @return true if this transaction is not in idle or closed state
2087: */
2088: public final boolean isActive() {
2089: // synchronized(this) -- int access, implicit synchronization
2090: // due to atomic action
2091: int localState = state;
2092:
2093: return (localState != CLOSED && localState != IDLE);
2094: }
2095:
2096: /**
2097: * see if this transaction is in PREPARED state.
2098: *
2099: * MT - single thread through synchronizing this. This method may be
2100: * called by other thread to test the state of this transaction.
2101: *
2102: * @return true if this transaction is in PREPARED state.
2103: */
2104: public final boolean isPrepared() {
2105: // synchronized(this) -- int access, implicit synchronization
2106: // due to atomic action
2107: return (state == PREPARED);
2108: }
2109:
2110: /**
2111: See if this transaction is in the idle state, called by other thread to
2112: test the state of this transaction. That's why we need to synchronzied
2113: with all methods whcih enters or exits the idle state
2114:
2115: @return true if it is idle, otherwise false
2116: */
2117: public boolean isIdle() {
2118: // synchronized(this) -- int access, implicit synchronization
2119: // due to atomic action
2120: if (SanityManager.DEBUG) {
2121: if (SanityManager.DEBUG_ON("XATrace"))
2122: SanityManager.DEBUG("XATrace",
2123: "RawTran, isIdle, state = " + state);
2124: }
2125:
2126: return (state == IDLE);
2127: }
2128:
2129: /**
2130: see if this transaction is in a pristine state.
2131:
2132: <BR>MT - called only by the same thread that owns the xact, no need to synchronize.
2133:
2134: @return true if it hasn't done any updates, otherwise false
2135: */
2136: public boolean isPristine() {
2137: return (state == IDLE || state == ACTIVE);
2138: }
2139:
2140: public boolean inAbort() {
2141: return ABORT.equals(inComplete);
2142: }
2143:
2144: public FileResource getFileHandler() {
2145: return dataFactory.getFileHandler();
2146: }
2147:
2148: /**
2149: * put this into the beginXact log record to help recovery
2150: * if we needs to rolled back first, put that in
2151: */
2152: protected int statusForBeginXactLog() {
2153: return recoveryRollbackFirst() ? RECOVERY_ROLLBACK_FIRST : 0;
2154: }
2155:
2156: /**
2157: * put this into the endXact log record to help recovery,
2158: * nothing to add
2159: */
2160: protected int statusForEndXactLog() {
2161: // during recovery, the beginXact may be logged by a non-standard
2162: // transaction and hence the end xact it log
2163: // must also contain whatever a non-standard Transaction will output.
2164: return savedEndStatus;
2165: }
2166:
2167: /**
2168: Set the transaction to issue pre complete work at postComplete
2169: time, instead of preComplete time. This means that latches
2170: and containers will be held open until after a commit or an abort.
2171: */
2172: void setPostComplete() {
2173: postCompleteMode = true;
2174: }
2175:
2176: /*
2177: * Make the transaction block the online backup.
2178: *
2179: * @param wait if <tt>true</tt>, waits until the transaction
2180: * can block the backup.
2181: * @return <tt>true</tt> if the transaction blocked the
2182: * backup. <tt>false</tt> otherwise.
2183: * @exception StandardException if interrupted while waiting
2184: * for the backup in progress to complete.
2185: */
2186: public boolean blockBackup(boolean wait) throws StandardException {
2187: if (!backupBlocked) {
2188: backupBlocked = xactFactory.blockBackup(wait);
2189: }
2190:
2191: return backupBlocked;
2192: }
2193:
2194: /*
2195: * Unblock the backup, if it was blocked by some operation in
2196: * this transaction. Unblocking is done at commit/abort of this
2197: * transaction.
2198: */
2199: private void unblockBackup() {
2200: if (backupBlocked)
2201: xactFactory.unblockBackup();
2202: backupBlocked = false;
2203: }
2204:
2205: /**
2206: * Check if the transaction is blocking the backup ?
2207: * @return <tt> true </tt> if this transaction is
2208: * blocking the backup, otherwise <tt> false </tt>
2209: */
2210: public boolean isBlockingBackup() {
2211: return backupBlocked;
2212: }
2213:
2214: /*
2215: ** Lock escalation related
2216: */
2217:
2218: /*
2219: ** Methods of Limit
2220: */
2221:
2222: public void reached(Object compatabilitySpace, Object group,
2223: int limit, Enumeration lockList, int lockCount)
2224: throws StandardException {
2225:
2226: // Count row locks by table
2227: Dictionary containers = new java.util.Hashtable();
2228:
2229: for (; lockList.hasMoreElements();) {
2230:
2231: Object plainLock = lockList.nextElement();
2232: if (!(plainLock instanceof RecordHandle)) {
2233: // only interested in rows locks
2234: continue;
2235: }
2236:
2237: ContainerKey ckey = ((RecordHandle) plainLock)
2238: .getContainerId();
2239:
2240: LockCount lc = (LockCount) containers.get(ckey);
2241: if (lc == null) {
2242: lc = new LockCount();
2243: containers.put(ckey, lc);
2244: }
2245: lc.count++;
2246: }
2247:
2248: // Determine the threshold for lock escalation
2249: // based upon our own limit, not the current count
2250: int threshold = limit / (containers.size() + 1);
2251: if (threshold < (limit / 4))
2252: threshold = limit / 4;
2253:
2254: // try to table lock all tables that are above
2255: // this threshold
2256:
2257: boolean didEscalate = false;
2258: for (Enumeration e = containers.keys(); e.hasMoreElements();) {
2259: ContainerKey ckey = (ContainerKey) e.nextElement();
2260:
2261: LockCount lc = (LockCount) containers.get(ckey);
2262:
2263: if (lc.count < threshold) {
2264: continue;
2265: }
2266:
2267: try {
2268: if (openContainer(ckey, new RowLocking3Escalate(
2269: getLockFactory()),
2270: ContainerHandle.MODE_OPEN_FOR_LOCK_ONLY
2271: | ContainerHandle.MODE_FORUPDATE
2272: | ContainerHandle.MODE_LOCK_NOWAIT) != null) {
2273:
2274: didEscalate = true;
2275: }
2276: } catch (StandardException se) {
2277: if (!se.getMessageId().equals(SQLState.LOCK_TIMEOUT)) {
2278: // if it is a timeout then escalate did not happen and
2279: // just fall through.
2280: throw se;
2281: }
2282: }
2283: }
2284:
2285: // Now notify all open containers that an escalation
2286: // event happened. This will cause all open row locked
2287: // containers to re-get their container intent locks,
2288: // those that are now covered by a container lock due
2289: // to the above escalation will move into no locking
2290: // mode. The open containers that were not escalated
2291: // will simply bump the lock count in the lock manager
2292: // and will not have to wait for the lock they already have.
2293: //
2294: // It would be possible to pass in the notifyObservers
2295: // some indication of which tables were escalated
2296: // to reduce the extra lock call for the un-escalated
2297: // containers. This would involve passing the Hashtable
2298: // of escalated containers and having the update method
2299: // of BaseContainerHandle look for its ContainerKey within it.
2300: if (didEscalate) {
2301: notifyObservers(LOCK_ESCALATE);
2302: checkObserverException();
2303: }
2304: }
2305:
2306: /**
2307: * Convert a local transaction to a global transaction.
2308: * <p>
2309: * Must only be called a previous local transaction was created and exists
2310: * in the context. Can only be called if the current transaction is in
2311: * the idle state, and no current global id.
2312: * <p>
2313: * Simply call setTransactionId() which takes care of error checking.
2314: *
2315: * @param format_id the format id part of the Xid - ie. Xid.getFormatId().
2316: * @param global_id the global transaction identifier part of XID - ie.
2317: * Xid.getGlobalTransactionId().
2318: * @param branch_id The branch qualifier of the Xid - ie.
2319: * Xid.getBranchQaulifier()
2320: *
2321: * @exception StandardException Standard exception policy.
2322: **/
2323: public void createXATransactionFromLocalTransaction(int format_id,
2324: byte[] global_id, byte[] branch_id)
2325: throws StandardException {
2326: GlobalXactId gid = new GlobalXactId(format_id, global_id,
2327: branch_id);
2328:
2329: if (((TransactionTable) xactFactory.getTransactionTable())
2330: .findTransactionContextByGlobalId(gid) != null) {
2331: throw StandardException
2332: .newException(SQLState.STORE_XA_XAER_DUPID);
2333: }
2334:
2335: setTransactionId(gid, this .getId());
2336:
2337: if (SanityManager.DEBUG)
2338: SanityManager.ASSERT(myGlobalId != null);
2339: }
2340:
2341: /**
2342: * This method is called to commit the current XA global transaction.
2343: * <p>
2344: * RESOLVE - how do we map to the "right" XAExceptions.
2345: * <p>
2346: *
2347: * @param onePhase If true, the resource manager should use a one-phase
2348: * commit protocol to commit the work done on behalf of
2349: * current xid.
2350: *
2351: * @exception StandardException Standard exception policy.
2352: **/
2353: public void xa_commit(boolean onePhase) throws StandardException {
2354: if (SanityManager.DEBUG)
2355: SanityManager.ASSERT(state != CLOSED);
2356:
2357: if (onePhase) {
2358: if (state == PREPARED) {
2359: throw StandardException
2360: .newException(SQLState.XACT_PROTOCOL_VIOLATION);
2361: }
2362:
2363: prepareCommit(COMMIT_SYNC);
2364:
2365: completeCommit(COMMIT_SYNC);
2366: } else {
2367: if (state != PREPARED) {
2368: throw StandardException
2369: .newException(SQLState.XACT_PROTOCOL_VIOLATION);
2370: }
2371:
2372: prepareCommit(COMMIT_SYNC);
2373:
2374: completeCommit(COMMIT_SYNC);
2375: }
2376:
2377: return;
2378: }
2379:
2380: /**
2381: * This method is called to ask the resource manager to prepare for
2382: * a transaction commit of the transaction specified in xid.
2383: * <p>
2384: *
2385: * @return A value indicating the resource manager's vote on the
2386: * the outcome of the transaction. The possible values
2387: * are: XA_RDONLY or XA_OK. If the resource manager wants
2388: * to roll back the transaction, it should do so by
2389: * throwing an appropriate XAException in the prepare
2390: * method.
2391: *
2392: * @exception StandardException Standard exception policy.
2393: **/
2394: public int xa_prepare() throws StandardException {
2395: if (SanityManager.DEBUG) {
2396: if (state == CLOSED) {
2397: SanityManager.THROWASSERT("state = " + state
2398: + ";myGlobalId = " + myGlobalId);
2399: }
2400:
2401: if (SanityManager.DEBUG_ON("XATrace"))
2402: SanityManager.DEBUG("XATrace",
2403: "in xa_prepare, state is " + state);
2404: }
2405:
2406: if ((state == IDLE) || (state == ACTIVE)) {
2407: abort();
2408: return (Transaction.XA_RDONLY);
2409: } else {
2410: prepareCommit(COMMIT_SYNC | COMMIT_PREPARE
2411: | Transaction.KEEP_LOCKS);
2412: //we set the following variable during prepareCommit
2413: // to what we are doing, so we unset here.
2414: inComplete = null;
2415:
2416: setPrepareState();
2417:
2418: return (Transaction.XA_OK);
2419: }
2420: }
2421:
2422: /**
2423: * rollback the current global transaction.
2424: * <p>
2425: * The given transaction is roll'ed back and it's history is not
2426: * maintained in the transaction table or long term log.
2427: * <p>
2428: *
2429: * @exception StandardException Standard exception policy.
2430: **/
2431: public void xa_rollback() throws StandardException {
2432: if (SanityManager.DEBUG)
2433: SanityManager.ASSERT(state != CLOSED);
2434:
2435: abort();
2436:
2437: return;
2438: }
2439:
2440: /**
2441: * Return the xid as a string.
2442: * <p>
2443: * The virtual lock table depends on this routine returning just the
2444: * local transaction id as a string, even if it is a global transaction.
2445: * Joins between the lock table and the transaction table will not work
2446: * if this routine returns anything other than myId.toString().
2447: * <p>
2448: *
2449: * @return The xid as a string.
2450: *
2451: **/
2452: public String toString() {
2453: // needed for virtual lock table
2454: try {
2455: return (myId.toString());
2456: } catch (Throwable t) {
2457: // using try/catch rather than if (myId != null) because on
2458: // multiple processor sometimes myId was going null after the
2459: // test but before the use.
2460: return ("null");
2461: }
2462: }
2463:
2464: /*
2465: * Get string id of the transaction that would be when the Transaction
2466: * is IN active state.
2467: *
2468: *This transaction "name" will be the same id which is returned in
2469: * the TransactionInfo information if Tx is already in Active State.
2470: * If the Transaction is in IDLE state, Transaction ID is
2471: * incremented when getActiveStateTxIdString() on raw transaction is called,
2472: * instead of the Tx ID being incremented when Transaction gets into
2473: * active state. The reason for incrementing the Tx ID earlier than when Tx
2474: * is actually goes into active state is some debug statement cases like
2475: * log statement text. SQL statements are wriited to log before they are
2476: * actully executed; In such cases we would like to display the actual TX ID on which
2477: * locks are acquired when the statement is executed.
2478: * @return The a string which identifies the transaction.
2479: */
2480: public String getActiveStateTxIdString() {
2481: if (!justCreated && state == IDLE) {
2482: // TransactionTable needs this
2483: xactFactory.setNewTransactionId(myId, this );
2484: //mark as if this tx is just created , so that setActiveState()
2485: //does not increment the transaction id number.
2486: justCreated = true;
2487: }
2488:
2489: return toString();
2490: }
2491:
2492: /* package */
2493: String getState() {
2494: int localState;
2495:
2496: // synchronized(this) -- int assignment, implicit synchronization
2497: // due to atomic action
2498: {
2499: localState = state;
2500: }
2501:
2502: switch (localState) {
2503: case CLOSED:
2504: return "CLOSED";
2505: case IDLE:
2506: return "IDLE";
2507: case ACTIVE:
2508: case UPDATE:
2509: return "ACTIVE";
2510:
2511: case PREPARED:
2512: return "PREPARED";
2513: }
2514: return null;
2515: }
2516:
2517: public String getTransName() {
2518: return transName;
2519: }
2520:
2521: public void setTransName(String name) {
2522: transName = name;
2523: }
2524:
2525: /**
2526: Is the transaction in rollforward recovery
2527: */
2528: public boolean inRollForwardRecovery() {
2529: return logFactory.inRFR();
2530: }
2531:
2532: /**
2533: perform a checkpoint during rollforward recovery
2534: */
2535: public void checkpointInRollForwardRecovery(LogInstant cinstant,
2536: long redoLWM) throws StandardException {
2537: logFactory.checkpointInRFR(cinstant, redoLWM, dataFactory);
2538: }
2539:
2540: }
2541:
2542: class LockCount {
2543: int count;
2544: }
|