org.hsqldb.Session Maven / Gradle / Ivy
Show all versions of sqltool Show documentation
/* Copyright (c) 2001-2020, 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.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Random;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
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.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.TypedComparator;
/**
* Implementation of SQL sessions.
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 2.5.1
* @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 statementStartTimestamp;
long transactionTimestamp;
long transactionEndTimestamp;
final boolean txConflictRollback;
final boolean txInterruptRollback;
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;
txInterruptRollback = database.txInterruptRollback;
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[] changedColumns) {
// tempActionHistory.add("add delete action " + actionTimestamp);
if (abortTransaction) {
throw Error.error(ErrorCode.X_40001);
}
if (abortAction) {
throw Error.error(ErrorCode.X_40502);
}
getTransactionUTC();
database.txManager.addDeleteAction(this, table, store, row,
changedColumns);
}
void addInsertAction(Table table, PersistentStore store, Row row,
int[] changedColumns) {
getTransactionUTC();
// 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;
timeoutManager.endTimeout();
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);
}
} else {
logSequences();
}
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) {
abortAction = false;
abortTransaction = false;
transactionUTCSet = false;
sessionContext.resetStack();
sessionContext.savepoints.clear();
sessionContext.savepointTimestamps.clear();
rowActionList.clear();
sessionData.persistentStoreCollection.clearTransactionTables();
sessionData.closeAllTransactionNavigators();
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();
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);
}
/**
* 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);
}
actionTimestamp = database.txManager.getNextGlobalChangeTimestamp();
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();
}
// campbell-burnet@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;
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;
statementStartTimestamp =
database.txManager.getGlobalChangeTimestamp();
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;
abortAction = false;
sessionData.persistentStoreCollection.clearStatementTables();
return r;
}
timeoutManager.startTimeout(timeout);
repeatLoop:
while (true) {
actionIndex = rowActionList.size();
database.txManager.beginAction(this, cs);
if (redoAction) {
redoAction = false;
continue;
}
cs = sessionContext.currentStatement;
if (cs == null) {
return Result.newErrorResult(Error.error(ErrorCode.X_07502));
}
if (abortTransaction) {
return handleAbortTransaction();
}
boolean interrupted = false;
while (true) {
try {
latch.await();
} catch (InterruptedException e) {
interrupted = txInterruptRollback;
Thread.interrupted();
continue;
}
break;
}
if (abortAction) {
r = Result.newErrorResult(Error.error(ErrorCode.X_40502));
endAction(r);
break repeatLoop;
}
if (abortTransaction || interrupted) {
Result result = handleAbortTransaction();
if (interrupted) {
Thread.currentThread().interrupt();
}
return result;
}
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.next()) {
Object[] pvals = nav.getCurrent();
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.next()) {
Object[] generatedRow = navgen.getCurrent();
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.next()) {
Result in;
Object[] data = nav.getCurrent();
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 currentTimestampSCN;
long currentMillis;
private TimestampData currentDate;
private TimestampData currentTimestamp;
private TimestampData localTimestamp;
private TimestampData transactionUTC;
boolean transactionUTCSet;
private TimeData currentTime;
private TimeData localTime;
/**
* Returns the current date, unchanged for the duration of the current
* execution unit (statement).
*
* 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.
*
* 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(this,
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;
}
}
static TimestampData getSystemTimestamp(boolean withZone, boolean utc) {
long millis = System.currentTimeMillis();
long seconds = millis / 1000;
int nanos = (int) (millis % 1000) * 1000000;
int offset = 0;
if (!utc) {
TimeZone zone = TimeZone.getDefault();
offset = zone.getOffset(millis) / 1000;
if (!withZone) {
seconds += offset;
offset = 0;
}
}
return new TimestampData(seconds, nanos, offset);
}
TimestampData getTransactionUTC() {
if (!transactionUTCSet) {
transactionUTC = getSystemTimestamp(true, true);
transactionUTCSet = true;
}
return transactionUTC;
}
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);
}
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);
}
return new ClobDataID(lobID);
}
public void registerResultLobs(Result result) {
sessionData.registerLobForResult(result);
}
public Result allocateResultLob(ResultLob result) {
return sessionData.allocateLobForResult(result);
}
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, Long.MAX_VALUE,
TransactionManager.resetSessionStatement);
}
}
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"));
calendarGMT.setFirstDayOfWeek(Calendar.MONDAY);
calendarGMT.setMinimalDaysInFirstWeek(4);
}
return calendarGMT;
}
public SimpleDateFormat getSimpleDateFormatGMT() {
if (simpleDateFormatGMT == null) {
simpleDateFormatGMT = new SimpleDateFormat("MMMM", Locale.ENGLISH);
simpleDateFormatGMT.setCalendar(getCalendarGMT());
}
return simpleDateFormatGMT;
}
// services
TypedComparator typedComparator = new TypedComparator(this);
Scanner secondaryScanner;
SimpleDateFormat simpleDateFormat;
SimpleDateFormat simpleDateFormatGMT;
Random randomGenerator = new Random();
long seed = -1;
public final int randomId = randomGenerator.nextInt(Integer.MAX_VALUE);
//
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() {
StringBuilder sb = new StringBuilder();
sb.append(Tokens.T_START).append(' ').append(Tokens.T_TRANSACTION);
if (isolationLevel != isolationLevelDefault) {
sb.append(' ');
appendIsolationSQL(sb, isolationLevel);
}
return sb.toString();
}
String getTransactionIsolationSQL() {
StringBuilder sb = new StringBuilder();
sb.append(Tokens.T_SET).append(' ').append(Tokens.T_TRANSACTION);
sb.append(' ');
appendIsolationSQL(sb, isolationLevel);
return sb.toString();
}
String getSessionIsolationSQL() {
StringBuilder sb = new StringBuilder();
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(StringBuilder 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 :
StringBuilder sb = new StringBuilder();
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 {
AtomicInteger currentTimeout = new AtomicInteger();
volatile long checkTimestamp;
void startTimeout(int timeout) {
currentTimeout.set(timeout);
if (timeout == 0) {
return;
}
checkTimestamp = Session.this.statementStartTimestamp;
database.timeoutRunner.addSession(Session.this);
}
boolean endTimeout() {
currentTimeout.set(0);
return true;
}
public boolean checkTimeout() {
if (currentTimeout.get() == 0) {
return true;
}
int result = currentTimeout.decrementAndGet();
if (result <= 0) {
currentTimeout.set(0);
database.txManager.resetSession(
null, Session.this, checkTimestamp,
TransactionManager.resetSessionStatement);
return true;
}
return false;
}
}
}