/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.jdbc;
import java.sql.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.transaction.xa.Xid;
import org.teiid.client.DQP;
import org.teiid.client.plan.Annotation;
import org.teiid.client.plan.PlanNode;
import org.teiid.client.util.ResultsFuture;
import org.teiid.client.xa.XATransactionException;
import org.teiid.client.xa.XidImpl;
import org.teiid.core.types.ArrayImpl;
import org.teiid.core.util.PropertiesUtils;
import org.teiid.core.util.SqlUtil;
import org.teiid.net.CommunicationException;
import org.teiid.net.ConnectionException;
import org.teiid.net.ServerConnection;
import org.teiid.net.TeiidURL;
import org.teiid.net.socket.SocketServerConnection;
/**
* Teiid's Connection implementation.
*/
public class ConnectionImpl extends WrapperImpl implements TeiidConnection {
private static final int MAX_OPEN_STATEMENTS = PropertiesUtils.getIntProperty(System.getProperties(), "org.teiid.maxOpenStatements", 1000); //$NON-NLS-1$
private static Logger logger = Logger.getLogger("org.teiid.jdbc"); //$NON-NLS-1$
public static final int DEFAULT_ISOLATION = Connection.TRANSACTION_READ_COMMITTED;
// constant value giving product name
private final static String SERVER_NAME = "Teiid Server"; //$NON-NLS-1$
private final static String EMBEDDED_NAME = "Teiid Embedded"; //$NON-NLS-1$
// Unique request ID generator
private long requestIDGenerator;
// url used to create the connection
private String url;
// properties object containing the connection properties.
protected Properties propInfo;
// status of connection object
private boolean closed = false;
// determines if a statement executed should be immediately committed.
private boolean autoCommitFlag = true;
private boolean inLocalTxn;
// collection of all open statements on this connection
private Collection<StatementImpl> statements = Collections.newSetFromMap(new ConcurrentHashMap<StatementImpl, Boolean>());
// cached DatabaseMetadata
private DatabaseMetaDataImpl dbmm;
//Xid for participating in TXN
private XidImpl transactionXid;
// Flag to represent if the connection state needs to be readOnly, default value false.
private boolean readOnly = false;
private DQP dqp;
protected ServerConnection serverConn;
private int transactionIsolation = DEFAULT_ISOLATION;
// the last query plan description
private PlanNode currentPlanDescription;
// the last query debug log
private String debugLog;
// the last query annotations
private Collection<Annotation> annotations;
private Properties connectionProps;
private Properties payload;
public ConnectionImpl(ServerConnection serverConn, Properties info, String url) {
this.connectionProps = info;
this.serverConn = serverConn;
this.url = url;
this.dqp = serverConn.getService(DQP.class);
if (logger.isLoggable(Level.FINE)) {
logger.fine(JDBCPlugin.Util.getString("MMConnection.Session_success")); //$NON-NLS-1$
logConnectionProperties(url, info);
}
setExecutionProperties(info);
}
boolean isInLocalTxn() {
return inLocalTxn;
}
private void setExecutionProperties(Properties info) {
this.propInfo = new Properties();
String defaultFetchSize = info.getProperty(ExecutionProperties.PROP_FETCH_SIZE);
if (defaultFetchSize != null) {
propInfo.put(ExecutionProperties.PROP_FETCH_SIZE, defaultFetchSize);
} else {
propInfo.put(ExecutionProperties.PROP_FETCH_SIZE, String.valueOf(BaseDataSource.DEFAULT_FETCH_SIZE));
}
String partialResultsMode = info.getProperty(ExecutionProperties.PROP_PARTIAL_RESULTS_MODE);
if (partialResultsMode != null) {
propInfo.put(ExecutionProperties.PROP_PARTIAL_RESULTS_MODE, partialResultsMode);
} else {
propInfo.put(ExecutionProperties.PROP_PARTIAL_RESULTS_MODE, BaseDataSource.DEFAULT_PARTIAL_RESULTS_MODE);
}
String resultSetCacheMode = info.getProperty(ExecutionProperties.RESULT_SET_CACHE_MODE);
if (resultSetCacheMode != null) {
propInfo.put(ExecutionProperties.RESULT_SET_CACHE_MODE, resultSetCacheMode);
} else {
propInfo.put(ExecutionProperties.RESULT_SET_CACHE_MODE, BaseDataSource.DEFAULT_RESULT_SET_CACHE_MODE);
}
String ansiQuotes = info.getProperty(ExecutionProperties.ANSI_QUOTED_IDENTIFIERS);
if (ansiQuotes != null) {
propInfo.put(ExecutionProperties.ANSI_QUOTED_IDENTIFIERS, ansiQuotes);
} else {
propInfo.put(ExecutionProperties.ANSI_QUOTED_IDENTIFIERS, Boolean.TRUE.toString());
}
for (String key : info.stringPropertyNames()) {
String actualKey = JDBCURL.EXECUTION_PROPERTIES.get(key);
if (actualKey != null) {
propInfo.setProperty(actualKey, info.getProperty(key));
}
}
}
public Collection<Annotation> getAnnotations() {
return annotations;
}
public void setAnnotations(Collection<Annotation> annotations) {
this.annotations = annotations;
}
public String getDebugLog() {
return debugLog;
}
public void setDebugLog(String debugLog) {
this.debugLog = debugLog;
}
public PlanNode getCurrentPlanDescription() {
return currentPlanDescription;
}
public void setCurrentPlanDescription(PlanNode currentPlanDescription) {
this.currentPlanDescription = currentPlanDescription;
}
protected Properties getExecutionProperties() {
return this.propInfo;
}
public void setExecutionProperty(String key, String value) {
JDBCURL.addNormalizedProperty(key, value, getExecutionProperties());
}
public String getExecutionProperty(String key) {
return this.getExecutionProperties().getProperty(JDBCURL.getValidKey(key));
}
DQP getDQP() {
return this.dqp;
}
/**
* Remove password & trusted token and log all other properties
* @param connUrl - URL used to connect to server
* @param info - properties object supplied
*/
private void logConnectionProperties(String connUrl, Properties info) {
StringBuffer modifiedUrl = new StringBuffer();
// If we have valid URL
if (connUrl != null) {
// We need wipe out the password here, before we write to the log
int startIndex = connUrl.indexOf("password="); //$NON-NLS-1$
if (startIndex != -1) {
modifiedUrl.append(connUrl.substring(0, startIndex));
modifiedUrl.append("password=***"); //$NON-NLS-1$
int endIndex = connUrl.indexOf(";", startIndex+9); //$NON-NLS-1$
if (endIndex != -1) {
modifiedUrl.append(";").append(connUrl.substring(endIndex)); //$NON-NLS-1$
}
}
logger.fine("Connection Url="+modifiedUrl); //$NON-NLS-1$
}
// Now clone the properties object and remove password and trusted token
if (info != null) {
Enumeration enumeration = info.keys();
while (enumeration.hasMoreElements()) {
String key = (String)enumeration.nextElement();
Object anObj = info.get(key);
// Log each property except for password and token.
if (!TeiidURL.CONNECTION.PASSWORD.equalsIgnoreCase(key)) {
logger.fine(key+"="+anObj); //$NON-NLS-1$
}
}
}
}
String getUrl() {
return this.url;
}
/**
* Connection identifier of this connection
* @return identifier
* @throws SQLException
*/
public String getConnectionId() {
return this.serverConn.getLogonResult().getSessionID();
}
/**
* Generate the next unique requestID for matching up requests with responses.
* These IDs should be unique only in the context of a ServerConnection instance.
* @return Request ID
*/
protected synchronized long nextRequestID() {
return requestIDGenerator++;
}
public void clearWarnings() throws SQLException {
// do nothing
}
public void close() throws SQLException {
Throwable firstException = null;
if(closed) {
return;
}
try {
// close any statements that were created on this connection
try {
closeStatements();
} catch (SQLException se) {
firstException = se;
} finally {
this.serverConn.close();
if ( firstException != null )
throw (SQLException)firstException;
}
} catch (SQLException se) {
throw TeiidSQLException.create(se, JDBCPlugin.Util.getString("MMConnection.Err_connection_close", se.getMessage())); //$NON-NLS-1$
} finally {
logger.fine(JDBCPlugin.Util.getString("MMConnection.Connection_close_success")); //$NON-NLS-1$
// set the status of the connection to closed
closed = true;
}
}
/**
* <p>
* Close all the statements open on this connection
* </p>
*
* @throws SQLException
* server statement object could not be closed.
*/
void closeStatements() throws SQLException {
// Closing the statement will cause the
// MMConnection.closeStatement() method to be called,
// which will modify this.statements. So, we do this iteration
// in a separate safe copy of the list
List<StatementImpl> statementsSafe = new ArrayList<StatementImpl>(this.statements);
SQLException ex = null;
for (StatementImpl statement : statementsSafe) {
try {
statement.close();
} catch (SQLException e) {
ex = e;
}
}
if (ex != null) {
throw TeiidSQLException.create(ex, JDBCPlugin.Util.getString("MMConnection.Err_closing_stmts")); //$NON-NLS-1$
}
}
/**
* Called by MMStatement to notify the connection that the
* statement has been closed.
* @param statement
*/
void closeStatement(Statement statement) {
this.statements.remove(statement);
}
/**
* <p>This method makes any changes involved in a transaction permanent and releases
* any locks held by the connection object. This is only used when auto-commit
* is set to false.</p>
* @throws SQLException if the transaction had been rolled back or marked to roll back.
*/
public void commit() throws SQLException {
checkConnection();
if (!autoCommitFlag) {
try {
directCommit();
} finally {
inLocalTxn = false;
}
}
}
private void directCommit() throws SQLException {
if (inLocalTxn) {
try {
ResultsFuture<?> future = this.dqp.commit();
future.get();
} catch (Exception e) {
throw TeiidSQLException.create(e);
}
logger.fine(JDBCPlugin.Util.getString("MMConnection.Commit_success")); //$NON-NLS-1$
}
}
void beginLocalTxnIfNeeded() throws SQLException {
if (this.transactionXid != null || inLocalTxn || this.autoCommitFlag || isDisableLocalTxn()) {
return;
}
try {
try {
this.dqp.begin();
} catch (XATransactionException e) {
throw TeiidSQLException.create(e);
}
inLocalTxn = true;
} finally {
if (!inLocalTxn) {
autoCommitFlag = true;
}
}
}
private boolean isDisableLocalTxn() {
String prop = this.propInfo.getProperty(ExecutionProperties.DISABLE_LOCAL_TRANSACTIONS);
return prop != null && Boolean.valueOf(prop);
}
public StatementImpl createStatement() throws SQLException {
return createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}
/**
* <p>Creates a Statement object that will produce ResultSet objects of the type
* resultSetType and concurrency level resultSetConcurrency.</p>
* @param intvalue indicating the ResultSet's type
* @param intValue indicating the ResultSet's concurrency
* @return Statement object.
*/
public StatementImpl createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return createStatement(resultSetType, resultSetConcurrency, ResultSet.HOLD_CURSORS_OVER_COMMIT);
}
/**
* @param resultSetType
* @throws TeiidSQLException
* @since 4.3
*/
private void validateResultSetType(int resultSetType) throws TeiidSQLException {
if (resultSetType == ResultSet.TYPE_SCROLL_SENSITIVE ) {
String msg = JDBCPlugin.Util.getString("MMConnection.Scrollable_type_not_supported", "ResultSet.TYPE_SCROLL_SENSITIVE"); //$NON-NLS-1$ //$NON-NLS-2$
throw new TeiidSQLException(msg);
}
}
/**
* @param resultSetConcurrency
* @throws TeiidSQLException
* @since 4.3
*/
private void validateResultSetConcurrency(int resultSetConcurrency) throws TeiidSQLException {
if (resultSetConcurrency == ResultSet.CONCUR_UPDATABLE) {
String msg = JDBCPlugin.Util.getString("MMConnection.Concurrency_type_not_supported", "ResultSet.CONCUR_UPDATABLE"); //$NON-NLS-1$ //$NON-NLS-2$
throw new TeiidSQLException(msg);
}
}
public boolean getAutoCommit() throws SQLException {
//Check to see the connection is open
checkConnection();
return autoCommitFlag;
}
public String getCatalog() throws SQLException {
//Check to see the connection is open
checkConnection();
//catalogs are not supported
return this.serverConn.getLogonResult().getVdbName();
}
/**
* <p>This method gets the ServerConnection object wrapped by this object.</p>
* @return ServerConnection object
*/
public ServerConnection getServerConnection() throws SQLException {
//Check to see the connection is open
checkConnection();
return serverConn;
}
String getVDBName() throws SQLException {
//Check to see the connection is open
checkConnection();
//get the virtual database name to which we are connected.
return this.serverConn.getLogonResult().getVdbName();
}
@Deprecated
/**
* Will return 0 for Teiid 9.0+ servers
* @return
* @throws SQLException
*/
public int getVDBVersion() throws SQLException {
checkConnection();
return this.serverConn.getLogonResult().getVdbVersion();
}
/**
* Get's the name of the user who got this connection.
* @return Sring object giving the user name
* @throws SQLException if the connection is closed
*/
String getUserName() throws SQLException {
checkConnection();
return this.serverConn.getLogonResult().getUserName();
}
public DatabaseMetaDataImpl getMetaData() throws SQLException {
//Check to see the connection is open
checkConnection();
if (dbmm == null) {
dbmm = new DatabaseMetaDataImpl(this);
}
return dbmm;
}
/**
* Get the database name that this connection is representing
* @return String name of the database
*/
public String getDatabaseName() {
if (this.serverConn instanceof SocketServerConnection) {
return SERVER_NAME;
}
return EMBEDDED_NAME;
}
/**
* Retrieves the current holdability of ResultSet objects created using this Connection object.
* @param holdability int indicating the holdability
* @return int holdability
* @throws SQLException
*/
public int getHoldability() throws SQLException {
return ResultSet.HOLD_CURSORS_OVER_COMMIT;
}
public int getTransactionIsolation() throws SQLException {
return this.transactionIsolation;
}
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
return Collections.emptyMap();
}
/**
* <p>This method will return the first warning reported by calls on this connection,
* or null if none exist.</p>
* @return A SQLWarning object if there are any warnings.
* @throws SQLException, should never occur
*/
public SQLWarning getWarnings() throws SQLException {
//Check to see the connection is open
checkConnection();
return null; // we don't have any warnings
}
/**
* <p>This method will return whether this connection is closed or not.</p>
* @return booleanvalue indicating if the connection is closed
* @throws SQLException, should never occur
*/
public boolean isClosed() throws SQLException {
return closed;
}
public boolean isReadOnly() throws SQLException {
checkConnection();
return readOnly;
}
public String nativeSQL(String sql) throws SQLException {
// return the string argument without any modifications.
// escape syntaxes are directly supported in the server
return sql;
}
/**
* <p>Creates a CallableStatement object that contains sql and that will produce
* ResultSet objects that are non-scrollable and non-updatable. A SQL stored
* procedure call statement is handled by creating a CallableStatement for it.</p>
* @param sql String(escape syntax) for invoking a stored procedure.
* @return CallableStatement object that can be used to execute the storedProcedure
* @throws SQLException if there is an error creating the callable statement object
*/
public CallableStatementImpl prepareCall(String sql) throws SQLException {
//there is a problem setting the result set type to be non-scrollable
//See defect 17768
return prepareCall(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
}
/**
* <p>Creates a CallableStatement object that contains a sql and that will produce
* ResultSet objects of the type resultSetType and with a concurrency level of
* resultSetConcurrency. A SQL stored procedure call statement is handled by
* creating a CallableStatement for it.</p>
* @param sql String(escape syntax) for invoking a stored procedure.
* @param intvalue indicating the ResultSet's type
* @param intValue indicating the ResultSet's concurrency
* @return CallableStatement object that can be used to execute the storedProcedure
*/
public CallableStatementImpl prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return prepareCall(sql, resultSetType, resultSetConcurrency, ResultSet.HOLD_CURSORS_OVER_COMMIT);
}
/**
* @param sql
* @throws TeiidSQLException
* @since 4.3
*/
private void validateSQL(String sql) throws TeiidSQLException {
if (sql == null) {
String msg = JDBCPlugin.Util.getString("MMConnection.SQL_cannot_be_null"); //$NON-NLS-1$
throw new TeiidSQLException(msg);
}
}
public PreparedStatementImpl prepareStatement(String sql) throws SQLException {
return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}
public PreparedStatementImpl prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return prepareStatement(sql, resultSetType, resultSetConcurrency, ResultSet.HOLD_CURSORS_OVER_COMMIT);
}
public PreparedStatementImpl prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability, int autoGeneratedKeys) throws SQLException {
//Check to see the connection is open
checkConnection();
validateResultSetType(resultSetType);
validateResultSetConcurrency(resultSetConcurrency);
validateSQL(sql);
// add the statement object to the map
PreparedStatementImpl newStatement = new PreparedStatementImpl(this, sql, resultSetType, resultSetConcurrency);
newStatement.setAutoGeneratedKeys(autoGeneratedKeys == Statement.RETURN_GENERATED_KEYS);
addStatement(newStatement);
return newStatement;
}
public PreparedStatementImpl prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability ) throws SQLException {
return prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability, Statement.NO_GENERATED_KEYS);
}
public void rollback() throws SQLException {
rollback(true);
}
/**
* Rollback the current local transaction
* @param startTxn
* @throws SQLException
*/
public void rollback(boolean startTxn) throws SQLException {
//Check to see the connection is open
checkConnection();
if (!autoCommitFlag) {
if (this.transactionXid != null) {
throw new TeiidSQLException(JDBCPlugin.Util.getString("MMStatement.In_XA_Transaction"));//$NON-NLS-1$
}
try {
if (this.inLocalTxn) {
this.inLocalTxn = false;
try {
ResultsFuture<?> future = this.dqp.rollback();
future.get();
} catch (Exception e) {
throw TeiidSQLException.create(e);
}
logger.fine(JDBCPlugin.Util.getString("MMConnection.Rollback_success")); //$NON-NLS-1$
}
} finally {
if (startTxn) {
this.inLocalTxn = false;
}
else {
this.autoCommitFlag = true;
}
}
}
}
public ResultsFuture<?> submitSetAutoCommitTrue(boolean commit) throws SQLException {
//Check to see the connection is open
checkConnection();
if (this.autoCommitFlag) {
return ResultsFuture.NULL_FUTURE;
}
this.autoCommitFlag = true;
if (isDisableLocalTxn()) {
return ResultsFuture.NULL_FUTURE;
}
try {
if (commit) {
return dqp.commit();
}
return dqp.rollback();
} catch (XATransactionException e) {
throw TeiidSQLException.create(e);
}
}
public void setAutoCommit(boolean autoCommit) throws SQLException {
//Check to see the connection is open
checkConnection();
if (autoCommit == this.autoCommitFlag) {
return;
}
if (autoCommit && this.transactionXid != null) {
throw new TeiidSQLException(JDBCPlugin.Util.getString("MMStatement.In_XA_Transaction"));//$NON-NLS-1$
}
this.autoCommitFlag = autoCommit;
if (autoCommit) {
directCommit();
} else {
inLocalTxn = false;
}
}
/**
* <p>Teiid does not allow setting a catalog through a connection. This
* method silently ignores the request as per the specification.</p>
* @param The string values which sets the catalog name on the connection.
* @throws SQLException This should never occur.
*/
public void setCatalog(String catalog) throws SQLException {
// do nothing, silently ignore the request
}
/**
* @param A boolean value specifying whether the connection is readonly.
* @throws throws SQLException.
*/
public void setReadOnly(boolean readOnly) throws SQLException {
checkConnection();
// During transaction do not allow to change this flag
if (isInLocalTxn() || this.transactionXid != null) {
throw new TeiidSQLException(JDBCPlugin.Util.getString("MMStatement.Invalid_During_Transaction", "setReadOnly(" + readOnly + ")"));//$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
}
this.readOnly = readOnly;
}
/**
* <p> This utility method checks if the jdbc connection is closed and
* throws an exception if it is closed. </p>
* @throws SQLException if the connection object is closed.
*/
void checkConnection() throws TeiidSQLException{
//Check to see the connection is closed and proceed if it is not
if (closed) {
throw new TeiidSQLException(JDBCPlugin.Util.getString("MMConnection.Cant_use_closed_connection")); //$NON-NLS-1$
}
}
protected void commitTransaction(XidImpl arg0, boolean arg1) throws SQLException {
checkConnection();
transactionXid = null;
this.autoCommitFlag = true;
try {
ResultsFuture<?> future = this.dqp.commit(arg0, arg1);
future.get();
} catch (Exception e) {
throw TeiidSQLException.create(e);
}
}
protected void endTransaction(XidImpl arg0, int arg1) throws SQLException {
checkConnection();
this.autoCommitFlag = true;
try {
ResultsFuture<?> future = this.dqp.end(arg0, arg1);
future.get();
} catch (Exception e) {
throw TeiidSQLException.create(e);
}
}
protected void forgetTransaction(XidImpl arg0) throws SQLException {
checkConnection();
try {
ResultsFuture<?> future = this.dqp.forget(arg0);
future.get();
} catch (Exception e) {
throw TeiidSQLException.create(e);
}
}
protected int prepareTransaction(XidImpl arg0) throws SQLException {
checkConnection();
transactionXid = null;
try {
ResultsFuture<Integer> future = this.dqp.prepare(arg0);
return future.get();
} catch (Exception e) {
throw TeiidSQLException.create(e);
}
}
protected Xid[] recoverTransaction(int arg0) throws SQLException {
checkConnection();
try {
ResultsFuture<Xid[]> future = this.dqp.recover(arg0);
return future.get();
} catch (Exception e) {
throw TeiidSQLException.create(e);
}
}
protected void rollbackTransaction(XidImpl arg0) throws SQLException {
checkConnection();
transactionXid = null;
this.autoCommitFlag = true;
try {
ResultsFuture<?> future = this.dqp.rollback(arg0);
future.get();
} catch (Exception e) {
throw TeiidSQLException.create(e);
}
}
protected void startTransaction(XidImpl arg0, int arg1, int timeout) throws SQLException {
checkConnection();
try {
ResultsFuture<?> future = this.dqp.start(arg0, arg1, timeout);
future.get();
} catch (Exception e) {
throw TeiidSQLException.create(e);
}
transactionXid = arg0;
this.autoCommitFlag = false;
}
protected XidImpl getTransactionXid() {
return transactionXid;
}
public boolean isValid(int timeout) throws SQLException {
return this.getServerConnection().isOpen(timeout * 1000);
}
public void recycleConnection(boolean selectNewInstance) {
this.payload = null;
try {
//close all open statements
this.closeStatements();
} catch (SQLException e) {
logger.log(Level.WARNING, JDBCPlugin.Util.getString("MMXAConnection.rolling_back_error"), e); //$NON-NLS-1$
}
try {
//rollback if still in a transaction
if (!this.getAutoCommit()) {
logger.warning(JDBCPlugin.Util.getString("MMXAConnection.rolling_back")); //$NON-NLS-1$
if (this.getTransactionXid() == null) {
this.rollback(false);
} else {
this.rollbackTransaction(getTransactionXid());
}
}
} catch (SQLException e) {
logger.log(Level.WARNING, JDBCPlugin.Util.getString("MMXAConnection.rolling_back_error"), e); //$NON-NLS-1$
}
if (selectNewInstance) {
this.serverConn.cleanUp();
}
}
public boolean isSameProcess(ConnectionImpl conn) throws CommunicationException {
return this.serverConn.isSameInstance(conn.serverConn);
}
public void setClientInfo(Properties properties)
throws SQLClientInfoException {
}
public void setClientInfo(String name, String value)
throws SQLClientInfoException {
}
public Properties getClientInfo() throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public String getClientInfo(String name) throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public Array createArrayOf(String typeName, Object[] elements)
throws SQLException {
return new ArrayImpl(elements);
}
public Blob createBlob() throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public Clob createClob() throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public NClob createNClob() throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public SQLXML createSQLXML() throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public StatementImpl createStatement(int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
//Check to see the connection is open
checkConnection();
validateResultSetType(resultSetType);
validateResultSetConcurrency(resultSetConcurrency);
//TODO: implement close cursors at commit
// add the statement object to the map
StatementImpl newStatement = new StatementImpl(this, resultSetType, resultSetConcurrency);
addStatement(newStatement);
return newStatement;
}
private void addStatement(StatementImpl newStatement) throws SQLException {
if (statements.size() >= MAX_OPEN_STATEMENTS) {
this.close();
throw new TeiidSQLException(JDBCPlugin.Util.gs(JDBCPlugin.Event.TEIID20036, MAX_OPEN_STATEMENTS));
}
statements.add(newStatement);
}
public Struct createStruct(String typeName, Object[] attributes)
throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public CallableStatementImpl prepareCall(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
//Check to see the connection is open
checkConnection();
validateResultSetType(resultSetType);
validateResultSetConcurrency(resultSetConcurrency);
validateSQL(sql);
//TODO: implement close cursors at commit
// add the statement object to the map
CallableStatementImpl newStatement = new CallableStatementImpl(this, sql, resultSetType, resultSetConcurrency);
addStatement(newStatement);
return newStatement;
}
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
throws SQLException {
return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT, Statement.RETURN_GENERATED_KEYS);
}
public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
throws SQLException {
return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT, Statement.RETURN_GENERATED_KEYS);
}
public PreparedStatement prepareStatement(String sql, String[] columnNames)
throws SQLException {
return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT, Statement.RETURN_GENERATED_KEYS);
}
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public void rollback(Savepoint savepoint) throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public void setHoldability(int holdability) throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public Savepoint setSavepoint() throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public Savepoint setSavepoint(String name) throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public void setTransactionIsolation(int level) throws SQLException {
this.transactionIsolation = level;
}
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
Object setPassword(Object newPassword) {
if (newPassword != null) {
return this.connectionProps.put(TeiidURL.CONNECTION.PASSWORD, newPassword);
}
return this.connectionProps.remove(TeiidURL.CONNECTION.PASSWORD);
}
String getPassword() {
Object result = this.connectionProps.get(TeiidURL.CONNECTION.PASSWORD);
if (result == null) {
return null;
}
return result.toString();
}
@Override
public void changeUser(String userName, String newPassword)
throws SQLException {
//TODO: recycleConnection();
Object oldName = null;
Object oldPassword = null;
if (userName != null) {
oldName = this.connectionProps.put(TeiidURL.CONNECTION.USER_NAME, userName);
} else {
oldName = this.connectionProps.remove(TeiidURL.CONNECTION.USER_NAME);
}
oldPassword = setPassword(newPassword);
boolean success = false;
try {
this.serverConn.authenticate();
success = true;
} catch (ConnectionException e) {
throw TeiidSQLException.create(e);
} catch (CommunicationException e) {
throw TeiidSQLException.create(e);
} finally {
if (!success) {
if (oldName != null) {
this.connectionProps.put(TeiidURL.CONNECTION.USER_NAME, oldName);
} else {
this.connectionProps.remove(TeiidURL.CONNECTION.USER_NAME);
}
setPassword(oldPassword);
}
}
}
public void abort(Executor executor) throws SQLException {
if (closed) {
return;
}
//TODO: ensure that threads are released. In theory they will be since close effectively cancels current executions
close();
}
public int getNetworkTimeout() throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public String getSchema() throws SQLException {
return null;
}
/**
* @see query timeouts and the synchronousTtl setting if using socket connections
*/
public void setNetworkTimeout(Executor executor, int milliseconds)
throws SQLException {
throw SqlUtil.createFeatureNotSupportedException();
}
public void setSchema(String schema) throws SQLException {
}
public Properties getPayload() {
return payload;
}
public void setPayload(Properties payload) {
this.payload = payload;
}
public Properties getConnectionProps() {
return connectionProps;
}
void setTransactionXid(XidImpl transactionXid) {
this.transactionXid = transactionXid;
}
public void setInLocalTxn(boolean inLocalTxn) {
this.inLocalTxn = inLocalTxn;
}
}