--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.store.raw.xact.TransactionTableEntry\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.store.raw.xact;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.services.io.Formatable;\r
+import org.apache.derby.iapi.services.io.FormatIdUtil;\r
+import org.apache.derby.iapi.services.io.StoredFormatIds;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+import org.apache.derby.iapi.sql.conn.StatementContext;\r
+import org.apache.derby.iapi.store.access.TransactionInfo;\r
+import org.apache.derby.iapi.store.raw.GlobalTransactionId;\r
+import org.apache.derby.iapi.store.raw.xact.TransactionId;\r
+import org.apache.derby.iapi.store.raw.log.LogInstant;\r
+\r
+import java.io.ObjectOutput;\r
+import java.io.ObjectInput;\r
+import java.io.IOException;\r
+\r
+/**\r
+ Transaction table entry is used to store all relavent information of a\r
+ transaction into the transaction table for the use of checkpoint, recovery,\r
+ Transaction management during Quiesce state, and for dumping transaction table. Only works\r
+ with the following classes: TransactionTable, XactFactory, Xact\r
+ <BR>\r
+ During run time, whenever any transaction is started, it is put into the\r
+ transaction table. Whenever any transaction is closed, it is removed from\r
+ the transaction table.\r
+\r
+\r
+*/\r
+\r
+public class TransactionTableEntry implements Formatable, TransactionInfo, Cloneable\r
+{\r
+ // These fields are only populated if this TTE has been read in from the\r
+ // log. Otherwise, they are gotten from the transaction object myxact.\r
+ private TransactionId xid;\r
+ private GlobalTransactionId gid;\r
+ private LogInstant firstLog;\r
+ private LogInstant lastLog;\r
+\r
+ // this field is always present - it is 0 for read only transaction, this\r
+ // is a copy of the status from the Xact (the copy is necessary as during\r
+ // recovery the Xact is shared by all transaction table entries during\r
+ // redo and undo).\r
+ private int transactionStatus;\r
+\r
+ // fields useful for returning transaction information if read from \r
+ // transaction log during recovery\r
+ private transient Xact myxact; \r
+ private transient boolean update;\r
+ private transient boolean recovery; // is this a transaction read \r
+ // from the log during recovery?\r
+ private transient boolean needExclusion; // in a quiesce state , this \r
+ // transaction needs to be \r
+ // barred from activation \r
+ // during quiesce state\r
+\r
+ private boolean isClone; // am I a clone made for the \r
+ // TransactionVTI?\r
+\r
+ private transient LanguageConnectionContext lcc;\r
+\r
+\r
+ /* package */\r
+ // entry attribute\r
+ static final int UPDATE = 0x1;\r
+ static final int RECOVERY = 0x2;\r
+ static final int EXCLUDE = 0x4;\r
+\r
+ TransactionTableEntry(\r
+ Xact xact, \r
+ TransactionId tid, \r
+ int status, \r
+ int attribute)\r
+ {\r
+ myxact = xact;\r
+ xid = tid;\r
+ transactionStatus = status;\r
+\r
+ update = (attribute & UPDATE) != 0;\r
+ needExclusion = (attribute & EXCLUDE) != 0;\r
+ recovery = (attribute & RECOVERY) != 0;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(tid != null, "tid is null");\r
+ if (update && xact.getFirstLogInstant() == null)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "update transaction has firstLog = null");\r
+ }\r
+\r
+\r
+ /*\r
+ if (!update && xact.getFirstLogInstant() != null)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "read only transaction has firstLog = " + \r
+ xact.getFirstLogInstant());\r
+ }\r
+ */\r
+ }\r
+\r
+ // Normally, we don't need to remember the gid, firstLog and lastLog\r
+ // because myxact will have the same information. However, in\r
+ // recovery, there is only one transaction taking on different identity\r
+ // as the log is replayed. Then each transaction table entry has keep\r
+ // its own identity and not rely on myxact. These recovery\r
+ // transactions are materialized in the transaction table via a\r
+ // readObject in the checkpoint log record, or are added by\r
+ // addUpdateTransaction when the log is scanned.\r
+ if (recovery)\r
+ {\r
+ // make a copy of everything\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(update, "recovery but not update");\r
+\r
+ if (tid != xact.getId())\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "adding a update transaction during recovery " + \r
+ " but the tids doesn't match" +\r
+ tid + " " + xact.getId());\r
+ }\r
+ }\r
+\r
+ gid = xact.getGlobalId();\r
+ firstLog = xact.getFirstLogInstant();\r
+ lastLog = xact.getLastLogInstant();\r
+ }\r
+ }\r
+\r
+ /*\r
+ * Formatable methods\r
+ */\r
+ public TransactionTableEntry()\r
+ { }\r
+\r
+ public void writeExternal(ObjectOutput out) throws IOException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(!recovery, "writing out a recovery transaction");\r
+ SanityManager.ASSERT(update, "writing out read only transaction");\r
+ SanityManager.ASSERT(myxact.getFirstLogInstant() != null, \r
+ "myxact.getFirstLogInstant is null");\r
+ SanityManager.ASSERT(!isClone, "cannot write out a clone");\r
+ }\r
+\r
+ // Why is is safe to access first and last log instant in myxact while\r
+ // this is happening? Because we only writes out update transaction\r
+ // during run time. When a read only transactions becomes an update\r
+ // transaction , or when an update transaction commits, the beginXact\r
+ // and endXact log record's doMe method will try to change the\r
+ // transaction table entry's state to updat and non-update\r
+ // respectively. That change needs to go thru the transaction table\r
+ // which is mutually exclusive to writing out the transaction table.\r
+ // Since we are only looking at update transactions and it is "stuck"\r
+ // in update state in the middle of a TransactionTable.writeExternal\r
+ // call, all the fields we access in myxact is stable (actually the xid\r
+ // is also stable but we already have it).\r
+ //\r
+ out.writeObject(xid);\r
+ out.writeObject(myxact.getGlobalId());\r
+ out.writeObject(myxact.getFirstLogInstant());\r
+ out.writeObject(myxact.getLastLogInstant());\r
+ out.writeInt(transactionStatus);\r
+ }\r
+\r
+ public void readExternal(ObjectInput in) \r
+ throws ClassNotFoundException, IOException\r
+ {\r
+ // the only time a transaction table entry is written out is to the\r
+ // log, so this must be read in during recovery.\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isClone, "cannot write out a clone");\r
+\r
+ xid = (TransactionId)in.readObject();\r
+ gid = (GlobalTransactionId)in.readObject();\r
+ firstLog = (LogInstant)in.readObject();\r
+ lastLog = (LogInstant)in.readObject();\r
+ transactionStatus = in.readInt();\r
+ update = true;\r
+ recovery = true;\r
+ needExclusion = true;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(xid != null, "read in transaction table entry with null id");\r
+ SanityManager.ASSERT(firstLog != null, "read in transaction table entry with firstLog");\r
+ }\r
+\r
+ }\r
+\r
+\r
+ // set my transaction instance variable for a recovery transaction\r
+ void setXact(Xact xact)\r
+ {\r
+ /*\r
+ RESOLVE (mikem) - prepared transactions now call setXact() when they are\r
+ not in recovery.\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(recovery, \r
+ "setting non-recovery transaction table entry xact");\r
+ SanityManager.ASSERT(!isClone, "cannot setXact with a clone");\r
+ }\r
+ */\r
+ myxact = xact;\r
+\r
+ }\r
+\r
+ /**\r
+ Return my format identifier.\r
+ */\r
+ public int getTypeFormatId() {\r
+ return StoredFormatIds.RAW_STORE_TRANSACTION_TABLE_ENTRY;\r
+ }\r
+\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ StringBuffer str = new StringBuffer(500).\r
+ append("Xid=").append(getXid()).\r
+ append(" gid=").append(getGid()).\r
+ append(" firstLog=").append(getFirstLog()).\r
+ append(" lastLog=").append(getLastLog()).\r
+ append(" transactionStatus=").append(transactionStatus).\r
+ append(" myxact=").append(myxact).\r
+ append(" update=").append(update).\r
+ append(" recovery=").append(recovery).\r
+ append(" prepare=").append(isPrepared()).\r
+ append(" needExclusion=").append(needExclusion).\r
+ append("\n");\r
+ return str.toString();\r
+ }\r
+ else\r
+ return null;\r
+ }\r
+\r
+ void updateTransactionStatus(Xact xact, int status, int attribute)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(myxact == xact,\r
+ "update transaction status for wrong xact");\r
+ SanityManager.ASSERT(!isClone, "cannot change a clone");\r
+ }\r
+\r
+ this.update = (attribute & UPDATE) != 0;\r
+ }\r
+\r
+ void removeUpdateTransaction()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isClone, "cannot change a clone");\r
+\r
+ this.update = false;\r
+ transactionStatus = 0;\r
+ \r
+ }\r
+\r
+ void unsetRecoveryStatus()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isClone, "cannot change a clone");\r
+\r
+ // RESOLVE (mikem) - this is kind of ugly. move to a better place?\r
+\r
+ firstLog = null;\r
+\r
+ this.recovery = false;\r
+ }\r
+\r
+ void prepareTransaction()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isClone, "cannot change a clone");\r
+\r
+ transactionStatus |= Xact.END_PREPARED;\r
+ }\r
+\r
+ /**************************************************************************\r
+ * get instance variables\r
+ **************************************************************************\r
+ */\r
+\r
+ TransactionId getXid()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(xid != null, "TTE with null xid");\r
+ SanityManager.ASSERT(!isClone, "cannot call method with a clone");\r
+ }\r
+\r
+ return xid;\r
+ }\r
+\r
+ public final GlobalTransactionId getGid()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isClone, "cannot call method with a clone");\r
+\r
+ if (gid != null)\r
+ return gid;\r
+\r
+ if (myxact != null)\r
+ return myxact.getGlobalId();\r
+\r
+ return null;\r
+ }\r
+\r
+ LogInstant getFirstLog()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(!isClone, "cannot call method with a clone");\r
+\r
+ if (recovery)\r
+ {\r
+ SanityManager.ASSERT(\r
+ firstLog != null, \r
+ "a recovery transaction with a null firstLog");\r
+ }\r
+ else\r
+ {\r
+ SanityManager.ASSERT(\r
+ firstLog == null, \r
+ "a normal transaction with a non-null firstLog" +\r
+ "myxact.getFirstLogInstant() = " + myxact.getFirstLogInstant());\r
+ }\r
+ }\r
+\r
+ if (firstLog != null)\r
+ return firstLog;\r
+\r
+ if (myxact != null)\r
+ return myxact.getFirstLogInstant();\r
+\r
+ return null;\r
+ }\r
+\r
+ LogInstant getLastLog()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isClone, "cannot call method with a clone");\r
+\r
+ if (lastLog != null)\r
+ return lastLog;\r
+\r
+ if (myxact != null)\r
+ return myxact.getLastLogInstant();\r
+\r
+ return null;\r
+ }\r
+\r
+ public final Xact getXact()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isClone, "cannot call method with a clone");\r
+\r
+ return myxact;\r
+ }\r
+\r
+ int getTransactionStatus()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isClone, "cannot call method with a clone");\r
+\r
+ return transactionStatus;\r
+ }\r
+\r
+ boolean isUpdate()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isClone, "cannot call method with a clone");\r
+\r
+ return update;\r
+ }\r
+\r
+ boolean isRecovery()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isClone, "cannot call method with a clone");\r
+\r
+ return recovery;\r
+ }\r
+\r
+ boolean isPrepared()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isClone, "cannot call method with a clone");\r
+\r
+ return((transactionStatus & Xact.END_PREPARED) != 0);\r
+ }\r
+\r
+\r
+\r
+\r
+ public boolean needExclusion()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!isClone, "cannot call method with a clone");\r
+\r
+ return needExclusion;\r
+ }\r
+\r
+ /**\r
+ Methods of TransactionInfo\r
+ */\r
+ public String getTransactionIdString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(\r
+ !recovery, "trying to display recovery transaction");\r
+ SanityManager.ASSERT(myxact != null, "my xact is null");\r
+ SanityManager.ASSERT(isClone, "Should only call method on a clone");\r
+ }\r
+\r
+ TransactionId t = myxact.getIdNoCheck();\r
+ return (t == null) ? "CLOSED" : t.toString();\r
+ }\r
+\r
+ public String getGlobalTransactionIdString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(\r
+ !recovery, "trying to display recovery transaction");\r
+ SanityManager.ASSERT(myxact != null, "my xact is null");\r
+ SanityManager.ASSERT(isClone, "Should only call method on a clone");\r
+ }\r
+\r
+ GlobalTransactionId gid = myxact.getGlobalId();\r
+ return (gid == null) ? null : gid.toString();\r
+ }\r
+\r
+ public String getUsernameString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(isClone, "Should only call method on a clone");\r
+\r
+ getlcc();\r
+ return (lcc == null) ? null : lcc.getAuthorizationId();\r
+ }\r
+\r
+ public String getTransactionTypeString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(isClone, "Should only call method on a clone");\r
+\r
+ if (myxact == null)\r
+ return null;\r
+ else if (myxact.getTransName() != null)\r
+ return myxact.getTransName();\r
+ else\r
+ return myxact.getContextId();\r
+ }\r
+\r
+ public String getTransactionStatusString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(isClone, "Should only call method on a clone");\r
+\r
+ return (myxact == null) ? null : myxact.getState();\r
+ }\r
+\r
+ public String getStatementTextString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(isClone, "Should only call method on a clone");\r
+\r
+ getlcc();\r
+ if (lcc != null)\r
+ {\r
+ StatementContext sc = lcc.getStatementContext();\r
+ if (sc != null)\r
+ return sc.getStatementText() ;\r
+ }\r
+ return null;\r
+\r
+ }\r
+\r
+ public String getFirstLogInstantString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(isClone, "Should only call method on a clone");\r
+\r
+ LogInstant logInstant = \r
+ (myxact == null) ? null : myxact.getFirstLogInstant();\r
+\r
+ return (logInstant == null) ? null : logInstant.toString();\r
+\r
+ } \r
+\r
+ private void getlcc()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(isClone, "Should only call method on a clone");\r
+\r
+ if (lcc == null && myxact != null && myxact.xc != null)\r
+ {\r
+ XactContext xc = myxact.xc;\r
+\r
+ lcc = (LanguageConnectionContext)\r
+ xc.getContextManager().getContext(\r
+ LanguageConnectionContext.CONTEXT_ID);\r
+ }\r
+ }\r
+\r
+ /**\r
+ Cloneable\r
+ */\r
+ protected Object clone()\r
+ {\r
+ try \r
+ {\r
+ Object c = super.clone();\r
+ ((TransactionTableEntry)c).isClone = true;\r
+\r
+ return c;\r
+ }\r
+ catch (CloneNotSupportedException e) \r
+ {\r
+ // this should not happen, we are cloneable\r
+ if (SanityManager.DEBUG) \r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "TransactionTableEntry cloneable but throws " +\r
+ "CloneNotSupportedException", e);\r
+ }\r
+ return null;\r
+ } \r
+ }\r
+}\r