--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.conn.GenericLanguageConnectionContext\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.sql.conn;\r
+\r
+import org.apache.derby.iapi.services.context.ContextImpl;\r
+import org.apache.derby.iapi.services.cache.CacheManager;\r
+\r
+import org.apache.derby.impl.sql.compile.CompilerContextImpl;\r
+import org.apache.derby.impl.sql.execute.InternalTriggerExecutionContext;\r
+import org.apache.derby.impl.sql.execute.AutoincrementCounter;\r
+import org.apache.derby.impl.sql.GenericPreparedStatement;\r
+import org.apache.derby.impl.sql.GenericStatement;\r
+import org.apache.derby.iapi.sql.Statement;\r
+\r
+import org.apache.derby.iapi.services.property.PropertyUtil;\r
+import org.apache.derby.iapi.services.context.ContextManager;\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.stream.HeaderPrintWriter;\r
+import org.apache.derby.iapi.services.loader.GeneratedClass;\r
+import org.apache.derby.iapi.services.cache.Cacheable;\r
+import org.apache.derby.iapi.db.Database;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.sql.compile.CompilerContext;\r
+import org.apache.derby.iapi.sql.compile.OptimizerFactory;\r
+import org.apache.derby.iapi.sql.conn.Authorizer;\r
+import org.apache.derby.iapi.error.ExceptionSeverity;\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionFactory;\r
+import org.apache.derby.iapi.sql.conn.StatementContext;\r
+import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptorList;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+import org.apache.derby.iapi.types.DataValueFactory;\r
+import org.apache.derby.iapi.sql.compile.TypeCompilerFactory;\r
+import org.apache.derby.iapi.sql.depend.DependencyManager;\r
+import org.apache.derby.iapi.sql.depend.Provider;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.reference.Limits;\r
+import org.apache.derby.iapi.sql.execute.ConstantAction;\r
+import org.apache.derby.iapi.sql.execute.CursorActivation;\r
+import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;\r
+import org.apache.derby.iapi.sql.execute.ExecutionContext;\r
+import org.apache.derby.iapi.sql.execute.ExecutionStmtValidator;\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.LanguageFactory;\r
+import org.apache.derby.iapi.sql.PreparedStatement;\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+import org.apache.derby.iapi.sql.ParameterValueSet;\r
+\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+import org.apache.derby.iapi.store.access.XATransactionController;\r
+import org.apache.derby.iapi.util.IdUtil;\r
+import org.apache.derby.iapi.util.StringUtil;\r
+\r
+import org.apache.derby.catalog.UUID;\r
+import org.apache.derby.iapi.sql.execute.RunTimeStatistics;\r
+import org.apache.derby.iapi.db.TriggerExecutionContext;\r
+import org.apache.derby.iapi.reference.Property;\r
+\r
+import java.util.List;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.Map;\r
+\r
+/**\r
+ * LanguageConnectionContext keeps the pool of prepared statements,\r
+ * activations, and cursors in use by the current connection.\r
+ * <p>\r
+ * The generic impl does not provide statement caching.\r
+ *\r
+ *\r
+ */\r
+public class GenericLanguageConnectionContext\r
+ extends ContextImpl \r
+ implements LanguageConnectionContext\r
+{\r
+\r
+ // make sure these are not zeros\r
+ private final static int NON_XA = 0;\r
+ private final static int XA_ONE_PHASE = 1;\r
+ private final static int XA_TWO_PHASE = 2;\r
+\r
+ /*\r
+ fields\r
+ */\r
+\r
+ private final ArrayList acts;\r
+ private volatile boolean unusedActs=false;\r
+ /** The maximum size of acts since the last time it was trimmed. Used to\r
+ * determine whether acts should be trimmed to reclaim space. */\r
+ private int maxActsSize;\r
+ protected int bindCount;\r
+ private boolean ddWriteMode;\r
+ private boolean runTimeStatisticsSetting ;\r
+ private boolean statisticsTiming;\r
+\r
+ //all the temporary tables declared for this connection\r
+ private ArrayList allDeclaredGlobalTempTables;\r
+ //The currentSavepointLevel is used to provide the rollback behavior of temporary tables.\r
+ //At any point, this variable has the total number of savepoints defined for the transaction.\r
+ private int currentSavepointLevel = 0;\r
+\r
+ protected long nextCursorId;\r
+\r
+ protected int nextSavepointId;\r
+\r
+ private RunTimeStatistics runTimeStatisticsObject;\r
+ private StringBuffer sb;\r
+\r
+ private Database db;\r
+\r
+ private final int instanceNumber;\r
+ private String drdaID;\r
+ private String dbname;\r
+ \r
+ /**\r
+ The transaction to use within this language connection context. It may\r
+ be more appropriate to have it in a separate context (LanguageTransactionContext?).\r
+ REVISIT (nat): I shoehorned the transaction context that\r
+ the language uses into this class. The main purpose is so\r
+ that the various language code can find out what its\r
+ transaction is.\r
+ **/\r
+ private final TransactionController tran;\r
+\r
+ /**\r
+ * If non-null indicates that a read-only nested \r
+ * user transaction is in progress.\r
+ */\r
+ private TransactionController readOnlyNestedTransaction;\r
+ \r
+ /**\r
+ * queryNestingDepth is a counter used to keep track of how many calls \r
+ * have been made to begin read-only nested transactions. Only the first call \r
+ * actually starts a Nested User Transaction with the store. Subsequent\r
+ * calls simply increment this counter. commitNestedTransaction only\r
+ * decrements the counter and when it drops to 0 actually commits the \r
+ * nested user transaction.\r
+ */\r
+ private int queryNestingDepth;\r
+\r
+ protected DataValueFactory dataFactory;\r
+ protected LanguageFactory langFactory;\r
+ protected TypeCompilerFactory tcf;\r
+ protected OptimizerFactory of;\r
+ protected LanguageConnectionFactory connFactory;\r
+ \r
+ /* \r
+ * A statement context is "pushed" and "popped" at the beginning and\r
+ * end of every statement so that only that statement is cleaned up\r
+ * on a Statement Exception. As a performance optimization, we only push\r
+ * the outermost statement context once, and never pop it. Also, we\r
+ * save off a 2nd StatementContext for speeding server side method\r
+ * invocation, though we still push and pop it as needed. All other\r
+ * statement contexts will allocated and pushed and popped on demand.\r
+ */\r
+ private final StatementContext[] statementContexts = new StatementContext[2];\r
+ private int statementDepth;\r
+ protected int outermostTrigger = -1;\r
+\r
+ protected Authorizer authorizer;\r
+ protected String userName = null; //The name the user connects with.\r
+ //May still be quoted.\r
+ \r
+ protected SchemaDescriptor sd;\r
+\r
+ // RESOLVE - How do we want to set the default.\r
+ private int defaultIsolationLevel = ExecutionContext.READ_COMMITTED_ISOLATION_LEVEL;\r
+ protected int isolationLevel = defaultIsolationLevel;\r
+\r
+ private boolean isolationLevelExplicitlySet = false;\r
+ // Isolation level can be changed using JDBC api Connection.setTransactionIsolation\r
+ // or it can be changed using SQL "set current isolation = NEWLEVEL".\r
+ // \r
+ // In XA transactions, BrokeredConnection keeps isolation state information.\r
+ // When isolation is changed in XA transaction using JDBC, that state gets\r
+ // correctly set in BrokeredConnection.setTransactionIsolation method. But\r
+ // when SQL is used to set the isolation level, the code path is different\r
+ // and it does not go through BrokeredConnection's setTransactionIsolation\r
+ // method and hence the state is not maintained correctly when coming through\r
+ // SQL. To get around this, I am adding following flag which will get set\r
+ // everytime the isolation level is set using JDBC or SQL. This flag will be\r
+ // checked at global transaction start and end time. If the flag is set to true\r
+ // then BrokeredConnection's isolation level state will be brought upto date\r
+ // with Real Connection's isolation level and this flag will be set to false\r
+ // after that.\r
+ private boolean isolationLevelSetUsingSQLorJDBC = false;\r
+\r
+ // isolation level to when preparing statements.\r
+ // if unspecified, the statement won't be prepared with a specific \r
+ // scan isolationlevel\r
+ protected int prepareIsolationLevel = ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL;\r
+\r
+ // Whether or not to write executing statement info to db2j.log\r
+ private boolean logStatementText;\r
+ private boolean logQueryPlan;\r
+ private HeaderPrintWriter istream;\r
+\r
+ // this used to be computed in OptimizerFactoryContextImpl; i.e everytime a\r
+ // connection was made. To keep the semantics same I'm putting it out here\r
+ // instead of in the OptimizerFactory which is only initialized when the\r
+ // database is booted.\r
+ private int lockEscalationThreshold; \r
+\r
+ private ArrayList stmtValidators;\r
+ private ArrayList triggerExecutionContexts;\r
+ private ArrayList triggerTables;\r
+\r
+ // OptimizerTrace\r
+ private boolean optimizerTrace;\r
+ private boolean optimizerTraceHtml;\r
+ private String lastOptimizerTraceOutput;\r
+ private String optimizerTraceOutput;\r
+\r
+ //// Support for AUTOINCREMENT\r
+\r
+ /**\r
+ * To support lastAutoincrementValue: This is a hashtable which maps\r
+ * schemaName,tableName,columnName to a Long value.\r
+ */\r
+ private HashMap autoincrementHT;\r
+ /**\r
+ * whether to allow updates or not. \r
+ */\r
+ private boolean autoincrementUpdate;\r
+ private long identityVal; //support IDENTITY_VAL_LOCAL function\r
+ private boolean identityNotNull; //frugal programmer\r
+\r
+ // cache of ai being handled in memory (bulk insert + alter table).\r
+ private HashMap autoincrementCacheHashtable;\r
+\r
+ /*\r
+ constructor\r
+ */\r
+ public GenericLanguageConnectionContext\r
+ (\r
+ ContextManager cm,\r
+ TransactionController tranCtrl,\r
+\r
+ LanguageFactory lf,\r
+ LanguageConnectionFactory lcf,\r
+ Database db,\r
+ String userName,\r
+ int instanceNumber,\r
+ String drdaID,\r
+ String dbname)\r
+ throws StandardException\r
+ {\r
+ super(cm, org.apache.derby.iapi.reference.ContextId.LANG_CONNECTION);\r
+ acts = new ArrayList();\r
+ tran = tranCtrl;\r
+\r
+ dataFactory = lcf.getDataValueFactory();\r
+ tcf = lcf.getTypeCompilerFactory();\r
+ of = lcf.getOptimizerFactory();\r
+ langFactory = lf;\r
+ connFactory = lcf;\r
+ this.db = db;\r
+ this.userName = userName;\r
+ this.instanceNumber = instanceNumber;\r
+ this.drdaID = drdaID;\r
+ this.dbname = dbname;\r
+\r
+ /* Find out whether or not to log info on executing statements to error log\r
+ */\r
+ String logStatementProperty = PropertyUtil.getServiceProperty(getTransactionCompile(),\r
+ "derby.language.logStatementText");\r
+ logStatementText = Boolean.valueOf(logStatementProperty).booleanValue();\r
+\r
+ String logQueryPlanProperty = PropertyUtil.getServiceProperty(getTransactionCompile(),\r
+ "derby.language.logQueryPlan");\r
+ logQueryPlan = Boolean.valueOf(logQueryPlanProperty).booleanValue();\r
+\r
+ setRunTimeStatisticsMode(logQueryPlan);\r
+\r
+ lockEscalationThreshold = \r
+ PropertyUtil.getServiceInt(tranCtrl,\r
+ Property.LOCKS_ESCALATION_THRESHOLD,\r
+ Property.MIN_LOCKS_ESCALATION_THRESHOLD,\r
+ Integer.MAX_VALUE,\r
+ Property.DEFAULT_LOCKS_ESCALATION_THRESHOLD); \r
+ stmtValidators = new ArrayList();\r
+ triggerExecutionContexts = new ArrayList();\r
+ triggerTables = new ArrayList();\r
+ }\r
+\r
+ public void initialize() throws StandardException\r
+ {\r
+ //\r
+ //Creating the authorizer authorizes the connection.\r
+ authorizer = new GenericAuthorizer(IdUtil.getUserAuthorizationId(userName),this);\r
+\r
+ /*\r
+ ** Set the authorization id. User shouldn't\r
+ ** be null or else we are going to blow up trying\r
+ ** to create a schema for this user.\r
+ */\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (getAuthorizationId() == null)\r
+ {\r
+ SanityManager.THROWASSERT("User name is null," +\r
+ " check the connection manager to make sure it is set" +\r
+ " reasonably");\r
+ }\r
+ }\r
+\r
+\r
+ setDefaultSchema(initDefaultSchemaDescriptor());\r
+ }\r
+\r
+ protected SchemaDescriptor initDefaultSchemaDescriptor()\r
+ throws StandardException {\r
+ /*\r
+ ** - If the database supports schemas and a schema with the\r
+ ** same name as the user's name exists (has been created using\r
+ ** create schema already) the database will set the users\r
+ ** default schema to the the schema with the same name as the\r
+ ** user.\r
+ ** - Else Set the default schema to APP.\r
+ */\r
+ // SchemaDescriptor sd;\r
+\r
+ DataDictionary dd = getDataDictionary();\r
+ String authorizationId = getAuthorizationId();\r
+ \r
+ if ( (sd = dd.getSchemaDescriptor(authorizationId, getTransactionCompile(), false)) == null )\r
+ {\r
+ sd = new SchemaDescriptor(dd, authorizationId, authorizationId, (UUID) null, false);\r
+ }\r
+ return sd;\r
+ }\r
+\r
+ //\r
+ // LanguageConnectionContext interface\r
+ //\r
+ /**\r
+ * @see LanguageConnectionContext#getLogStatementText\r
+ */\r
+ public boolean getLogStatementText()\r
+ {\r
+ return logStatementText;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#setLogStatementText\r
+ */\r
+ public void setLogStatementText(boolean logStatementText)\r
+ {\r
+ this.logStatementText = logStatementText;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getLogQueryPlan\r
+ */\r
+ public boolean getLogQueryPlan()\r
+ {\r
+ return logQueryPlan;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#usesSqlAuthorization\r
+ */\r
+ public boolean usesSqlAuthorization()\r
+ {\r
+ return getDataDictionary().usesSqlAuthorization();\r
+ }\r
+\r
+ /**\r
+ * get the lock escalation threshold.\r
+ */\r
+ public int getLockEscalationThreshold()\r
+ {\r
+ return lockEscalationThreshold;\r
+ }\r
+\r
+ /**\r
+ * Add the activation to those known about by this connection.\r
+ */\r
+ public void addActivation(Activation a) \r
+ throws StandardException {\r
+ acts.add(a);\r
+\r
+ if (acts.size() > maxActsSize) {\r
+ maxActsSize = acts.size();\r
+ }\r
+ }\r
+\r
+ public void closeUnusedActivations()\r
+ throws StandardException\r
+ {\r
+ // DERBY-418. Activations which are marked unused,\r
+ // are closed here. Activations Vector is iterated \r
+ // to identify and close unused activations, only if \r
+ // unusedActs flag is set to true and if the total \r
+ // size exceeds 20.\r
+ if( (unusedActs) && (acts.size() > 20) ) {\r
+ unusedActs = false;\r
+\r
+ for (int i = acts.size() - 1; i >= 0; i--) {\r
+\r
+ // it maybe the case that a Activation's reset() ends up\r
+ // closing one or more activation leaving our index beyond\r
+ // the end of the array\r
+ if (i >= acts.size())\r
+ continue;\r
+\r
+ Activation a1 = (Activation) acts.get(i);\r
+ if (!a1.isInUse()) {\r
+ a1.close();\r
+ }\r
+ }\r
+ }\r
+\r
+ if (SanityManager.DEBUG) {\r
+\r
+ if (SanityManager.DEBUG_ON("memoryLeakTrace")) {\r
+\r
+ if (acts.size() > 20)\r
+ System.out.println("memoryLeakTrace:GenericLanguageContext:activations " + acts.size());\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Make a note that some activations are marked unused\r
+ */\r
+ public void notifyUnusedActivation() {\r
+ unusedActs = true;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#checkIfAnyDeclaredGlobalTempTablesForThisConnection\r
+ */\r
+ public boolean checkIfAnyDeclaredGlobalTempTablesForThisConnection() {\r
+ return (allDeclaredGlobalTempTables == null ? false : true);\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#addDeclaredGlobalTempTable\r
+ */\r
+ public void addDeclaredGlobalTempTable(TableDescriptor td)\r
+ throws StandardException {\r
+\r
+ if (findDeclaredGlobalTempTable(td.getName()) != null) //if table already declared, throw an exception\r
+ {\r
+ throw\r
+ StandardException.newException(\r
+ SQLState.LANG_OBJECT_ALREADY_EXISTS_IN_OBJECT,\r
+ "Declared global temporary table",\r
+ td.getName(),\r
+ "Schema",\r
+ SchemaDescriptor.STD_DECLARED_GLOBAL_TEMPORARY_TABLES_SCHEMA_NAME);\r
+ }\r
+\r
+ //save all the information about temp table in this special class\r
+ TempTableInfo tempTableInfo = new TempTableInfo(td, currentSavepointLevel);\r
+\r
+ if (allDeclaredGlobalTempTables == null)\r
+ allDeclaredGlobalTempTables = new ArrayList();\r
+\r
+ allDeclaredGlobalTempTables.add(tempTableInfo);\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#dropDeclaredGlobalTempTable\r
+ */\r
+ public boolean dropDeclaredGlobalTempTable(String tableName) {\r
+ TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName);\r
+ if (tempTableInfo != null)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ if (tempTableInfo.getDeclaredInSavepointLevel() > currentSavepointLevel)\r
+ SanityManager.THROWASSERT("declared in savepoint level can not be higher than the current savepoint level");\r
+\r
+ //following checks if the table was declared in the current unit of work.\r
+ if (tempTableInfo.getDeclaredInSavepointLevel() == currentSavepointLevel)\r
+ {\r
+ //since the table was declared in this unit of work,\r
+ //the drop table method should remove it from the valid list of temp table for this unit of work\r
+ allDeclaredGlobalTempTables.remove(allDeclaredGlobalTempTables.indexOf(tempTableInfo));\r
+ if (allDeclaredGlobalTempTables.size() == 0)\r
+ allDeclaredGlobalTempTables = null;\r
+ }\r
+ else\r
+ {\r
+ //since the table was not declared in this unit of work, the drop table method will just mark the table as dropped\r
+ //in the current unit of work. This information will be used at rollback time.\r
+ tempTableInfo.setDroppedInSavepointLevel(currentSavepointLevel);\r
+ }\r
+ return true;\r
+ } else\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * After a release of a savepoint, we need to go through our temp tables list. If there are tables with their declare or drop\r
+ * or modified in savepoint levels set to savepoint levels higher than the current savepoint level, then we should change them\r
+ * to the current savepoint level\r
+ */\r
+ private void tempTablesReleaseSavepointLevels() {\r
+ //unlike rollback, here we check for dropped in / declared in / modified in savepoint levels > current savepoint level only.\r
+ //This is because the temp tables with their savepoint levels same as currentSavepointLevel have correct value assigned to them and\r
+ //do not need to be changed and hence no need to check for >=\r
+ for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) {\r
+ TempTableInfo tempTableInfo = (TempTableInfo)allDeclaredGlobalTempTables.get(i);\r
+ if (tempTableInfo.getDroppedInSavepointLevel() > currentSavepointLevel)\r
+ tempTableInfo.setDroppedInSavepointLevel(currentSavepointLevel);\r
+\r
+ if (tempTableInfo.getDeclaredInSavepointLevel() > currentSavepointLevel)\r
+ tempTableInfo.setDeclaredInSavepointLevel(currentSavepointLevel);\r
+\r
+ if (tempTableInfo.getModifiedInSavepointLevel() > currentSavepointLevel)\r
+ tempTableInfo.setModifiedInSavepointLevel(currentSavepointLevel);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * do the necessary work at commit time for temporary tables\r
+ * 1)If a temporary table was marked as dropped in this transaction, then remove it from the list of temp tables for this connection\r
+ * 2)If a temporary table was not dropped in this transaction, then mark it's declared savepoint level and modified savepoint level as -1\r
+ */\r
+ private void tempTablesAndCommit() {\r
+ for (int i = allDeclaredGlobalTempTables.size()-1; i >= 0; i--) {\r
+ TempTableInfo tempTableInfo = (TempTableInfo)allDeclaredGlobalTempTables.get(i);\r
+ if (tempTableInfo.getDroppedInSavepointLevel() != -1)\r
+ {\r
+ //this means table was dropped in this unit of work and hence should be removed from valid list of temp tables\r
+ allDeclaredGlobalTempTables.remove(i);\r
+ } else //this table was not dropped in this unit of work, hence set its declaredInSavepointLevel as -1 and also mark it as not modified \r
+ {\r
+ tempTableInfo.setDeclaredInSavepointLevel(-1);\r
+ tempTableInfo.setModifiedInSavepointLevel(-1);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ Reset the connection before it is returned (indirectly) by\r
+ a PooledConnection object. See EmbeddedConnection.\r
+ */\r
+ public void resetFromPool()\r
+ throws StandardException\r
+ {\r
+ // Reset IDENTITY_VAL_LOCAL\r
+ identityNotNull = false;\r
+\r
+ // drop all temp tables.\r
+ dropAllDeclaredGlobalTempTables();\r
+ }\r
+\r
+ /**\r
+ * Drop all the declared global temporary tables associated with this connection. This gets called\r
+ * when a getConnection() is done on a PooledConnection. This will ensure all the temporary tables\r
+ * declared on earlier connection handle associated with this physical database connection are dropped\r
+ * before a new connection handle is issued on that same physical database connection.\r
+ */\r
+ private void dropAllDeclaredGlobalTempTables() throws StandardException {\r
+ if (allDeclaredGlobalTempTables == null)\r
+ return;\r
+ \r
+ DependencyManager dm = getDataDictionary().getDependencyManager();\r
+ StandardException topLevelStandardException = null;\r
+\r
+ //collect all the exceptions we might receive while dropping the temporary tables and throw them as one chained exception at the end.\r
+ for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) {\r
+ try {\r
+ TempTableInfo tempTableInfo = (TempTableInfo)allDeclaredGlobalTempTables.get(i);\r
+ TableDescriptor td = tempTableInfo.getTableDescriptor();\r
+ //the following 2 lines of code has been copied from DropTableConstantAction. If there are any changes made there in future,\r
+ //we should check if they need to be made here too.\r
+ dm.invalidateFor(td, DependencyManager.DROP_TABLE, this);\r
+ tran.dropConglomerate(td.getHeapConglomerateId());\r
+ } catch (StandardException e) {\r
+ if (topLevelStandardException == null) {\r
+ // always keep the first exception unchanged\r
+ topLevelStandardException = e;\r
+ } else {\r
+ try {\r
+ // Try to create a chain of exceptions. If successful,\r
+ // the current exception is the top-level exception,\r
+ // and the previous exception the cause of it.\r
+ e.initCause(topLevelStandardException);\r
+ topLevelStandardException = e;\r
+ } catch (IllegalStateException ise) {\r
+ // initCause() has already been called on e. We don't\r
+ // expect this to happen, but if it happens, just skip\r
+ // the current exception from the chain. This is safe\r
+ // since we always keep the first exception.\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ allDeclaredGlobalTempTables = null;\r
+ try {\r
+ internalCommit(true);\r
+ } catch (StandardException e) {\r
+ // do the same chaining as above\r
+ if (topLevelStandardException == null) {\r
+ topLevelStandardException = e;\r
+ } else {\r
+ try {\r
+ e.initCause(topLevelStandardException);\r
+ topLevelStandardException = e;\r
+ } catch (IllegalStateException ise) { /* ignore */ }\r
+ }\r
+ }\r
+ if (topLevelStandardException != null) throw topLevelStandardException;\r
+ }\r
+\r
+ //do the necessary work at rollback time for temporary tables\r
+ /**\r
+ * do the necessary work at rollback time for temporary tables\r
+ * 1)If a temp table was declared in the UOW, then drop it and remove it from list of temporary tables.\r
+ * 2)If a temp table was declared and dropped in the UOW, then remove it from list of temporary tables.\r
+ * 3)If an existing temp table was dropped in the UOW, then recreate it with no data.\r
+ * 4)If an existing temp table was modified in the UOW, then get rid of all the rows from the table.\r
+ */\r
+ private void tempTablesAndRollback()\r
+ throws StandardException {\r
+ for (int i = allDeclaredGlobalTempTables.size()-1; i >= 0; i--) {\r
+ TempTableInfo tempTableInfo = (TempTableInfo)allDeclaredGlobalTempTables.get(i);\r
+ if (tempTableInfo.getDeclaredInSavepointLevel() >= currentSavepointLevel)\r
+ {\r
+ if (tempTableInfo.getDroppedInSavepointLevel() == -1)\r
+ {\r
+ //the table was declared but not dropped in the unit of work getting rolled back and hence we will remove\r
+ //it from valid list of temporary tables and drop the conglomerate associated with it\r
+ TableDescriptor td = tempTableInfo.getTableDescriptor();\r
+ tran.dropConglomerate(td.getHeapConglomerateId()); //remove the conglomerate created for this temp table\r
+ allDeclaredGlobalTempTables.remove(i); //remove it from the list of temp tables\r
+ } else if (tempTableInfo.getDroppedInSavepointLevel() >= currentSavepointLevel)\r
+ {\r
+ //the table was declared and dropped in the unit of work getting rolled back\r
+ allDeclaredGlobalTempTables.remove(i);\r
+ }\r
+ } else if (tempTableInfo.getDroppedInSavepointLevel() >= currentSavepointLevel) //this means the table was declared in an earlier savepoint unit / transaction and then dropped in current UOW \r
+ {\r
+ //restore the old definition of temp table because drop is being rolledback\r
+ TableDescriptor td = tempTableInfo.getTableDescriptor();\r
+ td = cleanupTempTableOnCommitOrRollback(td, false);\r
+ //In order to store the old conglomerate information for the temp table, we need to replace the\r
+ //existing table descriptor with the old table descriptor which has the old conglomerate information\r
+ tempTableInfo.setTableDescriptor(td);\r
+ tempTableInfo.setDroppedInSavepointLevel(-1);\r
+ //following will mark the table as not modified. This is because the table data has been deleted as part of the current rollback\r
+ tempTableInfo.setModifiedInSavepointLevel(-1);\r
+ allDeclaredGlobalTempTables.set(i, tempTableInfo);\r
+ } else if (tempTableInfo.getModifiedInSavepointLevel() >= currentSavepointLevel) //this means the table was declared in an earlier savepoint unit / transaction and modified in current UOW\r
+ {\r
+ //following will mark the table as not modified. This is because the table data will be deleted as part of the current rollback\r
+ tempTableInfo.setModifiedInSavepointLevel(-1);\r
+ TableDescriptor td = tempTableInfo.getTableDescriptor();\r
+ getDataDictionary().getDependencyManager().invalidateFor(td, DependencyManager.DROP_TABLE, this);\r
+ cleanupTempTableOnCommitOrRollback(td, true);\r
+ } // there is no else here because there is no special processing required for temp tables declares in earlier work of unit/transaction and not modified\r
+ }\r
+ \r
+ if (allDeclaredGlobalTempTables.size() == 0)\r
+ allDeclaredGlobalTempTables = null;\r
+ }\r
+\r
+ /**\r
+ * This is called at the commit time for temporary tables with ON COMMIT DELETE ROWS\r
+ * If a temp table with ON COMMIT DELETE ROWS doesn't have any held cursor open on them, we delete the data from\r
+ * them by dropping the conglomerate and recreating the conglomerate. In order to store the new conglomerate\r
+ * information for the temp table, we need to replace the existing table descriptor with the new table descriptor\r
+ * which has the new conglomerate information\r
+ * @param tableName Temporary table name whose table descriptor is getting changed\r
+ * @param td New table descriptor for the temporary table\r
+ */\r
+ private void replaceDeclaredGlobalTempTable(String tableName, TableDescriptor td) {\r
+ TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName);\r
+ tempTableInfo.setDroppedInSavepointLevel(-1);\r
+ tempTableInfo.setDeclaredInSavepointLevel(-1);\r
+ tempTableInfo.setTableDescriptor(td);\r
+ allDeclaredGlobalTempTables.set(allDeclaredGlobalTempTables.indexOf(tempTableInfo), tempTableInfo);\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getTableDescriptorForDeclaredGlobalTempTable\r
+ */\r
+ public TableDescriptor getTableDescriptorForDeclaredGlobalTempTable(String tableName) {\r
+ TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName);\r
+ if (tempTableInfo == null)\r
+ return null;\r
+ else\r
+ return tempTableInfo.getTableDescriptor();\r
+ }\r
+\r
+ /**\r
+ * Find the declared global temporary table in the list of temporary tables known by this connection.\r
+ * @param tableName look for this table name in the saved list\r
+ * @return data structure defining the temporary table if found. Else, return null \r
+ *\r
+ */\r
+ private TempTableInfo findDeclaredGlobalTempTable(String tableName) {\r
+ if (allDeclaredGlobalTempTables == null)\r
+ return null;\r
+\r
+ for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) {\r
+ if (((TempTableInfo)allDeclaredGlobalTempTables.get(i)).matches(tableName))\r
+ return (TempTableInfo)allDeclaredGlobalTempTables.get(i);\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#markTempTableAsModifiedInUnitOfWork\r
+ */\r
+ public void markTempTableAsModifiedInUnitOfWork(String tableName) {\r
+ TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName);\r
+ tempTableInfo.setModifiedInSavepointLevel(currentSavepointLevel);\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#prepareInternalStatement\r
+ */\r
+ public PreparedStatement prepareInternalStatement(SchemaDescriptor compilationSchema, String sqlText, boolean isForReadOnly, boolean forMetaData) \r
+ throws StandardException \r
+ {\r
+ return connFactory.getStatement(compilationSchema, sqlText, isForReadOnly).prepare(this, forMetaData);\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#prepareInternalStatement\r
+ */\r
+ public PreparedStatement prepareInternalStatement(String sqlText) \r
+ throws StandardException \r
+ {\r
+ return connFactory.getStatement(sd, sqlText, true).prepare(this);\r
+ } \r
+\r
+ /**\r
+ * Remove the activation to those known about by this connection.\r
+ *\r
+ */\r
+ public void removeActivation(Activation a) \r
+ {\r
+ if (SanityManager.DEBUG) {\r
+ SanityManager.ASSERT(a.isClosed(), "Activation is not closed");\r
+ }\r
+\r
+ acts.remove(a);\r
+\r
+ if (maxActsSize > 20 && (maxActsSize > 2 * acts.size())) {\r
+ acts.trimToSize();\r
+ maxActsSize = acts.size();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Return the number of activations known for this connection.\r
+ * Note that some of these activations may not be in use\r
+ * (when a prepared statement is finalized, its activations\r
+ * are marked as unused and later closed and removed on\r
+ * the next commit/rollback).\r
+ */\r
+ public int getActivationCount() {\r
+ return acts.size();\r
+ }\r
+\r
+ /**\r
+ * See if a given cursor is available for use.\r
+ * if so return its activation. Returns null if not found.\r
+ * For use in execution.\r
+ *\r
+ * @return the activation for the given cursor, null\r
+ * if none was found.\r
+ */\r
+ public CursorActivation lookupCursorActivation(String cursorName) {\r
+\r
+ int size = acts.size();\r
+ if (size > 0)\r
+ {\r
+ for (int i = 0; i < size; i++) {\r
+ Activation a = (Activation) acts.get(i);\r
+\r
+ if (!a.isInUse())\r
+ {\r
+ continue;\r
+ }\r
+\r
+\r
+\r
+ String executingCursorName = a.getCursorName();\r
+\r
+ if (cursorName.equals(executingCursorName)) {\r
+\r
+ ResultSet rs = a.getResultSet();\r
+ if (rs == null)\r
+ continue;\r
+\r
+ // if the result set is closed, the the cursor doesn't exist\r
+ if (rs.isClosed()) { \r
+ continue;\r
+ }\r
+\r
+ return (CursorActivation)a;\r
+ }\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * This method will remove a statement from the statement cache.\r
+ * It will be called, for example, if there is an exception preparing\r
+ * the statement.\r
+ *\r
+ * @param statement Statement to remove\r
+ * @exception StandardException thrown if lookup goes wrong.\r
+ */ \r
+ public void removeStatement(Statement statement)\r
+ throws StandardException {\r
+ \r
+ CacheManager statementCache =\r
+ getLanguageConnectionFactory().getStatementCache();\r
+\r
+ if (statementCache == null)\r
+ return;\r
+ \r
+ Cacheable cachedItem = statementCache.findCached(statement);\r
+ if (cachedItem != null)\r
+ statementCache.remove(cachedItem);\r
+ }\r
+\r
+ /**\r
+ * See if a given statement has already been compiled for this user, and\r
+ * if so use its prepared statement. Returns null if not found.\r
+ *\r
+ * @exception StandardException thrown if lookup goes wrong.\r
+ * @return the prepared statement for the given string, null\r
+ * if none was found.\r
+ */\r
+ public PreparedStatement lookupStatement(GenericStatement statement)\r
+ throws StandardException {\r
+\r
+ CacheManager statementCache =\r
+ getLanguageConnectionFactory().getStatementCache();\r
+ \r
+ if (statementCache == null)\r
+ return null;\r
+\r
+ // statement caching disable when in DDL mode\r
+ if (dataDictionaryInWriteMode()) {\r
+ return null;\r
+ }\r
+\r
+ Cacheable cachedItem = statementCache.find(statement);\r
+\r
+ CachedStatement cs = (CachedStatement) cachedItem;\r
+\r
+\r
+ GenericPreparedStatement ps = cs.getPreparedStatement();\r
+\r
+ synchronized (ps) {\r
+ if (ps.upToDate()) {\r
+ GeneratedClass ac = ps.getActivationClass();\r
+\r
+ // Check to see if the statement was prepared before some change\r
+ // in the class loading set. If this is the case then force it to be invalid\r
+ int currentClasses =\r
+ getLanguageConnectionFactory().getClassFactory().getClassLoaderVersion();\r
+\r
+ if (ac.getClassLoaderVersion() != currentClasses) {\r
+ ps.makeInvalid(DependencyManager.INTERNAL_RECOMPILE_REQUEST, this);\r
+ }\r
+\r
+ // note that the PreparedStatement is not kept in the cache. This is because\r
+ // having items kept in the cache that ultimately are held onto by\r
+ // user code is impossible to manage. E.g. an open ResultSet would hold onto\r
+ // a PreparedStatement (through its activation) and the user can allow\r
+ // this object to be garbage collected. Pushing a context stack is impossible\r
+ // in garbage collection as it may deadlock with the open connection and\r
+ // the context manager assumes a singel current thread per context stack\r
+ }\r
+ }\r
+\r
+ statementCache.release(cachedItem);\r
+ return ps;\r
+ }\r
+\r
+ /**\r
+ Get a connection unique system generated name for a cursor.\r
+ */\r
+ public String getUniqueCursorName() \r
+ {\r
+ return getNameString("SQLCUR", nextCursorId++);\r
+ }\r
+\r
+ /**\r
+ Get a connection unique system generated name for an unnamed savepoint.\r
+ */\r
+ public String getUniqueSavepointName()\r
+ {\r
+ return getNameString("SAVEPT", nextSavepointId++);\r
+ }\r
+\r
+ /**\r
+ Get a connection unique system generated id for an unnamed savepoint.\r
+ */\r
+ public int getUniqueSavepointID()\r
+ {\r
+ return nextSavepointId-1;\r
+ }\r
+\r
+ /**\r
+ * Build a String for a statement name.\r
+ *\r
+ * @param prefix The prefix for the statement name.\r
+ * @param number The number to append for uniqueness\r
+ *\r
+ * @return A unique String for a statement name.\r
+ */\r
+ private String getNameString(String prefix, long number)\r
+ {\r
+ if (sb != null)\r
+ {\r
+ sb.setLength(0);\r
+ }\r
+ else\r
+ {\r
+ sb = new StringBuffer();\r
+ }\r
+ sb.append(prefix).append(number);\r
+\r
+ return sb.toString();\r
+ }\r
+\r
+ /**\r
+ * Do a commit as appropriate for an internally generated\r
+ * commit (e.g. as needed by sync, or autocommit).\r
+ *\r
+ * @param commitStore true if we should commit the Store transaction\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ public void internalCommit(boolean commitStore) throws StandardException\r
+ {\r
+ doCommit(commitStore,\r
+ true,\r
+ NON_XA,\r
+ false);\r
+ }\r
+\r
+ /**\r
+ * Do a commmit as is appropriate for a user requested\r
+ * commit (e.g. a java.sql.Connection.commit() or a language\r
+ * 'COMMIT' statement. Does some extra checking to make\r
+ * sure that users aren't doing anything bad.\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ public void userCommit() throws StandardException\r
+ {\r
+ doCommit(true,\r
+ true,\r
+ NON_XA,\r
+ true);\r
+ }\r
+\r
+\r
+ /**\r
+ Commit the language transaction by doing a commitNoSync()\r
+ on the store's TransactionController.\r
+\r
+ <p>\r
+ Do *NOT* tell the data dictionary that the transaction is\r
+ finished. The reason is that this would allow other transactions\r
+ to see comitted DDL that could be undone in the event of a\r
+ system crash.\r
+\r
+ @param commitflag the flags to pass to commitNoSync in the store's\r
+ TransactionController\r
+\r
+ @exception StandardException thrown on failure\r
+ */\r
+ public final void internalCommitNoSync(int commitflag) throws StandardException\r
+ {\r
+ doCommit(true, false, commitflag, false);\r
+ }\r
+\r
+\r
+ /**\r
+ Same as userCommit except commit a distributed transaction. \r
+ This commit always commit store and sync the commit.\r
+\r
+ @param onePhase if true, allow it to commit without first going thru a\r
+ prepared state.\r
+\r
+ @exception StandardException thrown if something goes wrong\r
+ */\r
+ public final void xaCommit(boolean onePhase) throws StandardException\r
+ {\r
+ // further overload internalCommit to make it understand 2 phase commit\r
+ doCommit(true /* commit store */,\r
+ true /* sync */,\r
+ onePhase ? XA_ONE_PHASE : XA_TWO_PHASE,\r
+ true);\r
+ }\r
+\r
+\r
+ /**\r
+ * This is where the work on internalCommit(), userCOmmit() and \r
+ * internalCommitNoSync() actually takes place.\r
+ * <p>\r
+ * When a commit happens, the language connection context\r
+ * will close all open activations/cursors and commit the\r
+ * Store transaction.\r
+ * <p>\r
+ * REVISIT: we talked about having a LanguageTransactionContext,\r
+ * but since store transaction management is currently in flux\r
+ * and our context might want to delegate to that context,\r
+ * for now all commit/rollback actions are handled directly by\r
+ * the language connection context.\r
+ * REVISIT: this may need additional alterations when\r
+ * RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT show up.\r
+ * <P>\r
+ * Since the access manager's own context takes care of its own\r
+ * resources on commit, and the transaction stays open, there is\r
+ * nothing that this context has to do with the transaction controller.\r
+ * <p>\r
+ * Also, tell the data dictionary that the transaction is finished,\r
+ * if necessary (that is, if the data dictionary was put into\r
+ * DDL mode in this transaction.\r
+ *\r
+ *\r
+ * @param commitStore true if we should commit the Store transaction\r
+ * @param sync true means do a synchronized commit,\r
+ * false means do an unsynchronized commit\r
+ * @param commitflag if this is an unsynchronized commit, the flags to\r
+ * pass to commitNoSync in the store's\r
+ * TransactionController. If this is a synchronized\r
+ * commit, this flag is overloaded for xacommit.\r
+ * @param requestedByUser False iff the commit is for internal use and\r
+ * we should ignore the check to prevent commits\r
+ * in an atomic statement.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ protected void doCommit(boolean commitStore,\r
+ boolean sync,\r
+ int commitflag,\r
+ boolean requestedByUser)\r
+ throws StandardException\r
+ {\r
+ StatementContext statementContext = getStatementContext();\r
+ if (requestedByUser &&\r
+ (statementContext != null) &&\r
+ statementContext.inUse() &&\r
+ statementContext.isAtomic())\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_NO_COMMIT_IN_NESTED_CONNECTION);\r
+ }\r
+\r
+ // Log commit to error log, if appropriate\r
+ if (logStatementText)\r
+ {\r
+ if (istream == null)\r
+ {\r
+ istream = Monitor.getStream();\r
+ }\r
+ String xactId = tran.getTransactionIdString();\r
+ istream.printlnWithHeader(LanguageConnectionContext.xidStr + \r
+ xactId + \r
+ "), " +\r
+ LanguageConnectionContext.lccStr +\r
+ instanceNumber +\r
+ "), " + LanguageConnectionContext.dbnameStr +\r
+ dbname +\r
+ "), " +\r
+ LanguageConnectionContext.drdaStr +\r
+ drdaID +\r
+ "), Committing");\r
+ }\r
+\r
+ endTransactionActivationHandling(false);\r
+\r
+ //do the clean up work required for temporary tables at the commit time. This cleanup work\r
+ //can possibly remove entries from allDeclaredGlobalTempTables and that's why we need to check\r
+ //again later to see if we there are still any entries in allDeclaredGlobalTempTables\r
+ if (allDeclaredGlobalTempTables != null)\r
+ {\r
+ tempTablesAndCommit();\r
+ //at commit time, for all the temp tables declared with ON COMMIT DELETE ROWS, make sure there are no held cursor open on them.\r
+ //If there are no held cursors open on ON COMMIT DELETE ROWS, drop those temp tables and redeclare them to get rid of all the data in them\r
+ if (allDeclaredGlobalTempTables != null) {\r
+ for (int i=0; i<allDeclaredGlobalTempTables.size(); i++)\r
+ {\r
+ TableDescriptor td = ((TempTableInfo)(allDeclaredGlobalTempTables.get(i))).getTableDescriptor();\r
+ if (td.isOnCommitDeleteRows() == false) //do nothing for temp table with ON COMMIT PRESERVE ROWS\r
+ {\r
+ continue;\r
+ }\r
+ if (checkIfAnyActivationHasHoldCursor(td.getName()) == false)//temp tables with ON COMMIT DELETE ROWS and no open held cursors\r
+ {\r
+ getDataDictionary().getDependencyManager().invalidateFor(td, DependencyManager.DROP_TABLE, this);\r
+ cleanupTempTableOnCommitOrRollback(td, true);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ currentSavepointLevel = 0; //reset the current savepoint level for the connection to 0 at the end of commit work for temp tables\r
+\r
+ // Do *NOT* tell the DataDictionary to start using its cache again\r
+ // if this is an unsynchronized commit. The reason is that it\r
+ // would allow other transactions to see this transaction's DDL,\r
+ // which could be rolled back in case of a system crash.\r
+ if (sync)\r
+ {\r
+ finishDDTransaction();\r
+ }\r
+ \r
+ // Check that any nested transaction has been destoyed\r
+ // before a commit.\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (readOnlyNestedTransaction != null)\r
+ {\r
+ SanityManager.THROWASSERT("Nested transaction active!");\r
+ }\r
+ }\r
+\r
+ // now commit the Store transaction\r
+ TransactionController tc = getTransactionExecute();\r
+ if ( tc != null && commitStore ) \r
+ { \r
+ if (sync)\r
+ {\r
+ if (commitflag == NON_XA)\r
+ {\r
+ // regular commit\r
+ tc.commit();\r
+ }\r
+ else\r
+ {\r
+ // This may be a xa_commit, check overloaded commitflag.\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(commitflag == XA_ONE_PHASE ||\r
+ commitflag == XA_TWO_PHASE,\r
+ "invalid commit flag");\r
+\r
+ ((XATransactionController)tc).xa_commit(commitflag == XA_ONE_PHASE);\r
+\r
+ }\r
+ }\r
+ else\r
+ {\r
+ tc.commitNoSync(commitflag);\r
+ }\r
+\r
+ // reset the savepoints to the new\r
+ // location, since any outer nesting\r
+ // levels expect there to be a savepoint\r
+ resetSavepoints();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * If dropAndRedeclare is true, that means we have come here for temp \r
+ * tables with on commit delete rows and no held curosr open on them. We \r
+ * will drop the existing conglomerate and redeclare a new conglomerate\r
+ * similar to old conglomerate. This is a more efficient way of deleting \r
+ * all rows from the table.\r
+ *\r
+ * If dropAndRedeclare is false, that means we have come here for the \r
+ * rollback cleanup work. We are trying to restore old definition of the \r
+ * temp table (because the drop on it is being rolled back).\r
+ */\r
+ private TableDescriptor cleanupTempTableOnCommitOrRollback(\r
+ TableDescriptor td, \r
+ boolean dropAndRedeclare)\r
+ throws StandardException\r
+ {\r
+ //create new conglomerate with same properties as the old conglomerate \r
+ //and same row template as the old conglomerate\r
+ long conglomId = \r
+ tran.createConglomerate(\r
+ "heap", // we're requesting a heap conglomerate\r
+ td.getEmptyExecRow().getRowArray(), // row template\r
+ null, //column sort order - not required for heap\r
+ td.getColumnCollationIds(), // same ids as old conglomerate\r
+ null, // properties\r
+ (TransactionController.IS_TEMPORARY | \r
+ TransactionController.IS_KEPT));\r
+\r
+ long cid = td.getHeapConglomerateId();\r
+\r
+ //remove the old conglomerate descriptor from the table descriptor\r
+ ConglomerateDescriptor cgd = td.getConglomerateDescriptor(cid);\r
+ td.getConglomerateDescriptorList().dropConglomerateDescriptorByUUID(cgd.getUUID());\r
+ //add the new conglomerate descriptor to the table descriptor\r
+ cgd = getDataDictionary().getDataDescriptorGenerator().newConglomerateDescriptor(conglomId, null, false, null, false, null, td.getUUID(),\r
+ td.getSchemaDescriptor().getUUID());\r
+ ConglomerateDescriptorList conglomList = td.getConglomerateDescriptorList();\r
+ conglomList.add(cgd);\r
+\r
+ //reset the heap conglomerate number in table descriptor to -1 so it will be refetched next time with the new value\r
+ td.resetHeapConglomNumber();\r
+\r
+ if(dropAndRedeclare)\r
+ {\r
+ tran.dropConglomerate(cid); //remove the old conglomerate from the system\r
+ replaceDeclaredGlobalTempTable(td.getName(), td);\r
+ }\r
+\r
+ return(td);\r
+ }\r
+\r
+ /**\r
+ Do a rollback as appropriate for an internally generated\r
+ rollback (e.g. as needed by sync, or autocommit).\r
+ \r
+ When a rollback happens, we \r
+ close all open activations and invalidate their\r
+ prepared statements. We then tell the cache to\r
+ age out everything else, which effectively invalidates\r
+ them. Thus, all prepared statements will be\r
+ compiled anew on their 1st execution after\r
+ a rollback.\r
+ <p>\r
+ The invalidated statements can revalidate themselves without\r
+ a full recompile if they verify their dependencies' providers still\r
+ exist unchanged. REVISIT when invalidation types are created.\r
+ <p>\r
+ REVISIT: this may need additional alterations when\r
+ RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT show up.\r
+ <p>\r
+ Also, tell the data dictionary that the transaction is finished,\r
+ if necessary (that is, if the data dictionary was put into\r
+ DDL mode in this transaction.\r
+\r
+ @exception StandardException thrown on failure\r
+ */\r
+\r
+ public void internalRollback() throws StandardException \r
+ {\r
+ doRollback(false /* non-xa */, false);\r
+ }\r
+\r
+ /**\r
+ * Do a rollback as is appropriate for a user requested\r
+ * rollback (e.g. a java.sql.Connection.rollback() or a language\r
+ * 'ROLLBACk' statement. Does some extra checking to make\r
+ * sure that users aren't doing anything bad.\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ public void userRollback() throws StandardException\r
+ {\r
+ doRollback(false /* non-xa */, true);\r
+ }\r
+\r
+ /**\r
+ Same as userRollback() except rolls back a distrubuted transaction.\r
+\r
+ @exception StandardException thrown if something goes wrong\r
+ */\r
+ public void xaRollback() throws StandardException\r
+ {\r
+ doRollback(true /* xa */, true);\r
+ }\r
+\r
+ /**\r
+ * When a rollback happens, the language connection context\r
+ * will close all open activations and invalidate\r
+ * their prepared statements. Then the language will abort the\r
+ * Store transaction.\r
+ * <p>\r
+ * The invalidated statements can revalidate themselves without\r
+ * a full recompile if they verify their dependencies' providers still\r
+ * exist unchanged. REVISIT when invalidation types are created.\r
+ * <p>\r
+ * REVISIT: this may need additional alterations when\r
+ * RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT show up.\r
+ * <p>\r
+ * Also, tell the data dictionary that the transaction is finished,\r
+ * if necessary (that is, if the data dictionary was put into\r
+ * DDL mode in this transaction.\r
+ *\r
+ * @param xa true if this is an xa rollback\r
+ * @param requestedByUser true if requested by user\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ private void doRollback(boolean xa, boolean requestedByUser) throws StandardException\r
+ {\r
+ StatementContext statementContext = getStatementContext();\r
+ if (requestedByUser &&\r
+ (statementContext != null) &&\r
+ statementContext.inUse() &&\r
+ statementContext.isAtomic())\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_NO_ROLLBACK_IN_NESTED_CONNECTION);\r
+ }\r
+\r
+ // Log rollback to error log, if appropriate\r
+ if (logStatementText)\r
+ {\r
+ if (istream == null)\r
+ {\r
+ istream = Monitor.getStream();\r
+ }\r
+ String xactId = tran.getTransactionIdString();\r
+ istream.printlnWithHeader(LanguageConnectionContext.xidStr +\r
+ xactId + \r
+ "), " +\r
+ LanguageConnectionContext.lccStr +\r
+ instanceNumber +\r
+ "), " + LanguageConnectionContext.dbnameStr +\r
+ dbname +\r
+ "), " +\r
+ LanguageConnectionContext.dbnameStr +\r
+ dbname +\r
+ "), " +\r
+ LanguageConnectionContext.drdaStr +\r
+ drdaID +\r
+ "), Rolling back");\r
+ }\r
+\r
+ endTransactionActivationHandling(true);\r
+\r
+ currentSavepointLevel = 0; //reset the current savepoint level for the connection to 0 at the beginning of rollback work for temp tables\r
+ if (allDeclaredGlobalTempTables != null)\r
+ tempTablesAndRollback();\r
+\r
+ finishDDTransaction();\r
+ \r
+ // If a nested transaction is active then\r
+ // ensure it is destroyed before working\r
+ // with the user transaction.\r
+ if (readOnlyNestedTransaction != null)\r
+ {\r
+ readOnlyNestedTransaction.destroy();\r
+ readOnlyNestedTransaction = null;\r
+ queryNestingDepth = 0;\r
+ }\r
+\r
+ // now rollback the Store transaction\r
+ TransactionController tc = getTransactionExecute();\r
+ if (tc != null) \r
+ { \r
+ if (xa)\r
+ ((XATransactionController)tc).xa_rollback();\r
+ else\r
+ tc.abort(); \r
+ \r
+ // reset the savepoints to the new\r
+ // location, since any outer nesting\r
+ // levels expet there to be a savepoint\r
+ resetSavepoints();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Reset all statement savepoints. Traverses the StatementContext\r
+ * stack from bottom to top, calling resetSavePoint()\r
+ * on each element.\r
+ *\r
+ * @exception StandardException thrown if something goes wrong\r
+ */\r
+ private void resetSavepoints() throws StandardException \r
+ {\r
+ final ContextManager cm = getContextManager();\r
+ final List stmts = cm.getContextStack(org.apache.derby.\r
+ iapi.reference.\r
+ ContextId.LANG_STATEMENT);\r
+ final int end = stmts.size();\r
+ for (int i = 0; i < end; ++i) {\r
+ ((StatementContext)stmts.get(i)).resetSavePoint();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Let the context deal with a rollback to savepoint\r
+ *\r
+ * @param savepointName Name of the savepoint that needs to be rolled back\r
+ * @param refreshStyle boolean indicating whether or not the controller should close\r
+ * open conglomerates and scans. Also used to determine if language should close\r
+ * open activations.\r
+ * @param kindOfSavepoint A NULL value means it is an internal savepoint (ie not a user defined savepoint)\r
+ * Non NULL value means it is a user defined savepoint which can be a SQL savepoint or a JDBC savepoint\r
+ * A String value for kindOfSavepoint would mean it is SQL savepoint\r
+ * A JDBC Savepoint object value for kindOfSavepoint would mean it is JDBC savepoint\r
+ *\r
+ * @exception StandardException thrown if something goes wrong\r
+ */\r
+ public void internalRollbackToSavepoint( String savepointName, boolean refreshStyle, Object kindOfSavepoint )\r
+ throws StandardException\r
+ {\r
+ // now rollback the Store transaction to the savepoint\r
+ TransactionController tc = getTransactionExecute();\r
+ if (tc != null)\r
+ {\r
+ boolean closeConglomerates;\r
+\r
+ if ( refreshStyle ) \r
+ {\r
+ closeConglomerates = true;\r
+ // bug 5145 - don't forget to close the activations while rolling\r
+ // back to a savepoint\r
+ endTransactionActivationHandling(true);\r
+ }\r
+ else { closeConglomerates = false; }\r
+\r
+ currentSavepointLevel = tc.rollbackToSavePoint( savepointName, closeConglomerates, kindOfSavepoint );\r
+ }\r
+\r
+ if (tc != null && refreshStyle && allDeclaredGlobalTempTables != null)\r
+ tempTablesAndRollback();\r
+ }\r
+\r
+ /**\r
+ Let the context deal with a release of a savepoint\r
+\r
+ @param savepointName Name of the savepoint that needs to be released\r
+ @param kindOfSavepoint A NULL value means it is an internal savepoint (ie not a user defined savepoint)\r
+ Non NULL value means it is a user defined savepoint which can be a SQL savepoint or a JDBC savepoint\r
+ A String value for kindOfSavepoint would mean it is SQL savepoint\r
+ A JDBC Savepoint object value for kindOfSavepoint would mean it is JDBC savepoint\r
+\r
+ @exception StandardException thrown if something goes wrong\r
+ */\r
+ public void releaseSavePoint( String savepointName, Object kindOfSavepoint ) throws StandardException\r
+ {\r
+ TransactionController tc = getTransactionExecute();\r
+ if (tc != null)\r
+ {\r
+ currentSavepointLevel = tc.releaseSavePoint( savepointName, kindOfSavepoint );\r
+ //after a release of a savepoint, we need to go through our temp tables list.\r
+ if (allDeclaredGlobalTempTables != null)\r
+ tempTablesReleaseSavepointLevels();\r
+ }\r
+ }\r
+\r
+ /**\r
+ Sets a savepoint. Causes the Store to set a savepoint.\r
+\r
+ @param savepointName name of savepoint\r
+ @param kindOfSavepoint A NULL value means it is an internal savepoint (ie not a user defined savepoint)\r
+ Non NULL value means it is a user defined savepoint which can be a SQL savepoint or a JDBC savepoint\r
+ A String value for kindOfSavepoint would mean it is SQL savepoint\r
+ A JDBC Savepoint object value for kindOfSavepoint would mean it is JDBC savepoint\r
+\r
+ @exception StandardException thrown if something goes wrong\r
+ */\r
+ public void languageSetSavePoint( String savepointName, Object kindOfSavepoint ) throws StandardException\r
+ {\r
+ TransactionController tc = getTransactionExecute();\r
+ if (tc != null)\r
+ {\r
+ currentSavepointLevel = tc.setSavePoint( savepointName, kindOfSavepoint );\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Start a Nested User Transaction (NUT) with the store. If a NUT is \r
+ * already active simply increment a counter, queryNestingDepth, to keep\r
+ * track of how many times we have tried to start a NUT.\r
+ */\r
+ public void beginNestedTransaction(boolean readOnly) throws StandardException\r
+ {\r
+ // DERBY-2490 incremental rework, currently this is only called\r
+ // with read-only true. Future changes will have this\r
+ // method support read-write nested transactions as well\r
+ // instead of callers using the startNestedUserTransaction\r
+ // directly on tran.\r
+ if (readOnlyNestedTransaction == null)\r
+ readOnlyNestedTransaction = tran.startNestedUserTransaction(readOnly);\r
+ queryNestingDepth++;\r
+ }\r
+\r
+ public void commitNestedTransaction()\r
+ throws StandardException\r
+ {\r
+ if (--queryNestingDepth == 0)\r
+ {\r
+ readOnlyNestedTransaction.commit();\r
+ readOnlyNestedTransaction.destroy();\r
+ readOnlyNestedTransaction = null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the transaction controller to use at compile time with this language\r
+ * connection context. If a NUT is active then return NUT else return parent\r
+ * transaction.\r
+ */\r
+ public TransactionController getTransactionCompile()\r
+ {\r
+ return (readOnlyNestedTransaction != null) ? readOnlyNestedTransaction : tran;\r
+ }\r
+\r
+ public TransactionController getTransactionExecute()\r
+ {\r
+ return tran;\r
+ }\r
+\r
+ /** Get the data value factory to use with this language connection\r
+ context.\r
+ */\r
+ public DataValueFactory getDataValueFactory() {\r
+ return dataFactory;\r
+ }\r
+ \r
+ /**\r
+ Get the language factory to use with this language connection\r
+ context.\r
+ */\r
+ public LanguageFactory getLanguageFactory() {\r
+ return langFactory;\r
+ }\r
+ \r
+ public OptimizerFactory getOptimizerFactory() {\r
+ return of;\r
+ }\r
+\r
+ /**\r
+ Get the language connection factory to use with this language connection\r
+ context.\r
+ */\r
+ public LanguageConnectionFactory getLanguageConnectionFactory() {\r
+ return connFactory;\r
+ }\r
+\r
+ /**\r
+ * check if there are any activations that reference this temporary table\r
+ * @param tableName look for any activations referencing this table name\r
+ * @return boolean false if found no activations\r
+ */\r
+ private boolean checkIfAnyActivationHasHoldCursor(String tableName)\r
+ throws StandardException\r
+ {\r
+ for (int i = acts.size() - 1; i >= 0; i--) {\r
+ Activation a = (Activation) acts.get(i);\r
+ if (a.checkIfThisActivationHasHoldCursor(tableName))\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+\r
+ /**\r
+ * Verify that there are no activations with open held result sets.\r
+ *\r
+ * @return boolean Found no open (held) resultsets.\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ /* This gets used in case of hold cursors. If there are any hold cursors open\r
+ * then user can't change the isolation level without closing them. At the\r
+ * execution time, set transaction isolation level calls this method before\r
+ * changing the isolation level.\r
+ */\r
+ public boolean verifyAllHeldResultSetsAreClosed()\r
+ throws StandardException\r
+ {\r
+ boolean seenOpenResultSets = false;\r
+\r
+ /* For every activation */\r
+ for (int i = acts.size() - 1; i >= 0; i--) {\r
+\r
+ Activation a = (Activation) acts.get(i);\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(a instanceof CursorActivation, "a is not a CursorActivation");\r
+ }\r
+\r
+ if (!a.isInUse())\r
+ {\r
+ continue;\r
+ }\r
+\r
+ if (!a.getResultSetHoldability())\r
+ {\r
+ continue;\r
+ }\r
+\r
+ ResultSet rs = ((CursorActivation) a).getResultSet();\r
+\r
+ /* is there an open result set? */\r
+ if ((rs != null) && !rs.isClosed() && rs.returnsRows())\r
+ {\r
+ seenOpenResultSets = true;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (!seenOpenResultSets)\r
+ return(true);\r
+\r
+ // There may be open ResultSet's that are yet to be garbage collected\r
+ // let's try and force these out rather than throw an error\r
+ System.gc();\r
+ System.runFinalization();\r
+\r
+\r
+ /* For every activation */\r
+ for (int i = acts.size() - 1; i >= 0; i--) {\r
+ \r
+ Activation a = (Activation) acts.get(i);\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(a instanceof CursorActivation, "a is not a CursorActivation");\r
+ }\r
+\r
+ if (!a.isInUse())\r
+ {\r
+ continue;\r
+ }\r
+\r
+ if (!a.getResultSetHoldability())\r
+ {\r
+ continue;\r
+ }\r
+\r
+ ResultSet rs = ((CursorActivation) a).getResultSet();\r
+\r
+ /* is there an open held result set? */\r
+ if ((rs != null) && !rs.isClosed() && rs.returnsRows())\r
+ {\r
+ return(false);\r
+ }\r
+ }\r
+ return(true);\r
+ }\r
+\r
+ /**\r
+ * Verify that there are no activations with open result sets\r
+ * on the specified prepared statement.\r
+ *\r
+ * @param pStmt The prepared Statement\r
+ * @param provider The object precipitating a possible invalidation\r
+ * @param action The action causing the possible invalidation\r
+ *\r
+ * @return Nothing.\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ public boolean verifyNoOpenResultSets(PreparedStatement pStmt, Provider provider,\r
+ int action)\r
+ throws StandardException\r
+ {\r
+ /*\r
+ ** It is not a problem to create an index when there is an open\r
+ ** result set, since it doesn't invalidate the access path that was\r
+ ** chosen for the result set.\r
+ */\r
+ boolean seenOpenResultSets = false;\r
+\r
+ /* For every activation */\r
+\r
+ // synchronize on acts as other threads may be closing activations\r
+ // in this list, thus invalidating the Enumeration\r
+ for (int i = acts.size() - 1; i >= 0; i--) {\r
+ \r
+ Activation a = (Activation) acts.get(i);\r
+\r
+ if (!a.isInUse())\r
+ {\r
+ continue;\r
+ }\r
+ \r
+ /* for this prepared statement */\r
+ if (pStmt == a.getPreparedStatement()) {\r
+ ResultSet rs = a.getResultSet();\r
+\r
+ /* is there an open result set? */\r
+ if (rs != null && ! rs.isClosed())\r
+ {\r
+ if (!rs.returnsRows())\r
+ continue;\r
+ seenOpenResultSets = true;\r
+ break;\r
+ }\r
+ \r
+ }\r
+ }\r
+\r
+ if (!seenOpenResultSets)\r
+ return false;\r
+\r
+ // There may be open ResultSet's that are yet to be garbage collected\r
+ // let's try and force these out rather than throw an error\r
+ System.gc();\r
+ System.runFinalization();\r
+\r
+\r
+ /* For every activation */\r
+ // synchronize on acts as other threads may be closing activations\r
+ // in this list, thus invalidating the Enumeration\r
+ for (int i = acts.size() - 1; i >= 0; i--) {\r
+ \r
+ Activation a = (Activation) acts.get(i);\r
+\r
+ if (!a.isInUse())\r
+ {\r
+ continue;\r
+ }\r
+\r
+ /* for this prepared statement */\r
+ if (pStmt == a.getPreparedStatement()) {\r
+ ResultSet rs = a.getResultSet();\r
+\r
+ /* is there an open result set? */\r
+ if (rs != null && ! rs.isClosed())\r
+ {\r
+ if ((provider != null) && rs.returnsRows()) {\r
+ DependencyManager dmgr = getDataDictionary().getDependencyManager();\r
+\r
+ throw StandardException.newException(SQLState.LANG_CANT_INVALIDATE_OPEN_RESULT_SET, \r
+ dmgr.getActionString(action), \r
+ provider.getObjectName());\r
+\r
+ }\r
+ return true;\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Get the Authorization Id\r
+ *\r
+ * @return String the authorization id\r
+ */\r
+ public String getAuthorizationId()\r
+ { \r
+ return authorizer.getAuthorizationId();\r
+ }\r
+\r
+ /**\r
+ * Get the default schema\r
+ *\r
+ * @return SchemaDescriptor the default schema\r
+ */\r
+ public SchemaDescriptor getDefaultSchema() \r
+ { \r
+ return sd; \r
+ }\r
+ /**\r
+ * Get the current schema name\r
+ *\r
+ * @return current schema name\r
+ */\r
+ public String getCurrentSchemaName()\r
+ {\r
+ if( null == sd)\r
+ return null;\r
+ return sd.getSchemaName();\r
+ }\r
+\r
+ /**\r
+ * Set the default schema -- used by SET SCHEMA.\r
+ * \r
+ * @param sd the new default schema.\r
+ * If null, then the default schema descriptor is used.\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ public void setDefaultSchema(SchemaDescriptor sd)\r
+ throws StandardException\r
+ { \r
+ if (sd == null)\r
+ { \r
+ sd = initDefaultSchemaDescriptor();\r
+ }\r
+ this.sd = sd;\r
+ \r
+ }\r
+\r
+ /**\r
+ * Get the identity column value most recently generated.\r
+ *\r
+ * @return the generated identity column value\r
+ */\r
+ public Long getIdentityValue()\r
+ {\r
+ return identityNotNull ? new Long(identityVal) : null;\r
+ }\r
+\r
+ /**\r
+ * Set the field of most recently generated identity column value.\r
+ *\r
+ * @param val the generated identity column value\r
+ */\r
+ public void setIdentityValue(long val)\r
+ {\r
+ identityVal = val;\r
+ identityNotNull = true;\r
+ }\r
+\r
+ /**\r
+ * Push a CompilerContext on the context stack with\r
+ * the current default schema as the default schema\r
+ * which we compile against.\r
+ *\r
+ * @return the compiler context\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ public final CompilerContext pushCompilerContext()\r
+ {\r
+ return pushCompilerContext((SchemaDescriptor)null);\r
+ }\r
+\r
+ /**\r
+ * Push a CompilerContext on the context stack with\r
+ * the passed in schema sd as the default schema\r
+ * we compile against.\r
+ *\r
+ * @param sd the default schema \r
+ *\r
+ * @return the compiler context\r
+ *\r
+ * For the parameter sd, there are 3 possible values(of interest) that can \r
+ * get passed into this method:\r
+ * \r
+ * a) A null SchemaDescriptor which indicates to the system to use the \r
+ * CURRENT SCHEMA as the compilation schema.\r
+ * \r
+ * b) A SchemaDescriptor with its UUID == null, this indicates that either \r
+ * the schema has not been physically created yet or that the LCC's \r
+ * getDefaultSchema() is not yet up-to-date with its actual UUID. \r
+ * The system will use the CURRENT SCHEMA as the compilation schema. \r
+ * \r
+ * c) A SchemaDescriptor with its UUID != null, this means that the schema \r
+ * has been physically created. The system will use this schema as the \r
+ * compilation schema (e.g.: for trigger or view recompilation cases, \r
+ * etc.). \r
+ * \r
+ * The compiler context's compilation schema will be set accordingly based \r
+ * on the given input above. \r
+ */\r
+ public CompilerContext pushCompilerContext(SchemaDescriptor sd)\r
+ {\r
+ CompilerContext cc;\r
+ boolean firstCompilerContext = false;\r
+\r
+ // DEBUG END\r
+\r
+ cc = (CompilerContext) (getContextManager().getContext(CompilerContext.CONTEXT_ID));\r
+\r
+ /*\r
+ ** If there is no compiler context, this is the first one on the\r
+ ** stack, so don't pop it when we're done (saves time).\r
+ */\r
+ if (cc == null) { firstCompilerContext = true; }\r
+\r
+ if (cc == null || cc.getInUse())\r
+ {\r
+ cc = new CompilerContextImpl(getContextManager(), this, tcf);\r
+ if (firstCompilerContext) { cc.firstOnStack(); }\r
+ }\r
+ else\r
+ {\r
+ /* Reset the next column,table, subquery and ResultSet numbers at \r
+ * the beginning of each statement \r
+ */\r
+ cc.resetContext();\r
+ }\r
+\r
+ cc.setInUse(true);\r
+\r
+ // Save off the current isolation level on entry so that it gets restored\r
+ cc.setEntryIsolationLevel( getCurrentIsolationLevel());\r
+\r
+ StatementContext sc = getStatementContext();\r
+ if (sc.getSystemCode())\r
+ cc.setReliability(CompilerContext.INTERNAL_SQL_LEGAL);\r
+\r
+ /*\r
+ * Set the compilation schema when its UUID is available.\r
+ * i.e.: Schema may not have been physically created yet, so\r
+ * its UUID will be null.\r
+ * \r
+ * o For trigger SPS recompilation, the system must use its\r
+ * compilation schema to recompile the statement. \r
+ * \r
+ * o For view recompilation, we set the compilation schema\r
+ * for this compiler context if its UUID is available.\r
+ * Otherwise, the compilation schema will be determined\r
+ * at execution time of view creation.\r
+ */\r
+ if (sd != null && sd.getUUID() != null)\r
+ {\r
+ cc.setCompilationSchema(sd);\r
+ }\r
+ \r
+ return cc;\r
+ }\r
+\r
+\r
+ /**\r
+ * Pop a CompilerContext off the context stack.\r
+ *\r
+ * @param cc The compiler context.\r
+ */\r
+ public void popCompilerContext(CompilerContext cc)\r
+ {\r
+ cc.setCurrentDependent(null);\r
+\r
+ cc.setInUse(false);\r
+\r
+ // Restore the isolation level at the time of entry to CompilerContext\r
+ isolationLevel = cc.getEntryIsolationLevel();\r
+\r
+ /*\r
+ ** Only pop the compiler context if it's not the first one\r
+ ** on the stack.\r
+ */\r
+ if (! cc.isFirstOnStack()) \r
+ { \r
+ cc.popMe(); \r
+ }\r
+ else\r
+ {\r
+ cc.setCompilationSchema((SchemaDescriptor)null);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Push a StatementContext on the context stack.\r
+ *\r
+ * @param isAtomic whether this statement is atomic or not\r
+ * @param isForReadOnly whether this statement is for a read only resultset\r
+ * @param stmtText the text of the statement. Needed for any language\r
+ * statement (currently, for any statement that can cause a trigger\r
+ * to fire). Please set this unless you are some funky jdbc setXXX\r
+ * method or something.\r
+ * @param pvs parameter value set, if it has one\r
+ * @param rollbackParentContext True if 1) the statement context is\r
+ * NOT a top-level context, AND 2) in the event of a statement-level\r
+ * exception, the parent context needs to be rolled back, too.\r
+ * @param timeoutMillis timeout value for this statement, in milliseconds.\r
+ * The value 0 means that no timeout is set.\r
+ *\r
+ * @return StatementContext The statement context.\r
+ *\r
+ */\r
+ public StatementContext pushStatementContext (boolean isAtomic, boolean isForReadOnly, \r
+ String stmtText, ParameterValueSet pvs, \r
+ boolean rollbackParentContext, \r
+ long timeoutMillis)\r
+ {\r
+ int parentStatementDepth = statementDepth;\r
+ boolean inTrigger = false;\r
+ boolean parentIsAtomic = false;\r
+\r
+ // by default, assume we are going to use the outermost statement context\r
+ StatementContext statementContext = statementContexts[0];\r
+\r
+ /*\r
+ ** If we haven't allocated any statement contexts yet, allocate\r
+ ** the outermost stmt context now and push it.\r
+ */\r
+ if (statementContext == null)\r
+ {\r
+ statementContext = statementContexts[0] = new GenericStatementContext(this);\r
+ }\r
+ else if (statementDepth > 0)\r
+ {\r
+ StatementContext parentStatementContext;\r
+ /*\r
+ ** We also cache a 2nd statement context, though we still\r
+ ** push and pop it. Note, new contexts are automatically pushed.\r
+ */\r
+ if (statementDepth == 1)\r
+ {\r
+ statementContext = statementContexts[1];\r
+\r
+ if (statementContext == null)\r
+ statementContext = statementContexts[1] = new GenericStatementContext(this);\r
+ else\r
+ statementContext.pushMe();\r
+\r
+ parentStatementContext = statementContexts[0];\r
+ }\r
+ else\r
+ {\r
+ parentStatementContext = getStatementContext();\r
+ statementContext = new GenericStatementContext(this);\r
+ }\r
+\r
+ inTrigger = parentStatementContext.inTrigger() || (outermostTrigger == parentStatementDepth);\r
+ parentIsAtomic = parentStatementContext.isAtomic();\r
+ statementContext.setSQLAllowed(parentStatementContext.getSQLAllowed(), false);\r
+ if (parentStatementContext.getSystemCode())\r
+ statementContext.setSystemCode();\r
+ }\r
+\r
+ incrementStatementDepth();\r
+\r
+ statementContext.setInUse(inTrigger, isAtomic || parentIsAtomic, isForReadOnly, stmtText, pvs, timeoutMillis);\r
+ if (rollbackParentContext)\r
+ statementContext.setParentRollback();\r
+ return statementContext;\r
+ }\r
+\r
+ /**\r
+ * Pop a StatementContext of the context stack.\r
+ *\r
+ * @param statementContext The statement context.\r
+ * @param error The error, if any (Only relevant for DEBUG)\r
+ */\r
+ public void popStatementContext(StatementContext statementContext,\r
+ Throwable error) \r
+ {\r
+ if ( statementContext != null ) \r
+ { \r
+ /*\r
+ ** If someone beat us to the punch, then it is ok,\r
+ ** just silently ignore it. We probably got here\r
+ ** because we had a try catch block around a push/pop\r
+ ** statement context, and we already got the context\r
+ ** on a cleanupOnError.\r
+ */\r
+ if (!statementContext.inUse())\r
+ {\r
+ return;\r
+ }\r
+ statementContext.clearInUse(); \r
+ }\r
+\r
+ decrementStatementDepth();\r
+ if (statementDepth == -1)\r
+ {\r
+ /*\r
+ * Only ignore the pop request for an already\r
+ * empty stack when dealing with a session exception.\r
+ */\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ int severity = (error instanceof StandardException) ?\r
+ ((StandardException)error).getSeverity() :\r
+ 0;\r
+ SanityManager.ASSERT(error != null,\r
+ "Must have error to try popStatementContext with 0 depth");\r
+ SanityManager.ASSERT(\r
+ (severity == ExceptionSeverity.SESSION_SEVERITY),\r
+ "Must have session severity error to try popStatementContext with 0 depth");\r
+ SanityManager.ASSERT(statementContext == statementContexts[0],\r
+ "statementContext is expected to equal statementContexts[0]");\r
+ }\r
+ resetStatementDepth(); // pretend we did nothing.\r
+ }\r
+ else if (statementDepth == 0)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ /* Okay to pop last context on a session exception.\r
+ * (We call clean up on error when exiting connection.)\r
+ */\r
+ int severity = (error instanceof StandardException) ?\r
+ ((StandardException)error).getSeverity() :\r
+ 0;\r
+ if ((error == null) || \r
+ (severity != ExceptionSeverity.SESSION_SEVERITY))\r
+ {\r
+ SanityManager.ASSERT(statementContext == statementContexts[0],\r
+ "statementContext is expected to equal statementContexts[0]");\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(statementContext != statementContexts[0],\r
+ "statementContext is not expected to equal statementContexts[0]");\r
+ if (statementDepth <= 0)\r
+ SanityManager.THROWASSERT(\r
+ "statement depth expected to be >0, was "+statementDepth);\r
+ \r
+ if (getContextManager().getContext(statementContext.getIdName()) != statementContext)\r
+ {\r
+ SanityManager.THROWASSERT("trying to pop statement context from middle of stack");\r
+ }\r
+ }\r
+\r
+ statementContext.popMe(); \r
+ }\r
+\r
+ }\r
+\r
+ /**\r
+ * Push a new execution statement validator. An execution statement \r
+ * validator is an object that validates the current statement to\r
+ * ensure that it is permitted given the current execution context.\r
+ * An example of a validator a trigger ExecutionStmtValidator that\r
+ * doesn't allow ddl on the trigger target table.\r
+ * <p>\r
+ * Multiple ExecutionStmtValidators may be active at any given time.\r
+ * This mirrors the way there can be multiple connection nestings\r
+ * at a single time. The validation is performed by calling each\r
+ * validator's validateStatement() method. This yields the union\r
+ * of all validations.\r
+ *\r
+ * @param validator the validator to add\r
+ */\r
+ public void pushExecutionStmtValidator(ExecutionStmtValidator validator)\r
+ {\r
+ stmtValidators.add(validator);\r
+ }\r
+\r
+ /**\r
+ * Remove the validator. Does an object identity (validator == validator)\r
+ * comparison. Asserts that the validator is found.\r
+ *\r
+ * @param validator the validator to remove\r
+ *\r
+ * @exception StandardException on error\r
+ */\r
+ public void popExecutionStmtValidator(ExecutionStmtValidator validator)\r
+ throws StandardException\r
+ {\r
+ boolean foundElement = stmtValidators.remove(validator);\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!foundElement)\r
+ {\r
+ SanityManager.THROWASSERT("statement validator "+validator+" not found");\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Push a new trigger execution context.\r
+ * <p>\r
+ * Multiple TriggerExecutionContexts may be active at any given time.\r
+ *\r
+ * @param tec the trigger execution context\r
+ *\r
+ * @exception StandardException on trigger recursion error\r
+ */\r
+ public void pushTriggerExecutionContext(TriggerExecutionContext tec) throws StandardException\r
+ {\r
+ if (outermostTrigger == -1) \r
+ {\r
+ outermostTrigger = statementDepth; \r
+ }\r
+\r
+ /* Maximum 16 nesting levels allowed */\r
+ if (triggerExecutionContexts.size() >= Limits.DB2_MAX_TRIGGER_RECURSION)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_TRIGGER_RECURSION_EXCEEDED);\r
+ }\r
+\r
+ triggerExecutionContexts.add(tec);\r
+ }\r
+\r
+ /**\r
+ * Remove the tec. Does an object identity (tec == tec)\r
+ * comparison. Asserts that the tec is found.\r
+ *\r
+ * @param tec the tec to remove\r
+ *\r
+ * @exception StandardException on error\r
+ */\r
+ public void popTriggerExecutionContext(TriggerExecutionContext tec)\r
+ throws StandardException\r
+ {\r
+ if (outermostTrigger == statementDepth)\r
+ {\r
+ outermostTrigger = -1; \r
+ }\r
+\r
+ boolean foundElement = triggerExecutionContexts.remove(tec);\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!foundElement)\r
+ {\r
+ SanityManager.THROWASSERT("trigger execution context "+tec+" not found");\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the topmost tec. \r
+ *\r
+ * @return the tec\r
+ */\r
+ public TriggerExecutionContext getTriggerExecutionContext()\r
+ {\r
+ return triggerExecutionContexts.size() == 0 ? \r
+ (TriggerExecutionContext)null :\r
+ (TriggerExecutionContext)triggerExecutionContexts.get(\r
+ triggerExecutionContexts.size() - 1); \r
+ }\r
+\r
+ /**\r
+ * Validate a statement. Does so by stepping through all the validators\r
+ * and executing them. If a validator throws and exception, then the\r
+ * checking is stopped and the exception is passed up.\r
+ *\r
+ * @param constantAction the constantAction that is about to be executed (and\r
+ * should be validated\r
+ *\r
+ * @exception StandardException on validation failure\r
+ */\r
+ public void validateStmtExecution(ConstantAction constantAction)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(constantAction!=null, "constantAction is null");\r
+ }\r
+\r
+ if (stmtValidators.size() > 0)\r
+ {\r
+ for (Iterator it = stmtValidators.iterator(); it.hasNext(); )\r
+ {\r
+ ((ExecutionStmtValidator)it.next())\r
+ .validateStatement(constantAction);\r
+ }\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Set the trigger table descriptor. Used to compile\r
+ * statements that may special trigger pseudo tables.\r
+ *\r
+ * @param td the table that the trigger is \r
+ * defined upon\r
+ *\r
+ */\r
+ public void pushTriggerTable(TableDescriptor td)\r
+ {\r
+ triggerTables.add(td);\r
+ }\r
+\r
+ /**\r
+ * Remove the trigger table descriptor.\r
+ *\r
+ * @param td the table to remove from the stack.\r
+ */\r
+ public void popTriggerTable(TableDescriptor td)\r
+ {\r
+ boolean foundElement = triggerTables.remove(td);\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!foundElement)\r
+ {\r
+ SanityManager.THROWASSERT("trigger table not found: "+td);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the topmost trigger table descriptor\r
+ *\r
+ * @return the table descriptor, or null if we\r
+ * aren't in the middle of compiling a create\r
+ * trigger.\r
+ */\r
+ public TableDescriptor getTriggerTable()\r
+ {\r
+ return triggerTables.size() == 0 ? \r
+ (TableDescriptor)null :\r
+ (TableDescriptor)triggerTables.get(triggerTables.size() - 1);\r
+ }\r
+ /**\r
+ * @see LanguageConnectionContext#getDatabase\r
+ */\r
+ public Database\r
+ getDatabase()\r
+ {\r
+ return db;\r
+ }\r
+\r
+ /** @see LanguageConnectionContext#incrementBindCount */\r
+ public int incrementBindCount()\r
+ {\r
+ bindCount++;\r
+ return bindCount;\r
+ }\r
+\r
+ \r
+ /** @see LanguageConnectionContext#decrementBindCount */\r
+ public int decrementBindCount()\r
+ {\r
+ bindCount--;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (bindCount < 0)\r
+ SanityManager.THROWASSERT(\r
+ "Level of nested binding == " + bindCount);\r
+ }\r
+\r
+ return bindCount;\r
+ }\r
+\r
+ /** @see LanguageConnectionContext#getBindCount */\r
+ public int getBindCount()\r
+ {\r
+ return bindCount;\r
+ }\r
+\r
+ /** @see LanguageConnectionContext#setDataDictionaryWriteMode */\r
+ public final void setDataDictionaryWriteMode()\r
+ {\r
+ ddWriteMode = true;\r
+ }\r
+\r
+ /** @see LanguageConnectionContext#dataDictionaryInWriteMode */\r
+ public final boolean dataDictionaryInWriteMode()\r
+ {\r
+ return ddWriteMode;\r
+ }\r
+\r
+ /** @see LanguageConnectionContext#setRunTimeStatisticsMode */\r
+ public void setRunTimeStatisticsMode(boolean onOrOff)\r
+ {\r
+ runTimeStatisticsSetting = onOrOff;\r
+ }\r
+\r
+ /** @see LanguageConnectionContext#getRunTimeStatisticsMode */\r
+ public boolean getRunTimeStatisticsMode()\r
+ {\r
+ return runTimeStatisticsSetting;\r
+ }\r
+\r
+ /** @see LanguageConnectionContext#setStatisticsTiming */\r
+ public void setStatisticsTiming(boolean onOrOff)\r
+ {\r
+ statisticsTiming = onOrOff;\r
+ }\r
+\r
+ /** @see LanguageConnectionContext#getStatisticsTiming */\r
+ public boolean getStatisticsTiming()\r
+ {\r
+ return statisticsTiming;\r
+ }\r
+\r
+ /** @see LanguageConnectionContext#setRunTimeStatisticsObject */\r
+ public void setRunTimeStatisticsObject(RunTimeStatistics runTimeStatisticsObject)\r
+ {\r
+ this.runTimeStatisticsObject = runTimeStatisticsObject;\r
+ }\r
+\r
+ /** @see LanguageConnectionContext#getRunTimeStatisticsObject */\r
+ public RunTimeStatistics getRunTimeStatisticsObject()\r
+ {\r
+ return runTimeStatisticsObject;\r
+ }\r
+\r
+\r
+ /**\r
+ * Reports how many statement levels deep we are.\r
+ *\r
+ * @return a statement level >= OUTERMOST_STATEMENT\r
+ */\r
+ public int getStatementDepth()\r
+ { return statementDepth; }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#isIsolationLevelSetUsingSQLorJDBC\r
+ */\r
+ public boolean isIsolationLevelSetUsingSQLorJDBC()\r
+ {\r
+ return isolationLevelSetUsingSQLorJDBC;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#resetIsolationLevelFlagUsedForSQLandJDBC\r
+ */\r
+ public void resetIsolationLevelFlagUsedForSQLandJDBC()\r
+ {\r
+ isolationLevelSetUsingSQLorJDBC = false;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#setIsolationLevel\r
+ */\r
+ public void setIsolationLevel(int isolationLevel) throws StandardException\r
+ {\r
+ StatementContext stmtCtxt = getStatementContext();\r
+ if (stmtCtxt!= null && stmtCtxt.inTrigger())\r
+ throw StandardException.newException(SQLState.LANG_NO_XACT_IN_TRIGGER, getTriggerExecutionContext().toString());\r
+\r
+ // find if there are any held cursors from previous isolation level.\r
+ // if yes, then throw an exception that isolation change not allowed until\r
+ // the held cursors are closed.\r
+ // I had to move this check outside of transaction idle check because if a\r
+ // transactions creates held cursors and commits the transaction, then\r
+ // there still would be held cursors but the transaction state would be idle.\r
+ // In order to check the above mentioned case, the held cursor check\r
+ // shouldn't rely on transaction state.\r
+ if (this.isolationLevel != isolationLevel)\r
+ {\r
+ if (!verifyAllHeldResultSetsAreClosed())\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_CANT_CHANGE_ISOLATION_HOLD_CURSOR);\r
+ }\r
+ }\r
+\r
+ /* Commit and set to new isolation level.\r
+ * NOTE: We commit first in case there's some kind\r
+ * of error, like can't commit within a server side jdbc call.\r
+ */\r
+ TransactionController tc = getTransactionExecute();\r
+ if (!tc.isIdle())\r
+ {\r
+ // If this transaction is in progress, commit it.\r
+ // However, do not allow commit to happen if this is a global\r
+ // transaction.\r
+ if (tc.isGlobal())\r
+ throw StandardException.newException(SQLState.LANG_NO_SET_TRAN_ISO_IN_GLOBAL_CONNECTION);\r
+\r
+ userCommit();\r
+ }\r
+ this.isolationLevel = isolationLevel;\r
+ this.isolationLevelExplicitlySet = true;\r
+ this.isolationLevelSetUsingSQLorJDBC = true;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getCurrentIsolationLevel\r
+ */\r
+ public int getCurrentIsolationLevel()\r
+ {\r
+ return (isolationLevel == ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL) ? defaultIsolationLevel : isolationLevel;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getCurrentIsolationLevel\r
+ */\r
+ public String getCurrentIsolationLevelStr()\r
+ {\r
+ if( isolationLevel >= 0 && isolationLevel < ExecutionContext.CS_TO_SQL_ISOLATION_MAP.length)\r
+ return ExecutionContext.CS_TO_SQL_ISOLATION_MAP[ isolationLevel][0];\r
+ return ExecutionContext.CS_TO_SQL_ISOLATION_MAP[ ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL][0];\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#setPrepareIsolationLevel\r
+ */\r
+ public void setPrepareIsolationLevel(int level) \r
+ {\r
+ prepareIsolationLevel = level;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getPrepareIsolationLevel\r
+ */\r
+ public int getPrepareIsolationLevel()\r
+ {\r
+ if (!isolationLevelExplicitlySet)\r
+ return prepareIsolationLevel;\r
+ else\r
+ return ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getExecutionContext\r
+ */\r
+ public ExecutionContext getExecutionContext()\r
+ {\r
+ return (ExecutionContext) getContextManager().getContext(ExecutionContext.CONTEXT_ID);\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getStatementContext\r
+ */\r
+ public StatementContext getStatementContext()\r
+ { \r
+ return (StatementContext) getContextManager().getContext(org.apache.derby.iapi.reference.ContextId.LANG_STATEMENT);\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#setOptimizerTrace\r
+ */\r
+ public boolean setOptimizerTrace(boolean onOrOff)\r
+ {\r
+ if (of == null)\r
+ {\r
+ return false;\r
+ }\r
+ if (! of.supportsOptimizerTrace())\r
+ {\r
+ return false;\r
+ }\r
+ optimizerTrace = onOrOff;\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getOptimizerTrace\r
+ */\r
+ public boolean getOptimizerTrace()\r
+ {\r
+ return optimizerTrace;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#setOptimizerTraceHtml\r
+ */\r
+ public boolean setOptimizerTraceHtml(boolean onOrOff)\r
+ {\r
+ if (of == null)\r
+ {\r
+ return false;\r
+ }\r
+ if (! of.supportsOptimizerTrace())\r
+ {\r
+ return false;\r
+ }\r
+ optimizerTraceHtml = onOrOff;\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getOptimizerTraceHtml\r
+ */\r
+ public boolean getOptimizerTraceHtml()\r
+ {\r
+ return optimizerTraceHtml;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#setOptimizerTraceOutput\r
+ */\r
+ public void setOptimizerTraceOutput(String startingText)\r
+ {\r
+ if (optimizerTrace)\r
+ {\r
+ lastOptimizerTraceOutput = optimizerTraceOutput;\r
+ optimizerTraceOutput = startingText;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#appendOptimizerTraceOutput\r
+ */\r
+ public void appendOptimizerTraceOutput(String output)\r
+ {\r
+ optimizerTraceOutput = \r
+ (optimizerTraceOutput == null) ? output : optimizerTraceOutput + output;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getOptimizerTraceOutput\r
+ */\r
+ public String getOptimizerTraceOutput()\r
+ {\r
+ return lastOptimizerTraceOutput;\r
+ }\r
+\r
+ /**\r
+ * Reports whether there is any outstanding work in the transaction.\r
+ *\r
+ * @return true if there is outstanding work in the transaction\r
+ * false otherwise\r
+ */\r
+ public boolean isTransactionPristine()\r
+ {\r
+ return getTransactionExecute().isPristine();\r
+ }\r
+\r
+\r
+ //\r
+ // Context interface\r
+ //\r
+ /**\r
+ If worse than a transaction error, everything goes; we\r
+ rely on other contexts to kill the context manager\r
+ for this session.\r
+ <p>\r
+ If a transaction error, act like we saw a rollback.\r
+ <p>\r
+ If more severe or a java error, the outer cleanup\r
+ will shutdown the connection, so we don't have to clean up.\r
+ <p>\r
+ REMIND: connection should throw out all contexts and start\r
+ over when the connection is closed... perhaps by throwing\r
+ out the context manager?\r
+ <p>\r
+ REVISIT: If statement error, should we do anything?\r
+ <P>\r
+ Since the access manager's own context takes care of its own\r
+ resources on errors, there is nothing that this context has\r
+ to do with the transaction controller.\r
+\r
+ @exception StandardException thrown on error. REVISIT: don't want\r
+ cleanupOnError's to throw exceptions.\r
+ */\r
+ public void cleanupOnError(Throwable error) throws StandardException {\r
+\r
+ /*\r
+ ** If it isn't a StandardException, then assume\r
+ ** session severity. It is probably an unexpected\r
+ ** java error somewhere in the language.\r
+ ** Store layer treats JVM error as session severity, \r
+ ** hence to be consistent and to avoid getting rawstore\r
+ ** protocol violation errors, we treat java errors here\r
+ ** to be of session severity.\r
+ */ \r
+\r
+ int severity = (error instanceof StandardException) ?\r
+ ((StandardException) error).getSeverity() :\r
+ ExceptionSeverity.SESSION_SEVERITY;\r
+ \r
+ if (statementContexts[0] != null)\r
+ {\r
+ statementContexts[0].clearInUse();\r
+ \r
+ // Force the StatementContext that's normally\r
+ // left on the stack for optimization to be popped\r
+ // when the session is closed. Ensures full cleanup\r
+ // and no hanging refrences in the ContextManager.\r
+ if (severity >= ExceptionSeverity.SESSION_SEVERITY)\r
+ statementContexts[0].popMe();\r
+ }\r
+ if (statementContexts[1] != null)\r
+ {\r
+ statementContexts[1].clearInUse(); \r
+ }\r
+\r
+ // closing the activations closes all the open cursors.\r
+ // the activations are, for all intents and purposes, the\r
+ // cursors.\r
+ if (severity >= ExceptionSeverity.SESSION_SEVERITY) \r
+ {\r
+ for (int i = acts.size() - 1; i >= 0; i--) {\r
+ // it maybe the case that a reset()/close() ends up closing\r
+ // one or more activation leaving our index beyond\r
+ // the end of the array\r
+ if (i >= acts.size())\r
+ continue;\r
+ Activation a = (Activation) acts.get(i);\r
+ a.reset();\r
+ a.close();\r
+ }\r
+ \r
+ popMe();\r
+ }\r
+\r
+ /*\r
+ ** We have some global state that we need\r
+ ** to clean up no matter what. Be sure\r
+ ** to do so.\r
+ */\r
+ else if (severity >= ExceptionSeverity.TRANSACTION_SEVERITY) \r
+ {\r
+ internalRollback();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see org.apache.derby.iapi.services.context.Context#isLastHandler\r
+ */\r
+ public boolean isLastHandler(int severity)\r
+ {\r
+ return false;\r
+ }\r
+\r
+ //\r
+ // class implementation\r
+ //\r
+\r
+ /**\r
+ If we are called as part of rollback code path, then we will reset all \r
+ the activations that have resultset returning rows associated with \r
+ them. DERBY-3304 Resultsets that do not return rows should be left \r
+ alone when the rollback is through the JDBC Connection object. If the \r
+ rollback is caused by an exception, then at that time, all kinds of\r
+ resultsets should be closed. \r
+ \r
+ If we are called as part of commit code path, then we will do one of \r
+ the following if the activation has resultset assoicated with it. Also,\r
+ we will clear the conglomerate used while scanning for update/delete\r
+ 1)Close result sets that return rows and are not held across commit.\r
+ 2)Clear the current row of the resultsets that return rows and are\r
+ held across commit.\r
+ 3)Leave the result sets untouched if they do not return rows\r
+ \r
+ Additionally, clean up (close()) activations that have been\r
+ marked as unused during statement finalization.\r
+\r
+ @exception StandardException thrown on failure\r
+ */\r
+ private void endTransactionActivationHandling(boolean forRollback) throws StandardException {\r
+\r
+ // don't use an enumeration as the activation may remove\r
+ // itself from the list, thus invalidating the Enumeration\r
+ for (int i = acts.size() - 1; i >= 0; i--) {\r
+\r
+ // it maybe the case that a reset() ends up closing\r
+ // one or more activation leaving our index beyond\r
+ // the end of the array\r
+ if (i >= acts.size())\r
+ continue;\r
+\r
+ Activation a = (Activation) acts.get(i);\r
+ /*\r
+ ** Look for stale activations. Activations are\r
+ ** marked as unused during statement finalization.\r
+ ** Here, we sweep and remove this inactive ones.\r
+ */ \r
+ if (!a.isInUse())\r
+ {\r
+ a.close();\r
+ continue;\r
+ }\r
+\r
+ //Determine if the activation has a resultset and if that resultset\r
+ //returns rows. For such an activation, we need to take special\r
+ //actions during commit and rollback as explained in the comments\r
+ //below.\r
+ ResultSet activationResultSet = a.getResultSet();\r
+ boolean resultsetReturnsRows = \r
+ (activationResultSet != null) && activationResultSet.returnsRows(); ;\r
+\r
+ if (forRollback) { \r
+ if (resultsetReturnsRows)\r
+ //Since we are dealing with rollback, we need to reset \r
+ //the activation no matter what the holdability might \r
+ //be provided that resultset returns rows. An example\r
+ //where we do not want to close a resultset that does\r
+ //not return rows would be a java procedure which has\r
+ //user invoked rollback inside of it. That rollback\r
+ //should not reset the activation associated with\r
+ //the call to java procedure because that activation\r
+ //is still being used.\r
+ a.reset();\r
+ // Only invalidate statements if we performed DDL.\r
+ if (dataDictionaryInWriteMode()) {\r
+ ExecPreparedStatement ps = a.getPreparedStatement();\r
+ if (ps != null) {\r
+ ps.makeInvalid(DependencyManager.ROLLBACK, this);\r
+ }\r
+ }\r
+ } else {\r
+ //We are dealing with commit here. \r
+ if (resultsetReturnsRows){\r
+ if (a.getResultSetHoldability() == false)\r
+ //Close result sets that return rows and are not held \r
+ //across commit. This is to implement closing JDBC \r
+ //result sets that are CLOSE_CURSOR_ON_COMMIT at commit \r
+ //time. \r
+ activationResultSet.close();\r
+ else \r
+ //Clear the current row of the result sets that return\r
+ //rows and are held across commit. This is to implement\r
+ //keeping JDBC result sets open that are \r
+ //HOLD_CURSORS_OVER_COMMIT at commit time and marking\r
+ //the resultset to be not on a valid row position. The \r
+ //user will need to reposition within the resultset \r
+ //before doing any row operations.\r
+ activationResultSet.clearCurrentRow(); \r
+ }\r
+ a.clearHeapConglomerateController();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ Finish the data dictionary transaction, if any.\r
+\r
+ @exception StandardException Thrown on error\r
+ */\r
+ private void finishDDTransaction() throws StandardException {\r
+\r
+ /* Was the data dictionary put into write mode? */\r
+ if (ddWriteMode) {\r
+ DataDictionary dd = getDataDictionary();\r
+\r
+ /* Tell the data dictionary that the transaction is finished */\r
+ dd.transactionFinished();\r
+\r
+ /* The data dictionary isn't in write mode any more */\r
+ ddWriteMode = false;\r
+ }\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////\r
+ //\r
+ // MINIONS\r
+ //\r
+ ////////////////////////////////////////////////////////////////////\r
+\r
+ /**\r
+ * Increments the statement depth.\r
+ */\r
+ private void incrementStatementDepth() { statementDepth++; }\r
+\r
+ /**\r
+ * Decrements the statement depth\r
+ */\r
+ private void decrementStatementDepth()\r
+ {\r
+ statementDepth--;\r
+ }\r
+\r
+ /**\r
+ * Resets the statementDepth.\r
+ */\r
+ protected void resetStatementDepth()\r
+ {\r
+ statementDepth = 0;\r
+ }\r
+\r
+ public DataDictionary getDataDictionary()\r
+ {\r
+ return getDatabase().getDataDictionary();\r
+ }\r
+\r
+ /**\r
+ @see LanguageConnectionContext#setReadOnly\r
+ @exception StandardException The operation is disallowed.\r
+ */\r
+ public void setReadOnly(boolean on) throws StandardException\r
+ {\r
+ if (!tran.isPristine())\r
+ throw StandardException.newException(SQLState.AUTH_SET_CONNECTION_READ_ONLY_IN_ACTIVE_XACT);\r
+ authorizer.setReadOnlyConnection(on,true);\r
+ }\r
+\r
+ /**\r
+ @see LanguageConnectionContext#isReadOnly\r
+ */\r
+ public boolean isReadOnly()\r
+ {\r
+ return authorizer.isReadOnlyConnection();\r
+ }\r
+\r
+ /**\r
+ @see LanguageConnectionContext#getAuthorizer\r
+ */\r
+ public Authorizer getAuthorizer()\r
+ {\r
+ return authorizer;\r
+ }\r
+\r
+ /**\r
+ * Implements ConnectionInfo.lastAutoincrementValue.\r
+ * lastAutoincrementValue searches for the last autoincrement value inserted\r
+ * into a column specified by the user. The search for the "last" value\r
+ * supports nesting levels caused by triggers (Only triggers cause nesting,\r
+ * not server side JDBC). \r
+ * If lastAutoincrementValue is called from within a trigger, the search\r
+ * space for ai-values are those values that are inserted by this trigger as\r
+ * well as previous triggers; \r
+ * i.e if a SQL statement fires trigger T1, which in turn does something\r
+ * that fires trigger t2, and if lastAutoincrementValue is called from\r
+ * within t2, then autoincrement values genereated by t1 are visible to\r
+ * it. By the same logic, if it is called from within t1, then it does not\r
+ * see values inserted by t2.\r
+ *\r
+ * @see LanguageConnectionContext#lastAutoincrementValue\r
+ * @see org.apache.derby.iapi.db.ConnectionInfo#lastAutoincrementValue\r
+ */\r
+ public Long lastAutoincrementValue(String schemaName, String tableName,\r
+ String columnName)\r
+ {\r
+ String aiKey = AutoincrementCounter.makeIdentity(schemaName, tableName, columnName);\r
+ \r
+ int size = triggerExecutionContexts.size();\r
+ // System.out.println(" searching for " + aiKey);\r
+ for (int i = size - 1; i >= 0; i--)\r
+ {\r
+ // first loop through triggers.\r
+ InternalTriggerExecutionContext itec = \r
+ (InternalTriggerExecutionContext)triggerExecutionContexts.get(i);\r
+ Long value = itec.getAutoincrementValue(aiKey);\r
+ if (value == null)\r
+ continue;\r
+\r
+ return value;\r
+ }\r
+ if (autoincrementHT == null)\r
+ return null;\r
+ return (Long)autoincrementHT.get(aiKey);\r
+ } \r
+\r
+ /**\r
+ * @see LanguageConnectionContext#setAutoincrementUpdate\r
+ */\r
+ public void setAutoincrementUpdate(boolean flag)\r
+ {\r
+ autoincrementUpdate = flag;\r
+ }\r
+ \r
+ /**\r
+ * @see LanguageConnectionContext#getAutoincrementUpdate\r
+ */\r
+ public boolean getAutoincrementUpdate()\r
+ {\r
+ return autoincrementUpdate;\r
+ }\r
+ \r
+ /**\r
+ * @see LanguageConnectionContext#autoincrementCreateCounter\r
+ */\r
+ public void autoincrementCreateCounter(String s, String t, String c,\r
+ Long initialValue, long increment,\r
+ int position)\r
+ {\r
+ String key = AutoincrementCounter.makeIdentity(s,t,c);\r
+ \r
+ if (autoincrementCacheHashtable == null)\r
+ {\r
+ autoincrementCacheHashtable = new HashMap();\r
+ }\r
+\r
+ AutoincrementCounter aic = \r
+ (AutoincrementCounter)autoincrementCacheHashtable.get(key);\r
+ if (aic != null)\r
+ {\r
+ if (SanityManager.DEBUG) \r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "Autoincrement Counter already exists:" + key);\r
+ }\r
+ return;\r
+ }\r
+ \r
+ aic = new AutoincrementCounter(initialValue, \r
+ increment, 0, s, t, c, position);\r
+ autoincrementCacheHashtable.put(key, aic);\r
+ }\r
+\r
+ /**\r
+ * returns the <b>next</b> value to be inserted into an autoincrement col.\r
+ * This is used internally by the system to generate autoincrement values\r
+ * which are going to be inserted into a autoincrement column. This is\r
+ * used when as autoincrement column is added to a table by an alter \r
+ * table statemenet and during bulk insert.\r
+ *\r
+ * @param schemaName\r
+ * @param tableName\r
+ * @param columnName identify the column uniquely in the system.\r
+ */\r
+ public long nextAutoincrementValue(String schemaName, String tableName,\r
+ String columnName)\r
+ throws StandardException \r
+ {\r
+ String key = AutoincrementCounter.makeIdentity(schemaName,tableName,\r
+ columnName);\r
+ \r
+ AutoincrementCounter aic = \r
+ (AutoincrementCounter)autoincrementCacheHashtable.get(key);\r
+\r
+ if (aic == null)\r
+ {\r
+ if (SanityManager.DEBUG) \r
+ {\r
+ SanityManager.THROWASSERT("counter doesn't exist:" + key);\r
+ }\r
+ return 0;\r
+ }\r
+ else\r
+ {\r
+ return aic.update();\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Flush the cache of autoincrement values being kept by the lcc.\r
+ * This will result in the autoincrement values being written to the\r
+ * SYSCOLUMNS table as well as the mapping used by lastAutoincrementValue\r
+ * \r
+ * @exception StandardException thrown on error.\r
+ * @see LanguageConnectionContext#lastAutoincrementValue\r
+ * @see GenericLanguageConnectionContext#lastAutoincrementValue\r
+ * @see org.apache.derby.iapi.db.ConnectionInfo#lastAutoincrementValue\r
+ */\r
+ public void autoincrementFlushCache(UUID tableUUID)\r
+ throws StandardException\r
+ {\r
+ if (autoincrementCacheHashtable == null)\r
+ return;\r
+\r
+ if (autoincrementHT == null)\r
+ autoincrementHT = new HashMap();\r
+\r
+ DataDictionary dd = getDataDictionary();\r
+ for (Iterator it = autoincrementCacheHashtable.keySet().iterator();\r
+ it.hasNext(); )\r
+ {\r
+ Object key = it.next();\r
+ AutoincrementCounter aic = \r
+ (AutoincrementCounter)autoincrementCacheHashtable.get(key);\r
+ Long value = aic.getCurrentValue();\r
+ aic.flushToDisk(getTransactionExecute(), dd, tableUUID);\r
+ if (value != null)\r
+ {\r
+ autoincrementHT.put(key, value);\r
+ }\r
+ }\r
+ autoincrementCacheHashtable.clear();\r
+ }\r
+\r
+ /**\r
+ * Copies an existing autoincrement mapping\r
+ * into autoincrementHT, the cache of autoincrement values \r
+ * kept in the languageconnectioncontext.\r
+ */\r
+ public void copyHashtableToAIHT(Map from)\r
+ {\r
+ if (from.isEmpty())\r
+ return;\r
+ if (autoincrementHT == null)\r
+ autoincrementHT = new HashMap();\r
+ \r
+ autoincrementHT.putAll(from);\r
+ }\r
+ \r
+ /**\r
+ * @see LanguageConnectionContext#getInstanceNumber\r
+ */\r
+ public int getInstanceNumber()\r
+ {\r
+ return instanceNumber;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getDrdaID\r
+ */\r
+ public String getDrdaID()\r
+ {\r
+ return drdaID;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#setDrdaID\r
+ */\r
+ public void setDrdaID(String drdaID)\r
+ {\r
+ this.drdaID = drdaID;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getDbname\r
+ */\r
+ public String getDbname()\r
+ {\r
+ return dbname;\r
+ }\r
+\r
+ /**\r
+ * @see LanguageConnectionContext#getLastActivation\r
+ */\r
+ public Activation getLastActivation()\r
+ {\r
+ return (Activation)acts.get(acts.size() - 1);\r
+ }\r
+\r
+ public StringBuffer appendErrorInfo() {\r
+\r
+ TransactionController tc = getTransactionExecute();\r
+ if (tc == null)\r
+ return null;\r
+\r
+ StringBuffer sb = new StringBuffer(200);\r
+\r
+ sb.append(LanguageConnectionContext.xidStr);\r
+ sb.append(tc.getTransactionIdString());\r
+ sb.append("), ");\r
+\r
+ sb.append(LanguageConnectionContext.lccStr);\r
+ sb.append(Integer.toString(getInstanceNumber()));\r
+ sb.append("), ");\r
+\r
+ sb.append(LanguageConnectionContext.dbnameStr);\r
+ sb.append(getDbname());\r
+ sb.append("), ");\r
+\r
+ sb.append(LanguageConnectionContext.drdaStr);\r
+ sb.append(getDrdaID());\r
+ sb.append("), ");\r
+\r
+ return sb;\r
+ }\r
+}\r