/* Copyright (c) 2001-2016, The HSQL Development Group * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the HSQL Development Group nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.hsqldb; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Locale; import java.util.Random; import java.util.TimeZone; import org.hsqldb.HsqlNameManager.HsqlName; import org.hsqldb.error.Error; import org.hsqldb.error.ErrorCode; import org.hsqldb.jdbc.JDBCConnection; import org.hsqldb.jdbc.JDBCDriver; import org.hsqldb.lib.ArrayUtil; import org.hsqldb.lib.CountUpDownLatch; import org.hsqldb.lib.HashMap; import org.hsqldb.lib.HsqlArrayList; import org.hsqldb.lib.HsqlDeque; import org.hsqldb.lib.Iterator; import org.hsqldb.lib.OrderedHashSet; import org.hsqldb.lib.SimpleLog; import org.hsqldb.lib.java.JavaSystem; import org.hsqldb.map.ValuePool; import org.hsqldb.navigator.RowSetNavigator; import org.hsqldb.navigator.RowSetNavigatorClient; import org.hsqldb.persist.HsqlDatabaseProperties; import org.hsqldb.persist.HsqlProperties; import org.hsqldb.persist.PersistentStore; import org.hsqldb.result.Result; import org.hsqldb.result.ResultConstants; import org.hsqldb.result.ResultLob; import org.hsqldb.result.ResultProperties; import org.hsqldb.rights.Grantee; import org.hsqldb.rights.User; import org.hsqldb.types.BlobDataID; import org.hsqldb.types.ClobDataID; import org.hsqldb.types.TimeData; import org.hsqldb.types.TimestampData; import org.hsqldb.types.Type; import org.hsqldb.types.Type.TypedComparator; /** * Implementation of SQL sessions. * * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 2.3.4 * @since 1.7.0 */ public class Session implements SessionInterface { // private volatile boolean isClosed; // public Database database; private final User sessionUser; private User user; private Grantee role; // transaction support public boolean isReadOnlyDefault; int isolationLevelDefault = SessionInterface.TX_READ_COMMITTED; int isolationLevel = SessionInterface.TX_READ_COMMITTED; boolean isReadOnlyIsolation; int actionIndex; long actionStartTimestamp; long actionTimestamp; long transactionTimestamp; long transactionEndTimestamp; boolean txConflictRollback; boolean isPreTransaction; boolean isTransaction; boolean isBatch; volatile boolean abortAction; volatile boolean abortTransaction; volatile boolean redoAction; HsqlArrayList rowActionList; volatile boolean tempUnlocked; public OrderedHashSet waitedSessions; public OrderedHashSet waitingSessions; OrderedHashSet tempSet; OrderedHashSet actionSet; public CountUpDownLatch latch = new CountUpDownLatch(); TimeoutManager timeoutManager; // current settings final String zoneString; final int sessionTimeZoneSeconds; int timeZoneSeconds; boolean isNetwork; private int sessionMaxRows; int sessionOptimization = 8; private final long sessionId; int sessionTxId = -1; private boolean ignoreCase; private long sessionStartTimestamp; // internal connection private JDBCConnection intConnection; // external connection private JDBCConnection extConnection; // schema public HsqlName currentSchema; public HsqlName loggedSchema; // query processing ParserCommand parser; boolean isProcessingScript; boolean isProcessingLog; public SessionContext sessionContext; int resultMaxMemoryRows; // public SessionData sessionData; // public StatementManager statementManager; // public Object special; /** * Constructs a new Session object. * * @param db the database to which this represents a connection * @param user the initial user * @param autocommit the initial autocommit value * @param readonly the initial readonly value * @param id the session identifier, as known to the database */ Session(Database db, User user, boolean autocommit, boolean readonly, long id, String zoneString, int timeZoneSeconds) { sessionId = id; database = db; this.user = user; this.sessionUser = user; this.zoneString = zoneString; this.sessionTimeZoneSeconds = timeZoneSeconds; this.timeZoneSeconds = timeZoneSeconds; rowActionList = new HsqlArrayList(32, true); waitedSessions = new OrderedHashSet(); waitingSessions = new OrderedHashSet(); tempSet = new OrderedHashSet(); actionSet = new OrderedHashSet(); isolationLevelDefault = database.defaultIsolationLevel; ignoreCase = database.sqlIgnoreCase; isolationLevel = isolationLevelDefault; txConflictRollback = database.txConflictRollback; isReadOnlyDefault = readonly; isReadOnlyIsolation = isolationLevel == SessionInterface.TX_READ_UNCOMMITTED; sessionContext = new SessionContext(this); sessionContext.isAutoCommit = autocommit ? Boolean.TRUE : Boolean.FALSE; sessionContext.isReadOnly = isReadOnlyDefault ? Boolean.TRUE : Boolean.FALSE; parser = new ParserCommand(this, new Scanner()); setResultMemoryRowCount(database.getResultMaxMemoryRows()); resetSchema(); sessionData = new SessionData(database, this); statementManager = new StatementManager(database); timeoutManager = new TimeoutManager(); sessionStartTimestamp = System.currentTimeMillis(); } void resetSchema() { loggedSchema = null; currentSchema = user.getInitialOrDefaultSchema(); } /** * Retrieves the session identifier for this Session. * * @return the session identifier for this Session */ public long getId() { return sessionId; } public int getRandomId() { return randomId; } /** * Closes this Session. */ public synchronized void close() { if (isClosed) { return; } rollback(false); try { database.logger.writeOtherStatement(this, Tokens.T_DISCONNECT); } catch (HsqlException e) {} sessionData.closeAllNavigators(); sessionData.persistentStoreCollection.release(); statementManager.reset(); // keep sessionContext and sessionData rowActionList.clear(); isClosed = true; user = null; sessionContext.savepoints = null; sessionContext.lastIdentity = null; intConnection = null; database.sessionManager.removeSession(this); database.closeIfLast(); database = null; } /** * Retrieves whether this Session is closed. * * @return true if this Session is closed */ public boolean isClosed() { return isClosed; } public synchronized void setIsolationDefault(int level) { if (level == SessionInterface.TX_READ_UNCOMMITTED) { level = SessionInterface.TX_READ_COMMITTED; } if (level == isolationLevelDefault) { return; } isolationLevelDefault = level; if (!isInMidTransaction()) { isolationLevel = isolationLevelDefault; isReadOnlyIsolation = level == SessionInterface.TX_READ_UNCOMMITTED; } } /** * sets ISOLATION for the next transaction only */ public void setIsolation(int level) { if (isInMidTransaction()) { throw Error.error(ErrorCode.X_25001); } if (level == SessionInterface.TX_READ_UNCOMMITTED) { level = SessionInterface.TX_READ_COMMITTED; } if (isolationLevel != level) { isolationLevel = level; isReadOnlyIsolation = level == SessionInterface.TX_READ_UNCOMMITTED; } } public synchronized int getIsolation() { return isolationLevel; } /** * Setter for iLastIdentity attribute. * * @param i the new value */ void setLastIdentity(Number i) { sessionContext.lastIdentity = i; } /** * Getter for iLastIdentity attribute. * * @return the current value */ public Number getLastIdentity() { return sessionContext.lastIdentity; } /** * Retrieves the Database instance to which this * Session represents a connection. * * @return the Database object to which this Session is connected */ public Database getDatabase() { return database; } /** * Retrieves the name, as known to the database, of the * user currently controlling this Session. * * @return the name of the user currently connected within this Session */ public String getUsername() { return user.getName().getNameString(); } /** * Retrieves the User object representing the user currently controlling * this Session. * * @return this Session's User object */ public User getUser() { return user; } public Grantee getGrantee() { return user; } public Grantee getRole() { return role; } /** * Sets this Session's User object to the one specified by the * user argument. * * @param user the new User object for this session */ public void setUser(User user) { this.user = user; } public void setRole(Grantee role) { this.role = role; } int getMaxRows() { return sessionContext.currentMaxRows; } /** * The SQL command SET MAXROWS n will override the Statement.setMaxRows(n) * for the next direct statement only * * NB this is dedicated to the SET MAXROWS sql statement and should not * otherwise be called. (fredt@users) */ void setSQLMaxRows(int rows) { sessionMaxRows = rows; } void setFeature(String feature, boolean value) { int number = 8; if (value) { sessionOptimization |= number; } else { sessionOptimization &= ~number; } } /** * Checks whether this Session's current User has the privileges of * the ADMIN role. */ void checkAdmin() { user.checkAdmin(); } /** * This is used for reading - writing to existing tables. * @throws HsqlException */ void checkReadWrite() { if (sessionContext.isReadOnly.booleanValue() || isReadOnlyIsolation) { throw Error.error(ErrorCode.X_25006); } } /** * This is used for creating new database objects such as tables. * @throws HsqlException */ void checkDDLWrite() { if (isProcessingScript || isProcessingLog) { return; } checkReadWrite(); } public long getActionTimestamp() { return actionTimestamp; } /** * Adds a delete action to the row and the transaction manager. * * @param table the table of the row * @param row the deleted row * @throws HsqlException */ public void addDeleteAction(Table table, PersistentStore store, Row row, int[] colMap) { // tempActionHistory.add("add delete action " + actionTimestamp); if (abortTransaction) { throw Error.error(ErrorCode.X_40001); } if (abortAction) { throw Error.error(ErrorCode.X_40502); } database.txManager.addDeleteAction(this, table, store, row, colMap); } void addInsertAction(Table table, PersistentStore store, Row row, int[] changedColumns) { // tempActionHistory.add("add insert to transaction " + actionTimestamp); database.txManager.addInsertAction(this, table, store, row, changedColumns); // abort only after adding so that the new row gets removed from indexes if (abortTransaction) { throw Error.error(ErrorCode.X_40001); } if (abortAction) { throw Error.error(ErrorCode.X_40502); } } public HsqlArrayList getRowActionList() { return rowActionList; } /** * Setter for the autocommit attribute. * * @param autocommit the new value * @throws HsqlException */ public synchronized void setAutoCommit(boolean autocommit) { if (isClosed) { return; } if (sessionContext.depth > 0) { return; } if (sessionContext.isAutoCommit.booleanValue() != autocommit) { commit(false); sessionContext.isAutoCommit = autocommit ? Boolean.TRUE : Boolean.FALSE; } } public void beginAction(Statement cs) { actionIndex = rowActionList.size(); database.txManager.beginAction(this, cs); database.txManager.beginActionResume(this); } public void endAction(Result result) { // tempActionHistory.add("endAction " + actionTimestamp); abortAction = false; sessionData.persistentStoreCollection.clearStatementTables(); if (result.mode == ResultConstants.ERROR) { sessionData.persistentStoreCollection.clearResultTables( actionTimestamp); database.txManager.rollbackAction(this); } else { sessionContext .diagnosticsVariables[ExpressionColumn.idx_row_count] = result.mode == ResultConstants.UPDATECOUNT ? Integer.valueOf(result.getUpdateCount()) : ValuePool.INTEGER_0; database.txManager.completeActions(this); } // tempActionHistory.add("endAction ends " + actionTimestamp); } /** * Explicit start of transaction by user */ public void startTransaction() { database.txManager.beginTransaction(this); } public synchronized void startPhasedTransaction() {} /** * @todo - fredt - for two phased pre-commit - after this call, further * state changing calls should fail */ public synchronized void prepareCommit() { if (isClosed) { throw Error.error(ErrorCode.X_08003); } if (!database.txManager.prepareCommitActions(this)) { // tempActionHistory.add("commit aborts " + actionTimestamp); rollbackNoCheck(false); throw Error.error(ErrorCode.X_40001); } } /** * Commits any uncommitted transaction this Session may have open * * @throws HsqlException */ public synchronized void commit(boolean chain) { // tempActionHistory.add("commit " + actionTimestamp); if (isClosed) { return; } if (sessionContext.depth > 0) { return; } if (isTransaction) { if (!database.txManager.commitTransaction(this)) { // tempActionHistory.add("commit aborts " + actionTimestamp); rollbackNoCheck(chain); throw Error.error(ErrorCode.X_40001); } } endTransaction(true, chain); if (database != null && !sessionUser.isSystem() && database.logger.needsCheckpointReset()) { database.checkpointRunner.start(); } } /** * Rolls back any uncommitted transaction this Session may have open. * * @throws HsqlException */ public synchronized void rollback(boolean chain) { // tempActionHistory.add("rollback " + actionTimestamp); if (sessionContext.depth > 0) { return; } rollbackNoCheck(chain); } synchronized void rollbackNoCheck(boolean chain) { if (isClosed) { return; } if (isTransaction) { database.txManager.rollback(this); } endTransaction(false, chain); } private void endTransaction(boolean commit, boolean chain) { abortTransaction = false; sessionContext.resetStack(); sessionContext.savepoints.clear(); sessionContext.savepointTimestamps.clear(); rowActionList.clear(); sessionData.persistentStoreCollection.clearTransactionTables(); sessionData.closeAllTransactionNavigators(); sessionData.clearLobOps(); if (!chain) { sessionContext.isReadOnly = isReadOnlyDefault ? Boolean.TRUE : Boolean.FALSE; setIsolation(isolationLevelDefault); } if (database.logger.getSqlEventLogLevel() > 0) { Statement endTX = commit ? StatementSession.commitNoChainStatement : StatementSession .rollbackNoChainStatement; database.logger.logStatementEvent(this, endTX, null, Result.updateZeroResult, SimpleLog.LOG_ERROR); } /* debug 190 tempActionHistory.add("commit ends " + actionTimestamp); tempActionHistory.clear(); //*/ } /** * Clear structures and reset variables to original. For JDBC use only. * Note: sets autocommit true */ public synchronized void resetSession() { if (isClosed) { return; } rollbackNoCheck(false); sessionData.closeAllNavigators(); sessionData.persistentStoreCollection.clearAllTables(); sessionData.clearLobOps(); statementManager.reset(); sessionContext.lastIdentity = ValuePool.INTEGER_0; sessionContext.isAutoCommit = Boolean.TRUE; setResultMemoryRowCount(database.getResultMaxMemoryRows()); user = sessionUser; resetSchema(); setZoneSeconds(sessionTimeZoneSeconds); sessionMaxRows = 0; ignoreCase = database.sqlIgnoreCase; setIsolation(isolationLevelDefault); txConflictRollback = database.txConflictRollback; } /** * Registers a transaction SAVEPOINT. A new SAVEPOINT with the * name of an existing one replaces the old SAVEPOINT. * * @param name name of the savepoint * @throws HsqlException if there is no current transaction */ public synchronized void savepoint(String name) { int index = sessionContext.savepoints.getIndex(name); if (index != -1) { sessionContext.savepoints.remove(name); sessionContext.savepointTimestamps.remove(index); } sessionContext.savepoints.add(name, ValuePool.getInt(rowActionList.size())); sessionContext.savepointTimestamps.addLast(actionTimestamp); } /** * Performs a partial transaction ROLLBACK to savepoint. * * @param name name of savepoint * @throws HsqlException */ public synchronized void rollbackToSavepoint(String name) { if (isClosed) { return; } int index = sessionContext.savepoints.getIndex(name); if (index < 0) { throw Error.error(ErrorCode.X_3B001, name); } database.txManager.rollbackSavepoint(this, index); } /** * Performs a partial transaction ROLLBACK of current savepoint level. * * @throws HsqlException */ public synchronized void rollbackToSavepoint() { if (isClosed) { return; } database.txManager.rollbackSavepoint(this, 0); } public synchronized void rollbackAction(int start, long timestamp) { if (isClosed) { return; } database.txManager.rollbackPartial(this, start, timestamp); } /** * Releases a savepoint * * @param name name of savepoint * @throws HsqlException if name does not correspond to a savepoint */ public synchronized void releaseSavepoint(String name) { // remove this and all later savepoints int index = sessionContext.savepoints.getIndex(name); if (index < 0) { throw Error.error(ErrorCode.X_3B001, name); } while (sessionContext.savepoints.size() > index) { sessionContext.savepoints.remove(sessionContext.savepoints.size() - 1); sessionContext.savepointTimestamps.removeLast(); } } public boolean isInMidTransaction() { return isTransaction; } public void setNoSQL() { sessionContext.noSQL = Boolean.TRUE; } public void setIgnoreCase(boolean mode) { ignoreCase = mode; } public boolean isIgnorecase() { return ignoreCase; } /** * sets READ ONLY for next transaction / subtransaction only * * @param readonly the new value */ public void setReadOnly(boolean readonly) { if (!readonly && database.databaseReadOnly) { throw Error.error(ErrorCode.DATABASE_IS_READONLY); } if (isInMidTransaction()) { throw Error.error(ErrorCode.X_25001); } sessionContext.isReadOnly = readonly ? Boolean.TRUE : Boolean.FALSE; } public synchronized void setReadOnlyDefault(boolean readonly) { if (!readonly && database.databaseReadOnly) { throw Error.error(ErrorCode.DATABASE_IS_READONLY); } isReadOnlyDefault = readonly; if (!isInMidTransaction()) { sessionContext.isReadOnly = isReadOnlyDefault ? Boolean.TRUE : Boolean.FALSE; } } /** * Getter for readonly attribute. * * @return the current value */ public boolean isReadOnly() { return sessionContext.isReadOnly.booleanValue() || isReadOnlyIsolation; } public synchronized boolean isReadOnlyDefault() { return isReadOnlyDefault; } /** * Getter for autoCommit attribute. * * @return the current value */ public synchronized boolean isAutoCommit() { return sessionContext.isAutoCommit.booleanValue(); } public synchronized int getStreamBlockSize() { return lobStreamBlockSize; } /** * Retrieves an internal Connection object equivalent to the one * that created this Session. * * @return internal connection. */ JDBCConnection getInternalConnection() { if (intConnection == null) { intConnection = new JDBCConnection(this); } JDBCDriver.driverInstance.threadConnection.set(intConnection); return intConnection; } void releaseInternalConnection() { if (sessionContext.depth == 0) { JDBCDriver.driverInstance.threadConnection.set(null); } } /** * Retrieves the external JDBC connection */ public JDBCConnection getJDBCConnection() { return extConnection; } public void setJDBCConnection(JDBCConnection connection) { extConnection = connection; } public String getDatabaseUniqueName() { return database.getNameString(); } // boucherb@users 20020810 metadata 1.7.2 //---------------------------------------------------------------- private final long connectTime = System.currentTimeMillis(); // more efficient for MetaData concerns than checkAdmin /** * Getter for admin attribute. * * @return the current value */ public boolean isAdmin() { return user.isAdmin(); } /** * Getter for connectTime attribute. * * @return the value */ public long getConnectTime() { return connectTime; } /** * Count of actions in current transaction. * * @return the current value */ public int getTransactionSize() { return rowActionList.size(); } public long getTransactionTimestamp() { return transactionTimestamp; } public Statement compileStatement(String sql, int props) { parser.reset(this, sql); Statement cs = parser.compileStatement(props); return cs; } public Statement compileStatement(String sql) { parser.reset(this, sql); Statement cs = parser.compileStatement(ResultProperties.defaultPropsValue); cs.setCompileTimestamp(Long.MAX_VALUE); return cs; } /** * Executes the command encapsulated by the cmd argument. * * @param cmd the command to execute * @return the result of executing the command */ public synchronized Result execute(Result cmd) { if (isClosed) { return Result.newErrorResult(Error.error(ErrorCode.X_08503)); } sessionContext.currentMaxRows = 0; isBatch = false; JavaSystem.gc(); switch (cmd.mode) { case ResultConstants.LARGE_OBJECT_OP : { return performLOBOperation((ResultLob) cmd); } case ResultConstants.EXECUTE : { int maxRows = cmd.getUpdateCount(); if (maxRows == -1) { sessionContext.currentMaxRows = 0; } else { sessionContext.currentMaxRows = maxRows; } Statement cs = cmd.statement; if (cs == null || cs.compileTimestamp < database.schemaManager.schemaChangeTimestamp) { long csid = cmd.getStatementID(); cs = statementManager.getStatement(this, csid); cmd.setStatement(cs); if (cs == null) { // invalid sql has been removed already return Result.newErrorResult( Error.error(ErrorCode.X_07502)); } } Object[] pvals = (Object[]) cmd.valueData; Result result = executeCompiledStatement(cs, pvals, cmd.queryTimeout); result = performPostExecute(cmd, result); return result; } case ResultConstants.BATCHEXECUTE : { isBatch = true; Result result = executeCompiledBatchStatement(cmd); result = performPostExecute(cmd, result); return result; } case ResultConstants.EXECDIRECT : { Result result = executeDirectStatement(cmd); result = performPostExecute(cmd, result); return result; } case ResultConstants.BATCHEXECDIRECT : { isBatch = true; Result result = executeDirectBatchStatement(cmd); result = performPostExecute(cmd, result); return result; } case ResultConstants.PREPARE : { Statement cs; try { cs = statementManager.compile(this, cmd); } catch (Throwable t) { String errorString = cmd.getMainString(); return Result.newErrorResult(t, errorString); } Result result = Result.newPrepareResponse(cs); if (cs.getType() == StatementTypes.SELECT_CURSOR || cs.getType() == StatementTypes.CALL) { sessionData.setResultSetProperties(cmd, result); } result = performPostExecute(cmd, result); return result; } case ResultConstants.CLOSE_RESULT : { closeNavigator(cmd.getResultId()); return Result.updateZeroResult; } case ResultConstants.UPDATE_RESULT : { Result result = this.executeResultUpdate(cmd); result = performPostExecute(cmd, result); return result; } case ResultConstants.FREESTMT : { statementManager.freeStatement(cmd.getStatementID()); return Result.updateZeroResult; } case ResultConstants.GETSESSIONATTR : { int id = cmd.getStatementType(); return getAttributesResult(id); } case ResultConstants.SETSESSIONATTR : { return setAttributes(cmd); } case ResultConstants.ENDTRAN : { switch (cmd.getActionType()) { case ResultConstants.TX_COMMIT : try { commit(false); } catch (Throwable t) { return Result.newErrorResult(t); } break; case ResultConstants.TX_COMMIT_AND_CHAIN : try { commit(true); } catch (Throwable t) { return Result.newErrorResult(t); } break; case ResultConstants.TX_ROLLBACK : rollback(false); break; case ResultConstants.TX_ROLLBACK_AND_CHAIN : rollback(true); break; case ResultConstants.TX_SAVEPOINT_NAME_RELEASE : try { String name = cmd.getMainString(); releaseSavepoint(name); } catch (Throwable t) { return Result.newErrorResult(t); } break; case ResultConstants.TX_SAVEPOINT_NAME_ROLLBACK : try { rollbackToSavepoint(cmd.getMainString()); } catch (Throwable t) { return Result.newErrorResult(t); } break; case ResultConstants.PREPARECOMMIT : try { prepareCommit(); } catch (Throwable t) { return Result.newErrorResult(t); } break; } return Result.updateZeroResult; } case ResultConstants.SETCONNECTATTR : { switch (cmd.getConnectionAttrType()) { case ResultConstants.SQL_ATTR_SAVEPOINT_NAME : try { savepoint(cmd.getMainString()); } catch (Throwable t) { return Result.newErrorResult(t); } // case ResultConstants.SQL_ATTR_AUTO_IPD // - always true // default: throw - case never happens } return Result.updateZeroResult; } case ResultConstants.REQUESTDATA : { return sessionData.getDataResultSlice(cmd.getResultId(), cmd.getUpdateCount(), cmd.getFetchSize()); } case ResultConstants.DISCONNECT : { close(); return Result.updateZeroResult; } default : { return Result.newErrorResult( Error.runtimeError(ErrorCode.U_S0500, "Session")); } } } private Result performPostExecute(Result command, Result result) { if (result.mode == ResultConstants.DATA) { result = sessionData.getDataResultHead(command, result, isNetwork); } /* else if (result.mode == ResultConstants.ERROR) { while (sessionContext.depth > 0) { sessionContext.pop(); } } */ if (sqlWarnings != null && sqlWarnings.size() > 0) { if (result.mode == ResultConstants.UPDATECOUNT) { result = new Result(ResultConstants.UPDATECOUNT, result.getUpdateCount()); } HsqlException[] warnings = getAndClearWarnings(); result.addWarnings(warnings); } return result; } public RowSetNavigatorClient getRows(long navigatorId, int offset, int blockSize) { return sessionData.getRowSetSlice(navigatorId, offset, blockSize); } public synchronized void closeNavigator(long id) { sessionData.closeNavigator(id); } public Result executeDirectStatement(Result cmd) { String sql = cmd.getMainString(); HsqlArrayList list; int maxRows = cmd.getUpdateCount(); if (maxRows == -1) { sessionContext.currentMaxRows = 0; } else if (sessionMaxRows == 0) { sessionContext.currentMaxRows = maxRows; } else { sessionContext.currentMaxRows = sessionMaxRows; sessionMaxRows = 0; } try { list = parser.compileStatements(sql, cmd); } catch (Throwable e) { return Result.newErrorResult(e); } Result result = null; boolean recompile = false; HsqlName originalSchema = getCurrentSchemaHsqlName(); for (int i = 0; i < list.size(); i++) { Statement cs = (Statement) list.get(i); if (i > 0) { if (cs.getCompileTimestamp() > database.txManager.getGlobalChangeTimestamp()) { recompile = true; } if (cs.getSchemaName() != null && cs.getSchemaName() != originalSchema) { recompile = true; } } if (recompile) { cs = compileStatement(cs.getSQL(), cmd.getExecuteProperties()); } cs.setGeneratedColumnInfo(cmd.getGeneratedResultType(), cmd.getGeneratedResultMetaData()); result = executeCompiledStatement(cs, ValuePool.emptyObjectArray, cmd.queryTimeout); if (result.mode == ResultConstants.ERROR) { break; } } return result; } public Result executeDirectStatement(String sql) { try { Statement cs = compileStatement(sql); Result result = executeCompiledStatement(cs, ValuePool.emptyObjectArray, 0); return result; } catch (HsqlException e) { return Result.newErrorResult(e); } } public Result executeCompiledStatement(Statement cs, Object[] pvals, int timeout) { Result r; if (abortTransaction) { return handleAbortTransaction(); } if (sessionContext.depth > 0) { if (sessionContext.noSQL.booleanValue() || cs.isAutoCommitStatement()) { return Result.newErrorResult(Error.error(ErrorCode.X_46000)); } } if (cs.isAutoCommitStatement()) { if (isReadOnly()) { return Result.newErrorResult(Error.error(ErrorCode.X_25006)); } try { /** special autocommit for backward compatibility */ commit(false); } catch (HsqlException e) { database.logger.logInfoEvent("Exception at commit"); } } sessionContext.currentStatement = cs; boolean isTX = cs.isTransactionStatement(); if (!isTX) { actionTimestamp = database.txManager.getNextGlobalChangeTimestamp(); sessionContext.setDynamicArguments(pvals); // statements such as DISCONNECT may close the session if (database.logger.getSqlEventLogLevel() >= SimpleLog.LOG_NORMAL) { database.logger.logStatementEvent(this, cs, pvals, Result.updateZeroResult, SimpleLog.LOG_NORMAL); } r = cs.execute(this); sessionContext.currentStatement = null; return r; } repeatLoop: while (true) { actionIndex = rowActionList.size(); database.txManager.beginAction(this, cs); cs = sessionContext.currentStatement; if (cs == null) { return Result.newErrorResult(Error.error(ErrorCode.X_07502)); } if (abortTransaction) { return handleAbortTransaction(); } timeoutManager.startTimeout(timeout); while (true) { try { latch.await(); } catch (InterruptedException e) { Thread.interrupted(); continue; } break; } if (abortAction) { r = Result.newErrorResult(Error.error(ErrorCode.X_40502)); endAction(r); break repeatLoop; } if (abortTransaction) { return handleAbortTransaction(); } database.txManager.beginActionResume(this); // tempActionHistory.add("sql execute " + cs.sql + " " + actionTimestamp + " " + rowActionList.size()); sessionContext.setDynamicArguments(pvals); r = cs.execute(this); if (database.logger.getSqlEventLogLevel() >= SimpleLog.LOG_NORMAL) { database.logger.logStatementEvent(this, cs, pvals, r, SimpleLog.LOG_NORMAL); } // tempActionHistory.add("sql execute end " + actionTimestamp + " " + rowActionList.size()); endAction(r); if (abortTransaction) { break repeatLoop; } if (redoAction) { redoAction = false; while (true) { try { latch.await(); } catch (InterruptedException e) { Thread.interrupted(); continue; } break; } } else { break repeatLoop; } } if (abortTransaction) { return handleAbortTransaction(); } if (sessionContext.depth == 0 && (sessionContext.isAutoCommit.booleanValue() || cs.isAutoCommitStatement())) { try { if (r.mode == ResultConstants.ERROR) { rollbackNoCheck(false); } else { commit(false); } } catch (Exception e) { sessionContext.currentStatement = null; return Result.newErrorResult(Error.error(ErrorCode.X_40001, e)); } } sessionContext.currentStatement = null; return r; } private Result handleAbortTransaction() { rollbackNoCheck(false); sessionContext.currentStatement = null; return Result.newErrorResult(Error.error(ErrorCode.X_40001)); } private Result executeCompiledBatchStatement(Result cmd) { long csid; Statement cs; int[] updateCounts; int count; cs = cmd.statement; if (cs == null || cs.compileTimestamp < database.schemaManager.schemaChangeTimestamp) { csid = cmd.getStatementID(); cs = statementManager.getStatement(this, csid); if (cs == null) { // invalid sql has been removed already return Result.newErrorResult(Error.error(ErrorCode.X_07502)); } } count = 0; RowSetNavigator nav = cmd.initialiseNavigator(); updateCounts = new int[nav.getSize()]; Result generatedResult = null; if (cs.hasGeneratedColumns()) { generatedResult = Result.newGeneratedDataResult(cs.generatedResultMetaData()); } Result error = null; while (nav.hasNext()) { Object[] pvals = nav.getNext(); Result in = executeCompiledStatement(cs, pvals, cmd.queryTimeout); // On the client side, iterate over the vals and throw // a BatchUpdateException if a batch status value of // esultConstants.EXECUTE_FAILED is encountered in the result if (in.isUpdateCount()) { if (cs.hasGeneratedColumns()) { RowSetNavigator navgen = in.getChainedResult().getNavigator(); while (navgen.hasNext()) { Object[] generatedRow = navgen.getNext(); generatedResult.getNavigator().add(generatedRow); } } updateCounts[count++] = in.getUpdateCount(); } else if (in.isData()) { // FIXME: we don't have what it takes yet // to differentiate between things like // stored procedure calls to methods with // void return type and select statements with // a single row/column containing null updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO; } else if (in.mode == ResultConstants.CALL_RESPONSE) { updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO; } else if (in.mode == ResultConstants.ERROR) { updateCounts = ArrayUtil.arraySlice(updateCounts, 0, count); error = in; break; } else { throw Error.runtimeError(ErrorCode.U_S0500, "Session"); } } return Result.newBatchedExecuteResponse(updateCounts, generatedResult, error); } private Result executeDirectBatchStatement(Result cmd) { int[] updateCounts; int count; count = 0; RowSetNavigator nav = cmd.initialiseNavigator(); updateCounts = new int[nav.getSize()]; Result error = null; while (nav.hasNext()) { Result in; Object[] data = nav.getNext(); String sql = (String) data[0]; try { Statement cs = compileStatement(sql); in = executeCompiledStatement(cs, ValuePool.emptyObjectArray, cmd.queryTimeout); } catch (Throwable t) { in = Result.newErrorResult(t); // if (t instanceof OutOfMemoryError) { // System.gc(); // } // "in" already equals "err" // maybe test for OOME and do a gc() ? // t.printStackTrace(); } if (in.isUpdateCount()) { updateCounts[count++] = in.getUpdateCount(); } else if (in.isData()) { // FIXME: we don't have what it takes yet // to differentiate between things like // stored procedure calls to methods with // void return type and select statements with // a single row/column containing null updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO; } else if (in.mode == ResultConstants.CALL_RESPONSE) { updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO; } else if (in.mode == ResultConstants.ERROR) { updateCounts = ArrayUtil.arraySlice(updateCounts, 0, count); error = in; break; } else { throw Error.runtimeError(ErrorCode.U_S0500, "Session"); } } return Result.newBatchedExecuteResponse(updateCounts, null, error); } /** * Retrieves the result of inserting, updating or deleting a row * from an updatable result. * * @return the result of executing the statement */ private Result executeResultUpdate(Result cmd) { long id = cmd.getResultId(); int actionType = cmd.getActionType(); Result result = sessionData.getDataResult(id); if (result == null) { return Result.newErrorResult(Error.error(ErrorCode.X_24501)); } Object[] pvals = (Object[]) cmd.valueData; Type[] types = cmd.metaData.columnTypes; StatementQuery statement = (StatementQuery) result.getStatement(); sessionContext.rowUpdateStatement.setRowActionProperties(result, actionType, statement, types); Result resultOut = executeCompiledStatement(sessionContext.rowUpdateStatement, pvals, cmd.queryTimeout); return resultOut; } // session DATETIME functions long currentDateSCN; long currentTimestampSCN; long currentMillis; private TimestampData currentDate; private TimestampData currentTimestamp; private TimestampData localTimestamp; private TimeData currentTime; private TimeData localTime; /** * Returns the current date, unchanged for the duration of the current * execution unit (statement).<p> * * SQL standards require that CURRENT_DATE, CURRENT_TIME and * CURRENT_TIMESTAMP are all evaluated at the same point of * time in the duration of each SQL statement, no matter how long the * SQL statement takes to complete.<p> * * When this method or a corresponding method for CURRENT_TIME or * CURRENT_TIMESTAMP is first called in the scope of a system change * number, currentMillis is set to the current system time. All further * CURRENT_XXXX calls in this scope will use this millisecond value. * (fredt@users) */ public synchronized TimestampData getCurrentDate() { resetCurrentTimestamp(); if (currentDate == null) { currentDate = (TimestampData) Type.SQL_DATE.getValue(currentMillis / 1000, 0, getZoneSeconds()); } return currentDate; } /** * Returns the current time, unchanged for the duration of the current * execution unit (statement) */ synchronized TimeData getCurrentTime(boolean withZone) { resetCurrentTimestamp(); if (withZone) { if (currentTime == null) { int seconds = (int) (HsqlDateTime.getNormalisedTime( getCalendarGMT(), currentMillis)) / 1000; int nanos = (int) (currentMillis % 1000) * 1000000; currentTime = new TimeData(seconds, nanos, getZoneSeconds()); } return currentTime; } else { if (localTime == null) { int seconds = (int) (HsqlDateTime.getNormalisedTime( getCalendarGMT(), currentMillis + getZoneSeconds() * 1000L)) / 1000; int nanos = (int) (currentMillis % 1000) * 1000000; localTime = new TimeData(seconds, nanos, 0); } return localTime; } } /** * Returns the current timestamp, unchanged for the duration of the current * execution unit (statement) */ synchronized TimestampData getCurrentTimestamp(boolean withZone) { resetCurrentTimestamp(); if (withZone) { if (currentTimestamp == null) { int nanos = (int) (currentMillis % 1000) * 1000000; currentTimestamp = new TimestampData((currentMillis / 1000), nanos, getZoneSeconds()); } return currentTimestamp; } else { if (localTimestamp == null) { int nanos = (int) (currentMillis % 1000) * 1000000; localTimestamp = new TimestampData(currentMillis / 1000 + getZoneSeconds(), nanos, 0); } return localTimestamp; } } synchronized TimestampData getSystemTimestamp(boolean withZone) { long millis = System.currentTimeMillis(); long seconds = millis / 1000; int nanos = (int) (millis % 1000) * 1000000; TimeZone zone = TimeZone.getDefault(); int offset = zone.getOffset(millis) / 1000; if (!withZone) { seconds += offset; offset = 0; } return new TimestampData(seconds, nanos, offset); } private void resetCurrentTimestamp() { if (currentTimestampSCN != actionTimestamp) { currentTimestampSCN = actionTimestamp; currentMillis = System.currentTimeMillis(); currentDate = null; currentTimestamp = null; localTimestamp = null; currentTime = null; localTime = null; } } private Result getAttributesResult(int id) { Result r = Result.newSessionAttributesResult(); Object[] data = r.getSingleRowData(); data[SessionInterface.INFO_ID] = ValuePool.getInt(id); switch (id) { case SessionInterface.INFO_ISOLATION : data[SessionInterface.INFO_INTEGER] = ValuePool.getInt(isolationLevel); break; case SessionInterface.INFO_AUTOCOMMIT : data[SessionInterface.INFO_BOOLEAN] = sessionContext.isAutoCommit; break; case SessionInterface.INFO_CONNECTION_READONLY : data[SessionInterface.INFO_BOOLEAN] = sessionContext.isReadOnly; break; case SessionInterface.INFO_CATALOG : data[SessionInterface.INFO_VARCHAR] = database.getCatalogName().name; break; } return r; } private Result setAttributes(Result r) { Object[] row = r.getSessionAttributes(); int id = ((Integer) row[SessionInterface.INFO_ID]).intValue(); try { switch (id) { case SessionInterface.INFO_AUTOCOMMIT : { boolean value = ((Boolean) row[SessionInterface.INFO_BOOLEAN]) .booleanValue(); this.setAutoCommit(value); break; } case SessionInterface.INFO_CONNECTION_READONLY : { boolean value = ((Boolean) row[SessionInterface.INFO_BOOLEAN]) .booleanValue(); this.setReadOnlyDefault(value); break; } case SessionInterface.INFO_ISOLATION : { int value = ((Integer) row[SessionInterface.INFO_INTEGER]) .intValue(); this.setIsolationDefault(value); break; } case SessionInterface.INFO_CATALOG : { String value = ((String) row[SessionInterface.INFO_VARCHAR]); this.setCatalog(value); } } } catch (HsqlException e) { return Result.newErrorResult(e); } return Result.updateZeroResult; } public synchronized Object getAttribute(int id) { switch (id) { case SessionInterface.INFO_ISOLATION : return ValuePool.getInt(isolationLevel); case SessionInterface.INFO_AUTOCOMMIT : return sessionContext.isAutoCommit; case SessionInterface.INFO_CONNECTION_READONLY : return isReadOnlyDefault ? Boolean.TRUE : Boolean.FALSE; case SessionInterface.INFO_CATALOG : return database.getCatalogName().name; } return null; } public synchronized void setAttribute(int id, Object object) { switch (id) { case SessionInterface.INFO_AUTOCOMMIT : { boolean value = ((Boolean) object).booleanValue(); this.setAutoCommit(value); break; } case SessionInterface.INFO_CONNECTION_READONLY : { boolean value = ((Boolean) object).booleanValue(); this.setReadOnlyDefault(value); break; } case SessionInterface.INFO_ISOLATION : { int value = ((Integer) object).intValue(); this.setIsolationDefault(value); break; } case SessionInterface.INFO_CATALOG : { String value = ((String) object); this.setCatalog(value); } } } // lobs public BlobDataID createBlob(long length) { long lobID = database.lobManager.createBlob(this, length); if (lobID == 0) { throw Error.error(ErrorCode.X_0F502); } sessionData.registerNewLob(lobID); return new BlobDataID(lobID); } public ClobDataID createClob(long length) { long lobID = database.lobManager.createClob(this, length); if (lobID == 0) { throw Error.error(ErrorCode.X_0F502); } sessionData.registerNewLob(lobID); return new ClobDataID(lobID); } public void registerResultLobs(Result result) { sessionData.registerLobForResult(result); } public void allocateResultLob(ResultLob result, InputStream inputStream) { sessionData.allocateLobForResult(result, inputStream); } Result performLOBOperation(ResultLob cmd) { long id = cmd.getLobID(); int operation = cmd.getSubType(); switch (operation) { case ResultLob.LobResultTypes.REQUEST_GET_LOB : { return database.lobManager.getLob(id, cmd.getOffset(), cmd.getBlockLength()); } case ResultLob.LobResultTypes.REQUEST_GET_LENGTH : { return database.lobManager.getLength(id); } case ResultLob.LobResultTypes.REQUEST_GET_BYTES : { return database.lobManager.getBytes( id, cmd.getOffset(), (int) cmd.getBlockLength()); } case ResultLob.LobResultTypes.REQUEST_SET_BYTES : { return database.lobManager.setBytes( id, cmd.getOffset(), cmd.getByteArray(), (int) cmd.getBlockLength()); } case ResultLob.LobResultTypes.REQUEST_GET_CHARS : { return database.lobManager.getChars( id, cmd.getOffset(), (int) cmd.getBlockLength()); } case ResultLob.LobResultTypes.REQUEST_SET_CHARS : { return database.lobManager.setChars( id, cmd.getOffset(), cmd.getCharArray(), (int) cmd.getBlockLength()); } case ResultLob.LobResultTypes.REQUEST_TRUNCATE : { return database.lobManager.truncate(id, cmd.getOffset()); } case ResultLob.LobResultTypes.REQUEST_DUPLICATE_LOB : { return database.lobManager.createDuplicateLob(id); } case ResultLob.LobResultTypes.REQUEST_CREATE_BYTES : case ResultLob.LobResultTypes.REQUEST_CREATE_CHARS : case ResultLob.LobResultTypes.REQUEST_GET_BYTE_PATTERN_POSITION : case ResultLob.LobResultTypes.REQUEST_GET_CHAR_PATTERN_POSITION : { throw Error.error(ErrorCode.X_0A501); } default : { throw Error.runtimeError(ErrorCode.U_S0500, "Session"); } } } // DatabaseMetaData.getURL should work as specified for // internal connections too. public String getInternalConnectionURL() { return DatabaseURL.S_URL_PREFIX + database.getURI(); } public Result cancel(Result result) { if (result.getType() == ResultConstants.SQLCANCEL) { if (result.getSessionRandomID() == randomId) { database.txManager.resetSession( null, this, TransactionManager.resetSessionAbort); } } return Result.updateZeroResult; } public boolean isProcessingScript() { return isProcessingScript; } public boolean isProcessingLog() { return isProcessingLog; } // schema object methods public void setSchema(String schema) { currentSchema = database.schemaManager.getSchemaHsqlName(schema); } public void setCatalog(String catalog) { if (database.getCatalogName().name.equals(catalog)) { return; } throw Error.error(ErrorCode.X_3D000); } /** * If schemaName is null, return the current schema name, else return * the HsqlName object for the schema. If schemaName does not exist, * throw. */ HsqlName getSchemaHsqlName(String name) { return name == null ? currentSchema : database.schemaManager.getSchemaHsqlName(name); } /** * Same as above, but return string */ public String getSchemaName(String name) { return name == null ? currentSchema.name : database.schemaManager.getSchemaName(name); } public void setCurrentSchemaHsqlName(HsqlName name) { currentSchema = name; } public HsqlName getCurrentSchemaHsqlName() { return currentSchema; } public int getResultMemoryRowCount() { return resultMaxMemoryRows; } public void setResultMemoryRowCount(int count) { if (database.logger.getTempDirectoryPath() != null) { if (count < 0) { count = 0; } resultMaxMemoryRows = count; } } // warnings HsqlDeque sqlWarnings; public void addWarning(HsqlException warning) { if (sqlWarnings == null) { sqlWarnings = new HsqlDeque(); } if (sqlWarnings.size() > 9) { sqlWarnings.removeFirst(); } int index = sqlWarnings.indexOf(warning); if (index >= 0) { sqlWarnings.remove(index); } sqlWarnings.add(warning); } public HsqlException[] getAndClearWarnings() { if (sqlWarnings == null) { return HsqlException.emptyArray; } HsqlException[] array = new HsqlException[sqlWarnings.size()]; sqlWarnings.toArray(array); sqlWarnings.clear(); return array; } public HsqlException getLastWarning() { if (sqlWarnings == null || sqlWarnings.size() == 0) { return null; } return (HsqlException) sqlWarnings.getLast(); } public void clearWarnings() { if (sqlWarnings != null) { sqlWarnings.clear(); } } // session zone private Calendar calendar; private Calendar calendarGMT; public int getZoneSeconds() { return timeZoneSeconds; } public void setZoneSeconds(int seconds) { timeZoneSeconds = seconds; } public Calendar getCalendar() { if (calendar == null) { if (zoneString == null) { calendar = new GregorianCalendar(); } else { TimeZone zone = TimeZone.getTimeZone(zoneString); calendar = new GregorianCalendar(zone); } } return calendar; } public Calendar getCalendarGMT() { if (calendarGMT == null) { calendarGMT = new GregorianCalendar(TimeZone.getTimeZone("GMT")); } return calendarGMT; } public SimpleDateFormat getSimpleDateFormatGMT() { if (simpleDateFormatGMT == null) { simpleDateFormatGMT = new SimpleDateFormat("MMMM", Locale.ENGLISH); simpleDateFormatGMT.setCalendar(getCalendarGMT()); } return simpleDateFormatGMT; } // services TypedComparator typedComparator = Type.newComparator(this); Scanner secondaryScanner; SimpleDateFormat simpleDateFormat; SimpleDateFormat simpleDateFormatGMT; Random randomGenerator = new Random(); long seed = -1; public final int randomId = randomGenerator.nextInt(); // public TypedComparator getComparator() { return typedComparator; } public double random(long seed) { if (this.seed != seed) { randomGenerator.setSeed(seed); this.seed = seed; } return randomGenerator.nextDouble(); } public double random() { return randomGenerator.nextDouble(); } public Scanner getScanner() { if (secondaryScanner == null) { secondaryScanner = new Scanner(); } return secondaryScanner; } // properties HsqlProperties clientProperties; public HsqlProperties getClientProperties() { if (clientProperties == null) { clientProperties = new HsqlProperties(); clientProperties.setProperty( HsqlDatabaseProperties.jdbc_translate_tti_types, database.sqlTranslateTTI); clientProperties.setProperty( HsqlDatabaseProperties.sql_live_object, database.sqlLiveObject); } return clientProperties; } // logging and SEQUENCE current values void logSequences() { HashMap map = sessionData.sequenceUpdateMap; if (map == null || map.isEmpty()) { return; } Iterator it = map.keySet().iterator(); for (int i = 0, size = map.size(); i < size; i++) { NumberSequence sequence = (NumberSequence) it.next(); database.logger.writeSequenceStatement(this, sequence); } sessionData.sequenceUpdateMap.clear(); } String getStartTransactionSQL() { StringBuffer sb = new StringBuffer(); sb.append(Tokens.T_START).append(' ').append(Tokens.T_TRANSACTION); if (isolationLevel != isolationLevelDefault) { sb.append(' '); appendIsolationSQL(sb, isolationLevel); } return sb.toString(); } String getTransactionIsolationSQL() { StringBuffer sb = new StringBuffer(); sb.append(Tokens.T_SET).append(' ').append(Tokens.T_TRANSACTION); sb.append(' '); appendIsolationSQL(sb, isolationLevel); return sb.toString(); } String getSessionIsolationSQL() { StringBuffer sb = new StringBuffer(); sb.append(Tokens.T_SET).append(' ').append(Tokens.T_SESSION); sb.append(' ').append(Tokens.T_CHARACTERISTICS).append(' '); sb.append(Tokens.T_AS).append(' ').append(Tokens.T_TRANSACTION).append( ' '); appendIsolationSQL(sb, isolationLevelDefault); return sb.toString(); } static void appendIsolationSQL(StringBuffer sb, int isolationLevel) { sb.append(Tokens.T_ISOLATION).append(' '); sb.append(Tokens.T_LEVEL).append(' '); sb.append(getIsolationString(isolationLevel)); } static String getIsolationString(int isolationLevel) { switch (isolationLevel) { case SessionInterface.TX_READ_UNCOMMITTED : case SessionInterface.TX_READ_COMMITTED : StringBuffer sb = new StringBuffer(); sb.append(Tokens.T_READ).append(' '); sb.append(Tokens.T_COMMITTED); return sb.toString(); case SessionInterface.TX_REPEATABLE_READ : case SessionInterface.TX_SERIALIZABLE : default : return Tokens.T_SERIALIZABLE; } } String getSetSchemaStatement() { return "SET SCHEMA " + currentSchema.statementName; } // timeouts class TimeoutManager { volatile long actionTimestamp; volatile int currentTimeout; volatile boolean aborted; void startTimeout(int timeout) { aborted = false; if (timeout == 0) { return; } currentTimeout = timeout; actionTimestamp = Session.this.actionTimestamp; database.timeoutRunner.addSession(Session.this); } boolean endTimeout() { boolean aborted = this.aborted; currentTimeout = 0; this.aborted = false; return aborted; } public boolean checkTimeout() { if (currentTimeout == 0) { return true; } if (aborted || actionTimestamp != Session.this.actionTimestamp) { actionTimestamp = 0; currentTimeout = 0; aborted = false; return true; } --currentTimeout; if (currentTimeout <= 0) { currentTimeout = 0; aborted = true; database.txManager.resetSession( null, Session.this, TransactionManager.resetSessionAbort); return true; } return false; } } }