0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.sql.conn.GenericLanguageConnectionContext
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.sql.conn;
0023:
0024: import org.apache.derby.iapi.services.context.ContextImpl;
0025: import org.apache.derby.iapi.services.cache.CacheManager;
0026:
0027: import org.apache.derby.impl.sql.compile.CompilerContextImpl;
0028: import org.apache.derby.impl.sql.execute.InternalTriggerExecutionContext;
0029: import org.apache.derby.impl.sql.execute.AutoincrementCounter;
0030: import org.apache.derby.impl.sql.GenericPreparedStatement;
0031: import org.apache.derby.impl.sql.GenericStatement;
0032: import org.apache.derby.iapi.sql.Statement;
0033: import org.apache.derby.impl.sql.conn.CachedStatement;
0034:
0035: import org.apache.derby.iapi.services.property.PropertyUtil;
0036: import org.apache.derby.iapi.services.context.Context;
0037: import org.apache.derby.iapi.services.context.ContextManager;
0038: import org.apache.derby.iapi.services.monitor.Monitor;
0039: import org.apache.derby.iapi.services.sanity.SanityManager;
0040: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
0041: import org.apache.derby.iapi.services.loader.GeneratedClass;
0042: import org.apache.derby.iapi.services.cache.Cacheable;
0043: import org.apache.derby.iapi.db.Database;
0044: import org.apache.derby.iapi.error.StandardException;
0045: import org.apache.derby.iapi.sql.compile.CompilerContext;
0046: import org.apache.derby.iapi.sql.compile.OptimizerFactory;
0047: import org.apache.derby.iapi.sql.conn.Authorizer;
0048: import org.apache.derby.iapi.error.ExceptionSeverity;
0049: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
0050: import org.apache.derby.iapi.sql.conn.LanguageConnectionFactory;
0051: import org.apache.derby.iapi.sql.conn.StatementContext;
0052: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
0053: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptorList;
0054: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
0055: import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;
0056: import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
0057: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
0058: import org.apache.derby.iapi.types.DataValueFactory;
0059: import org.apache.derby.iapi.sql.compile.TypeCompilerFactory;
0060: import org.apache.derby.iapi.sql.depend.DependencyManager;
0061: import org.apache.derby.iapi.sql.depend.Provider;
0062: import org.apache.derby.iapi.reference.SQLState;
0063: import org.apache.derby.iapi.reference.Limits;
0064: import org.apache.derby.iapi.sql.execute.ConstantAction;
0065: import org.apache.derby.iapi.sql.execute.CursorActivation;
0066: import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;
0067: import org.apache.derby.iapi.sql.execute.ExecutionContext;
0068: import org.apache.derby.iapi.sql.execute.ExecutionStmtValidator;
0069: import org.apache.derby.iapi.sql.Activation;
0070: import org.apache.derby.iapi.sql.LanguageFactory;
0071: import org.apache.derby.iapi.sql.PreparedStatement;
0072: import org.apache.derby.iapi.sql.ResultSet;
0073: import org.apache.derby.iapi.sql.ParameterValueSet;
0074:
0075: import org.apache.derby.iapi.store.access.TransactionController;
0076: import org.apache.derby.iapi.store.access.AccessFactory;
0077: import org.apache.derby.iapi.store.access.XATransactionController;
0078: import org.apache.derby.iapi.util.IdUtil;
0079: import org.apache.derby.iapi.util.StringUtil;
0080:
0081: import org.apache.derby.catalog.UUID;
0082: import org.apache.derby.iapi.sql.execute.RunTimeStatistics;
0083: import org.apache.derby.iapi.db.TriggerExecutionContext;
0084: import org.apache.derby.iapi.reference.Property;
0085:
0086: import java.util.List;
0087: import java.util.ArrayList;
0088: import java.util.Enumeration;
0089: import java.util.Hashtable;
0090: import java.util.Properties;
0091: import java.util.Vector;
0092: import java.util.Stack;
0093: import java.io.Serializable;
0094:
0095: /**
0096: * LanguageConnectionContext keeps the pool of prepared statements,
0097: * activations, and cursors in use by the current connection.
0098: * <p>
0099: * The generic impl does not provide statement caching.
0100: *
0101: * @author ames
0102: *
0103: */
0104: public class GenericLanguageConnectionContext extends ContextImpl
0105: implements LanguageConnectionContext {
0106:
0107: // make sure these are not zeros
0108: private final static int NON_XA = 0;
0109: private final static int XA_ONE_PHASE = 1;
0110: private final static int XA_TWO_PHASE = 2;
0111:
0112: /*
0113: fields
0114: */
0115:
0116: private final Vector acts;
0117: private volatile boolean unusedActs = false;
0118: protected int bindCount;
0119: private boolean ddWriteMode;
0120: private boolean runTimeStatisticsSetting;
0121: private boolean statisticsTiming;
0122:
0123: //all the temporary tables declared for this connection
0124: private ArrayList allDeclaredGlobalTempTables;
0125: //The currentSavepointLevel is used to provide the rollback behavior of temporary tables.
0126: //At any point, this variable has the total number of savepoints defined for the transaction.
0127: private int currentSavepointLevel = 0;
0128:
0129: protected long nextCursorId;
0130:
0131: protected int nextSavepointId;
0132:
0133: private RunTimeStatistics runTimeStatisticsObject;
0134: private StringBuffer sb;
0135:
0136: private Database db;
0137:
0138: private final int instanceNumber;
0139: private String drdaID;
0140: private String dbname;
0141:
0142: private int identifierCasing = UNKNOWN_CASING;
0143:
0144: /**
0145: The transaction to use within this language connection context. It may
0146: be more appropriate to have it in a separate context (LanguageTransactionContext?).
0147: REVISIT (nat): I shoehorned the transaction context that
0148: the language uses into this class. The main purpose is so
0149: that the various language code can find out what its
0150: transaction is.
0151: **/
0152: protected TransactionController tran;
0153:
0154: /**
0155: * If non-null indicates that a nested user transaction is in progress.
0156: */
0157: protected TransactionController childTransaction;
0158:
0159: /**
0160: * queryNestingDepth is a counter used to keep track of how many calls
0161: * have been made to begin nested transactions. Only the first call
0162: * actually starts a Nested User Transaction with the store. Subsequent
0163: * calls simply increment this counter. commitNestedTransaction only
0164: * decrements the counter and when it drops to 0 actually commits the
0165: * nested user transaction.
0166: */
0167: protected int queryNestingDepth;
0168:
0169: protected DataValueFactory dataFactory;
0170: protected LanguageFactory langFactory;
0171: protected TypeCompilerFactory tcf;
0172: protected OptimizerFactory of;
0173: protected LanguageConnectionFactory connFactory;
0174:
0175: /*
0176: * A statement context is "pushed" and "popped" at the beginning and
0177: * end of every statement so that only that statement is cleaned up
0178: * on a Statement Exception. As a performance optimization, we only push
0179: * the outermost statement context once, and never pop it. Also, we
0180: * save off a 2nd StatementContext for speeding server side method
0181: * invocation, though we still push and pop it as needed. All other
0182: * statement contexts will allocated and pushed and popped on demand.
0183: */
0184: private final StatementContext[] statementContexts = new StatementContext[2];
0185: private int statementDepth;
0186: protected int outermostTrigger = -1;
0187:
0188: protected Authorizer authorizer;
0189: protected String userName = null; //The name the user connects with.
0190: //May still be quoted.
0191:
0192: protected SchemaDescriptor sd;
0193:
0194: // RESOLVE - How do we want to set the default.
0195: private int defaultIsolationLevel = ExecutionContext.READ_COMMITTED_ISOLATION_LEVEL;
0196: protected int isolationLevel = defaultIsolationLevel;
0197:
0198: private boolean isolationLevelExplicitlySet = false;
0199: // Isolation level can be changed using JDBC api Connection.setTransactionIsolation
0200: // or it can be changed using SQL "set current isolation = NEWLEVEL".
0201: //
0202: // In XA transactions, BrokeredConnection keeps isolation state information.
0203: // When isolation is changed in XA transaction using JDBC, that state gets
0204: // correctly set in BrokeredConnection.setTransactionIsolation method. But
0205: // when SQL is used to set the isolation level, the code path is different
0206: // and it does not go through BrokeredConnection's setTransactionIsolation
0207: // method and hence the state is not maintained correctly when coming through
0208: // SQL. To get around this, I am adding following flag which will get set
0209: // everytime the isolation level is set using JDBC or SQL. This flag will be
0210: // checked at global transaction start and end time. If the flag is set to true
0211: // then BrokeredConnection's isolation level state will be brought upto date
0212: // with Real Connection's isolation level and this flag will be set to false
0213: // after that.
0214: private boolean isolationLevelSetUsingSQLorJDBC = false;
0215:
0216: // isolation level to when preparing statements.
0217: // if unspecified, the statement won't be prepared with a specific
0218: // scan isolationlevel
0219: protected int prepareIsolationLevel = ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL;
0220:
0221: // Whether or not to write executing statement info to db2j.log
0222: private boolean logStatementText;
0223: private boolean logQueryPlan;
0224: private HeaderPrintWriter istream;
0225:
0226: // this used to be computed in OptimizerFactoryContextImpl; i.e everytime a
0227: // connection was made. To keep the semantics same I'm putting it out here
0228: // instead of in the OptimizerFactory which is only initialized when the
0229: // database is booted.
0230: private int lockEscalationThreshold;
0231:
0232: private Vector stmtValidators;
0233: private Vector triggerExecutionContexts;
0234: private Vector triggerTables;
0235:
0236: protected AccessFactory af;
0237:
0238: // OptimizerTrace
0239: private boolean optimizerTrace;
0240: private boolean optimizerTraceHtml;
0241: private String lastOptimizerTraceOutput;
0242: private String optimizerTraceOutput;
0243:
0244: //// Support for AUTOINCREMENT
0245:
0246: /**
0247: * To support lastAutoincrementValue: This is a hashtable which maps
0248: * schemaName,tableName,columnName to a Long value.
0249: */
0250: private Hashtable autoincrementHT;
0251: /**
0252: * whether to allow updates or not.
0253: */
0254: private boolean autoincrementUpdate;
0255: private long identityVal; //support IDENTITY_VAL_LOCAL function
0256: private boolean identityNotNull; //frugal programmer
0257:
0258: // cache of ai being handled in memory (bulk insert + alter table).
0259: private Hashtable autoincrementCacheHashtable;
0260:
0261: // temp public
0262: public CacheManager statementCache;
0263:
0264: /*
0265: constructor
0266: */
0267: public GenericLanguageConnectionContext(ContextManager cm,
0268: TransactionController tranCtrl,
0269:
0270: LanguageFactory lf, LanguageConnectionFactory lcf,
0271: Database db, String userName, int instanceNumber,
0272: String drdaID, String dbname) throws StandardException {
0273: super (
0274: cm,
0275: org.apache.derby.iapi.reference.ContextId.LANG_CONNECTION);
0276: acts = new Vector();
0277: tran = tranCtrl;
0278:
0279: dataFactory = lcf.getDataValueFactory();
0280: tcf = lcf.getTypeCompilerFactory();
0281: of = lcf.getOptimizerFactory();
0282: langFactory = lf;
0283: connFactory = lcf;
0284: this .db = db;
0285: this .userName = userName;
0286: this .instanceNumber = instanceNumber;
0287: this .drdaID = drdaID;
0288: this .dbname = dbname;
0289:
0290: /* Find out whether or not to log info on executing statements to error log
0291: */
0292: String logStatementProperty = PropertyUtil.getServiceProperty(
0293: getTransactionCompile(),
0294: "derby.language.logStatementText");
0295: logStatementText = Boolean.valueOf(logStatementProperty)
0296: .booleanValue();
0297:
0298: String logQueryPlanProperty = PropertyUtil.getServiceProperty(
0299: getTransactionCompile(), "derby.language.logQueryPlan");
0300: logQueryPlan = Boolean.valueOf(logQueryPlanProperty)
0301: .booleanValue();
0302:
0303: setRunTimeStatisticsMode(logQueryPlan);
0304:
0305: lockEscalationThreshold = PropertyUtil.getServiceInt(tranCtrl,
0306: Property.LOCKS_ESCALATION_THRESHOLD,
0307: Property.MIN_LOCKS_ESCALATION_THRESHOLD,
0308: Integer.MAX_VALUE,
0309: Property.DEFAULT_LOCKS_ESCALATION_THRESHOLD);
0310: stmtValidators = new Vector();
0311: triggerExecutionContexts = new Vector();
0312: triggerTables = new Vector();
0313:
0314: af = lcf.getAccessFactory();
0315: statementCache = lcf.getStatementCache();
0316: }
0317:
0318: public void initialize(boolean sqlConnection)
0319: throws StandardException {
0320: //
0321: //Creating the authorizer authorizes the connection.
0322: authorizer = new GenericAuthorizer(IdUtil
0323: .getUserAuthorizationId(userName), this , sqlConnection);
0324:
0325: //we can ignore the following if this is a database connection
0326: //associated with internal thread such as logSniffer and StageTrunc
0327: if (!sqlConnection)
0328: return;
0329:
0330: /*
0331: ** Set the authorization id. User shouldn't
0332: ** be null or else we are going to blow up trying
0333: ** to create a schema for this user.
0334: */
0335: if (SanityManager.DEBUG) {
0336: if (getAuthorizationId() == null) {
0337: SanityManager
0338: .THROWASSERT("User name is null,"
0339: + " check the connection manager to make sure it is set"
0340: + " reasonably");
0341: }
0342: }
0343:
0344: setDefaultSchema(initDefaultSchemaDescriptor());
0345: }
0346:
0347: protected SchemaDescriptor initDefaultSchemaDescriptor()
0348: throws StandardException {
0349: /*
0350: ** - If the database supports schemas and a schema with the
0351: ** same name as the user's name exists (has been created using
0352: ** create schema already) the database will set the users
0353: ** default schema to the the schema with the same name as the
0354: ** user.
0355: ** - Else Set the default schema to APP.
0356: */
0357: // SchemaDescriptor sd;
0358: DataDictionary dd = getDataDictionary();
0359: String authorizationId = getAuthorizationId();
0360:
0361: if ((sd = dd.getSchemaDescriptor(authorizationId,
0362: getTransactionCompile(), false)) == null) {
0363: sd = new SchemaDescriptor(dd, authorizationId,
0364: authorizationId, (UUID) null, false);
0365: }
0366: return sd;
0367: }
0368:
0369: //
0370: // LanguageConnectionContext interface
0371: //
0372: /**
0373: * @see LanguageConnectionContext#getLogStatementText
0374: */
0375: public boolean getLogStatementText() {
0376: return logStatementText;
0377: }
0378:
0379: /**
0380: * @see LanguageConnectionContext#setLogStatementText
0381: */
0382: public void setLogStatementText(boolean logStatementText) {
0383: this .logStatementText = logStatementText;
0384: }
0385:
0386: /**
0387: * @see LanguageConnectionContext#getLogQueryPlan
0388: */
0389: public boolean getLogQueryPlan() {
0390: return logQueryPlan;
0391: }
0392:
0393: /**
0394: * @see LanguageConnectionContext#usesSqlAuthorization
0395: */
0396: public boolean usesSqlAuthorization() {
0397: return getDataDictionary().usesSqlAuthorization();
0398: }
0399:
0400: /**
0401: * get the lock escalation threshold.
0402: */
0403: public int getLockEscalationThreshold() {
0404: return lockEscalationThreshold;
0405: }
0406:
0407: /*
0408: The methods that follow are for consistency checking purposes
0409: */
0410:
0411: public int getCacheSize() {
0412: if (statementCache != null)
0413: return statementCache.getNumberInUse();
0414: else
0415: return 0;
0416: }
0417:
0418: /**
0419: * Add the activation to those known about by this connection.
0420: */
0421: public void addActivation(Activation a) throws StandardException {
0422: acts.addElement(a);
0423:
0424: // DERBY-418. Activations which are marked unused,
0425: // are closed here. Activations Vector is iterated
0426: // to identify and close unused activations, only if
0427: // unusedActs flag is set to true and if the total
0428: // size exceeds 20.
0429: if ((unusedActs) && (acts.size() > 20)) {
0430: unusedActs = false;
0431: for (int i = acts.size() - 1; i >= 0; i--) {
0432:
0433: // it maybe the case that a Activation's reset() ends up
0434: // closing one or more activation leaving our index beyond
0435: // the end of the array
0436: if (i >= acts.size())
0437: continue;
0438:
0439: Activation a1 = (Activation) acts.elementAt(i);
0440: if (!a1.isInUse()) {
0441: a1.close();
0442: }
0443: }
0444: }
0445:
0446: if (SanityManager.DEBUG) {
0447:
0448: if (SanityManager.DEBUG_ON("memoryLeakTrace")) {
0449:
0450: if (acts.size() > 20)
0451: System.out
0452: .println("memoryLeakTrace:GenericLanguageContext:activations "
0453: + acts.size());
0454: }
0455: }
0456: }
0457:
0458: /**
0459: * Make a note that some activations are marked unused
0460: */
0461: public void notifyUnusedActivation() {
0462: unusedActs = true;
0463: }
0464:
0465: /**
0466: * @see LanguageConnectionContext#checkIfAnyDeclaredGlobalTempTablesForThisConnection
0467: */
0468: public boolean checkIfAnyDeclaredGlobalTempTablesForThisConnection() {
0469: return (allDeclaredGlobalTempTables == null ? false : true);
0470: }
0471:
0472: /**
0473: * @see LanguageConnectionContext#addDeclaredGlobalTempTable
0474: */
0475: public void addDeclaredGlobalTempTable(TableDescriptor td)
0476: throws StandardException {
0477:
0478: if (findDeclaredGlobalTempTable(td.getName()) != null) //if table already declared, throw an exception
0479: {
0480: throw StandardException
0481: .newException(
0482: SQLState.LANG_OBJECT_ALREADY_EXISTS_IN_OBJECT,
0483: "Declared global temporary table",
0484: td.getName(),
0485: "Schema",
0486: SchemaDescriptor.STD_DECLARED_GLOBAL_TEMPORARY_TABLES_SCHEMA_NAME);
0487: }
0488:
0489: //save all the information about temp table in this special class
0490: TempTableInfo tempTableInfo = new TempTableInfo(td,
0491: currentSavepointLevel);
0492:
0493: if (allDeclaredGlobalTempTables == null)
0494: allDeclaredGlobalTempTables = new ArrayList();
0495:
0496: allDeclaredGlobalTempTables.add(tempTableInfo);
0497: }
0498:
0499: /**
0500: * @see LanguageConnectionContext#dropDeclaredGlobalTempTable
0501: */
0502: public boolean dropDeclaredGlobalTempTable(String tableName) {
0503: TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName);
0504: if (tempTableInfo != null) {
0505: if (SanityManager.DEBUG)
0506: if (tempTableInfo.getDeclaredInSavepointLevel() > currentSavepointLevel)
0507: SanityManager
0508: .THROWASSERT("declared in savepoint level can not be higher than the current savepoint level");
0509:
0510: //following checks if the table was declared in the current unit of work.
0511: if (tempTableInfo.getDeclaredInSavepointLevel() == currentSavepointLevel) {
0512: //since the table was declared in this unit of work,
0513: //the drop table method should remove it from the valid list of temp table for this unit of work
0514: allDeclaredGlobalTempTables
0515: .remove(allDeclaredGlobalTempTables
0516: .indexOf(tempTableInfo));
0517: if (allDeclaredGlobalTempTables.size() == 0)
0518: allDeclaredGlobalTempTables = null;
0519: } else {
0520: //since the table was not declared in this unit of work, the drop table method will just mark the table as dropped
0521: //in the current unit of work. This information will be used at rollback time.
0522: tempTableInfo
0523: .setDroppedInSavepointLevel(currentSavepointLevel);
0524: }
0525: return true;
0526: } else
0527: return false;
0528: }
0529:
0530: /**
0531: * After a release of a savepoint, we need to go through our temp tables list. If there are tables with their declare or drop
0532: * or modified in savepoint levels set to savepoint levels higher than the current savepoint level, then we should change them
0533: * to the current savepoint level
0534: */
0535: private void tempTablesReleaseSavepointLevels() {
0536: //unlike rollback, here we check for dropped in / declared in / modified in savepoint levels > current savepoint level only.
0537: //This is because the temp tables with their savepoint levels same as currentSavepointLevel have correct value assigned to them and
0538: //do not need to be changed and hence no need to check for >=
0539: for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) {
0540: TempTableInfo tempTableInfo = (TempTableInfo) allDeclaredGlobalTempTables
0541: .get(i);
0542: if (tempTableInfo.getDroppedInSavepointLevel() > currentSavepointLevel)
0543: tempTableInfo
0544: .setDroppedInSavepointLevel(currentSavepointLevel);
0545:
0546: if (tempTableInfo.getDeclaredInSavepointLevel() > currentSavepointLevel)
0547: tempTableInfo
0548: .setDeclaredInSavepointLevel(currentSavepointLevel);
0549:
0550: if (tempTableInfo.getModifiedInSavepointLevel() > currentSavepointLevel)
0551: tempTableInfo
0552: .setModifiedInSavepointLevel(currentSavepointLevel);
0553: }
0554: }
0555:
0556: /**
0557: * do the necessary work at commit time for temporary tables
0558: * 1)If a temporary table was marked as dropped in this transaction, then remove it from the list of temp tables for this connection
0559: * 2)If a temporary table was not dropped in this transaction, then mark it's declared savepoint level and modified savepoint level as -1
0560: */
0561: private void tempTablesAndCommit() {
0562: for (int i = allDeclaredGlobalTempTables.size() - 1; i >= 0; i--) {
0563: TempTableInfo tempTableInfo = (TempTableInfo) allDeclaredGlobalTempTables
0564: .get(i);
0565: if (tempTableInfo.getDroppedInSavepointLevel() != -1) {
0566: //this means table was dropped in this unit of work and hence should be removed from valid list of temp tables
0567: allDeclaredGlobalTempTables.remove(i);
0568: } else //this table was not dropped in this unit of work, hence set its declaredInSavepointLevel as -1 and also mark it as not modified
0569: {
0570: tempTableInfo.setDeclaredInSavepointLevel(-1);
0571: tempTableInfo.setModifiedInSavepointLevel(-1);
0572: }
0573: }
0574: }
0575:
0576: /**
0577: Reset the connection before it is returned (indirectly) by
0578: a PooledConnection object. See EmbeddedConnection.
0579: */
0580: public void resetFromPool() throws StandardException {
0581: // Reset IDENTITY_VAL_LOCAL
0582: identityNotNull = false;
0583:
0584: // drop all temp tables.
0585: dropAllDeclaredGlobalTempTables();
0586: }
0587:
0588: /**
0589: * Drop all the declared global temporary tables associated with this connection. This gets called
0590: * when a getConnection() is done on a PooledConnection. This will ensure all the temporary tables
0591: * declared on earlier connection handle associated with this physical database connection are dropped
0592: * before a new connection handle is issued on that same physical database connection.
0593: */
0594: private void dropAllDeclaredGlobalTempTables()
0595: throws StandardException {
0596: if (allDeclaredGlobalTempTables == null)
0597: return;
0598:
0599: DependencyManager dm = getDataDictionary()
0600: .getDependencyManager();
0601: StandardException topLevelStandardException = null;
0602:
0603: //collect all the exceptions we might receive while dropping the temporary tables and throw them as one chained exception at the end.
0604: for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) {
0605: try {
0606: TempTableInfo tempTableInfo = (TempTableInfo) allDeclaredGlobalTempTables
0607: .get(i);
0608: TableDescriptor td = tempTableInfo.getTableDescriptor();
0609: //the following 2 lines of code has been copied from DropTableConstantAction. If there are any changes made there in future,
0610: //we should check if they need to be made here too.
0611: dm
0612: .invalidateFor(td,
0613: DependencyManager.DROP_TABLE, this );
0614: tran.dropConglomerate(td.getHeapConglomerateId());
0615: } catch (StandardException e) {
0616: e.setNestedException(topLevelStandardException);
0617: topLevelStandardException = e;
0618: }
0619: }
0620:
0621: allDeclaredGlobalTempTables = null;
0622: try {
0623: internalCommit(true);
0624: } catch (StandardException e) {
0625: e.setNestedException(topLevelStandardException);
0626: topLevelStandardException = e;
0627: }
0628: if (topLevelStandardException != null)
0629: throw topLevelStandardException;
0630: }
0631:
0632: //do the necessary work at rollback time for temporary tables
0633: /**
0634: * do the necessary work at rollback time for temporary tables
0635: * 1)If a temp table was declared in the UOW, then drop it and remove it from list of temporary tables.
0636: * 2)If a temp table was declared and dropped in the UOW, then remove it from list of temporary tables.
0637: * 3)If an existing temp table was dropped in the UOW, then recreate it with no data.
0638: * 4)If an existing temp table was modified in the UOW, then get rid of all the rows from the table.
0639: */
0640: private void tempTablesAndRollback() throws StandardException {
0641: for (int i = allDeclaredGlobalTempTables.size() - 1; i >= 0; i--) {
0642: TempTableInfo tempTableInfo = (TempTableInfo) allDeclaredGlobalTempTables
0643: .get(i);
0644: if (tempTableInfo.getDeclaredInSavepointLevel() >= currentSavepointLevel) {
0645: if (tempTableInfo.getDroppedInSavepointLevel() == -1) {
0646: //the table was declared but not dropped in the unit of work getting rolled back and hence we will remove
0647: //it from valid list of temporary tables and drop the conglomerate associated with it
0648: TableDescriptor td = tempTableInfo
0649: .getTableDescriptor();
0650: tran.dropConglomerate(td.getHeapConglomerateId()); //remove the conglomerate created for this temp table
0651: allDeclaredGlobalTempTables.remove(i); //remove it from the list of temp tables
0652: } else if (tempTableInfo.getDroppedInSavepointLevel() >= currentSavepointLevel) {
0653: //the table was declared and dropped in the unit of work getting rolled back
0654: allDeclaredGlobalTempTables.remove(i);
0655: }
0656: } else if (tempTableInfo.getDroppedInSavepointLevel() >= currentSavepointLevel) //this means the table was declared in an earlier savepoint unit / transaction and then dropped in current UOW
0657: {
0658: //restore the old definition of temp table because drop is being rolledback
0659: TableDescriptor td = tempTableInfo.getTableDescriptor();
0660: td = cleanupTempTableOnCommitOrRollback(td, false);
0661: //In order to store the old conglomerate information for the temp table, we need to replace the
0662: //existing table descriptor with the old table descriptor which has the old conglomerate information
0663: tempTableInfo.setTableDescriptor(td);
0664: tempTableInfo.setDroppedInSavepointLevel(-1);
0665: //following will mark the table as not modified. This is because the table data has been deleted as part of the current rollback
0666: tempTableInfo.setModifiedInSavepointLevel(-1);
0667: allDeclaredGlobalTempTables.set(i, tempTableInfo);
0668: } else if (tempTableInfo.getModifiedInSavepointLevel() >= currentSavepointLevel) //this means the table was declared in an earlier savepoint unit / transaction and modified in current UOW
0669: {
0670: //following will mark the table as not modified. This is because the table data will be deleted as part of the current rollback
0671: tempTableInfo.setModifiedInSavepointLevel(-1);
0672: TableDescriptor td = tempTableInfo.getTableDescriptor();
0673: getDataDictionary().getDependencyManager()
0674: .invalidateFor(td,
0675: DependencyManager.DROP_TABLE, this );
0676: cleanupTempTableOnCommitOrRollback(td, true);
0677: } // 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
0678: }
0679:
0680: if (allDeclaredGlobalTempTables.size() == 0)
0681: allDeclaredGlobalTempTables = null;
0682: }
0683:
0684: /**
0685: * This is called at the commit time for temporary tables with ON COMMIT DELETE ROWS
0686: * If a temp table with ON COMMIT DELETE ROWS doesn't have any held cursor open on them, we delete the data from
0687: * them by dropping the conglomerate and recreating the conglomerate. In order to store the new conglomerate
0688: * information for the temp table, we need to replace the existing table descriptor with the new table descriptor
0689: * which has the new conglomerate information
0690: * @param tableName Temporary table name whose table descriptor is getting changed
0691: * @param td New table descriptor for the temporary table
0692: */
0693: private void replaceDeclaredGlobalTempTable(String tableName,
0694: TableDescriptor td) {
0695: TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName);
0696: tempTableInfo.setDroppedInSavepointLevel(-1);
0697: tempTableInfo.setDeclaredInSavepointLevel(-1);
0698: tempTableInfo.setTableDescriptor(td);
0699: allDeclaredGlobalTempTables.set(allDeclaredGlobalTempTables
0700: .indexOf(tempTableInfo), tempTableInfo);
0701: }
0702:
0703: /**
0704: * @see LanguageConnectionContext#getTableDescriptorForDeclaredGlobalTempTable
0705: */
0706: public TableDescriptor getTableDescriptorForDeclaredGlobalTempTable(
0707: String tableName) {
0708: TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName);
0709: if (tempTableInfo == null)
0710: return null;
0711: else
0712: return tempTableInfo.getTableDescriptor();
0713: }
0714:
0715: /**
0716: * Find the declared global temporary table in the list of temporary tables known by this connection.
0717: * @param tableName look for this table name in the saved list
0718: * @return data structure defining the temporary table if found. Else, return null
0719: *
0720: */
0721: private TempTableInfo findDeclaredGlobalTempTable(String tableName) {
0722: if (allDeclaredGlobalTempTables == null)
0723: return null;
0724:
0725: for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) {
0726: if (((TempTableInfo) allDeclaredGlobalTempTables.get(i))
0727: .matches(tableName))
0728: return (TempTableInfo) allDeclaredGlobalTempTables
0729: .get(i);
0730: }
0731: return null;
0732: }
0733:
0734: /**
0735: * @see LanguageConnectionContext#markTempTableAsModifiedInUnitOfWork
0736: */
0737: public void markTempTableAsModifiedInUnitOfWork(String tableName) {
0738: TempTableInfo tempTableInfo = findDeclaredGlobalTempTable(tableName);
0739: tempTableInfo
0740: .setModifiedInSavepointLevel(currentSavepointLevel);
0741: }
0742:
0743: /**
0744: * @see LanguageConnectionContext#prepareInternalStatement
0745: */
0746: public PreparedStatement prepareInternalStatement(
0747: SchemaDescriptor compilationSchema, String sqlText,
0748: boolean isForReadOnly, boolean forMetaData)
0749: throws StandardException {
0750: return connFactory.getStatement(compilationSchema, sqlText,
0751: isForReadOnly).prepare(this , forMetaData);
0752: }
0753:
0754: /**
0755: * @see LanguageConnectionContext#prepareInternalStatement
0756: */
0757: public PreparedStatement prepareInternalStatement(String sqlText)
0758: throws StandardException {
0759: return connFactory.getStatement(sd, sqlText, true)
0760: .prepare(this );
0761: }
0762:
0763: /**
0764: * Remove the activation to those known about by this connection.
0765: *
0766: */
0767: public void removeActivation(Activation a) {
0768: if (SanityManager.DEBUG) {
0769: SanityManager.ASSERT(a.isClosed(),
0770: "Activation is not closed");
0771: }
0772:
0773: acts.removeElement(a);
0774:
0775: int capacity = acts.capacity();
0776:
0777: if (capacity > 20 && (capacity > 2 * acts.size())) {
0778: acts.trimToSize();
0779: }
0780: }
0781:
0782: /**
0783: * Return the number of activations known for this connection.
0784: * Note that some of these activations may not be in use
0785: * (when a prepared statement is finalized, its activations
0786: * are marked as unused and later closed and removed on
0787: * the next commit/rollback).
0788: */
0789: public int getActivationCount() {
0790: return acts.size();
0791: }
0792:
0793: /**
0794: * See if a given cursor is available for use.
0795: * if so return its activation. Returns null if not found.
0796: * For use in execution.
0797: *
0798: * @return the activation for the given cursor, null
0799: * if none was found.
0800: */
0801: public CursorActivation lookupCursorActivation(String cursorName) {
0802:
0803: int size = acts.size();
0804: if (size > 0) {
0805: for (int i = 0; i < size; i++) {
0806: Activation a = (Activation) acts.elementAt(i);
0807:
0808: if (!a.isInUse()) {
0809: continue;
0810: }
0811:
0812: String executingCursorName = a.getCursorName();
0813:
0814: if (cursorName.equals(executingCursorName)) {
0815:
0816: ResultSet rs = a.getResultSet();
0817: if (rs == null)
0818: continue;
0819:
0820: // if the result set is closed, the the cursor doesn't exist
0821: if (rs.isClosed()) {
0822: continue;
0823: }
0824:
0825: return (CursorActivation) a;
0826: }
0827: }
0828: }
0829: return null;
0830: }
0831:
0832: /**
0833: * This method will remove a statement from the statement cache.
0834: * It will be called, for example, if there is an exception preparing
0835: * the statement.
0836: *
0837: * @param statement Statement to remove
0838: * @exception StandardException thrown if lookup goes wrong.
0839: */
0840: public void removeStatement(Statement statement)
0841: throws StandardException {
0842:
0843: if (statementCache == null)
0844: return;
0845:
0846: Cacheable cachedItem = statementCache.findCached(statement);
0847: if (cachedItem != null)
0848: statementCache.remove(cachedItem);
0849: }
0850:
0851: /**
0852: * See if a given statement has already been compiled for this user, and
0853: * if so use its prepared statement. Returns null if not found.
0854: *
0855: * @exception StandardException thrown if lookup goes wrong.
0856: * @return the prepared statement for the given string, null
0857: * if none was found.
0858: */
0859: public PreparedStatement lookupStatement(GenericStatement statement)
0860: throws StandardException {
0861:
0862: if (statementCache == null)
0863: return null;
0864:
0865: // statement caching disable when in DDL mode
0866: if (dataDictionaryInWriteMode()) {
0867: return null;
0868: }
0869:
0870: Cacheable cachedItem = statementCache.find(statement);
0871:
0872: CachedStatement cs = (CachedStatement) cachedItem;
0873:
0874: GenericPreparedStatement ps = cs.getPreparedStatement();
0875:
0876: synchronized (ps) {
0877: if (ps.upToDate()) {
0878: GeneratedClass ac = ps.getActivationClass();
0879:
0880: // Check to see if the statement was prepared before some change
0881: // in the class loading set. If this is the case then force it to be invalid
0882: int currentClasses = getLanguageConnectionFactory()
0883: .getClassFactory().getClassLoaderVersion();
0884:
0885: if (ac.getClassLoaderVersion() != currentClasses) {
0886: ps
0887: .makeInvalid(
0888: DependencyManager.INTERNAL_RECOMPILE_REQUEST,
0889: this );
0890: }
0891:
0892: // note that the PreparedStatement is not kept in the cache. This is because
0893: // having items kept in the cache that ultimately are held onto by
0894: // user code is impossible to manage. E.g. an open ResultSet would hold onto
0895: // a PreparedStatement (through its activation) and the user can allow
0896: // this object to be garbage collected. Pushing a context stack is impossible
0897: // in garbage collection as it may deadlock with the open connection and
0898: // the context manager assumes a singel current thread per context stack
0899: }
0900: }
0901:
0902: statementCache.release(cachedItem);
0903: return ps;
0904: }
0905:
0906: /**
0907: Get a connection unique system generated name for a cursor.
0908: */
0909: public String getUniqueCursorName() {
0910: return getNameString("SQLCUR", nextCursorId++);
0911: }
0912:
0913: /**
0914: Get a connection unique system generated name for an unnamed savepoint.
0915: */
0916: public String getUniqueSavepointName() {
0917: return getNameString("SAVEPT", nextSavepointId++);
0918: }
0919:
0920: /**
0921: Get a connection unique system generated id for an unnamed savepoint.
0922: */
0923: public int getUniqueSavepointID() {
0924: return nextSavepointId - 1;
0925: }
0926:
0927: /**
0928: * Build a String for a statement name.
0929: *
0930: * @param prefix The prefix for the statement name.
0931: * @param number The number to append for uniqueness
0932: *
0933: * @return A unique String for a statement name.
0934: */
0935: private String getNameString(String prefix, long number) {
0936: if (sb != null) {
0937: sb.setLength(0);
0938: } else {
0939: sb = new StringBuffer();
0940: }
0941: sb.append(prefix).append(number);
0942:
0943: return sb.toString();
0944: }
0945:
0946: /**
0947: * Do a commit as appropriate for an internally generated
0948: * commit (e.g. as needed by sync, or autocommit).
0949: *
0950: * @param commitStore true if we should commit the Store transaction
0951: *
0952: * @exception StandardException thrown on failure
0953: */
0954: public void internalCommit(boolean commitStore)
0955: throws StandardException {
0956: doCommit(commitStore, true, NON_XA, false);
0957: }
0958:
0959: /**
0960: * Do a commmit as is appropriate for a user requested
0961: * commit (e.g. a java.sql.Connection.commit() or a language
0962: * 'COMMIT' statement. Does some extra checking to make
0963: * sure that users aren't doing anything bad.
0964: *
0965: * @exception StandardException thrown on failure
0966: */
0967: public void userCommit() throws StandardException {
0968: doCommit(true, true, NON_XA, true);
0969: }
0970:
0971: /**
0972: Commit the language transaction by doing a commitNoSync()
0973: on the store's TransactionController.
0974:
0975: <p>
0976: Do *NOT* tell the data dictionary that the transaction is
0977: finished. The reason is that this would allow other transactions
0978: to see comitted DDL that could be undone in the event of a
0979: system crash.
0980:
0981: @param commitflag the flags to pass to commitNoSync in the store's
0982: TransactionController
0983:
0984: @exception StandardException thrown on failure
0985: */
0986: public final void internalCommitNoSync(int commitflag)
0987: throws StandardException {
0988: doCommit(true, false, commitflag, false);
0989: }
0990:
0991: /**
0992: Same as userCommit except commit a distributed transaction.
0993: This commit always commit store and sync the commit.
0994:
0995: @param onePhase if true, allow it to commit without first going thru a
0996: prepared state.
0997:
0998: @exception StandardException thrown if something goes wrong
0999: */
1000: public final void xaCommit(boolean onePhase)
1001: throws StandardException {
1002: // further overload internalCommit to make it understand 2 phase commit
1003: doCommit(true /* commit store */, true /* sync */,
1004: onePhase ? XA_ONE_PHASE : XA_TWO_PHASE, true);
1005: }
1006:
1007: /**
1008: * This is where the work on internalCommit(), userCOmmit() and
1009: * internalCommitNoSync() actually takes place.
1010: * <p>
1011: * When a commit happens, the language connection context
1012: * will close all open activations/cursors and commit the
1013: * Store transaction.
1014: * <p>
1015: * REVISIT: we talked about having a LanguageTransactionContext,
1016: * but since store transaction management is currently in flux
1017: * and our context might want to delegate to that context,
1018: * for now all commit/rollback actions are handled directly by
1019: * the language connection context.
1020: * REVISIT: this may need additional alterations when
1021: * RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT show up.
1022: * <P>
1023: * Since the access manager's own context takes care of its own
1024: * resources on commit, and the transaction stays open, there is
1025: * nothing that this context has to do with the transaction controller.
1026: * <p>
1027: * Also, tell the data dictionary that the transaction is finished,
1028: * if necessary (that is, if the data dictionary was put into
1029: * DDL mode in this transaction.
1030: *
1031: *
1032: * @param commitStore true if we should commit the Store transaction
1033: * @param sync true means do a synchronized commit,
1034: * false means do an unsynchronized commit
1035: * @param commitflag if this is an unsynchronized commit, the flags to
1036: * pass to commitNoSync in the store's
1037: * TransactionController. If this is a synchronized
1038: * commit, this flag is overloaded for xacommit.
1039: * @param requestedByUser False iff the commit is for internal use and
1040: * we should ignore the check to prevent commits
1041: * in an atomic statement.
1042: *
1043: * @exception StandardException Thrown on error
1044: */
1045:
1046: protected void doCommit(boolean commitStore, boolean sync,
1047: int commitflag, boolean requestedByUser)
1048: throws StandardException {
1049: StatementContext statementContext = getStatementContext();
1050: if (requestedByUser && (statementContext != null)
1051: && statementContext.inUse()
1052: && statementContext.isAtomic()) {
1053: throw StandardException
1054: .newException(SQLState.LANG_NO_COMMIT_IN_NESTED_CONNECTION);
1055: }
1056:
1057: // Log commit to error log, if appropriate
1058: if (logStatementText) {
1059: if (istream == null) {
1060: istream = Monitor.getStream();
1061: }
1062: String xactId = tran.getTransactionIdString();
1063: istream.printlnWithHeader(LanguageConnectionContext.xidStr
1064: + xactId + "), " + LanguageConnectionContext.lccStr
1065: + instanceNumber + "), "
1066: + LanguageConnectionContext.dbnameStr + dbname
1067: + "), " + LanguageConnectionContext.drdaStr
1068: + drdaID + "), Committing");
1069: }
1070:
1071: resetActivations(false);
1072:
1073: //do the clean up work required for temporary tables at the commit time. This cleanup work
1074: //can possibly remove entries from allDeclaredGlobalTempTables and that's why we need to check
1075: //again later to see if we there are still any entries in allDeclaredGlobalTempTables
1076: if (allDeclaredGlobalTempTables != null) {
1077: tempTablesAndCommit();
1078: //at commit time, for all the temp tables declared with ON COMMIT DELETE ROWS, make sure there are no held cursor open on them.
1079: //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
1080: if (allDeclaredGlobalTempTables != null) {
1081: for (int i = 0; i < allDeclaredGlobalTempTables.size(); i++) {
1082: TableDescriptor td = ((TempTableInfo) (allDeclaredGlobalTempTables
1083: .get(i))).getTableDescriptor();
1084: if (td.isOnCommitDeleteRows() == false) //do nothing for temp table with ON COMMIT PRESERVE ROWS
1085: {
1086: continue;
1087: }
1088: if (checkIfAnyActivationHasHoldCursor(td.getName()) == false)//temp tables with ON COMMIT DELETE ROWS and no open held cursors
1089: {
1090: getDataDictionary().getDependencyManager()
1091: .invalidateFor(td,
1092: DependencyManager.DROP_TABLE,
1093: this );
1094: cleanupTempTableOnCommitOrRollback(td, true);
1095: }
1096: }
1097: }
1098: }
1099:
1100: currentSavepointLevel = 0; //reset the current savepoint level for the connection to 0 at the end of commit work for temp tables
1101:
1102: // Do *NOT* tell the DataDictionary to start using its cache again
1103: // if this is an unsynchronized commit. The reason is that it
1104: // would allow other transactions to see this transaction's DDL,
1105: // which could be rolled back in case of a system crash.
1106: if (sync) {
1107: finishDDTransaction();
1108: }
1109:
1110: // now commit the Store transaction
1111: TransactionController tc = getTransactionExecute();
1112: if (tc != null && commitStore) {
1113: if (sync) {
1114: if (commitflag == NON_XA) {
1115: // regular commit
1116: tc.commit();
1117: } else {
1118: // This may be a xa_commit, check overloaded commitflag.
1119:
1120: if (SanityManager.DEBUG)
1121: SanityManager.ASSERT(commitflag == XA_ONE_PHASE
1122: || commitflag == XA_TWO_PHASE,
1123: "invalid commit flag");
1124:
1125: ((XATransactionController) tc)
1126: .xa_commit(commitflag == XA_ONE_PHASE);
1127:
1128: }
1129: } else {
1130: tc.commitNoSync(commitflag);
1131: }
1132:
1133: // reset the savepoints to the new
1134: // location, since any outer nesting
1135: // levels expect there to be a savepoint
1136: resetSavepoints();
1137: }
1138: }
1139:
1140: /**
1141: * If dropAndRedeclare is true, that means we have come here for temp tables with on commit delete rows
1142: * and no held curosr open on them. We will drop the existing conglomerate and redeclare a new conglomerate
1143: * similar to old conglomerate. This is a more efficient way of deleting all rows from the table.
1144: *
1145: * If dropAndRedeclare is false, that means we have come here for the rollback cleanup work. We are trying
1146: * to restore old definition of the temp table (because the drop on it is being rolled back).
1147: */
1148: private TableDescriptor cleanupTempTableOnCommitOrRollback(
1149: TableDescriptor td, boolean dropAndRedeclare)
1150: throws StandardException {
1151: //create new conglomerate with same properties as the old conglomerate and same row template as the old conglomerate
1152: long conglomId = tran
1153: .createConglomerate(
1154: "heap", // we're requesting a heap conglomerate
1155: td.getEmptyExecRow(getContextManager())
1156: .getRowArray(), // row template
1157: null, //column sort order - not required for heap
1158: null, // properties
1159: (TransactionController.IS_TEMPORARY | TransactionController.IS_KEPT));
1160:
1161: long cid = td.getHeapConglomerateId();
1162:
1163: //remove the old conglomerate descriptor from the table descriptor
1164: ConglomerateDescriptor cgd = td.getConglomerateDescriptor(cid);
1165: td.getConglomerateDescriptorList()
1166: .dropConglomerateDescriptorByUUID(cgd.getUUID());
1167: //add the new conglomerate descriptor to the table descriptor
1168: cgd = getDataDictionary().getDataDescriptorGenerator()
1169: .newConglomerateDescriptor(conglomId, null, false,
1170: null, false, null, td.getUUID(),
1171: td.getSchemaDescriptor().getUUID());
1172: ConglomerateDescriptorList conglomList = td
1173: .getConglomerateDescriptorList();
1174: conglomList.add(cgd);
1175:
1176: //reset the heap conglomerate number in table descriptor to -1 so it will be refetched next time with the new value
1177: td.resetHeapConglomNumber();
1178:
1179: if (dropAndRedeclare) {
1180: tran.dropConglomerate(cid); //remove the old conglomerate from the system
1181: replaceDeclaredGlobalTempTable(td.getName(), td);
1182: }
1183:
1184: return (td);
1185: }
1186:
1187: /**
1188: Do a rollback as appropriate for an internally generated
1189: rollback (e.g. as needed by sync, or autocommit).
1190:
1191: When a rollback happens, we
1192: close all open activations and invalidate their
1193: prepared statements. We then tell the cache to
1194: age out everything else, which effectively invalidates
1195: them. Thus, all prepared statements will be
1196: compiled anew on their 1st execution after
1197: a rollback.
1198: <p>
1199: The invalidated statements can revalidate themselves without
1200: a full recompile if they verify their dependencies' providers still
1201: exist unchanged. REVISIT when invalidation types are created.
1202: <p>
1203: REVISIT: this may need additional alterations when
1204: RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT show up.
1205: <p>
1206: Also, tell the data dictionary that the transaction is finished,
1207: if necessary (that is, if the data dictionary was put into
1208: DDL mode in this transaction.
1209:
1210: @exception StandardException thrown on failure
1211: */
1212:
1213: public void internalRollback() throws StandardException {
1214: doRollback(false /* non-xa */, false);
1215: }
1216:
1217: /**
1218: * Do a rollback as is appropriate for a user requested
1219: * rollback (e.g. a java.sql.Connection.rollback() or a language
1220: * 'ROLLBACk' statement. Does some extra checking to make
1221: * sure that users aren't doing anything bad.
1222: *
1223: * @exception StandardException thrown on failure
1224: */
1225: public void userRollback() throws StandardException {
1226: doRollback(false /* non-xa */, true);
1227: }
1228:
1229: /**
1230: Same as userRollback() except rolls back a distrubuted transaction.
1231:
1232: @exception StandardException thrown if something goes wrong
1233: */
1234: public void xaRollback() throws StandardException {
1235: doRollback(true /* xa */, true);
1236: }
1237:
1238: /**
1239: * When a rollback happens, the language connection context
1240: * will close all open activations and invalidate
1241: * their prepared statements. Then the language will abort the
1242: * Store transaction.
1243: * <p>
1244: * The invalidated statements can revalidate themselves without
1245: * a full recompile if they verify their dependencies' providers still
1246: * exist unchanged. REVISIT when invalidation types are created.
1247: * <p>
1248: * REVISIT: this may need additional alterations when
1249: * RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT show up.
1250: * <p>
1251: * Also, tell the data dictionary that the transaction is finished,
1252: * if necessary (that is, if the data dictionary was put into
1253: * DDL mode in this transaction.
1254: *
1255: * @param xa true if this is an xa rollback
1256: * @param requestedByUser true if requested by user
1257: *
1258: * @exception StandardException thrown on failure
1259: */
1260: private void doRollback(boolean xa, boolean requestedByUser)
1261: throws StandardException {
1262: StatementContext statementContext = getStatementContext();
1263: if (requestedByUser && (statementContext != null)
1264: && statementContext.inUse()
1265: && statementContext.isAtomic()) {
1266: throw StandardException
1267: .newException(SQLState.LANG_NO_ROLLBACK_IN_NESTED_CONNECTION);
1268: }
1269:
1270: // Log rollback to error log, if appropriate
1271: if (logStatementText) {
1272: if (istream == null) {
1273: istream = Monitor.getStream();
1274: }
1275: String xactId = tran.getTransactionIdString();
1276: istream.printlnWithHeader(LanguageConnectionContext.xidStr
1277: + xactId + "), " + LanguageConnectionContext.lccStr
1278: + instanceNumber + "), "
1279: + LanguageConnectionContext.dbnameStr + dbname
1280: + "), " + LanguageConnectionContext.dbnameStr
1281: + dbname + "), "
1282: + LanguageConnectionContext.drdaStr + drdaID
1283: + "), Rolling back");
1284: }
1285:
1286: resetActivations(true);
1287:
1288: currentSavepointLevel = 0; //reset the current savepoint level for the connection to 0 at the beginning of rollback work for temp tables
1289: if (allDeclaredGlobalTempTables != null)
1290: tempTablesAndRollback();
1291:
1292: finishDDTransaction();
1293:
1294: // now rollback the Store transaction
1295: TransactionController tc = getTransactionExecute();
1296: if (tc != null) {
1297: if (xa)
1298: ((XATransactionController) tc).xa_rollback();
1299: else
1300: tc.abort();
1301: // reset the savepoints to the new
1302: // location, since any outer nesting
1303: // levels expet there to be a savepoint
1304: resetSavepoints();
1305: }
1306: }
1307:
1308: /**
1309: * Reset all statement savepoints. Traverses the StatementContext
1310: * stack from bottom to top, calling resetSavePoint()
1311: * on each element.
1312: *
1313: * @exception StandardException thrown if something goes wrong
1314: */
1315: private void resetSavepoints() throws StandardException {
1316: final ContextManager cm = getContextManager();
1317: final List stmts = cm
1318: .getContextStack(org.apache.derby.iapi.reference.ContextId.LANG_STATEMENT);
1319: final int end = stmts.size();
1320: for (int i = 0; i < end; ++i) {
1321: ((StatementContext) stmts.get(i)).resetSavePoint();
1322: }
1323: }
1324:
1325: /**
1326: * Let the context deal with a rollback to savepoint
1327: *
1328: * @param savepointName Name of the savepoint that needs to be rolled back
1329: * @param refreshStyle boolean indicating whether or not the controller should close
1330: * open conglomerates and scans. Also used to determine if language should close
1331: * open activations.
1332: * @param kindOfSavepoint A NULL value means it is an internal savepoint (ie not a user defined savepoint)
1333: * Non NULL value means it is a user defined savepoint which can be a SQL savepoint or a JDBC savepoint
1334: * A String value for kindOfSavepoint would mean it is SQL savepoint
1335: * A JDBC Savepoint object value for kindOfSavepoint would mean it is JDBC savepoint
1336: *
1337: * @exception StandardException thrown if something goes wrong
1338: */
1339: public void internalRollbackToSavepoint(String savepointName,
1340: boolean refreshStyle, Object kindOfSavepoint)
1341: throws StandardException {
1342: // now rollback the Store transaction to the savepoint
1343: TransactionController tc = getTransactionExecute();
1344: if (tc != null) {
1345: boolean closeConglomerates;
1346:
1347: if (refreshStyle) {
1348: closeConglomerates = true;
1349: // bug 5145 - don't forget to close the activations while rolling
1350: // back to a savepoint
1351: resetActivations(true);
1352: } else {
1353: closeConglomerates = false;
1354: }
1355:
1356: currentSavepointLevel = tc.rollbackToSavePoint(
1357: savepointName, closeConglomerates, kindOfSavepoint);
1358: }
1359:
1360: if (tc != null && refreshStyle
1361: && allDeclaredGlobalTempTables != null)
1362: tempTablesAndRollback();
1363: }
1364:
1365: /**
1366: Let the context deal with a release of a savepoint
1367:
1368: @param savepointName Name of the savepoint that needs to be released
1369: @param kindOfSavepoint A NULL value means it is an internal savepoint (ie not a user defined savepoint)
1370: Non NULL value means it is a user defined savepoint which can be a SQL savepoint or a JDBC savepoint
1371: A String value for kindOfSavepoint would mean it is SQL savepoint
1372: A JDBC Savepoint object value for kindOfSavepoint would mean it is JDBC savepoint
1373:
1374: @exception StandardException thrown if something goes wrong
1375: */
1376: public void releaseSavePoint(String savepointName,
1377: Object kindOfSavepoint) throws StandardException {
1378: TransactionController tc = getTransactionExecute();
1379: if (tc != null) {
1380: currentSavepointLevel = tc.releaseSavePoint(savepointName,
1381: kindOfSavepoint);
1382: //after a release of a savepoint, we need to go through our temp tables list.
1383: if (allDeclaredGlobalTempTables != null)
1384: tempTablesReleaseSavepointLevels();
1385: }
1386: }
1387:
1388: /**
1389: Sets a savepoint. Causes the Store to set a savepoint.
1390:
1391: @param savepointName name of savepoint
1392: @param kindOfSavepoint A NULL value means it is an internal savepoint (ie not a user defined savepoint)
1393: Non NULL value means it is a user defined savepoint which can be a SQL savepoint or a JDBC savepoint
1394: A String value for kindOfSavepoint would mean it is SQL savepoint
1395: A JDBC Savepoint object value for kindOfSavepoint would mean it is JDBC savepoint
1396:
1397: @exception StandardException thrown if something goes wrong
1398: */
1399: public void languageSetSavePoint(String savepointName,
1400: Object kindOfSavepoint) throws StandardException {
1401: TransactionController tc = getTransactionExecute();
1402: if (tc != null) {
1403: currentSavepointLevel = tc.setSavePoint(savepointName,
1404: kindOfSavepoint);
1405: }
1406: }
1407:
1408: /**
1409: * Return true if any transaction is blocked, even if not by this one
1410: *
1411: */
1412: public boolean anyoneBlocked() {
1413: return getTransactionExecute().anyoneBlocked();
1414: }
1415:
1416: /**
1417: Sets the transaction controller to use with this language connection
1418: context.
1419:
1420: @param tran the transaction to use with this language connection context
1421: */
1422: public void setTransaction(TransactionController tran) {
1423: this .tran = tran;
1424: }
1425:
1426: /**
1427: * Start a Nested User Transaction (NUT) with the store. If a NUT is
1428: * already active simply increment a counter, queryNestingDepth, to keep
1429: * track of how many times we have tried to start a NUT.
1430: */
1431: public void beginNestedTransaction(boolean readOnly)
1432: throws StandardException {
1433: if (childTransaction == null)
1434: childTransaction = tran
1435: .startNestedUserTransaction(readOnly);
1436: queryNestingDepth++;
1437: return;
1438: }
1439:
1440: public void commitNestedTransaction() throws StandardException {
1441: if (--queryNestingDepth == 0) {
1442: childTransaction.commit();
1443: childTransaction.destroy();
1444: childTransaction = null;
1445: return;
1446: }
1447: }
1448:
1449: /**
1450: * Get the transaction controller to use at compile time with this language
1451: * connection context. If a NUT is active then return NUT else return parent
1452: * transaction.
1453: */
1454: public TransactionController getTransactionCompile() {
1455: return (childTransaction != null) ? childTransaction : tran;
1456: }
1457:
1458: public TransactionController getTransactionExecute() {
1459: return tran;
1460: }
1461:
1462: /** Get the data value factory to use with this language connection
1463: context.
1464: */
1465: public DataValueFactory getDataValueFactory() {
1466: return dataFactory;
1467: }
1468:
1469: /**
1470: Get the language factory to use with this language connection
1471: context.
1472: */
1473: public LanguageFactory getLanguageFactory() {
1474: return langFactory;
1475: }
1476:
1477: public OptimizerFactory getOptimizerFactory() {
1478: return of;
1479: }
1480:
1481: /**
1482: Get the language connection factory to use with this language connection
1483: context.
1484: */
1485: public LanguageConnectionFactory getLanguageConnectionFactory() {
1486: return connFactory;
1487: }
1488:
1489: /**
1490: * check if there are any activations that reference this temporary table
1491: * @param tableName look for any activations referencing this table name
1492: * @return boolean false if found no activations
1493: */
1494: private boolean checkIfAnyActivationHasHoldCursor(String tableName)
1495: throws StandardException {
1496: for (int i = acts.size() - 1; i >= 0; i--) {
1497: Activation a = (Activation) acts.elementAt(i);
1498: if (a.checkIfThisActivationHasHoldCursor(tableName))
1499: return true;
1500: }
1501: return false;
1502: }
1503:
1504: /**
1505: * Verify that there are no activations with open held result sets.
1506: *
1507: * @return boolean Found no open (held) resultsets.
1508: *
1509: * @exception StandardException thrown on failure
1510: */
1511: /* This gets used in case of hold cursors. If there are any hold cursors open
1512: * then user can't change the isolation level without closing them. At the
1513: * execution time, set transaction isolation level calls this method before
1514: * changing the isolation level.
1515: */
1516: public boolean verifyAllHeldResultSetsAreClosed()
1517: throws StandardException {
1518: boolean seenOpenResultSets = false;
1519:
1520: /* For every activation */
1521: for (int i = acts.size() - 1; i >= 0; i--) {
1522:
1523: Activation a = (Activation) acts.elementAt(i);
1524:
1525: if (SanityManager.DEBUG) {
1526: SanityManager.ASSERT(a instanceof CursorActivation,
1527: "a is not a CursorActivation");
1528: }
1529:
1530: if (!a.isInUse()) {
1531: continue;
1532: }
1533:
1534: if (!a.getResultSetHoldability()) {
1535: continue;
1536: }
1537:
1538: ResultSet rs = ((CursorActivation) a).getResultSet();
1539:
1540: /* is there an open result set? */
1541: if ((rs != null) && !rs.isClosed() && rs.returnsRows()) {
1542: seenOpenResultSets = true;
1543: break;
1544: }
1545: }
1546:
1547: if (!seenOpenResultSets)
1548: return (true);
1549:
1550: // There may be open ResultSet's that are yet to be garbage collected
1551: // let's try and force these out rather than throw an error
1552: System.gc();
1553: System.runFinalization();
1554:
1555: /* For every activation */
1556: for (int i = acts.size() - 1; i >= 0; i--) {
1557:
1558: Activation a = (Activation) acts.elementAt(i);
1559:
1560: if (SanityManager.DEBUG) {
1561: SanityManager.ASSERT(a instanceof CursorActivation,
1562: "a is not a CursorActivation");
1563: }
1564:
1565: if (!a.isInUse()) {
1566: continue;
1567: }
1568:
1569: if (!a.getResultSetHoldability()) {
1570: continue;
1571: }
1572:
1573: ResultSet rs = ((CursorActivation) a).getResultSet();
1574:
1575: /* is there an open held result set? */
1576: if ((rs != null) && !rs.isClosed() && rs.returnsRows()) {
1577: return (false);
1578: }
1579: }
1580: return (true);
1581: }
1582:
1583: /**
1584: * Verify that there are no activations with open result sets
1585: * on the specified prepared statement.
1586: *
1587: * @param pStmt The prepared Statement
1588: * @param provider The object precipitating a possible invalidation
1589: * @param action The action causing the possible invalidation
1590: *
1591: * @return Nothing.
1592: *
1593: * @exception StandardException thrown on failure
1594: */
1595: public boolean verifyNoOpenResultSets(PreparedStatement pStmt,
1596: Provider provider, int action) throws StandardException {
1597: /*
1598: ** It is not a problem to create an index when there is an open
1599: ** result set, since it doesn't invalidate the access path that was
1600: ** chosen for the result set.
1601: */
1602: boolean seenOpenResultSets = false;
1603:
1604: /* For every activation */
1605:
1606: // synchronize on acts as other threads may be closing activations
1607: // in this list, thus invalidating the Enumeration
1608: for (int i = acts.size() - 1; i >= 0; i--) {
1609:
1610: Activation a = (Activation) acts.elementAt(i);
1611:
1612: if (!a.isInUse()) {
1613: continue;
1614: }
1615:
1616: /* for this prepared statement */
1617: if (pStmt == a.getPreparedStatement()) {
1618: ResultSet rs = a.getResultSet();
1619:
1620: /* is there an open result set? */
1621: if (rs != null && !rs.isClosed()) {
1622: if (!rs.returnsRows())
1623: continue;
1624: seenOpenResultSets = true;
1625: break;
1626: }
1627:
1628: }
1629: }
1630:
1631: if (!seenOpenResultSets)
1632: return false;
1633:
1634: // There may be open ResultSet's that are yet to be garbage collected
1635: // let's try and force these out rather than throw an error
1636: System.gc();
1637: System.runFinalization();
1638:
1639: /* For every activation */
1640: // synchronize on acts as other threads may be closing activations
1641: // in this list, thus invalidating the Enumeration
1642: for (int i = acts.size() - 1; i >= 0; i--) {
1643:
1644: Activation a = (Activation) acts.elementAt(i);
1645:
1646: if (!a.isInUse()) {
1647: continue;
1648: }
1649:
1650: /* for this prepared statement */
1651: if (pStmt == a.getPreparedStatement()) {
1652: ResultSet rs = a.getResultSet();
1653:
1654: /* is there an open result set? */
1655: if (rs != null && !rs.isClosed()) {
1656: if ((provider != null) && rs.returnsRows()) {
1657: DependencyManager dmgr = getDataDictionary()
1658: .getDependencyManager();
1659:
1660: throw StandardException
1661: .newException(
1662: SQLState.LANG_CANT_INVALIDATE_OPEN_RESULT_SET,
1663: dmgr.getActionString(action),
1664: provider.getObjectName());
1665:
1666: }
1667: return true;
1668: }
1669: }
1670: }
1671: return false;
1672: }
1673:
1674: /**
1675: * Get the Authorization Id
1676: *
1677: * @return String the authorization id
1678: */
1679: public String getAuthorizationId() {
1680: return authorizer.getAuthorizationId();
1681: }
1682:
1683: /**
1684: * Get the default schema
1685: *
1686: * @return SchemaDescriptor the default schema
1687: */
1688: public SchemaDescriptor getDefaultSchema() {
1689: return sd;
1690: }
1691:
1692: /**
1693: * Get the current schema name
1694: *
1695: * @return current schema name
1696: */
1697: public String getCurrentSchemaName() {
1698: if (null == sd)
1699: return null;
1700: return sd.getSchemaName();
1701: }
1702:
1703: /**
1704: * Set the default schema -- used by SET SCHEMA.
1705: *
1706: * @param sd the new default schema.
1707: * If null, then the default schema descriptor is used.
1708: *
1709: * @exception StandardException thrown on failure
1710: */
1711: public void setDefaultSchema(SchemaDescriptor sd)
1712: throws StandardException {
1713: if (sd == null) {
1714: sd = initDefaultSchemaDescriptor();
1715: }
1716: this .sd = sd;
1717:
1718: }
1719:
1720: /**
1721: * Get the identity column value most recently generated.
1722: *
1723: * @return the generated identity column value
1724: */
1725: public Long getIdentityValue() {
1726: return identityNotNull ? new Long(identityVal) : null;
1727: }
1728:
1729: /**
1730: * Set the field of most recently generated identity column value.
1731: *
1732: * @param val the generated identity column value
1733: */
1734: public void setIdentityValue(long val) {
1735: identityVal = val;
1736: identityNotNull = true;
1737: }
1738:
1739: /**
1740: Empty as much of the cache as possible. It is not guaranteed
1741: that the cache is empty after this call, as statements may be kept
1742: by currently executing queries, activations that are about to be garbage
1743: collected.
1744: */
1745: public void emptyCache() {
1746: /* We know prepared statements don't become dirty
1747: ** statementCache.cleanAll();
1748: */
1749: if (statementCache != null)
1750: statementCache.ageOut();
1751: }
1752:
1753: /**
1754: * Push a CompilerContext on the context stack with
1755: * the current default schema as the default schema
1756: * which we compile against.
1757: *
1758: * @return the compiler context
1759: *
1760: * @exception StandardException thrown on failure
1761: */
1762: public final CompilerContext pushCompilerContext() {
1763: return pushCompilerContext((SchemaDescriptor) null);
1764: }
1765:
1766: /**
1767: * Push a CompilerContext on the context stack with
1768: * the passed in default schema as the default schema
1769: * we compile against.
1770: *
1771: * @param sd the default schema
1772: *
1773: * @return the compiler context
1774: *
1775: */
1776: public CompilerContext pushCompilerContext(SchemaDescriptor sd) {
1777: CompilerContext cc;
1778: boolean firstCompilerContext = false;
1779:
1780: // DEBUG END
1781:
1782: cc = (CompilerContext) (getContextManager()
1783: .getContext(CompilerContext.CONTEXT_ID));
1784:
1785: /*
1786: ** If there is no compiler context, this is the first one on the
1787: ** stack, so don't pop it when we're done (saves time).
1788: */
1789: if (cc == null) {
1790: firstCompilerContext = true;
1791: }
1792:
1793: if (cc == null || cc.getInUse()) {
1794: cc = new CompilerContextImpl(getContextManager(), this , tcf);
1795: if (firstCompilerContext) {
1796: cc.firstOnStack();
1797: }
1798: } else {
1799: /* Reset the next column,table, subquery and ResultSet numbers at
1800: * the beginning of each statement
1801: */
1802: cc.resetContext();
1803: }
1804:
1805: cc.setInUse(true);
1806:
1807: // Save off the current isolation level on entry so that it gets restored
1808: cc.setEntryIsolationLevel(getCurrentIsolationLevel());
1809:
1810: StatementContext sc = getStatementContext();
1811: if (sc.getSystemCode())
1812: cc.setReliability(CompilerContext.INTERNAL_SQL_LEGAL);
1813:
1814: return cc;
1815: }
1816:
1817: /**
1818: * Pop a CompilerContext off the context stack.
1819: *
1820: * @param cc The compiler context.
1821: */
1822: public void popCompilerContext(CompilerContext cc) {
1823: cc.setCurrentDependent(null);
1824:
1825: cc.setInUse(false);
1826:
1827: // Restore the isolation level at the time of entry to CompilerContext
1828: isolationLevel = cc.getEntryIsolationLevel();
1829:
1830: /*
1831: ** Only pop the compiler context if it's not the first one
1832: ** on the stack.
1833: */
1834: if (!cc.isFirstOnStack()) {
1835: cc.popMe();
1836: } else {
1837: cc.setCompilationSchema((SchemaDescriptor) null);
1838: }
1839: }
1840:
1841: /**
1842: * Push a StatementContext on the context stack.
1843: *
1844: * @param isAtomic whether this statement is atomic or not
1845: * @param isForReadOnly whether this statement is for a read only resultset
1846: * @param stmtText the text of the statement. Needed for any language
1847: * statement (currently, for any statement that can cause a trigger
1848: * to fire). Please set this unless you are some funky jdbc setXXX
1849: * method or something.
1850: * @param pvs parameter value set, if it has one
1851: * @param rollbackParentContext True if 1) the statement context is
1852: * NOT a top-level context, AND 2) in the event of a statement-level
1853: * exception, the parent context needs to be rolled back, too.
1854: * @param timeoutMillis timeout value for this statement, in milliseconds.
1855: * The value 0 means that no timeout is set.
1856: *
1857: * @return StatementContext The statement context.
1858: *
1859: */
1860: public StatementContext pushStatementContext(boolean isAtomic,
1861: boolean isForReadOnly, String stmtText,
1862: ParameterValueSet pvs, boolean rollbackParentContext,
1863: long timeoutMillis) {
1864: int parentStatementDepth = statementDepth;
1865: boolean inTrigger = false;
1866: boolean parentIsAtomic = false;
1867:
1868: // by default, assume we are going to use the outermost statement context
1869: StatementContext statementContext = statementContexts[0];
1870:
1871: /*
1872: ** If we haven't allocated any statement contexts yet, allocate
1873: ** the outermost stmt context now and push it.
1874: */
1875: if (statementContext == null) {
1876: statementContext = statementContexts[0] = new GenericStatementContext(
1877: this , tran);
1878: } else if (statementDepth > 0) {
1879: StatementContext parentStatementContext;
1880: /*
1881: ** We also cache a 2nd statement context, though we still
1882: ** push and pop it. Note, new contexts are automatically pushed.
1883: */
1884: if (statementDepth == 1) {
1885: statementContext = statementContexts[1];
1886:
1887: if (statementContext == null)
1888: statementContext = statementContexts[1] = new GenericStatementContext(
1889: this , tran);
1890: else
1891: statementContext.pushMe();
1892:
1893: parentStatementContext = statementContexts[0];
1894: } else {
1895: parentStatementContext = getStatementContext();
1896: statementContext = new GenericStatementContext(this ,
1897: tran);
1898: }
1899:
1900: inTrigger = parentStatementContext.inTrigger()
1901: || (outermostTrigger == parentStatementDepth);
1902: parentIsAtomic = parentStatementContext.isAtomic();
1903: statementContext.setSQLAllowed(parentStatementContext
1904: .getSQLAllowed(), false);
1905: if (parentStatementContext.getSystemCode())
1906: statementContext.setSystemCode();
1907: }
1908:
1909: incrementStatementDepth();
1910:
1911: statementContext.setInUse(inTrigger,
1912: isAtomic || parentIsAtomic, isForReadOnly, stmtText,
1913: pvs, timeoutMillis);
1914: if (rollbackParentContext)
1915: statementContext.setParentRollback();
1916: return statementContext;
1917: }
1918:
1919: /**
1920: * Pop a StatementContext of the context stack.
1921: *
1922: * @param statementContext The statement context.
1923: * @param error The error, if any (Only relevant for DEBUG)
1924: */
1925: public void popStatementContext(StatementContext statementContext,
1926: Throwable error) {
1927: if (statementContext != null) {
1928: /*
1929: ** If someone beat us to the punch, then it is ok,
1930: ** just silently ignore it. We probably got here
1931: ** because we had a try catch block around a push/pop
1932: ** statement context, and we already got the context
1933: ** on a cleanupOnError.
1934: */
1935: if (!statementContext.inUse()) {
1936: return;
1937: }
1938: statementContext.clearInUse();
1939: }
1940:
1941: decrementStatementDepth();
1942: if (statementDepth == -1) {
1943: /*
1944: * Only ignore the pop request for an already
1945: * empty stack when dealing with a session exception.
1946: */
1947: if (SanityManager.DEBUG) {
1948: int severity = (error instanceof StandardException) ? ((StandardException) error)
1949: .getSeverity()
1950: : 0;
1951: SanityManager
1952: .ASSERT(error != null,
1953: "Must have error to try popStatementContext with 0 depth");
1954: SanityManager
1955: .ASSERT(
1956: (severity == ExceptionSeverity.SESSION_SEVERITY),
1957: "Must have session severity error to try popStatementContext with 0 depth");
1958: SanityManager
1959: .ASSERT(
1960: statementContext == statementContexts[0],
1961: "statementContext is expected to equal statementContexts[0]");
1962: }
1963: resetStatementDepth(); // pretend we did nothing.
1964: } else if (statementDepth == 0) {
1965: if (SanityManager.DEBUG) {
1966: /* Okay to pop last context on a session exception.
1967: * (We call clean up on error when exiting connection.)
1968: */
1969: int severity = (error instanceof StandardException) ? ((StandardException) error)
1970: .getSeverity()
1971: : 0;
1972: if ((error == null)
1973: || (severity != ExceptionSeverity.SESSION_SEVERITY)) {
1974: SanityManager
1975: .ASSERT(
1976: statementContext == statementContexts[0],
1977: "statementContext is expected to equal statementContexts[0]");
1978: }
1979: }
1980: } else {
1981: if (SanityManager.DEBUG) {
1982: SanityManager
1983: .ASSERT(
1984: statementContext != statementContexts[0],
1985: "statementContext is not expected to equal statementContexts[0]");
1986: if (statementDepth <= 0)
1987: SanityManager
1988: .THROWASSERT("statement depth expected to be >0, was "
1989: + statementDepth);
1990:
1991: if (getContextManager().getContext(
1992: statementContext.getIdName()) != statementContext) {
1993: SanityManager
1994: .THROWASSERT("trying to pop statement context from middle of stack");
1995: }
1996: }
1997:
1998: statementContext.popMe();
1999: }
2000:
2001: }
2002:
2003: /**
2004: * Push a new execution statement validator. An execution statement
2005: * validator is an object that validates the current statement to
2006: * ensure that it is permitted given the current execution context.
2007: * An example of a validator a trigger ExecutionStmtValidator that
2008: * doesn't allow ddl on the trigger target table.
2009: * <p>
2010: * Multiple ExecutionStmtValidators may be active at any given time.
2011: * This mirrors the way there can be multiple connection nestings
2012: * at a single time. The validation is performed by calling each
2013: * validator's validateStatement() method. This yields the union
2014: * of all validations.
2015: *
2016: * @param validator the validator to add
2017: */
2018: public void pushExecutionStmtValidator(
2019: ExecutionStmtValidator validator) {
2020: stmtValidators.addElement(validator);
2021: }
2022:
2023: /**
2024: * Remove the validator. Does an object identity (validator == validator)
2025: * comparison. Asserts that the validator is found.
2026: *
2027: * @param validator the validator to remove
2028: *
2029: * @exception StandardException on error
2030: */
2031: public void popExecutionStmtValidator(
2032: ExecutionStmtValidator validator) throws StandardException {
2033: boolean foundElement = stmtValidators.removeElement(validator);
2034: if (SanityManager.DEBUG) {
2035: if (!foundElement) {
2036: SanityManager.THROWASSERT("statement validator "
2037: + validator + " not found");
2038: }
2039: }
2040: }
2041:
2042: /**
2043: * Push a new trigger execution context.
2044: * <p>
2045: * Multiple TriggerExecutionContexts may be active at any given time.
2046: *
2047: * @param tec the trigger execution context
2048: *
2049: * @exception StandardException on trigger recursion error
2050: */
2051: public void pushTriggerExecutionContext(TriggerExecutionContext tec)
2052: throws StandardException {
2053: if (outermostTrigger == -1) {
2054: outermostTrigger = statementDepth;
2055: }
2056:
2057: /* Maximum 16 nesting levels allowed */
2058: if (triggerExecutionContexts.size() >= Limits.DB2_MAX_TRIGGER_RECURSION) {
2059: throw StandardException
2060: .newException(SQLState.LANG_TRIGGER_RECURSION_EXCEEDED);
2061: }
2062:
2063: triggerExecutionContexts.addElement(tec);
2064: }
2065:
2066: /**
2067: * Remove the tec. Does an object identity (tec == tec)
2068: * comparison. Asserts that the tec is found.
2069: *
2070: * @param tec the tec to remove
2071: *
2072: * @exception StandardException on error
2073: */
2074: public void popTriggerExecutionContext(TriggerExecutionContext tec)
2075: throws StandardException {
2076: if (outermostTrigger == statementDepth) {
2077: outermostTrigger = -1;
2078: }
2079:
2080: boolean foundElement = triggerExecutionContexts
2081: .removeElement(tec);
2082: if (SanityManager.DEBUG) {
2083: if (!foundElement) {
2084: SanityManager.THROWASSERT("trigger execution context "
2085: + tec + " not found");
2086: }
2087: }
2088: }
2089:
2090: /**
2091: * Get the topmost tec.
2092: *
2093: * @return the tec
2094: */
2095: public TriggerExecutionContext getTriggerExecutionContext() {
2096: return triggerExecutionContexts.size() == 0 ? (TriggerExecutionContext) null
2097: : (TriggerExecutionContext) triggerExecutionContexts
2098: .lastElement();
2099: }
2100:
2101: /**
2102: * Validate a statement. Does so by stepping through all the validators
2103: * and executing them. If a validator throws and exception, then the
2104: * checking is stopped and the exception is passed up.
2105: *
2106: * @param constantAction the constantAction that is about to be executed (and
2107: * should be validated
2108: *
2109: * @exception StandardException on validation failure
2110: */
2111: public void validateStmtExecution(ConstantAction constantAction)
2112: throws StandardException {
2113: if (SanityManager.DEBUG) {
2114: SanityManager.ASSERT(constantAction != null,
2115: "constantAction is null");
2116: }
2117:
2118: if (stmtValidators.size() > 0) {
2119: for (Enumeration e = stmtValidators.elements(); e
2120: .hasMoreElements();) {
2121: ((ExecutionStmtValidator) e.nextElement())
2122: .validateStatement(constantAction);
2123: }
2124: }
2125: }
2126:
2127: /**
2128: * Set the trigger table descriptor. Used to compile
2129: * statements that may special trigger pseudo tables.
2130: *
2131: * @param td the table that the trigger is
2132: * defined upon
2133: *
2134: */
2135: public void pushTriggerTable(TableDescriptor td) {
2136: triggerTables.addElement(td);
2137: }
2138:
2139: /**
2140: * Remove the trigger table descriptor.
2141: *
2142: * @param td the table to remove from the stack.
2143: */
2144: public void popTriggerTable(TableDescriptor td) {
2145: boolean foundElement = triggerTables.removeElement(td);
2146: if (SanityManager.DEBUG) {
2147: if (!foundElement) {
2148: SanityManager.THROWASSERT("trigger table not found: "
2149: + td);
2150: }
2151: }
2152: }
2153:
2154: /**
2155: * Get the topmost trigger table descriptor
2156: *
2157: * @return the table descriptor, or null if we
2158: * aren't in the middle of compiling a create
2159: * trigger.
2160: */
2161: public TableDescriptor getTriggerTable() {
2162: return triggerTables.size() == 0 ? (TableDescriptor) null
2163: : (TableDescriptor) triggerTables.lastElement();
2164: }
2165:
2166: /**
2167: * @see LanguageConnectionContext#getDatabase
2168: */
2169: public Database getDatabase() {
2170: return db;
2171: }
2172:
2173: /** @see LanguageConnectionContext#incrementBindCount */
2174: public int incrementBindCount() {
2175: bindCount++;
2176: return bindCount;
2177: }
2178:
2179: /** @see LanguageConnectionContext#decrementBindCount */
2180: public int decrementBindCount() {
2181: bindCount--;
2182:
2183: if (SanityManager.DEBUG) {
2184: if (bindCount < 0)
2185: SanityManager.THROWASSERT("Level of nested binding == "
2186: + bindCount);
2187: }
2188:
2189: return bindCount;
2190: }
2191:
2192: /** @see LanguageConnectionContext#getBindCount */
2193: public int getBindCount() {
2194: return bindCount;
2195: }
2196:
2197: /** @see LanguageConnectionContext#setDataDictionaryWriteMode */
2198: public final void setDataDictionaryWriteMode() {
2199: ddWriteMode = true;
2200: }
2201:
2202: /** @see LanguageConnectionContext#dataDictionaryInWriteMode */
2203: public final boolean dataDictionaryInWriteMode() {
2204: return ddWriteMode;
2205: }
2206:
2207: /** @see LanguageConnectionContext#setRunTimeStatisticsMode */
2208: public void setRunTimeStatisticsMode(boolean onOrOff) {
2209: runTimeStatisticsSetting = onOrOff;
2210: }
2211:
2212: /** @see LanguageConnectionContext#getRunTimeStatisticsMode */
2213: public boolean getRunTimeStatisticsMode() {
2214: return runTimeStatisticsSetting;
2215: }
2216:
2217: /** @see LanguageConnectionContext#setStatisticsTiming */
2218: public void setStatisticsTiming(boolean onOrOff) {
2219: statisticsTiming = onOrOff;
2220: }
2221:
2222: /** @see LanguageConnectionContext#getStatisticsTiming */
2223: public boolean getStatisticsTiming() {
2224: return statisticsTiming;
2225: }
2226:
2227: /** @see LanguageConnectionContext#setRunTimeStatisticsObject */
2228: public void setRunTimeStatisticsObject(
2229: RunTimeStatistics runTimeStatisticsObject) {
2230: this .runTimeStatisticsObject = runTimeStatisticsObject;
2231: }
2232:
2233: /** @see LanguageConnectionContext#getRunTimeStatisticsObject */
2234: public RunTimeStatistics getRunTimeStatisticsObject() {
2235: return runTimeStatisticsObject;
2236: }
2237:
2238: /**
2239: * Reports how many statement levels deep we are.
2240: *
2241: * @return a statement level >= OUTERMOST_STATEMENT
2242: */
2243: public int getStatementDepth() {
2244: return statementDepth;
2245: }
2246:
2247: /**
2248: * @see LanguageConnectionContext#isIsolationLevelSetUsingSQLorJDBC
2249: */
2250: public boolean isIsolationLevelSetUsingSQLorJDBC() {
2251: return isolationLevelSetUsingSQLorJDBC;
2252: }
2253:
2254: /**
2255: * @see LanguageConnectionContext#resetIsolationLevelFlagUsedForSQLandJDBC
2256: */
2257: public void resetIsolationLevelFlagUsedForSQLandJDBC() {
2258: isolationLevelSetUsingSQLorJDBC = false;
2259: }
2260:
2261: /**
2262: * @see LanguageConnectionContext#setIsolationLevel
2263: */
2264: public void setIsolationLevel(int isolationLevel)
2265: throws StandardException {
2266: StatementContext stmtCtxt = getStatementContext();
2267: if (stmtCtxt != null && stmtCtxt.inTrigger())
2268: throw StandardException.newException(
2269: SQLState.LANG_NO_XACT_IN_TRIGGER,
2270: getTriggerExecutionContext().toString());
2271:
2272: // find if there are any held cursors from previous isolation level.
2273: // if yes, then throw an exception that isolation change not allowed until
2274: // the held cursors are closed.
2275: // I had to move this check outside of transaction idle check because if a
2276: // transactions creates held cursors and commits the transaction, then
2277: // there still would be held cursors but the transaction state would be idle.
2278: // In order to check the above mentioned case, the held cursor check
2279: // shouldn't rely on transaction state.
2280: if (this .isolationLevel != isolationLevel) {
2281: if (!verifyAllHeldResultSetsAreClosed()) {
2282: throw StandardException
2283: .newException(SQLState.LANG_CANT_CHANGE_ISOLATION_HOLD_CURSOR);
2284: }
2285: }
2286:
2287: /* Commit and set to new isolation level.
2288: * NOTE: We commit first in case there's some kind
2289: * of error, like can't commit within a server side jdbc call.
2290: */
2291: TransactionController tc = getTransactionExecute();
2292: if (!tc.isIdle()) {
2293: // If this transaction is in progress, commit it.
2294: // However, do not allow commit to happen if this is a global
2295: // transaction.
2296: if (tc.isGlobal())
2297: throw StandardException
2298: .newException(SQLState.LANG_NO_SET_TRAN_ISO_IN_GLOBAL_CONNECTION);
2299:
2300: userCommit();
2301: }
2302: this .isolationLevel = isolationLevel;
2303: this .isolationLevelExplicitlySet = true;
2304: this .isolationLevelSetUsingSQLorJDBC = true;
2305: }
2306:
2307: /**
2308: * @see LanguageConnectionContext#getCurrentIsolationLevel
2309: */
2310: public int getCurrentIsolationLevel() {
2311: return (isolationLevel == ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL) ? defaultIsolationLevel
2312: : isolationLevel;
2313: }
2314:
2315: /**
2316: * @see LanguageConnectionContext#getCurrentIsolationLevel
2317: */
2318: public String getCurrentIsolationLevelStr() {
2319: if (isolationLevel >= 0
2320: && isolationLevel < ExecutionContext.CS_TO_SQL_ISOLATION_MAP.length)
2321: return ExecutionContext.CS_TO_SQL_ISOLATION_MAP[isolationLevel][0];
2322: return ExecutionContext.CS_TO_SQL_ISOLATION_MAP[ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL][0];
2323: }
2324:
2325: /**
2326: * @see LanguageConnectionContext#setPrepareIsolationLevel
2327: */
2328: public void setPrepareIsolationLevel(int level) {
2329: prepareIsolationLevel = level;
2330: }
2331:
2332: /**
2333: * @see LanguageConnectionContext#getPrepareIsolationLevel
2334: */
2335: public int getPrepareIsolationLevel() {
2336: if (!isolationLevelExplicitlySet)
2337: return prepareIsolationLevel;
2338: else
2339: return ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL;
2340: }
2341:
2342: /**
2343: * @see LanguageConnectionContext#getExecutionContext
2344: */
2345: public ExecutionContext getExecutionContext() {
2346: return (ExecutionContext) getContextManager().getContext(
2347: ExecutionContext.CONTEXT_ID);
2348: }
2349:
2350: /**
2351: * @see LanguageConnectionContext#getStatementContext
2352: */
2353: public StatementContext getStatementContext() {
2354: return (StatementContext) getContextManager()
2355: .getContext(
2356: org.apache.derby.iapi.reference.ContextId.LANG_STATEMENT);
2357: }
2358:
2359: /**
2360: * @see LanguageConnectionContext#setOptimizerTrace
2361: */
2362: public boolean setOptimizerTrace(boolean onOrOff) {
2363: if (of == null) {
2364: return false;
2365: }
2366: if (!of.supportsOptimizerTrace()) {
2367: return false;
2368: }
2369: optimizerTrace = onOrOff;
2370: return true;
2371: }
2372:
2373: /**
2374: * @see LanguageConnectionContext#getOptimizerTrace
2375: */
2376: public boolean getOptimizerTrace() {
2377: return optimizerTrace;
2378: }
2379:
2380: /**
2381: * @see LanguageConnectionContext#setOptimizerTraceHtml
2382: */
2383: public boolean setOptimizerTraceHtml(boolean onOrOff) {
2384: if (of == null) {
2385: return false;
2386: }
2387: if (!of.supportsOptimizerTrace()) {
2388: return false;
2389: }
2390: optimizerTraceHtml = onOrOff;
2391: return true;
2392: }
2393:
2394: /**
2395: * @see LanguageConnectionContext#getOptimizerTraceHtml
2396: */
2397: public boolean getOptimizerTraceHtml() {
2398: return optimizerTraceHtml;
2399: }
2400:
2401: /**
2402: * @see LanguageConnectionContext#setOptimizerTraceOutput
2403: */
2404: public void setOptimizerTraceOutput(String startingText) {
2405: if (optimizerTrace) {
2406: lastOptimizerTraceOutput = optimizerTraceOutput;
2407: optimizerTraceOutput = startingText;
2408: }
2409: }
2410:
2411: /**
2412: * @see LanguageConnectionContext#appendOptimizerTraceOutput
2413: */
2414: public void appendOptimizerTraceOutput(String output) {
2415: optimizerTraceOutput = (optimizerTraceOutput == null) ? output
2416: : optimizerTraceOutput + output;
2417: }
2418:
2419: /**
2420: * @see LanguageConnectionContext#getOptimizerTraceOutput
2421: */
2422: public String getOptimizerTraceOutput() {
2423: return lastOptimizerTraceOutput;
2424: }
2425:
2426: /**
2427: * Reports whether there is any outstanding work in the transaction.
2428: *
2429: * @return true if there is outstanding work in the transaction
2430: * false otherwise
2431: */
2432: public boolean isTransactionPristine() {
2433: return getTransactionExecute().isPristine();
2434: }
2435:
2436: /**
2437: * Get the lock handle for the current transaction.
2438: *
2439: * @param lockScope SINGLE_TRANSACTION_LOCK or MULTI_TRANSACTION_LOCK
2440: *
2441: * @return the current lock handle
2442: *
2443: * @exception StandardException thrown if something goes wrong
2444: */
2445: public Object getLockObject(int lockScope) throws StandardException {
2446: switch (lockScope) {
2447: case SINGLE_TRANSACTION_LOCK:
2448: return getTransactionExecute().getLockObject();
2449:
2450: case MULTI_TRANSACTION_LOCK:
2451: return new Object();
2452:
2453: default:
2454: throw StandardException
2455: .newException(SQLState.NOT_IMPLEMENTED);
2456: }
2457: }
2458:
2459: /**
2460: * Convert an identifier to the proper case for this connection. This method
2461: * is here to support the Plugin.
2462: *
2463: * @param id an identifier string
2464: * @return the string converted to upper or lower case, as appropriate
2465: *
2466: * @exception StandardException thrown if something goes wrong
2467: */
2468: public String convertIdentifierCase(String id)
2469: throws StandardException {
2470: if (ANTI_ANSI_CASING == getIdentifierCasing())
2471: return StringUtil.SQLToLowerCase(id);
2472: else
2473: return StringUtil.SQLToUpperCase(id);
2474: }
2475:
2476: /**
2477: * Get casing for delimited identifiers. This feature is here to
2478: * support the Plugin.
2479: *
2480: * @return ANSI_CASING or ANTI_ANSI_CASING.
2481: *
2482: * @exception StandardException thrown if something goes wrong
2483: */
2484: public int getIdentifierCasing() throws StandardException {
2485: if (UNKNOWN_CASING == identifierCasing) {
2486: identifierCasing = ANSI_CASING;
2487: }
2488: return identifierCasing;
2489: }
2490:
2491: //
2492: // Context interface
2493: //
2494: /**
2495: If worse than a transaction error, everything goes; we
2496: rely on other contexts to kill the context manager
2497: for this session.
2498: <p>
2499: If a transaction error, act like we saw a rollback.
2500: <p>
2501: If more severe or a java error, the outer cleanup
2502: will shutdown the connection, so we don't have to clean up.
2503: <p>
2504: REMIND: connection should throw out all contexts and start
2505: over when the connection is closed... perhaps by throwing
2506: out the context manager?
2507: <p>
2508: REVISIT: If statement error, should we do anything?
2509: <P>
2510: Since the access manager's own context takes care of its own
2511: resources on errors, there is nothing that this context has
2512: to do with the transaction controller.
2513:
2514: @exception StandardException thrown on error. REVISIT: don't want
2515: cleanupOnError's to throw exceptions.
2516: */
2517: public void cleanupOnError(Throwable error)
2518: throws StandardException {
2519:
2520: /*
2521: ** If it isn't a StandardException, then assume
2522: ** session severity. It is probably an unexpected
2523: ** java error somewhere in the language.
2524: ** Store layer treats JVM error as session severity,
2525: ** hence to be consistent and to avoid getting rawstore
2526: ** protocol violation errors, we treat java errors here
2527: ** to be of session severity.
2528: */
2529:
2530: int severity = (error instanceof StandardException) ? ((StandardException) error)
2531: .getSeverity()
2532: : ExceptionSeverity.SESSION_SEVERITY;
2533:
2534: if (statementContexts[0] != null) {
2535: statementContexts[0].clearInUse();
2536:
2537: // Force the StatementContext that's normally
2538: // left on the stack for optimization to be popped
2539: // when the session is closed. Ensures full cleanup
2540: // and no hanging refrences in the ContextManager.
2541: if (severity >= ExceptionSeverity.SESSION_SEVERITY)
2542: statementContexts[0].popMe();
2543: }
2544: if (statementContexts[1] != null) {
2545: statementContexts[1].clearInUse();
2546: }
2547:
2548: // closing the activations closes all the open cursors.
2549: // the activations are, for all intents and purposes, the
2550: // cursors.
2551: if (severity >= ExceptionSeverity.SESSION_SEVERITY) {
2552: for (int i = acts.size() - 1; i >= 0; i--) {
2553: // it maybe the case that a reset()/close() ends up closing
2554: // one or more activation leaving our index beyond
2555: // the end of the array
2556: if (i >= acts.size())
2557: continue;
2558: Activation a = (Activation) acts.elementAt(i);
2559: a.reset();
2560: a.close();
2561: }
2562:
2563: popMe();
2564: }
2565:
2566: /*
2567: ** We have some global state that we need
2568: ** to clean up no matter what. Be sure
2569: ** to do so.
2570: */
2571: else if (severity >= ExceptionSeverity.TRANSACTION_SEVERITY) {
2572: internalRollback();
2573: }
2574: }
2575:
2576: /**
2577: * @see Context#isLastHandler
2578: */
2579: public boolean isLastHandler(int severity) {
2580: return false;
2581: }
2582:
2583: //
2584: // class implementation
2585: //
2586:
2587: /**
2588: resets all open activations, to close their result sets.
2589: Also cleans up (close()) activations that have been
2590: marked as unused during statement finalization.
2591:
2592: @exception StandardException thrown on failure
2593: */
2594: private void resetActivations(boolean andClose)
2595: throws StandardException {
2596:
2597: // don't use an enumeration as the activation may remove
2598: // itself from the list, thus invalidating the Enumeration
2599: for (int i = acts.size() - 1; i >= 0; i--) {
2600:
2601: // it maybe the case that a reset() ends up closing
2602: // one or more activation leaving our index beyond
2603: // the end of the array
2604: if (i >= acts.size())
2605: continue;
2606:
2607: Activation a = (Activation) acts.elementAt(i);
2608: /*
2609: ** andClose true means we are here for rollback.
2610: ** In case of rollback, we don't care for holding
2611: ** cursors and that's why I am resetting holdability
2612: ** to false for all activations just before rollback
2613: */
2614: if (andClose)
2615: a.setResultSetHoldability(false);
2616:
2617: /*
2618: ** Look for stale activations. Activations are
2619: ** marked as unused during statement finalization.
2620: ** Here, we sweep and remove this inactive ones.
2621: */
2622: if (!a.isInUse()) {
2623: a.close();
2624: continue;
2625: }
2626:
2627: a.reset();
2628:
2629: // Only invalidate statements if we performed DDL.
2630: if (andClose && dataDictionaryInWriteMode()) {
2631: ExecPreparedStatement ps = a.getPreparedStatement();
2632: if (ps != null) {
2633: ps.makeInvalid(DependencyManager.ROLLBACK, this );
2634: }
2635: }
2636: }
2637: }
2638:
2639: /**
2640: Finish the data dictionary transaction, if any.
2641:
2642: @exception StandardException Thrown on error
2643: */
2644: private void finishDDTransaction() throws StandardException {
2645:
2646: /* Was the data dictionary put into write mode? */
2647: if (ddWriteMode) {
2648: DataDictionary dd = getDataDictionary();
2649:
2650: /* Tell the data dictionary that the transaction is finished */
2651: dd.transactionFinished();
2652:
2653: /* The data dictionary isn't in write mode any more */
2654: ddWriteMode = false;
2655: }
2656: }
2657:
2658: ////////////////////////////////////////////////////////////////////
2659: //
2660: // MINIONS
2661: //
2662: ////////////////////////////////////////////////////////////////////
2663:
2664: /**
2665: * Increments the statement depth.
2666: */
2667: private void incrementStatementDepth() {
2668: statementDepth++;
2669: }
2670:
2671: /**
2672: * Decrements the statement depth
2673: */
2674: private void decrementStatementDepth() {
2675: statementDepth--;
2676: }
2677:
2678: /**
2679: * Resets the statementDepth.
2680: */
2681: protected void resetStatementDepth() {
2682: statementDepth = 0;
2683: }
2684:
2685: /**
2686: * Get the name of the system schema.
2687: *
2688: * @return A String containing the name of the system schema.
2689: * @throws StandardException
2690: */
2691: public String getSystemSchemaName() throws StandardException {
2692: return convertIdentifierCase(SchemaDescriptor.STD_SYSTEM_SCHEMA_NAME);
2693: }
2694:
2695: /**
2696: * Get the name of the SYSIBM schema.
2697: *
2698: * @return A String containing the name of the SYSIBM schema.
2699: * @throws StandardException
2700: */
2701: public String getSysIBMSchemaName() throws StandardException {
2702: return convertIdentifierCase(SchemaDescriptor.IBM_SYSTEM_SCHEMA_NAME);
2703: }
2704:
2705: /**
2706: * Get the name of the SYSCS_DIAG schema.
2707: *
2708: * @return A String containing the name of the SYSIBM schema.
2709: * @throws StandardException
2710: */
2711: public String getSystemDiagSchemaName() throws StandardException {
2712: return (convertIdentifierCase(SchemaDescriptor.STD_SYSTEM_DIAG_SCHEMA_NAME));
2713: }
2714:
2715: /**
2716: * Get the name of the SYSCS_UTIL schema.
2717: *
2718: * @return A String containing the name of the SYSIBM schema.
2719: * @throws StandardException
2720: */
2721: public String getSystemUtilSchemaName() throws StandardException {
2722: return (convertIdentifierCase(SchemaDescriptor.STD_SYSTEM_UTIL_SCHEMA_NAME));
2723: }
2724:
2725: /**
2726: * Get the declared global temporary tables schema name.
2727: *
2728: * @return a String containing the declared global temporary tables schema name.
2729: * @throws StandardException
2730: */
2731: public String getDeclaredGlobalTemporaryTablesSchemaName()
2732: throws StandardException {
2733: return convertIdentifierCase(SchemaDescriptor.STD_DECLARED_GLOBAL_TEMPORARY_TABLES_SCHEMA_NAME);
2734: }
2735:
2736: public DataDictionary getDataDictionary() {
2737: DataDictionaryContext ddc = (DataDictionaryContext) getContextManager()
2738: .getContext(DataDictionaryContext.CONTEXT_ID);
2739:
2740: if (ddc != null) {
2741: return ddc.getDataDictionary();
2742: } else {
2743: return null;
2744: }
2745: }
2746:
2747: /**
2748: @see LanguageConnectionContext#setReadOnly
2749: @exception StandardException The operation is disallowed.
2750: */
2751: public void setReadOnly(boolean on) throws StandardException {
2752: if (!tran.isPristine())
2753: throw StandardException
2754: .newException(SQLState.AUTH_SET_CONNECTION_READ_ONLY_IN_ACTIVE_XACT);
2755: authorizer.setReadOnlyConnection(on, true);
2756: }
2757:
2758: /**
2759: @see LanguageConnectionContext#isReadOnly
2760: */
2761: public boolean isReadOnly() {
2762: return authorizer.isReadOnlyConnection();
2763: }
2764:
2765: /**
2766: @see LanguageConnectionContext#getAuthorizer
2767: */
2768: public Authorizer getAuthorizer() {
2769: return authorizer;
2770: }
2771:
2772: /** @see LanguageConnectionContext#getAccessFactory */
2773: public AccessFactory getAccessFactory() {
2774: return af;
2775: }
2776:
2777: /**
2778: * Implements ConnectionInfo.lastAutoincrementValue.
2779: * lastAutoincrementValue searches for the last autoincrement value inserted
2780: * into a column specified by the user. The search for the "last" value
2781: * supports nesting levels caused by triggers (Only triggers cause nesting,
2782: * not server side JDBC).
2783: * If lastAutoincrementValue is called from within a trigger, the search
2784: * space for ai-values are those values that are inserted by this trigger as
2785: * well as previous triggers;
2786: * i.e if a SQL statement fires trigger T1, which in turn does something
2787: * that fires trigger t2, and if lastAutoincrementValue is called from
2788: * within t2, then autoincrement values genereated by t1 are visible to
2789: * it. By the same logic, if it is called from within t1, then it does not
2790: * see values inserted by t2.
2791: *
2792: * @see LanguageConnectionContext#lastAutoincrementValue
2793: * @see org.apache.derby.iapi.db.ConnectionInfo#lastAutoincrementValue
2794: */
2795: public Long lastAutoincrementValue(String schemaName,
2796: String tableName, String columnName) {
2797: String aiKey = AutoincrementCounter.makeIdentity(schemaName,
2798: tableName, columnName);
2799:
2800: int size = triggerExecutionContexts.size();
2801: // System.out.println(" searching for " + aiKey);
2802: for (int i = size - 1; i >= 0; i--) {
2803: // first loop through triggers.
2804: InternalTriggerExecutionContext itec = (InternalTriggerExecutionContext) triggerExecutionContexts
2805: .elementAt(i);
2806: Long value = itec.getAutoincrementValue(aiKey);
2807: if (value == null)
2808: continue;
2809:
2810: return value;
2811: }
2812: if (autoincrementHT == null)
2813: return null;
2814: return (Long) autoincrementHT.get(aiKey);
2815: }
2816:
2817: /**
2818: * @see LanguageConnectionContext#setAutoincrementUpdate
2819: */
2820: public void setAutoincrementUpdate(boolean flag) {
2821: autoincrementUpdate = flag;
2822: }
2823:
2824: /**
2825: * @see LanguageConnectionContext#getAutoincrementUpdate
2826: */
2827: public boolean getAutoincrementUpdate() {
2828: return autoincrementUpdate;
2829: }
2830:
2831: /**
2832: * @see LanguageConnectionContext#autoincrementCreateCounter
2833: */
2834: public void autoincrementCreateCounter(String s, String t,
2835: String c, Long initialValue, long increment, int position) {
2836: String key = AutoincrementCounter.makeIdentity(s, t, c);
2837:
2838: if (autoincrementCacheHashtable == null) {
2839: autoincrementCacheHashtable = new Hashtable();
2840: }
2841:
2842: AutoincrementCounter aic = (AutoincrementCounter) autoincrementCacheHashtable
2843: .get(key);
2844: if (aic != null) {
2845: if (SanityManager.DEBUG) {
2846: SanityManager
2847: .THROWASSERT("Autoincrement Counter already exists:"
2848: + key);
2849: }
2850: return;
2851: }
2852:
2853: aic = new AutoincrementCounter(initialValue, increment, 0, s,
2854: t, c, position);
2855: autoincrementCacheHashtable.put(key, aic);
2856: }
2857:
2858: /**
2859: * returns the <b>next</b> value to be inserted into an autoincrement col.
2860: * This is used internally by the system to generate autoincrement values
2861: * which are going to be inserted into a autoincrement column. This is
2862: * used when as autoincrement column is added to a table by an alter
2863: * table statemenet and during bulk insert.
2864: *
2865: * @param schemaName
2866: * @param tableName
2867: * @param columnName identify the column uniquely in the system.
2868: */
2869: public long nextAutoincrementValue(String schemaName,
2870: String tableName, String columnName)
2871: throws StandardException {
2872: String key = AutoincrementCounter.makeIdentity(schemaName,
2873: tableName, columnName);
2874:
2875: AutoincrementCounter aic = (AutoincrementCounter) autoincrementCacheHashtable
2876: .get(key);
2877:
2878: if (aic == null) {
2879: if (SanityManager.DEBUG) {
2880: SanityManager.THROWASSERT("counter doesn't exist:"
2881: + key);
2882: }
2883: return 0;
2884: } else {
2885: return aic.update();
2886: }
2887: }
2888:
2889: /**
2890: * Flush the cache of autoincrement values being kept by the lcc.
2891: * This will result in the autoincrement values being written to the
2892: * SYSCOLUMNS table as well as the mapping used by lastAutoincrementValue
2893: *
2894: * @exception StandardException thrown on error.
2895: * @see LanguageConnectionContext#lastAutoincrementValue
2896: * @see GenericLanguageConnectionContext#lastAutoincrementValue
2897: * @see org.apache.derby.iapi.db.ConnectionInfo#lastAutoincrementValue
2898: */
2899: public void autoincrementFlushCache(UUID tableUUID)
2900: throws StandardException {
2901: if (autoincrementCacheHashtable == null)
2902: return;
2903:
2904: if (autoincrementHT == null)
2905: autoincrementHT = new Hashtable();
2906:
2907: DataDictionary dd = getDataDictionary();
2908: for (Enumeration e = autoincrementCacheHashtable.keys(); e
2909: .hasMoreElements();) {
2910: Object key = e.nextElement();
2911: AutoincrementCounter aic = (AutoincrementCounter) autoincrementCacheHashtable
2912: .get(key);
2913: Long value = aic.getCurrentValue();
2914: aic.flushToDisk(getTransactionExecute(), dd, tableUUID);
2915: if (value != null) {
2916: autoincrementHT.put(key, value);
2917: }
2918: }
2919: autoincrementCacheHashtable.clear();
2920: }
2921:
2922: /**
2923: * Copies an existing hashtable of autoincrement mappings
2924: * into autoincrementHT, the cache of autoincrement values
2925: * kept in the languageconnectioncontext.
2926: */
2927: public void copyHashtableToAIHT(Hashtable from) {
2928: if (from.isEmpty())
2929: return;
2930: if (autoincrementHT == null)
2931: autoincrementHT = new Hashtable();
2932:
2933: for (Enumeration e = from.keys(); e.hasMoreElements();) {
2934: Object key = e.nextElement();
2935: Object value = from.get(key);
2936: autoincrementHT.put(key, value);
2937: }
2938: }
2939:
2940: /**
2941: * @see LanguageConnectionContext#getInstanceNumber
2942: */
2943: public int getInstanceNumber() {
2944: return instanceNumber;
2945: }
2946:
2947: /**
2948: * @see LanguageConnectionContext#getDrdaID
2949: */
2950: public String getDrdaID() {
2951: return drdaID;
2952: }
2953:
2954: /**
2955: * @see LanguageConnectionContext#setDrdaID
2956: */
2957: public void setDrdaID(String drdaID) {
2958: this .drdaID = drdaID;
2959: }
2960:
2961: /**
2962: * @see LanguageConnectionContext#getDbname
2963: */
2964: public String getDbname() {
2965: return dbname;
2966: }
2967:
2968: /**
2969: * @see LanguageConnectionContext#getLastActivation
2970: */
2971: public Activation getLastActivation() {
2972: return (Activation) acts.lastElement();
2973: }
2974:
2975: public StringBuffer appendErrorInfo() {
2976:
2977: TransactionController tc = getTransactionExecute();
2978: if (tc == null)
2979: return null;
2980:
2981: StringBuffer sb = new StringBuffer(200);
2982:
2983: sb.append(LanguageConnectionContext.xidStr);
2984: sb.append(tc.getTransactionIdString());
2985: sb.append("), ");
2986:
2987: sb.append(LanguageConnectionContext.lccStr);
2988: sb.append(Integer.toString(getInstanceNumber()));
2989: sb.append("), ");
2990:
2991: sb.append(LanguageConnectionContext.dbnameStr);
2992: sb.append(getDbname());
2993: sb.append("), ");
2994:
2995: sb.append(LanguageConnectionContext.drdaStr);
2996: sb.append(getDrdaID());
2997: sb.append("), ");
2998:
2999: return sb;
3000: }
3001: }
|