--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.store.raw.xact.XactFactory\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.reference.Property;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.services.context.ContextService;\r
+import org.apache.derby.iapi.services.context.ContextManager;\r
+import org.apache.derby.iapi.services.daemon.DaemonService;\r
+import org.apache.derby.iapi.services.daemon.Serviceable;\r
+import org.apache.derby.iapi.services.locks.CompatibilitySpace;\r
+import org.apache.derby.iapi.services.locks.LockFactory;\r
+import org.apache.derby.iapi.services.monitor.ModuleControl;\r
+import org.apache.derby.iapi.services.monitor.ModuleSupportable;\r
+import org.apache.derby.iapi.services.monitor.Monitor;\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.uuid.UUIDFactory;\r
+import org.apache.derby.catalog.UUID;\r
+\r
+import org.apache.derby.iapi.store.access.AccessFactoryGlobals;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+import org.apache.derby.iapi.store.access.TransactionInfo;\r
+\r
+import org.apache.derby.iapi.store.access.AccessFactory;\r
+\r
+import org.apache.derby.iapi.store.access.xa.XAResourceManager;\r
+\r
+import org.apache.derby.iapi.store.raw.LockingPolicy;\r
+import org.apache.derby.iapi.store.raw.GlobalTransactionId;\r
+import org.apache.derby.iapi.store.raw.RawStoreFactory;\r
+import org.apache.derby.iapi.store.raw.Transaction;\r
+\r
+import org.apache.derby.iapi.store.raw.data.DataFactory;\r
+\r
+import org.apache.derby.iapi.store.raw.log.LogFactory;\r
+import org.apache.derby.iapi.store.raw.log.LogInstant;\r
+\r
+import org.apache.derby.iapi.store.raw.xact.RawTransaction;\r
+import org.apache.derby.iapi.store.raw.xact.TransactionFactory;\r
+import org.apache.derby.iapi.store.raw.xact.TransactionId;\r
+import org.apache.derby.impl.store.raw.xact.XactXAResourceManager;\r
+\r
+import org.apache.derby.iapi.types.DataValueFactory;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+\r
+import java.util.Enumeration;\r
+import java.util.Properties;\r
+import java.util.Hashtable;\r
+\r
+public class XactFactory implements TransactionFactory, ModuleControl, ModuleSupportable\r
+{\r
+ protected static final String USER_CONTEXT_ID = "UserTransaction";\r
+ protected static final String NESTED_READONLY_USER_CONTEXT_ID = \r
+ "NestedRawReadOnlyUserTransaction";\r
+ protected static final String NESTED_UPDATE_USER_CONTEXT_ID = \r
+ "NestedRawUpdateUserTransaction";\r
+ protected static final String INTERNAL_CONTEXT_ID = "InternalTransaction";\r
+ protected static final String NTT_CONTEXT_ID = "NestedTransaction";\r
+\r
+ /*\r
+ ** Fields\r
+ */\r
+\r
+ protected DaemonService rawStoreDaemon;\r
+\r
+ private UUIDFactory uuidFactory;\r
+ protected ContextService contextFactory;\r
+ protected LockFactory lockFactory;\r
+ protected LogFactory logFactory;\r
+ protected DataFactory dataFactory;\r
+ protected DataValueFactory dataValueFactory;\r
+ protected RawStoreFactory rawStoreFactory;\r
+\r
+ public TransactionTable ttab;\r
+ private long tranId;\r
+ private LockingPolicy[][] lockingPolicies = new LockingPolicy[3][6];\r
+\r
+ private boolean inCreateNoLog = false; // creating database, no logging\r
+\r
+ private XAResourceManager xa_resource;\r
+\r
+ private Object backupSemaphore = new Object();\r
+ private long backupBlockingOperations = 0;\r
+ private boolean inBackup = false;\r
+\r
+ /*\r
+ ** Constructor\r
+ */\r
+\r
+ public XactFactory() {\r
+ super();\r
+ }\r
+\r
+ /*\r
+ ** Methods of ModuleControl\r
+ */\r
+ public boolean canSupport(Properties startParams) {\r
+ return true;\r
+ }\r
+\r
+ public void boot(boolean create, Properties properties)\r
+ throws StandardException\r
+ {\r
+\r
+ uuidFactory = Monitor.getMonitor().getUUIDFactory();\r
+\r
+ /*\r
+ dataValueFactory = (DataValueFactory)\r
+ Monitor.findServiceModule(\r
+ this,\r
+ org.apache.derby.iapi.reference.ClassName.DataValueFactory);\r
+ */\r
+ // if datafactory has not been booted yet, try now. This can\r
+ // happen in the unit tests. Usually it is booted before store\r
+ // booting is called.\r
+ dataValueFactory = (DataValueFactory) \r
+ Monitor.bootServiceModule(\r
+ create, \r
+ this,\r
+ org.apache.derby.iapi.reference.ClassName.DataValueFactory, \r
+ properties);\r
+ \r
+\r
+ contextFactory = ContextService.getFactory();\r
+\r
+ lockFactory = \r
+ (LockFactory) Monitor.bootServiceModule(false, this,\r
+ org.apache.derby.iapi.reference.Module.LockFactory, properties);\r
+\r
+ \r
+ // adding entries to locking policy table which means we support that\r
+ // level of concurrency.\r
+ lockingPolicies[LockingPolicy.MODE_NONE]\r
+ [TransactionController.ISOLATION_NOLOCK] =\r
+ new NoLocking();\r
+\r
+ lockingPolicies[LockingPolicy.MODE_RECORD]\r
+ [TransactionController.ISOLATION_NOLOCK] =\r
+ new NoLocking();\r
+ lockingPolicies[LockingPolicy.MODE_RECORD]\r
+ [TransactionController.ISOLATION_READ_UNCOMMITTED] =\r
+ new RowLocking1(lockFactory);\r
+ lockingPolicies[LockingPolicy.MODE_RECORD]\r
+ [TransactionController.ISOLATION_READ_COMMITTED] =\r
+ new RowLocking2(lockFactory);\r
+ lockingPolicies[LockingPolicy.MODE_RECORD]\r
+ [TransactionController.ISOLATION_READ_COMMITTED_NOHOLDLOCK] =\r
+ new RowLocking2nohold(lockFactory);\r
+ lockingPolicies[LockingPolicy.MODE_RECORD]\r
+ [TransactionController.ISOLATION_REPEATABLE_READ] =\r
+ new RowLockingRR(lockFactory);\r
+ lockingPolicies[LockingPolicy.MODE_RECORD]\r
+ [TransactionController.ISOLATION_SERIALIZABLE] =\r
+ new RowLocking3(lockFactory);\r
+\r
+ lockingPolicies[LockingPolicy.MODE_CONTAINER]\r
+ [TransactionController.ISOLATION_NOLOCK] =\r
+ new NoLocking();\r
+\r
+ // note that current implementation of read uncommitted still gets\r
+ // container and container intent locks to prevent concurrent ddl. Thus\r
+ // the read uncommitted containerlocking implementation is the same as\r
+ // the read committed implementation. Future customer requests may \r
+ // force us to change this - we will then have to figure out how to\r
+ // handle a table being dropped while a read uncommitted scanner is\r
+ // reading it - currently we just block that from happening.\r
+ lockingPolicies[LockingPolicy.MODE_CONTAINER]\r
+ [TransactionController.ISOLATION_READ_UNCOMMITTED] =\r
+ new ContainerLocking2(lockFactory);\r
+ lockingPolicies[LockingPolicy.MODE_CONTAINER]\r
+ [TransactionController.ISOLATION_READ_COMMITTED] =\r
+ new ContainerLocking2(lockFactory);\r
+ lockingPolicies[LockingPolicy.MODE_CONTAINER]\r
+ [TransactionController.ISOLATION_READ_COMMITTED_NOHOLDLOCK] =\r
+ new ContainerLocking2(lockFactory);\r
+ lockingPolicies[LockingPolicy.MODE_CONTAINER]\r
+ [TransactionController.ISOLATION_REPEATABLE_READ] =\r
+ new ContainerLocking3(lockFactory);\r
+ lockingPolicies[LockingPolicy.MODE_CONTAINER]\r
+ [TransactionController.ISOLATION_SERIALIZABLE] =\r
+ new ContainerLocking3(lockFactory);\r
+\r
+\r
+ if (create)\r
+ {\r
+ ttab = new TransactionTable();\r
+\r
+ String noLog =\r
+ properties.getProperty(Property.CREATE_WITH_NO_LOG);\r
+\r
+ inCreateNoLog = (noLog != null && Boolean.valueOf(noLog).booleanValue());\r
+\r
+ }\r
+ }\r
+\r
+ public void stop() {\r
+\r
+ if (rawStoreDaemon != null)\r
+ rawStoreDaemon.stop();\r
+\r
+ }\r
+\r
+ /*\r
+ ** Methods of TransactionFactory\r
+ */\r
+\r
+ /**\r
+ Get the LockFactory to use with this store.\r
+ */\r
+ public LockFactory getLockFactory() {\r
+ return lockFactory;\r
+ }\r
+\r
+\r
+ /**\r
+ Database creation finished\r
+ @exception StandardException standard Derby error policy\r
+ */\r
+ public void createFinished() throws StandardException\r
+ {\r
+ if (!inCreateNoLog) \r
+ {\r
+ throw StandardException.newException(SQLState.XACT_CREATE_NO_LOG);\r
+ }\r
+\r
+ // make sure there is no active update transaction\r
+ if (ttab.hasActiveUpdateTransaction())\r
+ {\r
+ throw StandardException.newException(SQLState.XACT_CREATE_NO_LOG);\r
+ }\r
+\r
+ inCreateNoLog = false;\r
+ }\r
+\r
+ /**\r
+ * Common work done to create local or global transactions.\r
+ *\r
+ * @param rsf the raw store factory creating this xact.\r
+ * @param cm the current context manager to associate the xact with.\r
+ * @param compatibilitySpace \r
+ * if null, use the transaction being created, else if \r
+ * non-null use this compatibilitySpace.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ private RawTransaction startCommonTransaction(\r
+ RawStoreFactory rsf,\r
+ ContextManager cm,\r
+ boolean readOnly,\r
+ CompatibilitySpace compatibilitySpace,\r
+ String xact_context_id,\r
+ String transName,\r
+ boolean excludeMe)\r
+ throws StandardException\r
+ {\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (rawStoreFactory != null)\r
+ SanityManager.ASSERT(\r
+ rawStoreFactory == rsf, "raw store factory different");\r
+\r
+ SanityManager.ASSERT(\r
+ cm == contextFactory.getCurrentContextManager());\r
+ }\r
+\r
+ Xact xact = \r
+ new Xact(\r
+ this, logFactory, dataFactory, dataValueFactory, \r
+ readOnly, compatibilitySpace);\r
+\r
+ xact.setTransName(transName);\r
+ pushTransactionContext(cm, xact_context_id, xact,\r
+ false /* abortAll */,\r
+ rsf,\r
+ excludeMe /* excludeMe during quiesce state */);\r
+ return xact;\r
+ }\r
+\r
+ public RawTransaction startTransaction(\r
+ RawStoreFactory rsf,\r
+ ContextManager cm,\r
+ String transName)\r
+ throws StandardException\r
+ {\r
+ return(startCommonTransaction(\r
+ rsf, cm, false, null, USER_CONTEXT_ID, transName, true));\r
+ }\r
+\r
+ public RawTransaction startNestedReadOnlyUserTransaction(\r
+ RawStoreFactory rsf,\r
+ CompatibilitySpace compatibilitySpace,\r
+ ContextManager cm,\r
+ String transName)\r
+ throws StandardException\r
+ {\r
+ return(startCommonTransaction(\r
+ rsf, cm, true, compatibilitySpace, \r
+ NESTED_READONLY_USER_CONTEXT_ID, transName, false));\r
+ }\r
+\r
+ public RawTransaction startNestedUpdateUserTransaction(\r
+ RawStoreFactory rsf,\r
+ ContextManager cm,\r
+ String transName)\r
+ throws StandardException\r
+ {\r
+ return(startCommonTransaction(\r
+ rsf, cm, false, null, \r
+ NESTED_UPDATE_USER_CONTEXT_ID, transName, true));\r
+ }\r
+\r
+ public RawTransaction startGlobalTransaction(\r
+ RawStoreFactory rsf,\r
+ ContextManager cm,\r
+ int format_id,\r
+ byte[] global_id,\r
+ byte[] branch_id)\r
+ throws StandardException\r
+ {\r
+ GlobalXactId gid = new GlobalXactId(format_id, global_id, branch_id);\r
+\r
+ if (ttab.findTransactionContextByGlobalId(gid) != null)\r
+ {\r
+ throw StandardException.newException(SQLState.STORE_XA_XAER_DUPID);\r
+ }\r
+\r
+ RawTransaction xact = \r
+ startCommonTransaction(\r
+ rsf, cm, false, null, \r
+ USER_CONTEXT_ID, AccessFactoryGlobals.USER_TRANS_NAME, true);\r
+\r
+ xact.setTransactionId(gid, xact.getId());\r
+\r
+ return(xact);\r
+ }\r
+\r
+\r
+\r
+ public RawTransaction findUserTransaction(\r
+ RawStoreFactory rsf,\r
+ ContextManager contextMgr,\r
+ String transName)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(\r
+ contextMgr == contextFactory.getCurrentContextManager(),\r
+ "passed in context mgr not the same as current context mgr");\r
+\r
+ if (rawStoreFactory != null)\r
+ SanityManager.ASSERT(\r
+ rawStoreFactory == rsf, "raw store factory different");\r
+ }\r
+\r
+ XactContext xc = (XactContext)contextMgr.getContext(USER_CONTEXT_ID);\r
+ if (xc == null)\r
+ return startTransaction(rsf, contextMgr, transName);\r
+ else\r
+ return xc.getTransaction();\r
+ }\r
+\r
+\r
+ public RawTransaction startNestedTopTransaction(RawStoreFactory rsf, ContextManager cm)\r
+ throws StandardException\r
+ {\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (rawStoreFactory != null)\r
+ SanityManager.ASSERT(\r
+ rawStoreFactory == rsf, "raw store factory different");\r
+ }\r
+\r
+ Xact xact = \r
+ new Xact(\r
+ this, logFactory, dataFactory, dataValueFactory, false, null);\r
+\r
+ // hold latches etc. past commit in NTT\r
+ xact.setPostComplete();\r
+ pushTransactionContext(cm, NTT_CONTEXT_ID, xact, \r
+ true /* abortAll */,\r
+ rsf, \r
+ true /* excludeMe during quiesce state*/);\r
+ return xact;\r
+ }\r
+\r
+ public RawTransaction startInternalTransaction(RawStoreFactory rsf, ContextManager cm) \r
+ throws StandardException \r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (rawStoreFactory != null)\r
+ SanityManager.ASSERT(\r
+ rawStoreFactory == rsf, "raw store factory different");\r
+ }\r
+\r
+\r
+ Xact xact = \r
+ new InternalXact(this, logFactory, dataFactory, dataValueFactory);\r
+\r
+ pushTransactionContext(cm, INTERNAL_CONTEXT_ID, xact, \r
+ true /* abortAll*/,\r
+ rsf,\r
+ true /* excludeMe during quiesce state */);\r
+ return xact;\r
+ }\r
+\r
+ /*\r
+ * the following TransactionFactory methods are to support recovery and\r
+ * should only be used by recovery!\r
+ */\r
+\r
+ /**\r
+ Find the TransactionTableEntry with the given ID and make the passed in\r
+ transaction assume the identity and properties of that\r
+ TransactionTableEntry.\r
+ Used in recovery only.\r
+ */\r
+ public boolean findTransaction(TransactionId id, RawTransaction tran)\r
+ {\r
+ return ttab.findAndAssumeTransaction(id, tran);\r
+ }\r
+\r
+\r
+ /**\r
+ Rollback all active transactions that has updated the raw store.\r
+ Use the recovery Transaction that is passed in to do all the work.\r
+ Used in recovery only.\r
+\r
+ <P>\r
+ Transactions are rolled back in the following order:\r
+ <OL>\r
+ <LI>internal transactions in reversed beginXact chronological order,\r
+ <LI>all other transactions in reversed beginXact chronological order,\r
+ </NL>\r
+\r
+ @param recoveryTransaction use this transaction to do all the user \r
+ transaction work\r
+\r
+ @exception StandardException any exception thrown during rollback\r
+ */\r
+ public void rollbackAllTransactions(\r
+ RawTransaction recoveryTransaction,\r
+ RawStoreFactory rsf) \r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (rawStoreFactory != null)\r
+ SanityManager.ASSERT(\r
+ rawStoreFactory == rsf, "raw store factory different");\r
+\r
+ SanityManager.ASSERT(\r
+ recoveryTransaction != null, "recovery transaction null");\r
+ }\r
+\r
+ int irbcount = 0;\r
+\r
+ // First undo internal transactions if there is any\r
+ if (ttab.hasRollbackFirstTransaction())\r
+ {\r
+ RawTransaction internalTransaction = startInternalTransaction(rsf,\r
+ recoveryTransaction.getContextManager());\r
+\r
+ // make this transaction be aware that it is being used by recovery\r
+ internalTransaction.recoveryTransaction();\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(\r
+ internalTransaction.handlesPostTerminationWork() == false,\r
+ "internal recovery xact handles post termination work");\r
+\r
+ while(ttab.getMostRecentRollbackFirstTransaction(\r
+ internalTransaction))\r
+ {\r
+ irbcount++;\r
+ internalTransaction.abort();\r
+ }\r
+\r
+ internalTransaction.close();\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(\r
+ ttab.hasRollbackFirstTransaction() == false,\r
+ "cant rollback user xacts with existing active internal xacts");\r
+ }\r
+\r
+ int rbcount = 0;\r
+\r
+ // recoveryTransacion assumes the identity of the most recent xact\r
+ while(ttab.getMostRecentTransactionForRollback(recoveryTransaction))\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(\r
+ recoveryTransaction.handlesPostTerminationWork() == false,\r
+ "recovery transaction handles post termination work");\r
+ }\r
+\r
+ rbcount++;\r
+ recoveryTransaction.abort();\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (rbcount > 0 || irbcount > 0)\r
+ {\r
+ // RESOLVE: put this in the log trace\r
+ // System.out.println(\r
+ // "Recovery rolled back " + irbcount + \r
+ // " internal transactions,"\r
+ // + rbcount + " user transactions");\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ Run through all prepared transactions known to this factory \r
+ and restore their state such that they remain after recovery, and\r
+ can be found and handled by a XA transaction manager. This includes\r
+ creating a context manager for each, pushing a xact context, and\r
+ reclaiming update locks on all data changed by the transaction.\r
+\r
+ Expected to be called just after the redo and undo recovery loops, \r
+ where the transaction table should be empty except for prepared\r
+ xacts.\r
+\r
+ Used only in recovery.\r
+\r
+ @exception StandardException Derby Standard Error policy\r
+ */\r
+ public void handlePreparedXacts(\r
+ RawStoreFactory rsf)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+\r
+ if (rawStoreFactory != null)\r
+ SanityManager.ASSERT(\r
+ rawStoreFactory == rsf, "raw store factory different");\r
+ }\r
+\r
+ int prepared_count = 0;\r
+\r
+ if (ttab.hasPreparedRecoveredXact())\r
+ {\r
+ // if there any prepared xacts \r
+\r
+ // At this point recovery has used one context and one transaction\r
+ // to deal with all transactions. Prepared transactions are to\r
+ // be left in the transaction table, but the must have real and\r
+ // separate CM's and transactions associated with them.\r
+\r
+ // save old context. Errors may go to funky contexts (the new\r
+ // context we created to bring the prepared transaction into the\r
+ // real world after recovery) after we switch contexts, but any \r
+ // error we get at this point is going to shut down the db.\r
+\r
+ while (true)\r
+ {\r
+ // allocate new context and associate new xact with it.\r
+ ContextManager cm = contextFactory.newContextManager();\r
+ contextFactory.setCurrentContextManager(cm);\r
+\r
+ try {\r
+ RawTransaction rawtran = \r
+ startTransaction(\r
+ rawStoreFactory, cm, \r
+ AccessFactoryGlobals.USER_TRANS_NAME);\r
+\r
+ if (ttab.getMostRecentPreparedRecoveredXact(rawtran))\r
+ {\r
+ // found a prepared xact. The reprepare() call will \r
+ // accumulate locks, and change the transaction table entry\r
+ // to not be "in-recovery" so that it won't show up again.\r
+ rawtran.reprepare();\r
+\r
+ if (SanityManager.DEBUG)\r
+ prepared_count++;\r
+ }\r
+ else\r
+ {\r
+ // get rid of last transaction allocated.\r
+ rawtran.destroy();\r
+ break;\r
+ }\r
+ }\r
+ finally\r
+ {\r
+ contextFactory.resetCurrentContextManager(cm);\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // RESOLVE - need to only do this under a debug flag.\r
+ // SanityManager.DEBUG_PRINT("",\r
+ // "Recovery re-prepared " + prepared_count + " xa transactions.");\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ Get the earliest log instant that is still active, ie, the first log\r
+ record logged by the earliest transaction that is still active.\r
+ <BR>\r
+ The logging system must guarentee that the transaction table is\r
+ populated in the order transactions are started.\r
+ Used in recovery only.\r
+ */\r
+\r
+ public LogInstant firstUpdateInstant()\r
+ {\r
+ return ttab.getFirstLogInstant();\r
+ }\r
+\r
+ /*\r
+ ** Methods of Corruptable\r
+ */\r
+\r
+ /**\r
+ Really this is just a convience routine for callers that might not\r
+ have access to a log factory.\r
+ */\r
+ public StandardException markCorrupt(StandardException originalError) {\r
+ logFactory.markCorrupt(originalError);\r
+ return originalError;\r
+ }\r
+\r
+ /*\r
+ ** Implementation specific methods.\r
+ */\r
+\r
+ public void setNewTransactionId(TransactionId oldxid, Xact t)\r
+ {\r
+ XactId xid;\r
+ boolean excludeMe = true; // by default\r
+\r
+ if (oldxid != null)\r
+ excludeMe = remove(oldxid);\r
+\r
+ synchronized(this)\r
+ {\r
+ xid = new XactId(tranId++);\r
+ }\r
+\r
+ t.setTransactionId(t.getGlobalId(), xid);\r
+\r
+ // RESOLVE: How does a real global xact id get set?\r
+\r
+ // If we got rid of the oldxid, that means this transaction object has\r
+ // merely committed and starting the next transaction with the same\r
+ // xact object. In that case, the transaction context will remain the\r
+ // same and won't be pushed. We need to add this transaction with the\r
+ // new id back into the transaction table. If we did not get rid of\r
+ // the old oldxid, that means this is a brand new transaction being\r
+ // created. The pushTransactionContext call will add it to the\r
+ // transaction table with the appropriate flags\r
+ if (oldxid != null)\r
+ add(t, excludeMe);\r
+ }\r
+\r
+ /**\r
+ ** Set the shortTranId, this is called by the log factory after recovery\r
+ */\r
+ public void resetTranId()\r
+ {\r
+ XactId xid = (XactId)ttab.largestUpdateXactId();\r
+ if (xid != null)\r
+ tranId = xid.getId() + 1;\r
+ else\r
+ tranId = 1;\r
+ }\r
+\r
+\r
+ /**\r
+ Create a new RawTransaction, a context for it and push the context\r
+ onto the current context manager. Then add the transacion to the\r
+ transaction table.\r
+\r
+ @param contextName the name of the transaction context\r
+ @param xact the Transaction object\r
+ @param abortAll if true, then any error will abort the whole\r
+ transaction. Otherwise, let XactContext.cleanupOnError decide what to\r
+ do\r
+ @param rsf the raw store factory\r
+ @param excludeMe during systeme quiesce, i.e., this transaction should\r
+ not be allowed to be active during a quiesce state.\r
+\r
+\r
+ @exception StandardException Standard Derby error policy\r
+\r
+ */\r
+ protected void pushTransactionContext(ContextManager cm, String contextName, \r
+ Xact xact,\r
+ boolean abortAll, \r
+ RawStoreFactory rsf,\r
+ boolean excludeMe)\r
+ throws StandardException \r
+ {\r
+ if (cm.getContext(contextName) != null) \r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.XACT_TRANSACTION_ACTIVE);\r
+ }\r
+ \r
+ XactContext xc = new XactContext(cm, contextName, xact, abortAll, rsf);\r
+\r
+ // this transaction is now added to the transaction table.\r
+ // This will cause an idle transaction to take on an identity, which is\r
+ // unfortunate. The reason why we have to add the transaction to the\r
+ // table right now is because the transaction table is used to bring\r
+ // system to quisce state to regulate who can go active during quiesce\r
+ // state, and if we add the transaction\r
+ // when it goes active, then there is a window where this transaction\r
+ // can sneak in. The transaction table itself does not keep track of\r
+ // whether transactions can be started or not because quiesce related\r
+ // transactions can start after all other user\r
+ // transactions are excluded. \r
+ // RESOLVE: need to put more thought on the overall requirement and\r
+ // design of the transaction table that satisfies the need of all the\r
+ // clients, namely: checkpoint, recovery, quiesce mode, transaction table.\r
+\r
+ add(xact, excludeMe);\r
+\r
+ }\r
+\r
+ /**\r
+ Add a transaction to the list of transactions that has updated\r
+ the raw store. \r
+ <P>\r
+ This is called underneath the BeginXact log operation's doMe method.\r
+ The logging system must guarentee that transactions are added in the\r
+ true order they are started, as defined by the order of beginXact log\r
+ record in the log.\r
+ */\r
+ protected void addUpdateTransaction(\r
+ TransactionId id, \r
+ RawTransaction t, \r
+ int transactionStatus)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(\r
+ id != null, "addding update transaction with null id");\r
+\r
+ ttab.addUpdateTransaction(id, t, transactionStatus);\r
+ }\r
+\r
+ /**\r
+ Remove a transaction from the list of transactions that has updated the\r
+ raw store.\r
+ */\r
+ protected void removeUpdateTransaction(TransactionId id)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(\r
+ id != null, "remove update transaction with null id");\r
+\r
+ ttab.removeUpdateTransaction(id);\r
+ } \r
+\r
+ /**\r
+ Change state of transaction to prepared. Used by recovery to update\r
+ the transaction table entry to prepared state.\r
+ */\r
+ protected void prepareTransaction(TransactionId id)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(\r
+ id != null, "prepare transaction with null id");\r
+\r
+ ttab.prepareTransaction(id);\r
+ } \r
+\r
+ /**\r
+ Submit this post commit work to the post commit daemon\r
+ */\r
+ public boolean submitPostCommitWork(Serviceable work)\r
+ {\r
+ if (rawStoreDaemon != null)\r
+ return rawStoreDaemon.enqueue(work, work.serviceASAP());\r
+ return false;\r
+ }\r
+\r
+ public void setRawStoreFactory(RawStoreFactory rsf) throws StandardException \r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(rsf != null, "rawStoreFactory == null");\r
+ }\r
+\r
+ rawStoreFactory = rsf;\r
+\r
+ // no need to remember raw store factory, \r
+ // just remember which daemon to use\r
+ rawStoreDaemon = rsf.getDaemon();\r
+\r
+ // now its ok to look for the log and data factory\r
+ // log factory is booted by the data factory\r
+ logFactory = (LogFactory) Monitor.findServiceModule(this, rsf.getLogFactoryModule());\r
+\r
+ // data factory is booted by the raw store implementation\r
+ dataFactory = (DataFactory) Monitor.findServiceModule(this, rsf.getDataFactoryModule());\r
+ }\r
+\r
+ /**\r
+ Returns true if there is no in flight updating tranasaction.\r
+ Caller must be aware that if there is no other mechanism to stop\r
+ transactions from starting and ending, then this information is\r
+ outdated as soon as it is reported.\r
+\r
+ Only call this function in special times - e.g, during recovery\r
+ */\r
+ public boolean noActiveUpdateTransaction()\r
+ {\r
+ return (ttab.hasActiveUpdateTransaction() == false);\r
+ }\r
+\r
+\r
+ /**\r
+ * Check if there are any prepared transanctions in the \r
+ * transaction table. \r
+ *\r
+ * Caller must be aware that if there is no other mechanism to stop\r
+ * transactions from starting and ending, then this information is\r
+ * outdated as soon as it is reported.\r
+ *\r
+ * @return <tt>true</tt> if there are prepared \r
+ * transactions in the transaction table,\r
+ * <tt>false</tt> otherwise.\r
+ */\r
+ public boolean hasPreparedXact()\r
+ {\r
+ return (ttab.hasPreparedXact());\r
+ }\r
+\r
+\r
+\r
+ /**\r
+ remove the transaction Id an return false iff the transaction is found\r
+ in the table and it doesn't need exclusion from quiesce state\r
+ */\r
+ protected boolean remove(TransactionId xactId)\r
+ {\r
+ return ttab.remove(xactId);\r
+ }\r
+\r
+ protected void add(Xact xact, boolean excludeMe)\r
+ {\r
+ ttab.add(xact, excludeMe);\r
+ }\r
+\r
+\r
+ /**\r
+ Make a new UUID for whomever that wants it\r
+ */\r
+ public UUID makeNewUUID()\r
+ {\r
+ return uuidFactory.createUUID();\r
+ }\r
+\r
+ /**\r
+ Decide if a transaction of this contextId needs to flush the log when\r
+ it commits\r
+ */\r
+ public boolean flushLogOnCommit(String contextName)\r
+ {\r
+ //\r
+ // if this is a user transaction, flush the log\r
+ // if this is an internal or nested top transaction, do not\r
+ // flush, let it age out.\r
+ //\r
+ return (contextName == USER_CONTEXT_ID || \r
+ contextName.equals(USER_CONTEXT_ID));\r
+ }\r
+\r
+\r
+ /**\r
+ Get a locking policy for a transaction.\r
+ */\r
+ final LockingPolicy getLockingPolicy(\r
+ int mode, \r
+ int isolation, \r
+ boolean stricterOk)\r
+ {\r
+\r
+ if (mode == LockingPolicy.MODE_NONE)\r
+ isolation = TransactionController.ISOLATION_NOLOCK;\r
+\r
+ LockingPolicy policy = lockingPolicies[mode][isolation];\r
+\r
+ if ((policy != null) || (!stricterOk))\r
+ return policy;\r
+\r
+ for (mode++; mode <= LockingPolicy.MODE_CONTAINER; mode++) \r
+ {\r
+ for (int i = isolation; \r
+ i <= TransactionController.ISOLATION_SERIALIZABLE; \r
+ i++) \r
+ {\r
+ policy = lockingPolicies[mode][i];\r
+ if (policy != null)\r
+ return policy;\r
+ }\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ Return the transaction table to be logged with the checkpoint operation\r
+ */\r
+ public Formatable getTransactionTable()\r
+ {\r
+ return ttab;\r
+ }\r
+\r
+ /**\r
+ Use this transaction table, which is gotten from a checkpoint\r
+ operation. Use ONLY during recovery.\r
+ */\r
+ public void useTransactionTable(Formatable transactionTable) \r
+ throws StandardException \r
+ {\r
+ if (ttab != null && transactionTable != null)\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.XACT_TRANSACTION_TABLE_IN_USE);\r
+ }\r
+\r
+ if (ttab == null)\r
+ {\r
+ if (transactionTable == null)\r
+ ttab = new TransactionTable();\r
+ else\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if ((transactionTable instanceof TransactionTable) ==\r
+ false)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "using transaction table which is of class " + \r
+ transactionTable.getClass().getName());\r
+ }\r
+ }\r
+ ttab = (TransactionTable)transactionTable;\r
+ }\r
+ }\r
+ // else transactionTable must be null, if we already have a transaction\r
+ // table, no need to do anything\r
+ }\r
+\r
+ public TransactionInfo[] getTransactionInfo()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(ttab != null, "transaction table is null");\r
+ return ttab.getTransactionInfo();\r
+ }\r
+\r
+\r
+ /**\r
+ * @return false, if the Database creation finished\r
+ */\r
+ public boolean inDatabaseCreation()\r
+ {\r
+ return inCreateNoLog;\r
+ }\r
+ \r
+ /**\r
+ * Return the module providing XAresource interface to the transaction \r
+ * table. \r
+ *\r
+ * @exception StandardException Standard Derby exception policy.\r
+ */\r
+ public /* XAResourceManager */ Object getXAResourceManager()\r
+ throws StandardException\r
+ {\r
+ if (xa_resource == null)\r
+ xa_resource = new XactXAResourceManager(rawStoreFactory, ttab);\r
+\r
+ return(xa_resource);\r
+ }\r
+\r
+\r
+ /**\r
+ * Block the online backup. Backup needs to be blocked while \r
+ * executing any unlogged operations or any opearation that \r
+ * prevents from making a consistent backup.\r
+ * \r
+ * @param wait if <tt>true</tt>, waits until the backup \r
+ * is blocked. \r
+ * @return <tt>true</tt> if backup is blocked.\r
+ * <tt>false</tt> otherwise.\r
+ * @exception StandardException if interrupted while waiting for a \r
+ * backup to complete.\r
+ */\r
+ protected boolean blockBackup(boolean wait)\r
+ throws StandardException \r
+ {\r
+ synchronized(backupSemaphore) {\r
+ // do not allow backup blocking operations, if online backup is\r
+ // is in progress.\r
+ if (inBackup) \r
+ {\r
+ if(wait) {\r
+ while(inBackup) {\r
+ try {\r
+ backupSemaphore.wait();\r
+ } catch (InterruptedException ie) {\r
+ throw StandardException.interrupt(ie);\r
+ }\r
+ }\r
+ }else {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ // not in online backup, allow backup blocking operations\r
+ backupBlockingOperations++;\r
+ return true;\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * Unblock the backup, a backup blocking operation finished. \r
+ */\r
+ protected void unblockBackup()\r
+ {\r
+ synchronized(backupSemaphore) {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(backupBlockingOperations > 0, \r
+ "no backup blocking opeations in progress"); \r
+ \r
+ backupBlockingOperations--;\r
+\r
+ if (inBackup) {\r
+ // wake up the online backupthread\r
+ backupSemaphore.notifyAll(); \r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Checks if there are any backup blocking operations in progress and \r
+ * prevents new ones from starting until the backup is finished. \r
+ * If backup blocking operations are in progress and <code> wait </code>\r
+ * parameter value is <tt>true</tt>, then it will wait for the current \r
+ * backup blocking operations to finish. \r
+ * \r
+ * A Consistent backup can not be made if there are any backup \r
+ * blocking operations (like unlogged operations) are in progress\r
+ *\r
+ * @param wait if <tt>true</tt>, waits for the current backup blocking \r
+ * operation in progress to finish.\r
+ * @return <tt>true</tt> if no backup blocking operations are in \r
+ * progress\r
+ * <tt>false</tt> otherwise.\r
+ * @exception StandardException if interrupted or a runtime exception occurs\r
+ */\r
+ public boolean blockBackupBlockingOperations(boolean wait) \r
+ throws StandardException \r
+ {\r
+ synchronized(backupSemaphore) {\r
+ if (wait) {\r
+ // set the inBackup state to true first to stop new backup\r
+ // blocking operation from starting.\r
+ inBackup= true;\r
+ try {\r
+ // wait for backup blocking operation in progress to finish\r
+ while(backupBlockingOperations > 0)\r
+ {\r
+ try {\r
+ backupSemaphore.wait();\r
+ }\r
+ catch (InterruptedException ie) {\r
+ // make sure we are not stuck in the backup state \r
+ // if we caught an interrupt exception and the \r
+ // calling thread may not have a chance to clear \r
+ // the in backup state.\r
+\r
+ inBackup = false;\r
+ backupSemaphore.notifyAll();\r
+ throw StandardException.interrupt(ie);\r
+ }\r
+ }\r
+ }\r
+ catch (RuntimeException rte) {\r
+ // make sure we are not stuck in backup state if we\r
+ // caught a run time exception and the calling thread may \r
+ // not have a chance to clear the in backup state.\r
+ inBackup= false;\r
+ backupSemaphore.notifyAll();\r
+ throw rte; // rethrow run time exception\r
+ }\r
+ } else {\r
+ // check if any backup blocking operations that are in progress\r
+ if (backupBlockingOperations == 0)\r
+ inBackup = true;\r
+ }\r
+ \r
+ }\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (inBackup) {\r
+ SanityManager.ASSERT(backupBlockingOperations == 0 ,\r
+ "store is not in correct state for backup");\r
+ }\r
+ }\r
+\r
+ return inBackup;\r
+ }\r
+\r
+\r
+ /**\r
+ * Backup completed. Allow backup blocking operations. \r
+ */\r
+ public void unblockBackupBlockingOperations()\r
+ {\r
+ synchronized(backupSemaphore) {\r
+ inBackup = false;\r
+ backupSemaphore.notifyAll();\r
+ }\r
+ }\r
+ \r
+}\r