Return-Path: Delivered-To: apmail-db-derby-commits-archive@www.apache.org Received: (qmail 89194 invoked from network); 3 Jul 2010 15:58:39 -0000 Received: from unknown (HELO mail.apache.org) (140.211.11.3) by 140.211.11.9 with SMTP; 3 Jul 2010 15:58:39 -0000 Received: (qmail 42944 invoked by uid 500); 3 Jul 2010 15:58:39 -0000 Delivered-To: apmail-db-derby-commits-archive@db.apache.org Received: (qmail 42911 invoked by uid 500); 3 Jul 2010 15:58:38 -0000 Mailing-List: contact derby-commits-help@db.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: Reply-To: "Derby Development" List-Id: Delivered-To: mailing list derby-commits@db.apache.org Received: (qmail 42903 invoked by uid 99); 3 Jul 2010 15:58:38 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 03 Jul 2010 15:58:38 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 03 Jul 2010 15:58:30 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id DA13F23889DD; Sat, 3 Jul 2010 15:57:35 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r960231 [2/2] - /db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java Date: Sat, 03 Jul 2010 15:57:35 -0000 To: derby-commits@db.apache.org From: mikem@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100703155735.DA13F23889DD@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java?rev=960231&r1=960230&r2=960231&view=diff ============================================================================== --- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java (original) +++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java Sat Jul 3 15:57:35 2010 @@ -97,257 +97,259 @@ import java.util.Map; * */ public class GenericLanguageConnectionContext - extends ContextImpl - implements LanguageConnectionContext + extends ContextImpl + implements LanguageConnectionContext { - // make sure these are not zeros - private final static int NON_XA = 0; - private final static int XA_ONE_PHASE = 1; - private final static int XA_TWO_PHASE = 2; - - /* - fields - */ - - private final ArrayList acts; - private volatile boolean unusedActs=false; - /** The maximum size of acts since the last time it was trimmed. Used to - * determine whether acts should be trimmed to reclaim space. */ - private int maxActsSize; - protected int bindCount; - private boolean ddWriteMode; - private boolean runTimeStatisticsSetting ; - private boolean statisticsTiming; - - /** - * If xplainOnlyMode is set (via SYSCS_SET_XPLAIN_MODE), then the - * connection does not actually execute statements, but only - * compiles them, and emits the query plan information into the - * XPLAIN tables. - */ - private boolean xplainOnlyMode = false; - - /** the current xplain schema. Is usually NULL. Can be set via - * SYSCS_SET_XPLAIN_SCHEMA, in which case it species the schema into - * which XPLAIN information should be stored in user tables. - */ - private String xplain_schema = null; - /** - * For each XPLAIN table, this map stores a SQL INSERT statement which - * can be prepared and used to insert rows into the table during the - * capturing of statistics data into the user XPLAIN tables. - */ - private Map xplain_statements = new HashMap(); - - //all the temporary tables declared for this connection - private ArrayList allDeclaredGlobalTempTables; - //The currentSavepointLevel is used to provide the rollback behavior of temporary tables. - //At any point, this variable has the total number of savepoints defined for the transaction. - private int currentSavepointLevel = 0; - - protected long nextCursorId; - - protected int nextSavepointId; - - private RunTimeStatistics runTimeStatisticsObject; - private StringBuffer sb; - - private Database db; - - private final int instanceNumber; - private String drdaID; - private String dbname; + // make sure these are not zeros + private final static int NON_XA = 0; + private final static int XA_ONE_PHASE = 1; + private final static int XA_TWO_PHASE = 2; + + /* + fields + */ + + private final ArrayList acts; + private volatile boolean unusedActs=false; + /** The maximum size of acts since the last time it was trimmed. Used to + * determine whether acts should be trimmed to reclaim space. */ + private int maxActsSize; + protected int bindCount; + private boolean ddWriteMode; + private boolean runTimeStatisticsSetting ; + private boolean statisticsTiming; + + /** + * If xplainOnlyMode is set (via SYSCS_SET_XPLAIN_MODE), then the + * connection does not actually execute statements, but only + * compiles them, and emits the query plan information into the + * XPLAIN tables. + */ + private boolean xplainOnlyMode = false; + + /** the current xplain schema. Is usually NULL. Can be set via + * SYSCS_SET_XPLAIN_SCHEMA, in which case it species the schema into + * which XPLAIN information should be stored in user tables. + */ + private String xplain_schema = null; + /** + * For each XPLAIN table, this map stores a SQL INSERT statement which + * can be prepared and used to insert rows into the table during the + * capturing of statistics data into the user XPLAIN tables. + */ + private Map xplain_statements = new HashMap(); + + //all the temporary tables declared for this connection + private ArrayList allDeclaredGlobalTempTables; + + //The currentSavepointLevel is used to provide the rollback behavior of + //temporary tables. At any point, this variable has the total number of + //savepoints defined for the transaction. + private int currentSavepointLevel = 0; + + protected long nextCursorId; + + protected int nextSavepointId; + + private RunTimeStatistics runTimeStatisticsObject; + private StringBuffer sb; + + private Database db; + + private final int instanceNumber; + private String drdaID; + private String dbname; private Object lastQueryTree; // for debugging - /** - The transaction to use within this language connection context. It may - be more appropriate to have it in a separate context (LanguageTransactionContext?). - REVISIT (nat): I shoehorned the transaction context that - the language uses into this class. The main purpose is so - that the various language code can find out what its - transaction is. - **/ - private final TransactionController tran; + /** + The transaction to use within this language connection context. It may + be more appropriate to have it in a separate context (LanguageTransactionContext?). + REVISIT (nat): I shoehorned the transaction context that + the language uses into this class. The main purpose is so + that the various language code can find out what its + transaction is. + **/ + private final TransactionController tran; - /** - * If non-null indicates that a read-only nested + /** + * If non-null indicates that a read-only nested * user transaction is in progress. - */ - private TransactionController readOnlyNestedTransaction; - - /** - * queryNestingDepth is a counter used to keep track of how many calls - * have been made to begin read-only nested transactions. Only the first call - * actually starts a Nested User Transaction with the store. Subsequent - * calls simply increment this counter. commitNestedTransaction only - * decrements the counter and when it drops to 0 actually commits the - * nested user transaction. - */ - private int queryNestingDepth; - - protected DataValueFactory dataFactory; - protected LanguageFactory langFactory; - protected TypeCompilerFactory tcf; - protected OptimizerFactory of; - protected LanguageConnectionFactory connFactory; - - /* - * A statement context is "pushed" and "popped" at the beginning and + */ + private TransactionController readOnlyNestedTransaction; + + /** + * queryNestingDepth is a counter used to keep track of how many calls + * have been made to begin read-only nested transactions. Only the first call + * actually starts a Nested User Transaction with the store. Subsequent + * calls simply increment this counter. commitNestedTransaction only + * decrements the counter and when it drops to 0 actually commits the + * nested user transaction. + */ + private int queryNestingDepth; + + protected DataValueFactory dataFactory; + protected LanguageFactory langFactory; + protected TypeCompilerFactory tcf; + protected OptimizerFactory of; + protected LanguageConnectionFactory connFactory; + + /* + * A statement context is "pushed" and "popped" at the beginning and * end of every statement so that only that statement is cleaned up * on a Statement Exception. As a performance optimization, we only push * the outermost statement context once, and never pop it. Also, we - * save off a 2nd StatementContext for speeding server side method - * invocation, though we still push and pop it as needed. All other + * save off a 2nd StatementContext for speeding server side method + * invocation, though we still push and pop it as needed. All other * statement contexts will allocated and pushed and popped on demand. */ - private final StatementContext[] statementContexts = new StatementContext[2]; - private int statementDepth; - protected int outermostTrigger = -1; + private final StatementContext[] statementContexts = new StatementContext[2]; + private int statementDepth; + protected int outermostTrigger = -1; protected Authorizer authorizer; - protected String userName = null; //The name the user connects with. - //May still be quoted. - /** - * The top SQL session context stack frame (SQL 2003, section - * 4.37.3), is kept in topLevelSSC. For nested session contexts, - * the SQL session context is held by the activation of the - * calling statement, cf. setupNestedSessionContext and it is - * accessible through the current statement context - * (compile-time), or via the current activation (execution-time). - * @see GenericLanguageConnectionContext#getTopLevelSQLSessionContext - */ - private SQLSessionContext topLevelSSC; - - /** - * Used to hold the computed value of the initial default schema, - * cf logic in initDefaultSchemaDescriptor. - */ - private SchemaDescriptor cachedInitialDefaultSchemaDescr = null; + protected String userName = null; //The name the user connects with. + //May still be quoted. + /** + * The top SQL session context stack frame (SQL 2003, section + * 4.37.3), is kept in topLevelSSC. For nested session contexts, + * the SQL session context is held by the activation of the + * calling statement, cf. setupNestedSessionContext and it is + * accessible through the current statement context + * (compile-time), or via the current activation (execution-time). + * @see GenericLanguageConnectionContext#getTopLevelSQLSessionContext + */ + private SQLSessionContext topLevelSSC; + + /** + * Used to hold the computed value of the initial default schema, + * cf logic in initDefaultSchemaDescriptor. + */ + private SchemaDescriptor cachedInitialDefaultSchemaDescr = null; - // RESOLVE - How do we want to set the default. + // RESOLVE - How do we want to set the default. private int defaultIsolationLevel = ExecutionContext.READ_COMMITTED_ISOLATION_LEVEL; - protected int isolationLevel = defaultIsolationLevel; + protected int isolationLevel = defaultIsolationLevel; + + private boolean isolationLevelExplicitlySet = false; + // Isolation level can be changed using JDBC api Connection.setTransactionIsolation + // or it can be changed using SQL "set current isolation = NEWLEVEL". + // + // In XA transactions, BrokeredConnection keeps isolation state information. + // When isolation is changed in XA transaction using JDBC, that state gets + // correctly set in BrokeredConnection.setTransactionIsolation method. But + // when SQL is used to set the isolation level, the code path is different + // and it does not go through BrokeredConnection's setTransactionIsolation + // method and hence the state is not maintained correctly when coming through + // SQL. To get around this, I am adding following flag which will get set + // everytime the isolation level is set using JDBC or SQL. This flag will be + // checked at global transaction start and end time. If the flag is set to true + // then BrokeredConnection's isolation level state will be brought upto date + // with Real Connection's isolation level and this flag will be set to false + // after that. + private boolean isolationLevelSetUsingSQLorJDBC = false; + + // isolation level to when preparing statements. + // if unspecified, the statement won't be prepared with a specific + // scan isolationlevel + protected int prepareIsolationLevel = ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL; + + // Whether or not to write executing statement info to db2j.log + private boolean logStatementText; + private boolean logQueryPlan; + private HeaderPrintWriter istream; + + // this used to be computed in OptimizerFactoryContextImpl; i.e everytime a + // connection was made. To keep the semantics same I'm putting it out here + // instead of in the OptimizerFactory which is only initialized when the + // database is booted. + private int lockEscalationThreshold; + + private ArrayList stmtValidators; + private ArrayList triggerExecutionContexts; + private ArrayList triggerTables; + + // OptimizerTrace + private boolean optimizerTrace; + private boolean optimizerTraceHtml; + private String lastOptimizerTraceOutput; + private String optimizerTraceOutput; + + //// Support for AUTOINCREMENT - private boolean isolationLevelExplicitlySet = false; - // Isolation level can be changed using JDBC api Connection.setTransactionIsolation - // or it can be changed using SQL "set current isolation = NEWLEVEL". - // - // In XA transactions, BrokeredConnection keeps isolation state information. - // When isolation is changed in XA transaction using JDBC, that state gets - // correctly set in BrokeredConnection.setTransactionIsolation method. But - // when SQL is used to set the isolation level, the code path is different - // and it does not go through BrokeredConnection's setTransactionIsolation - // method and hence the state is not maintained correctly when coming through - // SQL. To get around this, I am adding following flag which will get set - // everytime the isolation level is set using JDBC or SQL. This flag will be - // checked at global transaction start and end time. If the flag is set to true - // then BrokeredConnection's isolation level state will be brought upto date - // with Real Connection's isolation level and this flag will be set to false - // after that. - private boolean isolationLevelSetUsingSQLorJDBC = false; - - // isolation level to when preparing statements. - // if unspecified, the statement won't be prepared with a specific - // scan isolationlevel - protected int prepareIsolationLevel = ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL; - - // Whether or not to write executing statement info to db2j.log - private boolean logStatementText; - private boolean logQueryPlan; - private HeaderPrintWriter istream; - - // this used to be computed in OptimizerFactoryContextImpl; i.e everytime a - // connection was made. To keep the semantics same I'm putting it out here - // instead of in the OptimizerFactory which is only initialized when the - // database is booted. - private int lockEscalationThreshold; - - private ArrayList stmtValidators; - private ArrayList triggerExecutionContexts; - private ArrayList triggerTables; - - // OptimizerTrace - private boolean optimizerTrace; - private boolean optimizerTraceHtml; - private String lastOptimizerTraceOutput; - private String optimizerTraceOutput; - - //// Support for AUTOINCREMENT - - /** - * To support lastAutoincrementValue: This is a hashtable which maps - * schemaName,tableName,columnName to a Long value. - */ - private HashMap autoincrementHT; - /** - * whether to allow updates or not. - */ - private boolean autoincrementUpdate; - private long identityVal; //support IDENTITY_VAL_LOCAL function - private boolean identityNotNull; //frugal programmer + /** + * To support lastAutoincrementValue: This is a hashtable which maps + * schemaName,tableName,columnName to a Long value. + */ + private HashMap autoincrementHT; + /** + * whether to allow updates or not. + */ + private boolean autoincrementUpdate; + private long identityVal; //support IDENTITY_VAL_LOCAL function + private boolean identityNotNull; //frugal programmer - // cache of ai being handled in memory (bulk insert + alter table). - private HashMap autoincrementCacheHashtable; + // cache of ai being handled in memory (bulk insert + alter table). + private HashMap autoincrementCacheHashtable; // User-written inspector to print out query tree private ASTVisitor astWalker; - /* - constructor - */ - public GenericLanguageConnectionContext - ( - ContextManager cm, - TransactionController tranCtrl, - - LanguageFactory lf, - LanguageConnectionFactory lcf, - Database db, - String userName, - int instanceNumber, - String drdaID, - String dbname) - throws StandardException - { - super(cm, org.apache.derby.iapi.reference.ContextId.LANG_CONNECTION); - acts = new ArrayList(); - tran = tranCtrl; - - dataFactory = lcf.getDataValueFactory(); - tcf = lcf.getTypeCompilerFactory(); - of = lcf.getOptimizerFactory(); - langFactory = lf; - connFactory = lcf; + /* + constructor + */ + public GenericLanguageConnectionContext + ( + ContextManager cm, + TransactionController tranCtrl, + + LanguageFactory lf, + LanguageConnectionFactory lcf, + Database db, + String userName, + int instanceNumber, + String drdaID, + String dbname) + throws StandardException + { + super(cm, org.apache.derby.iapi.reference.ContextId.LANG_CONNECTION); + acts = new ArrayList(); + tran = tranCtrl; + + dataFactory = lcf.getDataValueFactory(); + tcf = lcf.getTypeCompilerFactory(); + of = lcf.getOptimizerFactory(); + langFactory = lf; + connFactory = lcf; this.db = db; - this.userName = userName; - this.instanceNumber = instanceNumber; - this.drdaID = drdaID; - this.dbname = dbname; - - /* Find out whether or not to log info on executing statements to error log - */ - String logStatementProperty = PropertyUtil.getServiceProperty(getTransactionCompile(), - "derby.language.logStatementText"); - logStatementText = Boolean.valueOf(logStatementProperty).booleanValue(); - - String logQueryPlanProperty = PropertyUtil.getServiceProperty(getTransactionCompile(), - "derby.language.logQueryPlan"); - logQueryPlan = Boolean.valueOf(logQueryPlanProperty).booleanValue(); - - setRunTimeStatisticsMode(logQueryPlan); - - lockEscalationThreshold = - PropertyUtil.getServiceInt(tranCtrl, - Property.LOCKS_ESCALATION_THRESHOLD, - Property.MIN_LOCKS_ESCALATION_THRESHOLD, - Integer.MAX_VALUE, - Property.DEFAULT_LOCKS_ESCALATION_THRESHOLD); - stmtValidators = new ArrayList(); - triggerExecutionContexts = new ArrayList(); - triggerTables = new ArrayList(); - } + this.userName = userName; + this.instanceNumber = instanceNumber; + this.drdaID = drdaID; + this.dbname = dbname; + + /* Find out whether or not to log info on executing statements to error log + */ + String logStatementProperty = PropertyUtil.getServiceProperty(getTransactionCompile(), + "derby.language.logStatementText"); + logStatementText = Boolean.valueOf(logStatementProperty).booleanValue(); + + String logQueryPlanProperty = PropertyUtil.getServiceProperty(getTransactionCompile(), + "derby.language.logQueryPlan"); + logQueryPlan = Boolean.valueOf(logQueryPlanProperty).booleanValue(); + + setRunTimeStatisticsMode(logQueryPlan); + + lockEscalationThreshold = + PropertyUtil.getServiceInt(tranCtrl, + Property.LOCKS_ESCALATION_THRESHOLD, + Property.MIN_LOCKS_ESCALATION_THRESHOLD, + Integer.MAX_VALUE, + Property.DEFAULT_LOCKS_ESCALATION_THRESHOLD); + stmtValidators = new ArrayList(); + triggerExecutionContexts = new ArrayList(); + triggerTables = new ArrayList(); + } /** * In contrast to current user id, which may change (inside a routine @@ -356,558 +358,708 @@ public class GenericLanguageConnectionCo */ private String sessionUser = null; - public void initialize() throws StandardException - { + public void initialize() throws StandardException + { sessionUser = IdUtil.getUserAuthorizationId(userName); - // - //Creating the authorizer authorizes the connection. + // + //Creating the authorizer authorizes the connection. authorizer = new GenericAuthorizer(this); - /* - ** Set the authorization id. User shouldn't - ** be null or else we are going to blow up trying - ** to create a schema for this user. - */ - if (SanityManager.DEBUG) - { + /* + ** Set the authorization id. User shouldn't + ** be null or else we are going to blow up trying + ** to create a schema for this user. + */ + if (SanityManager.DEBUG) + { if (getSessionUserId() == null) - { - SanityManager.THROWASSERT("User name is null," + - " check the connection manager to make sure it is set" + - " reasonably"); - } - } - - - setDefaultSchema(initDefaultSchemaDescriptor()); - } - - /** - * Compute the initial default schema and set - * cachedInitialDefaultSchemaDescr accordingly. - * - * @return computed initial default schema value for this session - * @throws StandardException - */ - protected SchemaDescriptor initDefaultSchemaDescriptor() - throws StandardException { - /* - ** - If the database supports schemas and a schema with the - ** same name as the user's name exists (has been created using - ** create schema already) the database will set the users - ** default schema to the the schema with the same name as the - ** user. + { + SanityManager.THROWASSERT("User name is null," + + " check the connection manager to make sure it is set" + + " reasonably"); + } + } + + + setDefaultSchema(initDefaultSchemaDescriptor()); + } + + /** + * Compute the initial default schema and set + * cachedInitialDefaultSchemaDescr accordingly. + * + * @return computed initial default schema value for this session + * @throws StandardException + */ + protected SchemaDescriptor initDefaultSchemaDescriptor() + throws StandardException { + /* + ** - If the database supports schemas and a schema with the + ** same name as the user's name exists (has been created using + ** create schema already) the database will set the users + ** default schema to the the schema with the same name as the + ** user. ** - Else Set the default schema to APP. */ - if (cachedInitialDefaultSchemaDescr == null) { - DataDictionary dd = getDataDictionary(); + if (cachedInitialDefaultSchemaDescr == null) { + DataDictionary dd = getDataDictionary(); String authorizationId = getSessionUserId(); - SchemaDescriptor sd = - dd.getSchemaDescriptor( + SchemaDescriptor sd = + dd.getSchemaDescriptor( getSessionUserId(), getTransactionCompile(), false); - if (sd == null) { - sd = new SchemaDescriptor( + if (sd == null) { + sd = new SchemaDescriptor( dd, getSessionUserId(), getSessionUserId(), (UUID) null, false); - } + } - cachedInitialDefaultSchemaDescr = sd; - } - return cachedInitialDefaultSchemaDescr; - } - - /** - * Get the computed value for the initial default schema. - * @return the schema descriptor of the computed initial default schema - */ - private SchemaDescriptor getInitialDefaultSchemaDescriptor() { - return cachedInitialDefaultSchemaDescr; - } - - - // - // LanguageConnectionContext interface - // - /** - * @see LanguageConnectionContext#getLogStatementText - */ - public boolean getLogStatementText() - { - return logStatementText; - } - - /** - * @see LanguageConnectionContext#setLogStatementText - */ - public void setLogStatementText(boolean logStatementText) - { - this.logStatementText = logStatementText; - } - - /** - * @see LanguageConnectionContext#getLogQueryPlan - */ - public boolean getLogQueryPlan() - { - return logQueryPlan; - } - - /** - * @see LanguageConnectionContext#usesSqlAuthorization - */ - public boolean usesSqlAuthorization() - { - return getDataDictionary().usesSqlAuthorization(); - } - - /** - * get the lock escalation threshold. - */ - public int getLockEscalationThreshold() - { - return lockEscalationThreshold; - } - - /** - * Add the activation to those known about by this connection. - */ - public void addActivation(Activation a) - throws StandardException { - acts.add(a); - - if (acts.size() > maxActsSize) { - maxActsSize = acts.size(); - } - } - - public void closeUnusedActivations() - throws StandardException - { - // DERBY-418. Activations which are marked unused, - // are closed here. Activations Vector is iterated - // to identify and close unused activations, only if - // unusedActs flag is set to true and if the total - // size exceeds 20. - if( (unusedActs) && (acts.size() > 20) ) { - unusedActs = false; - - for (int i = acts.size() - 1; i >= 0; i--) { - - // it maybe the case that a Activation's reset() ends up - // closing one or more activation leaving our index beyond - // the end of the array - if (i >= acts.size()) - continue; - - Activation a1 = (Activation) acts.get(i); - if (!a1.isInUse()) { - a1.close(); - } - } - } - - if (SanityManager.DEBUG) { - - if (SanityManager.DEBUG_ON("memoryLeakTrace")) { - - if (acts.size() > 20) - System.out.println("memoryLeakTrace:GenericLanguageContext:activations " + acts.size()); - } - } - } - - /** - * Make a note that some activations are marked unused - */ - public void notifyUnusedActivation() { - unusedActs = true; - } - - /** - * @see LanguageConnectionContext#checkIfAnyDeclaredGlobalTempTablesForThisConnection - */ - public boolean checkIfAnyDeclaredGlobalTempTablesForThisConnection() { - return (allDeclaredGlobalTempTables == null ? false : true); - } - - /** - * @see LanguageConnectionContext#addDeclaredGlobalTempTable - */ - public void addDeclaredGlobalTempTable(TableDescriptor td) - throws StandardException { - - if (findDeclaredGlobalTempTable(td.getName()) != null) //if table already declared, throw an exception - { - throw - StandardException.newException( - SQLState.LANG_OBJECT_ALREADY_EXISTS_IN_OBJECT, - "Declared global temporary table", - td.getName(), - "Schema", - SchemaDescriptor.STD_DECLARED_GLOBAL_TEMPORARY_TABLES_SCHEMA_NAME); - } - - //save all the information about temp table in this special class - TempTableInfo tempTableInfo = new TempTableInfo(td, currentSavepointLevel); - - if (allDeclaredGlobalTempTables == null) - allDeclaredGlobalTempTables = new ArrayList(); - - allDeclaredGlobalTempTables.add(tempTableInfo); - } - - /** - * @see LanguageConnectionContext#dropDeclaredGlobalTempTable - */ - public boolean dropDeclaredGlobalTempTable(String tableName) { - TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName); - if (tempTableInfo != null) - { - if (SanityManager.DEBUG) - if (tempTableInfo.getDeclaredInSavepointLevel() > currentSavepointLevel) - SanityManager.THROWASSERT("declared in savepoint level can not be higher than the current savepoint level"); - - //following checks if the table was declared in the current unit of work. - if (tempTableInfo.getDeclaredInSavepointLevel() == currentSavepointLevel) - { - //since the table was declared in this unit of work, - //the drop table method should remove it from the valid list of temp table for this unit of work - allDeclaredGlobalTempTables.remove(allDeclaredGlobalTempTables.indexOf(tempTableInfo)); - if (allDeclaredGlobalTempTables.size() == 0) - allDeclaredGlobalTempTables = null; - } - else - { - //since the table was not declared in this unit of work, the drop table method will just mark the table as dropped - //in the current unit of work. This information will be used at rollback time. - tempTableInfo.setDroppedInSavepointLevel(currentSavepointLevel); - } - return true; - } else - return false; - } - - /** - * After a release of a savepoint, we need to go through our temp tables list. If there are tables with their declare or drop - * or modified in savepoint levels set to savepoint levels higher than the current savepoint level, then we should change them - * to the current savepoint level - */ - private void tempTablesReleaseSavepointLevels() { - //unlike rollback, here we check for dropped in / declared in / modified in savepoint levels > current savepoint level only. - //This is because the temp tables with their savepoint levels same as currentSavepointLevel have correct value assigned to them and - //do not need to be changed and hence no need to check for >= - for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) { - TempTableInfo tempTableInfo = (TempTableInfo)allDeclaredGlobalTempTables.get(i); - if (tempTableInfo.getDroppedInSavepointLevel() > currentSavepointLevel) - tempTableInfo.setDroppedInSavepointLevel(currentSavepointLevel); - - if (tempTableInfo.getDeclaredInSavepointLevel() > currentSavepointLevel) - tempTableInfo.setDeclaredInSavepointLevel(currentSavepointLevel); - - if (tempTableInfo.getModifiedInSavepointLevel() > currentSavepointLevel) - tempTableInfo.setModifiedInSavepointLevel(currentSavepointLevel); - } - } - - /** - * do the necessary work at commit time for temporary tables - * 1)If a temporary table was marked as dropped in this transaction, then remove it from the list of temp tables for this connection - * 2)If a temporary table was not dropped in this transaction, then mark it's declared savepoint level and modified savepoint level as -1 - */ - private void tempTablesAndCommit() { - for (int i = allDeclaredGlobalTempTables.size()-1; i >= 0; i--) { - TempTableInfo tempTableInfo = (TempTableInfo)allDeclaredGlobalTempTables.get(i); - if (tempTableInfo.getDroppedInSavepointLevel() != -1) - { - //this means table was dropped in this unit of work and hence should be removed from valid list of temp tables - allDeclaredGlobalTempTables.remove(i); - } else //this table was not dropped in this unit of work, hence set its declaredInSavepointLevel as -1 and also mark it as not modified - { - tempTableInfo.setDeclaredInSavepointLevel(-1); - tempTableInfo.setModifiedInSavepointLevel(-1); - } - } - } - - /** - Reset the connection before it is returned (indirectly) by - a PooledConnection object. See EmbeddedConnection. - */ - public void resetFromPool() - throws StandardException - { - // Reset IDENTITY_VAL_LOCAL - identityNotNull = false; + cachedInitialDefaultSchemaDescr = sd; + } + return cachedInitialDefaultSchemaDescr; + } + + /** + * Get the computed value for the initial default schema. + * @return the schema descriptor of the computed initial default schema + */ + private SchemaDescriptor getInitialDefaultSchemaDescriptor() { + return cachedInitialDefaultSchemaDescr; + } + + + // + // LanguageConnectionContext interface + // + /** + * @see LanguageConnectionContext#getLogStatementText + */ + public boolean getLogStatementText() + { + return logStatementText; + } + + /** + * @see LanguageConnectionContext#setLogStatementText + */ + public void setLogStatementText(boolean logStatementText) + { + this.logStatementText = logStatementText; + } + + /** + * @see LanguageConnectionContext#getLogQueryPlan + */ + public boolean getLogQueryPlan() + { + return logQueryPlan; + } + + /** + * @see LanguageConnectionContext#usesSqlAuthorization + */ + public boolean usesSqlAuthorization() + { + return getDataDictionary().usesSqlAuthorization(); + } + + /** + * get the lock escalation threshold. + */ + public int getLockEscalationThreshold() + { + return lockEscalationThreshold; + } + + /** + * Add the activation to those known about by this connection. + */ + public void addActivation(Activation a) + throws StandardException { + acts.add(a); + + if (acts.size() > maxActsSize) { + maxActsSize = acts.size(); + } + } + + public void closeUnusedActivations() + throws StandardException + { + // DERBY-418. Activations which are marked unused, + // are closed here. Activations Vector is iterated + // to identify and close unused activations, only if + // unusedActs flag is set to true and if the total + // size exceeds 20. + if( (unusedActs) && (acts.size() > 20) ) { + unusedActs = false; + + for (int i = acts.size() - 1; i >= 0; i--) { + + // it maybe the case that a Activation's reset() ends up + // closing one or more activation leaving our index beyond + // the end of the array + if (i >= acts.size()) + continue; + + Activation a1 = (Activation) acts.get(i); + if (!a1.isInUse()) { + a1.close(); + } + } + } + + if (SanityManager.DEBUG) { + + if (SanityManager.DEBUG_ON("memoryLeakTrace")) { + + if (acts.size() > 20) + System.out.println("memoryLeakTrace:GenericLanguageContext:activations " + acts.size()); + } + } + } + + /** + * Make a note that some activations are marked unused + */ + public void notifyUnusedActivation() { + unusedActs = true; + } + + /** + * @see LanguageConnectionContext#checkIfAnyDeclaredGlobalTempTablesForThisConnection + */ + public boolean checkIfAnyDeclaredGlobalTempTablesForThisConnection() { + return (allDeclaredGlobalTempTables == null ? false : true); + } + + /** + * @see LanguageConnectionContext#addDeclaredGlobalTempTable + */ + public void addDeclaredGlobalTempTable(TableDescriptor td) + throws StandardException + { + + if (findDeclaredGlobalTempTable(td.getName()) != null) + { + //if table already declared, throw an exception + throw StandardException.newException( + SQLState.LANG_OBJECT_ALREADY_EXISTS_IN_OBJECT, + "Declared global temporary table", + td.getName(), + "Schema", + SchemaDescriptor.STD_DECLARED_GLOBAL_TEMPORARY_TABLES_SCHEMA_NAME); + } + + //save all the information about temp table in this special class + TempTableInfo tempTableInfo = + new TempTableInfo(td, currentSavepointLevel); + + // Rather than exist in a catalog, a simple array is kept of the + // tables currently active in the transaction. + + if (allDeclaredGlobalTempTables == null) + allDeclaredGlobalTempTables = new ArrayList(); + + allDeclaredGlobalTempTables.add(tempTableInfo); + } - // drop all temp tables. - dropAllDeclaredGlobalTempTables(); + /** + * @see LanguageConnectionContext#dropDeclaredGlobalTempTable + */ + public boolean dropDeclaredGlobalTempTable(String tableName) + { + TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName); + + if (tempTableInfo != null) + { + if (SanityManager.DEBUG) + { + if (tempTableInfo.getDeclaredInSavepointLevel() > + currentSavepointLevel) + { + SanityManager.THROWASSERT( + "declared in savepoint level (" + + tempTableInfo.getDeclaredInSavepointLevel() + + ") can not be higher than current savepoint level (" + + currentSavepointLevel + + ")."); + } + } + + // check if the table was declared in the current unit of work. + if (tempTableInfo.getDeclaredInSavepointLevel() == + currentSavepointLevel) + { + // since the table was declared in this unit of work, the drop + // table method should remove it from the valid list of temp + // table for this unit of work + allDeclaredGlobalTempTables.remove( + allDeclaredGlobalTempTables.indexOf(tempTableInfo)); + + if (allDeclaredGlobalTempTables.size() == 0) + allDeclaredGlobalTempTables = null; + } + else + { + // since the table was not declared in this unit of work, the + // drop table method will just mark the table as dropped + // in the current unit of work. This information will be used + // at rollback time. + + tempTableInfo.setDroppedInSavepointLevel(currentSavepointLevel); + } + + return true; + } + else + { + return false; + } + } + + /** + * After a release of a savepoint, we need to go through our temp tables + * list. If there are tables with their declare or drop or modified in + * savepoint levels set to savepoint levels higher than the current + * savepoint level, then we should change them to the current savepoint + * level + */ + private void tempTablesReleaseSavepointLevels() + { + // unlike rollback, here we check for dropped in / declared in / + // modified in savepoint levels > current savepoint level only. + // This is because the temp tables with their savepoint levels same as + // currentSavepointLevel have correct value assigned to them and + // do not need to be changed and hence no need to check for >= + + for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) + { + TempTableInfo tempTableInfo = + (TempTableInfo)allDeclaredGlobalTempTables.get(i); + + if (tempTableInfo.getDroppedInSavepointLevel() > + currentSavepointLevel) + { + tempTableInfo.setDroppedInSavepointLevel(currentSavepointLevel); + } + + if (tempTableInfo.getDeclaredInSavepointLevel() > + currentSavepointLevel) + { + tempTableInfo.setDeclaredInSavepointLevel( + currentSavepointLevel); + } + + if (tempTableInfo.getModifiedInSavepointLevel() > + currentSavepointLevel) + { + tempTableInfo.setModifiedInSavepointLevel( + currentSavepointLevel); + } + } + } + + /** + * do the necessary work at commit time for temporary tables + * 1)If a temporary table was marked as dropped in this transaction, then + * remove it from the list of temp tables for this connection + * 2)If a temporary table was not dropped in this transaction, then mark + * it's declared savepoint level and modified savepoint level as -1 + */ + private void tempTablesAndCommit() + { + for (int i = allDeclaredGlobalTempTables.size()-1; i >= 0; i--) + { + TempTableInfo tempTableInfo = + (TempTableInfo)allDeclaredGlobalTempTables.get(i); + + if (tempTableInfo.getDroppedInSavepointLevel() != -1) + { + // this means table was dropped in this unit of work and hence + // should be removed from valid list of temp tables + + allDeclaredGlobalTempTables.remove(i); + } + else + { + //this table was not dropped in this unit of work, hence set + //its declaredInSavepointLevel as -1 and also mark it as not + //modified + + tempTableInfo.setDeclaredInSavepointLevel(-1); + tempTableInfo.setModifiedInSavepointLevel(-1); + } + } + } + + /** + Reset the connection before it is returned (indirectly) by + a PooledConnection object. See EmbeddedConnection. + */ + public void resetFromPool() + throws StandardException + { + // Reset IDENTITY_VAL_LOCAL + identityNotNull = false; + + // drop all temp tables. + dropAllDeclaredGlobalTempTables(); // Reset the current schema (see DERBY-3690). setDefaultSchema(null); - // Reset the current role - getCurrentSQLSessionContext().setRole(null); + // Reset the current role + getCurrentSQLSessionContext().setRole(null); // Reset the current user getCurrentSQLSessionContext().setUser(getSessionUserId()); - } + } // debug methods public void setLastQueryTree( Object queryTree ) { lastQueryTree = queryTree; } public Object getLastQueryTree() { return lastQueryTree; } - /** - * Drop all the declared global temporary tables associated with this connection. This gets called - * when a getConnection() is done on a PooledConnection. This will ensure all the temporary tables - * declared on earlier connection handle associated with this physical database connection are dropped - * before a new connection handle is issued on that same physical database connection. - */ - private void dropAllDeclaredGlobalTempTables() throws StandardException { - if (allDeclaredGlobalTempTables == null) - return; - - DependencyManager dm = getDataDictionary().getDependencyManager(); - StandardException topLevelStandardException = null; - - //collect all the exceptions we might receive while dropping the temporary tables and throw them as one chained exception at the end. - for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) { - try { - TempTableInfo tempTableInfo = (TempTableInfo)allDeclaredGlobalTempTables.get(i); - TableDescriptor td = tempTableInfo.getTableDescriptor(); - //the following 2 lines of code has been copied from DropTableConstantAction. If there are any changes made there in future, - //we should check if they need to be made here too. - dm.invalidateFor(td, DependencyManager.DROP_TABLE, this); - tran.dropConglomerate(td.getHeapConglomerateId()); - } catch (StandardException e) { - if (topLevelStandardException == null) { - // always keep the first exception unchanged - topLevelStandardException = e; - } else { - try { - // Try to create a chain of exceptions. If successful, - // the current exception is the top-level exception, - // and the previous exception the cause of it. - e.initCause(topLevelStandardException); - topLevelStandardException = e; - } catch (IllegalStateException ise) { - // initCause() has already been called on e. We don't - // expect this to happen, but if it happens, just skip - // the current exception from the chain. This is safe - // since we always keep the first exception. - } - } - } - } - - allDeclaredGlobalTempTables = null; - try { - internalCommit(true); - } catch (StandardException e) { - // do the same chaining as above - if (topLevelStandardException == null) { - topLevelStandardException = e; - } else { - try { - e.initCause(topLevelStandardException); - topLevelStandardException = e; - } catch (IllegalStateException ise) { /* ignore */ } - } - } - if (topLevelStandardException != null) throw topLevelStandardException; - } - - //do the necessary work at rollback time for temporary tables - /** - * do the necessary work at rollback time for temporary tables - * 1)If a temp table was declared in the UOW, then drop it and remove it from list of temporary tables. - * 2)If a temp table was declared and dropped in the UOW, then remove it from list of temporary tables. - * 3)If an existing temp table was dropped in the UOW, then recreate it with no data. - * 4)If an existing temp table was modified in the UOW, then get rid of all the rows from the table. - */ - private void tempTablesAndRollback() - throws StandardException { - for (int i = allDeclaredGlobalTempTables.size()-1; i >= 0; i--) { - TempTableInfo tempTableInfo = (TempTableInfo)allDeclaredGlobalTempTables.get(i); - if (tempTableInfo.getDeclaredInSavepointLevel() >= currentSavepointLevel) - { - if (tempTableInfo.getDroppedInSavepointLevel() == -1) - { - //the table was declared but not dropped in the unit of work getting rolled back and hence we will remove - //it from valid list of temporary tables and drop the conglomerate associated with it - TableDescriptor td = tempTableInfo.getTableDescriptor(); - tran.dropConglomerate(td.getHeapConglomerateId()); //remove the conglomerate created for this temp table - allDeclaredGlobalTempTables.remove(i); //remove it from the list of temp tables - } else if (tempTableInfo.getDroppedInSavepointLevel() >= currentSavepointLevel) - { - //the table was declared and dropped in the unit of work getting rolled back - allDeclaredGlobalTempTables.remove(i); - } - } else if (tempTableInfo.getDroppedInSavepointLevel() >= currentSavepointLevel) //this means the table was declared in an earlier savepoint unit / transaction and then dropped in current UOW - { - //restore the old definition of temp table because drop is being rolledback - TableDescriptor td = tempTableInfo.getTableDescriptor(); - td = cleanupTempTableOnCommitOrRollback(td, false); - //In order to store the old conglomerate information for the temp table, we need to replace the - //existing table descriptor with the old table descriptor which has the old conglomerate information - tempTableInfo.setTableDescriptor(td); - tempTableInfo.setDroppedInSavepointLevel(-1); - //following will mark the table as not modified. This is because the table data has been deleted as part of the current rollback - tempTableInfo.setModifiedInSavepointLevel(-1); - allDeclaredGlobalTempTables.set(i, tempTableInfo); - } else if (tempTableInfo.getModifiedInSavepointLevel() >= currentSavepointLevel) //this means the table was declared in an earlier savepoint unit / transaction and modified in current UOW - { - //following will mark the table as not modified. This is because the table data will be deleted as part of the current rollback - tempTableInfo.setModifiedInSavepointLevel(-1); - TableDescriptor td = tempTableInfo.getTableDescriptor(); - getDataDictionary().getDependencyManager().invalidateFor(td, DependencyManager.DROP_TABLE, this); - cleanupTempTableOnCommitOrRollback(td, true); - } // 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 - } - - if (allDeclaredGlobalTempTables.size() == 0) - allDeclaredGlobalTempTables = null; - } - - /** - * This is called at the commit time for temporary tables with ON COMMIT DELETE ROWS - * If a temp table with ON COMMIT DELETE ROWS doesn't have any held cursor open on them, we delete the data from - * them by dropping the conglomerate and recreating the conglomerate. In order to store the new conglomerate - * information for the temp table, we need to replace the existing table descriptor with the new table descriptor - * which has the new conglomerate information - * @param tableName Temporary table name whose table descriptor is getting changed - * @param td New table descriptor for the temporary table - */ - private void replaceDeclaredGlobalTempTable(String tableName, TableDescriptor td) { - TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName); - tempTableInfo.setDroppedInSavepointLevel(-1); - tempTableInfo.setDeclaredInSavepointLevel(-1); - tempTableInfo.setTableDescriptor(td); - allDeclaredGlobalTempTables.set(allDeclaredGlobalTempTables.indexOf(tempTableInfo), tempTableInfo); - } - - /** - * @see LanguageConnectionContext#getTableDescriptorForDeclaredGlobalTempTable - */ - public TableDescriptor getTableDescriptorForDeclaredGlobalTempTable(String tableName) { + /** + * Drop all the declared global temporary tables associated with this + * connection. This gets called when a getConnection() is done on a + * PooledConnection. This will ensure all the temporary tables declared on + * earlier connection handle associated with this physical database + * connection are dropped before a new connection handle is issued on that + * same physical database connection. + */ + private void dropAllDeclaredGlobalTempTables() throws StandardException + { + if (allDeclaredGlobalTempTables == null) + return; + + DependencyManager dm = getDataDictionary().getDependencyManager(); + StandardException topLevelStandardException = null; + + // collect all the exceptions we might receive while dropping the + // temporary tables and throw them as one chained exception at the end. + for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) + { + try + { + TempTableInfo tempTableInfo = + (TempTableInfo)allDeclaredGlobalTempTables.get(i); + + TableDescriptor td = tempTableInfo.getTableDescriptor(); + + // the following 2 lines of code has been copied from + // DropTableConstantAction. If there are any changes made there + // in future, we should check if they need to be made here too. + dm.invalidateFor(td, DependencyManager.DROP_TABLE, this); + tran.dropConglomerate(td.getHeapConglomerateId()); + + } + catch (StandardException e) + { + if (topLevelStandardException == null) + { + // always keep the first exception unchanged + topLevelStandardException = e; + } + else + { + try + { + // Try to create a chain of exceptions. If successful, + // the current exception is the top-level exception, + // and the previous exception the cause of it. + e.initCause(topLevelStandardException); + topLevelStandardException = e; + } + catch (IllegalStateException ise) + { + // initCause() has already been called on e. We don't + // expect this to happen, but if it happens, just skip + // the current exception from the chain. This is safe + // since we always keep the first exception. + } + } + } + } + + allDeclaredGlobalTempTables = null; + try + { + internalCommit(true); + } + catch (StandardException e) + { + // do the same chaining as above + if (topLevelStandardException == null) + { + topLevelStandardException = e; + } + else + { + try + { + e.initCause(topLevelStandardException); + topLevelStandardException = e; + } + catch (IllegalStateException ise) + { + /* ignore */ + } + } + } + + if (topLevelStandardException != null) + throw topLevelStandardException; + } + + /** + * do the necessary work at rollback time for temporary tables + * 1)If a temp table was declared in the UOW, then drop it and remove it + * from list of temporary tables. + * 2)If a temp table was declared and dropped in the UOW, then remove it + * from list of temporary tables. + * 3)If an existing temp table was dropped in the UOW, then recreate it + * with no data. + * 4)If an existing temp table was modified in the UOW, then get rid of + * all the rows from the table. + */ + private void tempTablesAndRollback() + throws StandardException + { + for (int i = allDeclaredGlobalTempTables.size()-1; i >= 0; i--) + { + TempTableInfo tempTableInfo = + (TempTableInfo)allDeclaredGlobalTempTables.get(i); + + if (tempTableInfo.getDeclaredInSavepointLevel() >= + currentSavepointLevel) + { + if (tempTableInfo.getDroppedInSavepointLevel() == -1) + { + // the table was declared but not dropped in the unit of + // work getting rolled back and hence we will remove it + // from valid list of temporary tables and drop the + // conglomerate associated with it + + TableDescriptor td = tempTableInfo.getTableDescriptor(); + + //remove the conglomerate created for this temp table + tran.dropConglomerate(td.getHeapConglomerateId()); + + //remove it from the list of temp tables + allDeclaredGlobalTempTables.remove(i); + + } + else if (tempTableInfo.getDroppedInSavepointLevel() >= + currentSavepointLevel) + { + // the table was declared and dropped in the unit of work + // getting rolled back + allDeclaredGlobalTempTables.remove(i); + } + } + else if (tempTableInfo.getDroppedInSavepointLevel() >= + currentSavepointLevel) + { + // this means the table was declared in an earlier savepoint + // unit / transaction and then dropped in current UOW + + // restore the old definition of temp table because drop is + // being rolledback + TableDescriptor td = tempTableInfo.getTableDescriptor(); + td = cleanupTempTableOnCommitOrRollback(td, false); + + // In order to store the old conglomerate information for the + // temp table, we need to replace the existing table descriptor + // with the old table descriptor which has the old conglomerate + // information + tempTableInfo.setTableDescriptor(td); + tempTableInfo.setDroppedInSavepointLevel(-1); + + // following will mark the table as not modified. This is + // because the table data has been deleted as part of the + // current rollback + tempTableInfo.setModifiedInSavepointLevel(-1); + allDeclaredGlobalTempTables.set(i, tempTableInfo); + + } + else if (tempTableInfo.getModifiedInSavepointLevel() >= + currentSavepointLevel) + { + // this means the table was declared in an earlier savepoint + // unit / transaction and modified in current UOW + + // following will mark the table as not modified. This is + // because the table data will be deleted as part of the + // current rollback + tempTableInfo.setModifiedInSavepointLevel(-1); + TableDescriptor td = tempTableInfo.getTableDescriptor(); + + getDataDictionary().getDependencyManager().invalidateFor( + td, DependencyManager.DROP_TABLE, this); + + cleanupTempTableOnCommitOrRollback(td, true); + } + // 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 + } + + if (allDeclaredGlobalTempTables.size() == 0) + allDeclaredGlobalTempTables = null; + } + + /** + * This is called at the commit time for temporary tables with + * ON COMMIT DELETE ROWS + * + * If a temp table with ON COMMIT DELETE ROWS doesn't have any held cursor + * open on them, we delete the data from them by dropping the conglomerate + * and recreating the conglomerate. In order to store the new conglomerate + * information for the temp table, we need to replace the existing table + * descriptor with the new table descriptor which has the new conglomerate + * information + * + * @param tableName Temporary table name whose table descriptor is + * getting changed + * @param td New table descriptor for the temporary table + */ + private void replaceDeclaredGlobalTempTable( + String tableName, + TableDescriptor td) + { + TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName); + + tempTableInfo.setDroppedInSavepointLevel(-1); + tempTableInfo.setDeclaredInSavepointLevel(-1); + tempTableInfo.setTableDescriptor(td); + + allDeclaredGlobalTempTables.set( + allDeclaredGlobalTempTables.indexOf(tempTableInfo), tempTableInfo); + } + + /** + * @see LanguageConnectionContext#getTableDescriptorForDeclaredGlobalTempTable + */ + public TableDescriptor getTableDescriptorForDeclaredGlobalTempTable(String tableName) { TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName); - if (tempTableInfo == null) - return null; - else - return tempTableInfo.getTableDescriptor(); - } - - /** - * Find the declared global temporary table in the list of temporary tables known by this connection. - * @param tableName look for this table name in the saved list - * @return data structure defining the temporary table if found. Else, return null - * - */ - private TempTableInfo findDeclaredGlobalTempTable(String tableName) { - if (allDeclaredGlobalTempTables == null) - return null; - - for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) { - if (((TempTableInfo)allDeclaredGlobalTempTables.get(i)).matches(tableName)) - return (TempTableInfo)allDeclaredGlobalTempTables.get(i); - } - return null; - } - - /** - * @see LanguageConnectionContext#markTempTableAsModifiedInUnitOfWork - */ - public void markTempTableAsModifiedInUnitOfWork(String tableName) { + if (tempTableInfo == null) + return null; + else + return tempTableInfo.getTableDescriptor(); + } + + /** + * Find the declared global temporary table in the list of temporary tables known by this connection. + * @param tableName look for this table name in the saved list + * @return data structure defining the temporary table if found. Else, return null + * + */ + private TempTableInfo findDeclaredGlobalTempTable(String tableName) { + if (allDeclaredGlobalTempTables == null) + return null; + + for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) { + if (((TempTableInfo)allDeclaredGlobalTempTables.get(i)).matches(tableName)) + return (TempTableInfo)allDeclaredGlobalTempTables.get(i); + } + return null; + } + + /** + * @see LanguageConnectionContext#markTempTableAsModifiedInUnitOfWork + */ + public void markTempTableAsModifiedInUnitOfWork(String tableName) { TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName); tempTableInfo.setModifiedInSavepointLevel(currentSavepointLevel); - } + } /** - * @see LanguageConnectionContext#prepareInternalStatement - */ + * @see LanguageConnectionContext#prepareInternalStatement + */ public PreparedStatement prepareInternalStatement(SchemaDescriptor compilationSchema, String sqlText, boolean isForReadOnly, boolean forMetaData) - throws StandardException + throws StandardException { - if (forMetaData) { - //DERBY-2946 - //Make sure that metadata queries always run with SYS as - //compilation schema. This will make sure that the collation - //type of character string constants will be UCS_BASIC which - //is also the collation of character string columns belonging - //to system tables. - compilationSchema = getDataDictionary().getSystemSchemaDescriptor(); - } - return connFactory.getStatement(compilationSchema, sqlText, isForReadOnly).prepare(this, forMetaData); - } + if (forMetaData) { + //DERBY-2946 + //Make sure that metadata queries always run with SYS as + //compilation schema. This will make sure that the collation + //type of character string constants will be UCS_BASIC which + //is also the collation of character string columns belonging + //to system tables. + compilationSchema = getDataDictionary().getSystemSchemaDescriptor(); + } + return connFactory.getStatement(compilationSchema, sqlText, isForReadOnly).prepare(this, forMetaData); + } /** - * @see LanguageConnectionContext#prepareInternalStatement - */ + * @see LanguageConnectionContext#prepareInternalStatement + */ public PreparedStatement prepareInternalStatement(String sqlText) - throws StandardException + throws StandardException + { + return connFactory. + getStatement(getDefaultSchema(), sqlText, true).prepare(this); + } + + /** + * Remove the activation to those known about by this connection. + * + */ + public void removeActivation(Activation a) + { + if (SanityManager.DEBUG) { + SanityManager.ASSERT(a.isClosed(), "Activation is not closed"); + } + + acts.remove(a); + + if (maxActsSize > 20 && (maxActsSize > 2 * acts.size())) { + acts.trimToSize(); + maxActsSize = acts.size(); + } + } + + /** + * Return the number of activations known for this connection. + * Note that some of these activations may not be in use + * (when a prepared statement is finalized, its activations + * are marked as unused and later closed and removed on + * the next commit/rollback). + */ + public int getActivationCount() { + return acts.size(); + } + + /** + * See if a given cursor is available for use. + * if so return its activation. Returns null if not found. + * For use in execution. + * + * @return the activation for the given cursor, null + * if none was found. + */ + public CursorActivation lookupCursorActivation(String cursorName) { + + int size = acts.size(); + if (size > 0) { - return connFactory. - getStatement(getDefaultSchema(), sqlText, true).prepare(this); - } - - /** - * Remove the activation to those known about by this connection. - * - */ - public void removeActivation(Activation a) - { - if (SanityManager.DEBUG) { - SanityManager.ASSERT(a.isClosed(), "Activation is not closed"); - } - - acts.remove(a); - - if (maxActsSize > 20 && (maxActsSize > 2 * acts.size())) { - acts.trimToSize(); - maxActsSize = acts.size(); - } - } - - /** - * Return the number of activations known for this connection. - * Note that some of these activations may not be in use - * (when a prepared statement is finalized, its activations - * are marked as unused and later closed and removed on - * the next commit/rollback). - */ - public int getActivationCount() { - return acts.size(); - } - - /** - * See if a given cursor is available for use. - * if so return its activation. Returns null if not found. - * For use in execution. - * - * @return the activation for the given cursor, null - * if none was found. - */ - public CursorActivation lookupCursorActivation(String cursorName) { - - int size = acts.size(); - if (size > 0) - { int cursorHash = cursorName.hashCode(); - for (int i = 0; i < size; i++) { - Activation a = (Activation) acts.get(i); + for (int i = 0; i < size; i++) { + Activation a = (Activation) acts.get(i); - if (!a.isInUse()) - { - continue; - } + if (!a.isInUse()) + { + continue; + } - String executingCursorName = a.getCursorName(); + String executingCursorName = a.getCursorName(); // If the executing cursor has no name, or if the hash code of // its name is different from the one we're looking for, it @@ -925,2418 +1077,2441 @@ public class GenericLanguageConnectionCo continue; } - if (cursorName.equals(executingCursorName)) { + if (cursorName.equals(executingCursorName)) { + + ResultSet rs = a.getResultSet(); + if (rs == null) + continue; + + // if the result set is closed, the the cursor doesn't exist + if (rs.isClosed()) { + continue; + } + + return (CursorActivation)a; + } + } + } + return null; + } + + /** + * This method will remove a statement from the statement cache. + * It should only be called if there is an exception preparing + * the statement. The caller must have set the flag + * {@code preparedStmt.compilingStatement} in the {@code GenericStatement} + * before calling this method in order to prevent race conditions when + * calling {@link CacheManager#remove(Cacheable)}. + * + * @param statement Statement to remove + * @exception StandardException thrown if lookup goes wrong. + */ + public void removeStatement(GenericStatement statement) + throws StandardException { + + CacheManager statementCache = + getLanguageConnectionFactory().getStatementCache(); + + if (statementCache == null) + return; + + Cacheable cachedItem = statementCache.findCached(statement); + // No need to do anything if the statement is already removed + if (cachedItem != null) { + CachedStatement cs = (CachedStatement) cachedItem; + if (statement.getPreparedStatement() != cs.getPreparedStatement()) { + // DERBY-3786: Someone else has removed the statement from + // the cache, probably because of the same error that brought + // us here. In addition, someone else has recreated the + // statement. Since the recreated statement is not the same + // object as the one we are working on, we don't have the + // proper guarding (through the synchronized flag + // GenericStatement.preparedStmt.compilingStatement) to ensure + // that we're the only ones calling CacheManager.remove() on + // this statement. Therefore, just release the statement here + // so that we don't get in the way for the other thread that + // is trying to compile the same query. + statementCache.release(cachedItem); + } else { + // The statement object that we were trying to compile is still + // in the cache. Since the compilation failed, remove it. + statementCache.remove(cachedItem); + } + } + } + + /** + * See if a given statement has already been compiled for this user, and + * if so use its prepared statement. Returns null if not found. + * + * @exception StandardException thrown if lookup goes wrong. + * @return the prepared statement for the given string, null + * if none was found. + */ + public PreparedStatement lookupStatement(GenericStatement statement) + throws StandardException { + + CacheManager statementCache = + getLanguageConnectionFactory().getStatementCache(); + + if (statementCache == null) + return null; + + // statement caching disable when in DDL mode + if (dataDictionaryInWriteMode()) { + return null; + } + + Cacheable cachedItem = statementCache.find(statement); + + CachedStatement cs = (CachedStatement) cachedItem; + + + GenericPreparedStatement ps = cs.getPreparedStatement(); + + synchronized (ps) { + if (ps.upToDate()) { + GeneratedClass ac = ps.getActivationClass(); + + // Check to see if the statement was prepared before some change + // in the class loading set. If this is the case then force it to be invalid + int currentClasses = + getLanguageConnectionFactory().getClassFactory().getClassLoaderVersion(); + + if (ac.getClassLoaderVersion() != currentClasses) { + ps.makeInvalid(DependencyManager.INTERNAL_RECOMPILE_REQUEST, this); + } + + // note that the PreparedStatement is not kept in the cache. This is because + // having items kept in the cache that ultimately are held onto by + // user code is impossible to manage. E.g. an open ResultSet would hold onto + // a PreparedStatement (through its activation) and the user can allow + // this object to be garbage collected. Pushing a context stack is impossible + // in garbage collection as it may deadlock with the open connection and + // the context manager assumes a singel current thread per context stack + } + } + + statementCache.release(cachedItem); + return ps; + } + + /** + Get a connection unique system generated name for a cursor. + */ + public String getUniqueCursorName() + { + return getNameString("SQLCUR", nextCursorId++); + } + + /** + Get a connection unique system generated name for an unnamed savepoint. + */ + public String getUniqueSavepointName() + { + return getNameString("SAVEPT", nextSavepointId++); + } + + /** + Get a connection unique system generated id for an unnamed savepoint. + */ + public int getUniqueSavepointID() + { + return nextSavepointId-1; + } + + /** + * Build a String for a statement name. + * + * @param prefix The prefix for the statement name. + * @param number The number to append for uniqueness + * + * @return A unique String for a statement name. + */ + private String getNameString(String prefix, long number) + { + if (sb != null) + { + sb.setLength(0); + } + else + { + sb = new StringBuffer(); + } + sb.append(prefix).append(number); + + return sb.toString(); + } + + /** + * Do a commit as appropriate for an internally generated + * commit (e.g. as needed by sync, or autocommit). + * + * @param commitStore true if we should commit the Store transaction + * + * @exception StandardException thrown on failure + */ + public void internalCommit(boolean commitStore) throws StandardException + { + doCommit(commitStore, true, NON_XA, false); + } + + /** + * Do a commmit as is appropriate for a user requested + * commit (e.g. a java.sql.Connection.commit() or a language + * 'COMMIT' statement. Does some extra checking to make + * sure that users aren't doing anything bad. + * + * @exception StandardException thrown on failure + */ + public void userCommit() throws StandardException + { + doCommit(true, true, NON_XA, true); + } + + + /** + Commit the language transaction by doing a commitNoSync() + on the store's TransactionController. + +

+ Do *NOT* tell the data dictionary that the transaction is + finished. The reason is that this would allow other transactions + to see comitted DDL that could be undone in the event of a + system crash. + + @param commitflag the flags to pass to commitNoSync in the store's + TransactionController + + @exception StandardException thrown on failure + */ + public final void internalCommitNoSync(int commitflag) + throws StandardException + { + doCommit(true, false, commitflag, false); + } + + + /** + Same as userCommit except commit a distributed transaction. + This commit always commit store and sync the commit. + + @param onePhase if true, allow it to commit without first going thru a + prepared state. + + @exception StandardException thrown if something goes wrong + */ + public final void xaCommit(boolean onePhase) throws StandardException + { + // further overload internalCommit to make it understand 2 phase commit + doCommit(true /* commit store */, + true /* sync */, + onePhase ? XA_ONE_PHASE : XA_TWO_PHASE, + true); + } + + + /** + * This is where the work on internalCommit(), userCOmmit() and + * internalCommitNoSync() actually takes place. + *

+ * When a commit happens, the language connection context + * will close all open activations/cursors and commit the + * Store transaction. + *

+ * REVISIT: we talked about having a LanguageTransactionContext, + * but since store transaction management is currently in flux + * and our context might want to delegate to that context, + * for now all commit/rollback actions are handled directly by + * the language connection context. + * REVISIT: this may need additional alterations when + * RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT show up. + *

+ * Since the access manager's own context takes care of its own + * resources on commit, and the transaction stays open, there is + * nothing that this context has to do with the transaction controller. + *

+ * Also, tell the data dictionary that the transaction is finished, + * if necessary (that is, if the data dictionary was put into + * DDL mode in this transaction. + * + * + * @param commitStore true if we should commit the Store transaction + * @param sync true means do a synchronized commit, + * false means do an unsynchronized commit + * @param commitflag if this is an unsynchronized commit, the flags to + * pass to commitNoSync in the store's + * TransactionController. If this is a synchronized + * commit, this flag is overloaded for xacommit. + * @param requestedByUser False iff the commit is for internal use and + * we should ignore the check to prevent commits + * in an atomic statement. + * + * @exception StandardException Thrown on error + */ + + protected void doCommit(boolean commitStore, + boolean sync, + int commitflag, + boolean requestedByUser) + throws StandardException + { + StatementContext statementContext = getStatementContext(); + if (requestedByUser && + (statementContext != null) && + statementContext.inUse() && + statementContext.isAtomic()) + { + throw StandardException.newException(SQLState.LANG_NO_COMMIT_IN_NESTED_CONNECTION); + } + + // Log commit to error log, if appropriate + if (logStatementText) + { + if (istream == null) + { + istream = Monitor.getStream(); + } + String xactId = tran.getTransactionIdString(); + istream.printlnWithHeader( + LanguageConnectionContext.xidStr + + xactId + + "), " + + LanguageConnectionContext.lccStr + + instanceNumber + + "), " + LanguageConnectionContext.dbnameStr + + dbname + + "), " + + LanguageConnectionContext.drdaStr + + drdaID + + "), Committing"); + } + + endTransactionActivationHandling(false); + + // do the clean up work required for temporary tables at the commit + // time. This cleanup work can possibly remove entries from + // allDeclaredGlobalTempTables and that's why we need to check + // again later to see if we there are still any entries in + // allDeclaredGlobalTempTables + if (allDeclaredGlobalTempTables != null) + { + tempTablesAndCommit(); + + // at commit time, for all the temp tables declared with + // ON COMMIT DELETE ROWS, make sure there are no held cursor open + // on them. + // 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 + + if (allDeclaredGlobalTempTables != null) + { + for (int i=0; i + The invalidated statements can revalidate themselves without + a full recompile if they verify their dependencies' providers still + exist unchanged. REVISIT when invalidation types are created. +

+ REVISIT: this may need additional alterations when + RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT show up. +

+ Also, tell the data dictionary that the transaction is finished, + if necessary (that is, if the data dictionary was put into + DDL mode in this transaction. + + @exception StandardException thrown on failure + */ + + public void internalRollback() throws StandardException + { + doRollback(false /* non-xa */, false); + } + + /** + * Do a rollback as is appropriate for a user requested + * rollback (e.g. a java.sql.Connection.rollback() or a language + * 'ROLLBACk' statement. Does some extra checking to make + * sure that users aren't doing anything bad. + * + * @exception StandardException thrown on failure + */ + public void userRollback() throws StandardException + { + doRollback(false /* non-xa */, true); + } + + /** + Same as userRollback() except rolls back a distrubuted transaction. + + @exception StandardException thrown if something goes wrong + */ + public void xaRollback() throws StandardException + { + doRollback(true /* xa */, true); + } + + /** + * When a rollback happens, the language connection context + * will close all open activations and invalidate + * their prepared statements. Then the language will abort the + * Store transaction. + *

+ * The invalidated statements can revalidate themselves without + * a full recompile if they verify their dependencies' providers still + * exist unchanged. REVISIT when invalidation types are created. + *

+ * REVISIT: this may need additional alterations when + * RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT show up. + *

+ * Also, tell the data dictionary that the transaction is finished, + * if necessary (that is, if the data dictionary was put into + * DDL mode in this transaction. + * + * @param xa true if this is an xa rollback + * @param requestedByUser true if requested by user + * + * @exception StandardException thrown on failure + */ + private void doRollback(boolean xa, boolean requestedByUser) throws StandardException + { + StatementContext statementContext = getStatementContext(); + if (requestedByUser && + (statementContext != null) && + statementContext.inUse() && + statementContext.isAtomic()) + { + throw StandardException.newException(SQLState.LANG_NO_ROLLBACK_IN_NESTED_CONNECTION); + } + + // Log rollback to error log, if appropriate + if (logStatementText) + { + if (istream == null) + { + istream = Monitor.getStream(); + } + String xactId = tran.getTransactionIdString(); + istream.printlnWithHeader(LanguageConnectionContext.xidStr + + xactId + + "), " + + LanguageConnectionContext.lccStr + + instanceNumber + + "), " + LanguageConnectionContext.dbnameStr + + dbname + + "), " + + LanguageConnectionContext.drdaStr + + drdaID + + "), Rolling back"); + } + + endTransactionActivationHandling(true); + + currentSavepointLevel = 0; //reset the current savepoint level for the connection to 0 at the beginning of rollback work for temp tables + if (allDeclaredGlobalTempTables != null) + tempTablesAndRollback(); + + finishDDTransaction(); + + // If a nested transaction is active then + // ensure it is destroyed before working + // with the user transaction. + if (readOnlyNestedTransaction != null) + { + readOnlyNestedTransaction.destroy(); + readOnlyNestedTransaction = null; + queryNestingDepth = 0; + } + + // now rollback the Store transaction + TransactionController tc = getTransactionExecute(); + if (tc != null) + { + if (xa) + ((XATransactionController)tc).xa_rollback(); + else + tc.abort(); + + // reset the savepoints to the new + // location, since any outer nesting [... 4607 lines stripped ...]