org.firebirdsql.jdbc.AbstractConnection Maven / Gradle / Ivy
Show all versions of jaybird-jdk15 Show documentation
/*
* $Id: AbstractConnection.java 60257 2014-11-17 21:10:45Z mrotteveel $
*
* Firebird Open Source J2ee connector - jdbc driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
*
* This program 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
* LGPL License for more details.
*
* This file was created by members of the firebird development team.
* All individual contributions remain the Copyright (C) of those
* individuals. Contributors to this file are either listed here or
* can be obtained from a CVS history command.
*
* All rights reserved.
*/
package org.firebirdsql.jdbc;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.resource.ResourceException;
import org.firebirdsql.gds.DatabaseParameterBuffer;
import org.firebirdsql.gds.GDS;
import org.firebirdsql.gds.GDSException;
import org.firebirdsql.gds.ISCConstants;
import org.firebirdsql.gds.IscDbHandle;
import org.firebirdsql.gds.TransactionParameterBuffer;
import org.firebirdsql.gds.impl.DatabaseParameterBufferExtension;
import org.firebirdsql.gds.impl.GDSHelper;
import org.firebirdsql.jca.FBConnectionRequestInfo;
import org.firebirdsql.jca.FBLocalTransaction;
import org.firebirdsql.jca.FBManagedConnection;
import org.firebirdsql.jca.FirebirdLocalTransaction;
import org.firebirdsql.util.SQLExceptionChainBuilder;
/**
* The class AbstractConnection
is a handle to a
* {@link FBManagedConnection}.
*
* @author David Jencks
* @version 1.0
*/
public abstract class AbstractConnection implements FirebirdConnection {
// This flag is set tu true in close() method to indicate that this
// instance is invalid and cannot be used anymore
private boolean invalid = false;
protected FBManagedConnection mc;
private FBLocalTransaction localTransaction;
private FBDatabaseMetaData metaData;
protected InternalTransactionCoordinator txCoordinator;
private SQLWarning firstWarning;
// This set contains all allocated but not closed statements
// It is used to close them before the connection is closed
protected HashSet activeStatements = new HashSet();
private int resultSetHoldability = ResultSet.CLOSE_CURSORS_AT_COMMIT;
private boolean autoCommit;
private StoredProcedureMetaData storedProcedureMetaData;
/**
* Create a new AbstractConnection instance based on a
* {@link FBManagedConnection}.
*
* @param mc A FBManagedConnection around which this connection is based
*/
public AbstractConnection(FBManagedConnection mc) {
this.mc = mc;
this.txCoordinator = new InternalTransactionCoordinator();
FBConnectionRequestInfo cri = (FBConnectionRequestInfo)mc.getConnectionRequestInfo();
if (cri.hasArgument(DatabaseParameterBufferExtension.RESULT_SET_HOLDABLE))
resultSetHoldability = ResultSet.HOLD_CURSORS_OVER_COMMIT;
else
resultSetHoldability = ResultSet.CLOSE_CURSORS_AT_COMMIT;
}
public FBObjectListener.StatementListener getStatementListener() {
return txCoordinator;
}
public int getHoldability() throws SQLException {
return this.resultSetHoldability;
}
public void setHoldability(int holdability) throws SQLException {
this.resultSetHoldability = holdability;
}
/**
* Check if this connection is valid. This method should be invoked before
* executing any action in this class.
*
* @throws SQLException if this connection has been closed and cannot be
* used anymore.
*/
protected void checkValidity() throws SQLException {
if (invalid || isClosed()) {
throw new FBSQLException(
"This connection is closed and cannot be used now.",
FBSQLException.SQL_STATE_CONNECTION_CLOSED);
}
}
/**
* This method should be invoked by each of the statements in the
* {@link Statement#close()} method. Here we remove statement from the
* activeStatements
set, so we do not need to close it
* later.
*
* @param stmt statement that was closed.
*/
void notifyStatementClosed(AbstractStatement stmt) {
if (!activeStatements.remove(stmt))
throw new IllegalArgumentException(
"Specified statement was not created by this connection.");
}
/**
* This method closes all active statements and cleans resources.
*
* @throws SQLException if at least one of the active statements failed
* to close gracefully.
*/
private void freeStatements() throws SQLException {
// clone statements to avoid concurrent modification exception
Set statements = (Set)activeStatements.clone();
// iterate through the set, close statements and collect exceptions
Iterator iter = statements.iterator();
SQLExceptionChainBuilder chain = new SQLExceptionChainBuilder();
while(iter.hasNext()) {
try {
AbstractStatement stmt = (AbstractStatement)iter.next();
stmt.close();
} catch(SQLException ex) {
chain.append(ex);
}
}
// throw exception if there is any
if (chain.hasException()) throw chain.getException();
}
/**
* Set the {@link FBManagedConnection} around which this connection is
* based.
* @param mc The FBManagedConnection around which this connection is based
*/
public void setManagedConnection(FBManagedConnection mc) {
//close any prepared statements we may have executed.
if (this.mc != mc && metaData != null) {
metaData.close();
metaData = null;
}
this.mc = mc;
}
public FBManagedConnection getManagedConnection() {
return mc;
}
/**
* Get connection handle for direct Firebird API access
*
* @return internal handle for connection
* @exception GDSException if handle needed to be created and creation failed
*/
public IscDbHandle getIscDBHandle() throws GDSException {
return getGDSHelper().getIscDBHandle();
}
/**
* Get Firebird API handler (sockets/native/embeded/etc)
* @return handler object for internal API calls
*/
public GDS getInternalAPIHandler() throws SQLException {
try {
return getGDSHelper().getInternalAPIHandler();
} catch(GDSException ex) {
throw new FBSQLException(ex);
}
}
/**
* Get database parameter buffer for this connection.
*
* @return instance of {@link DatabaseParameterBuffer}.
*/
public DatabaseParameterBuffer getDatabaseParameterBuffer() {
return mc.getConnectionRequestInfo().getDpb();
}
public void setTransactionParameters(int isolationLevel, int[] parameters)
throws SQLException {
TransactionParameterBuffer tpbParams = createTransactionParameterBuffer();
for (int i = 0; i < parameters.length; i++) {
tpbParams.addArgument(parameters[i]);
}
setTransactionParameters(isolationLevel, tpbParams);
}
public TransactionParameterBuffer getTransactionParameters(int isolationLevel) throws SQLException {
return mc.getTransactionParameters(isolationLevel);
}
public TransactionParameterBuffer createTransactionParameterBuffer() throws SQLException {
return getInternalAPIHandler().newTransactionParameterBuffer();
}
public void setTransactionParameters(int isolationLevel, TransactionParameterBuffer tpb) throws SQLException {
if (mc.isManagedEnvironment())
throw new FBSQLException("Cannot set transaction parameters " +
"in managed environment.");
mc.setTransactionParameters(isolationLevel, tpb);
}
public void setTransactionParameters(TransactionParameterBuffer tpb) throws SQLException {
try {
if (getLocalTransaction().inTransaction())
throw new FBSQLException("Cannot set transaction parameters " +
"when transaction is already started.");
mc.setTransactionParameters(tpb);
} catch(ResourceException ex) {
throw new FBSQLException(ex);
}
}
/**
* Creates a Statement
object for sending
* SQL statements to the database.
* SQL statements without parameters are normally
* executed using Statement objects. If the same SQL statement
* is executed many times, it is more efficient to use a
* PreparedStatement
object.
*
*
* Result sets created using the returned Statement
* object will by default have forward-only type and read-only concurrency.
*
* @return a new Statement object
* @exception SQLException if a database access error occurs
*/
public synchronized Statement createStatement() throws SQLException {
return createStatement(
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY,
resultSetHoldability
);
}
/**
* Creates a PreparedStatement
object for sending
* parameterized SQL statements to the database.
*
* A SQL statement with or without IN parameters can be
* pre-compiled and stored in a PreparedStatement object. This
* object can then be used to efficiently execute this statement
* multiple times.
*
*
Note: This method is optimized for handling
* parametric SQL statements that benefit from precompilation. If
* the driver supports precompilation,
* the method prepareStatement
will send
* the statement to the database for precompilation. Some drivers
* may not support precompilation. In this case, the statement may
* not be sent to the database until the PreparedStatement
is
* executed. This has no direct effect on users; however, it does
* affect which method throws certain SQLExceptions.
*
*
* Result sets created using the returned PreparedStatement will have
* forward-only type and read-only concurrency, by default.
*
* @param sql a SQL statement that may contain one or more '?' IN
* parameter placeholders
* @return a new PreparedStatement object containing the
* pre-compiled statement
* @exception SQLException if a database access error occurs
*/
public synchronized PreparedStatement prepareStatement(String sql)
throws SQLException {
return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}
/**
* Creates a CallableStatement
object for calling
* database stored procedures.
* The CallableStatement
object provides
* methods for setting up its IN and OUT parameters, and
* methods for executing the call to a stored procedure.
*
*
Note: This method is optimized for handling stored
* procedure call statements. Some drivers may send the call
* statement to the database when the method prepareCall
* is done; others
* may wait until the CallableStatement
object
* is executed. This has no
* direct effect on users; however, it does affect which method
* throws certain SQLExceptions.
*
*
* Result sets created using the returned CallableStatement will have
* forward-only type and read-only concurrency, by default.
*
* @param sql a SQL statement that may contain one or more '?'
* parameter placeholders. Typically this statement is a JDBC
* function call escape string.
* @return a new CallableStatement object containing the
* pre-compiled SQL statement
* @exception SQLException if a database access error occurs
*/
public synchronized CallableStatement prepareCall(String sql)
throws SQLException {
return prepareCall(sql,
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}
public synchronized Blob createBlob() throws SQLException {
try {
return new FBBlob(getGDSHelper(), txCoordinator);
} catch(GDSException ex) {
throw new FBSQLException(ex);
}
}
public Clob createClob() throws SQLException {
FBBlob blob = (FBBlob)createBlob();
return new FBClob(blob);
}
public Struct createStruct(String typeName, Object[] attributes)
throws SQLException {
throw new FBDriverNotCapableException();
}
public Array createArrayOf(String typeName, Object[] elements)
throws SQLException {
throw new FBDriverNotCapableException();
}
/**
* Converts the given SQL statement into the system's native SQL grammar.
* A driver may convert the JDBC sql grammar into its system's
* native SQL grammar prior to sending it; this method returns the
* native form of the statement that the driver would have sent.
*
* @param sql a SQL statement that may contain one or more '?'
* parameter placeholders
* @return the native form of this statement
* @exception SQLException if a database access error occurs
*/
public synchronized String nativeSQL(String sql) throws SQLException {
DatabaseParameterBuffer dpb = getDatabaseParameterBuffer();
int mode = FBEscapedParser.USE_BUILT_IN;
if (dpb.hasArgument(DatabaseParameterBufferExtension.USE_STANDARD_UDF))
mode = FBEscapedParser.USE_STANDARD_UDF;
return new FBEscapedParser(mode).parse(sql);
}
/**
* Sets this connection's auto-commit mode.
* If a connection is in auto-commit mode, then all its SQL
* statements will be executed and committed as individual
* transactions. Otherwise, its SQL statements are grouped into
* transactions that are terminated by a call to either
* the method commit
or the method rollback
.
* By default, new connections are in auto-commit
* mode.
*
* The commit occurs when the statement completes or the next
* execute occurs, whichever comes first. In the case of
* statements returning a ResultSet, the statement completes when
* the last row of the ResultSet has been retrieved or the
* ResultSet has been closed. In advanced cases, a single
* statement may return multiple results as well as output
* parameter values. In these cases the commit occurs when all results and
* output parameter values have been retrieved.
*
* @param autoCommit true enables auto-commit; false disables
* auto-commit.
* @exception SQLException if a database access error occurs
*/
public synchronized void setAutoCommit(boolean autoCommit) throws SQLException {
checkValidity();
if (this.autoCommit == autoCommit)
return;
if (autoCommit && mc.inDistributedTransaction()) {
throw new FBSQLException("Connection enlisted in distributed transaction", FBSQLException.SQL_STATE_INVALID_TX_STATE);
}
InternalTransactionCoordinator.AbstractTransactionCoordinator coordinator;
if (autoCommit)
coordinator = new InternalTransactionCoordinator.AutoCommitCoordinator(this, getLocalTransaction());
else
coordinator = new InternalTransactionCoordinator.LocalTransactionCoordinator(this, getLocalTransaction());
txCoordinator.setCoordinator(coordinator);
this.autoCommit = autoCommit;
}
public synchronized void setManagedEnvironment(boolean managedConnection) throws SQLException {
checkValidity();
InternalTransactionCoordinator.AbstractTransactionCoordinator coordinator;
if (managedConnection && mc.inDistributedTransaction()) {
coordinator = new InternalTransactionCoordinator.ManagedTransactionCoordinator(this);
this.autoCommit = false;
} else {
coordinator = new InternalTransactionCoordinator.AutoCommitCoordinator(this, getLocalTransaction());
this.autoCommit = true;
}
txCoordinator.setCoordinator(coordinator);
}
/**
* Gets the current auto-commit state.
*
* @return the current state of auto-commit mode
* @exception SQLException if a database access error occurs
* @see #setAutoCommit
*/
public synchronized boolean getAutoCommit() throws SQLException {
if (isClosed())
throw new FBSQLException("You cannot getAutomcommit on an " +
"unassociated closed connection.");
return this.autoCommit;
}
/**
* Makes all changes made since the previous
* commit/rollback permanent and releases any database locks
* currently held by the Connection. This method should be
* used only when auto-commit mode has been disabled.
*
* @exception SQLException if a database access error occurs
* @see #setAutoCommit
*/
public synchronized void commit() throws SQLException {
if (isClosed()) {
throw new FBSQLException(
"You cannot commit a closed connection.",
FBSQLException.SQL_STATE_CONNECTION_CLOSED);
}
if (mc.inDistributedTransaction()) {
throw new FBSQLException("Connection enlisted in distributed transaction", FBSQLException.SQL_STATE_INVALID_TX_STATE);
}
txCoordinator.commit();
invalidateTransactionLifetimeObjects();
}
/**
* Drops all changes made since the previous
* commit/rollback and releases any database locks currently held
* by this Connection. This method should be used only when auto-
* commit has been disabled.
*
* @exception SQLException if a database access error occurs
* @see #setAutoCommit
*/
public synchronized void rollback() throws SQLException {
if (isClosed()) {
throw new FBSQLException(
"You cannot rollback closed connection.",
FBSQLException.SQL_STATE_CONNECTION_CLOSED);
}
if (mc.inDistributedTransaction()) {
throw new FBSQLException("Connection enlisted in distributed transaction", FBSQLException.SQL_STATE_INVALID_TX_STATE);
}
txCoordinator.rollback();
invalidateTransactionLifetimeObjects();
}
/**
* Invalidate everything that should only last for the lifetime of the current transaction.
*/
protected void invalidateTransactionLifetimeObjects(){
invalidateSavepoints();
storedProcedureMetaData = null;
}
/**
* Releases a Connection's database and JDBC resources
* immediately instead of waiting for
* them to be automatically released.
*
*
Note: A Connection is automatically closed when it is
* garbage collected. Certain fatal errors also result in a closed
* Connection.
*
* @exception SQLException if a database access error occurs
*/
public synchronized void close() throws SQLException {
try {
try {
freeStatements();
} finally {
if (mc != null) {
// if we are in a transaction started
// automatically because autocommit = false, roll it back.
// leave managed transactions alone, they are normally
// committed after the Connection handle is closed.
if (!mc.inDistributedTransaction()
&& !getAutoCommit()
&& getLocalTransaction().inTransaction()) {
try {
txCoordinator.rollback();
} finally {
setAutoCommit(true);
}
}
mc.close(this);
mc = null;
}
}
} catch (ResourceException ex) {
throw new FBSQLException(ex);
}
}
/**
* Tests to see if a Connection is closed.
*
* @return true if the connection is closed; false if it's still open
*/
public boolean isClosed() {
return mc == null;
}
public boolean isValid(int timeout) throws SQLException {
try {
GDS gds = getInternalAPIHandler();
byte[] infoRequest = new byte[] {ISCConstants.isc_info_user_names, ISCConstants.isc_info_end};
gds.iscDatabaseInfo(getIscDBHandle(), infoRequest, 1024);
return true;
} catch(GDSException ex) {
return false;
}
}
//======================================================================
// Advanced features:
/**
* Gets the metadata regarding this connection's database.
* A Connection's database is able to provide information
* describing its tables, its supported SQL grammar, its stored
* procedures, the capabilities of this connection, and so on. This
* information is made available through a DatabaseMetaData
* object.
*
* @return a DatabaseMetaData object for this Connection
* @exception SQLException if a database access error occurs
*/
public synchronized DatabaseMetaData getMetaData() throws SQLException {
try {
if (metaData == null)
metaData = new FBDatabaseMetaData(this);
return metaData;
} catch(GDSException ex) {
throw new FBSQLException(ex);
}
}
/**
* Puts this connection in read-only mode as a hint to enable
* database optimizations.
*
*
Note: This method cannot be called while in the
* middle of a transaction.
*
* @param readOnly true enables read-only mode; false disables
* read-only mode.
* @exception SQLException if a database access error occurs
*/
public synchronized void setReadOnly(boolean readOnly) throws SQLException {
try {
if (getLocalTransaction().inTransaction() && !mc.isManagedEnvironment())
throw new FBSQLException("Calling setReadOnly(boolean) method " +
"is not allowed when transaction is already started.");
mc.setReadOnly(readOnly);
} catch(ResourceException ex) {
throw new FBSQLException(ex);
}
}
/**
* Tests to see if the connection is in read-only mode.
*
* @return true if connection is read-only and false otherwise
* @exception SQLException if a database access error occurs
*/
public boolean isReadOnly() throws SQLException {
return mc.isReadOnly();
}
/**
* Sets a catalog name in order to select
* a subspace of this Connection's database in which to work.
* If the driver does not support catalogs, it will
* silently ignore this request.
*
* @exception SQLException if a database access error occurs
*/
public void setCatalog(String catalog) throws SQLException {
checkValidity();
}
/**
* Returns the Connection's current catalog name.
*
* @return the current catalog name or null
* @exception SQLException if a database access error occurs
*/
public String getCatalog() throws SQLException {
checkValidity();
return null;
}
/**
* Attempts to change the transaction
* isolation level to the one given.
* The constants defined in the interface Connection
* are the possible transaction isolation levels.
*
*
Calling this method will commit any current transaction.
*
* @param level one of the TRANSACTION_* isolation values with the
* exception of TRANSACTION_NONE; some databases may not support
* other values
* @exception SQLException if a database access error occurs
* @see DatabaseMetaData#supportsTransactionIsolationLevel
*/
public synchronized void setTransactionIsolation(int level)
throws SQLException
{
if (isClosed())
throw new FBSQLException(
"Connection has being closed.",
FBSQLException.SQL_STATE_CONNECTION_CLOSED);
try {
if (!getAutoCommit() && !mc.isManagedEnvironment())
txCoordinator.commit();
mc.setTransactionIsolation(level);
} catch (ResourceException re) {
throw new FBSQLException(re);
}
}
/**
* Gets this Connection's current transaction isolation level.
*
* @return the current TRANSACTION_* mode value
* @exception SQLException if a database access error occurs
*/
public synchronized int getTransactionIsolation() throws SQLException {
try
{
return mc.getTransactionIsolation();
}
catch (ResourceException e)
{
throw new FBSQLException(e);
} // end of try-catch
}
/**
* Returns the first warning reported by calls on this Connection.
*
*
Note: Subsequent warnings will be chained to this
* SQLWarning.
*
* @return the first SQLWarning or null
* @exception SQLException if a database access error occurs
*/
public synchronized SQLWarning getWarnings() throws SQLException {
SQLWarning warning = firstWarning;
SQLWarning iscWarning = getIscWarnings();
if (warning == null)
warning = iscWarning;
else
if (iscWarning != null)
warning.setNextWarning(iscWarning);
return warning;
}
/**
* Clears all warnings reported for this Connection
object.
* After a call to this method, the method getWarnings
* returns null until a new warning is
* reported for this Connection.
*
* @exception SQLException if a database access error occurs
*/
public synchronized void clearWarnings() throws SQLException {
firstWarning = null;
clearIscWarnings();
}
/**
*
* Creates a Statement
object that will generate
* ResultSet
objects with the given type and concurrency.
* This method is the same as the createStatement
method
* above, but it allows the default result set
* type and result set concurrency type to be overridden.
*
* @param resultSetType a result set type; see ResultSet.TYPE_XXX
* @param resultSetConcurrency a concurrency type; see ResultSet.CONCUR_XXX
* @return a new Statement object
* @exception SQLException if a database access error occurs
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public synchronized Statement createStatement(int resultSetType,
int resultSetConcurrency) throws SQLException {
return createStatement(resultSetType, resultSetConcurrency, this.resultSetHoldability);
}
/**
* Creates a Statement
object that will generate
* ResultSet
objects with the given type, concurrency,
* and holdability.
* This method is the same as the createStatement
method
* above, but it allows the default result set
* type, concurrency, and holdability to be overridden.
*
* @param resultSetType one of the following ResultSet
* constants:
* ResultSet.TYPE_FORWARD_ONLY
,
* ResultSet.TYPE_SCROLL_INSENSITIVE
, or
* ResultSet.TYPE_SCROLL_SENSITIVE
* @param resultSetConcurrency one of the following ResultSet
* constants:
* ResultSet.CONCUR_READ_ONLY
or
* ResultSet.CONCUR_UPDATABLE
* @param resultSetHoldability one of the following ResultSet
* constants:
* ResultSet.HOLD_CURSORS_OVER_COMMIT
or
* ResultSet.CLOSE_CURSORS_AT_COMMIT
* @return a new Statement
object that will generate
* ResultSet
objects with the given type,
* concurrency, and holdability
* @exception SQLException if a database access error occurs
* or the given parameters are not ResultSet
* constants indicating type, concurrency, and holdability
* @see ResultSet
* @since 1.4
*/
public synchronized Statement createStatement(int resultSetType,
int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
if (resultSetHoldability == ResultSet.HOLD_CURSORS_OVER_COMMIT &&
resultSetType == ResultSet.TYPE_FORWARD_ONLY) {
addWarning(new FBSQLWarning("Holdable result set must be scrollable."));
resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
}
if (resultSetType == ResultSet.TYPE_SCROLL_SENSITIVE) {
addWarning(new FBSQLWarning("Unsupported type and/or concurrency"));
resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
}
checkHoldability(resultSetType, resultSetHoldability);
try {
Statement stmt = FBStatementFactory.createStatement(getGDSHelper(), resultSetType,
resultSetConcurrency, resultSetHoldability, txCoordinator);
activeStatements.add(stmt);
return stmt;
} catch(GDSException ex) {
throw new FBSQLException(ex);
}
}
/**
* Check whether result set type and holdability are compatible.
*
* @param resultSetType desired result set type.
* @param resultSetHoldability desired result set holdability.
*
* @throws SQLException if specified result set type and holdability are
* not compatibe.
*/
private void checkHoldability(int resultSetType, int resultSetHoldability) throws SQLException {
boolean holdable =
resultSetHoldability == ResultSet.HOLD_CURSORS_OVER_COMMIT;
boolean notScrollable = resultSetType != ResultSet.TYPE_SCROLL_INSENSITIVE;
if (holdable && notScrollable)
throw new FBDriverNotCapableException(
"Holdable cursors are supported only " +
"for scrollable insensitive result sets.");
}
/**
*
* Creates a PreparedStatement
object that will generate
* ResultSet
objects with the given type and concurrency.
* This method is the same as the prepareStatement
method
* above, but it allows the default result set
* type and result set concurrency type to be overridden.
*
* @param resultSetType a result set type; see ResultSet.TYPE_XXX
* @param resultSetConcurrency a concurrency type; see ResultSet.CONCUR_XXX
* @return a new PreparedStatement object containing the
* pre-compiled SQL statement
* @exception SQLException if a database access error occurs
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public synchronized PreparedStatement prepareStatement(String sql,
int resultSetType, int resultSetConcurrency) throws SQLException {
return prepareStatement(sql, resultSetType, resultSetConcurrency, this.resultSetHoldability);
}
/**
* Creates a PreparedStatement
object that will generate
* ResultSet
objects with the given type, concurrency,
* and holdability.
*
* This method is the same as the prepareStatement
method
* above, but it allows the default result set
* type, concurrency, and holdability to be overridden.
*
* @param sql a String
object that is the SQL statement to
* be sent to the database; may contain one or more '?' IN
* parameters
* @param resultSetType one of the following ResultSet
* constants:
* ResultSet.TYPE_FORWARD_ONLY
,
* ResultSet.TYPE_SCROLL_INSENSITIVE
, or
* ResultSet.TYPE_SCROLL_SENSITIVE
* @param resultSetConcurrency one of the following ResultSet
* constants:
* ResultSet.CONCUR_READ_ONLY
or
* ResultSet.CONCUR_UPDATABLE
* @param resultSetHoldability one of the following ResultSet
* constants:
* ResultSet.HOLD_CURSORS_OVER_COMMIT
or
* ResultSet.CLOSE_CURSORS_AT_COMMIT
* @return a new PreparedStatement
object, containing the
* pre-compiled SQL statement, that will generate
* ResultSet
objects with the given type,
* concurrency, and holdability
* @exception SQLException if a database access error occurs, this
* method is called on a closed connection
* or the given parameters are not ResultSet
* constants indicating type, concurrency, and holdability
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method or this method is not supported for the specified result
* set type, result set holdability and result set concurrency.
* @see ResultSet
* @since 1.4
*/
public synchronized PreparedStatement prepareStatement(String sql,
int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
return prepareStatement(sql, resultSetType, resultSetConcurrency,
resultSetHoldability, false, false);
}
protected synchronized PreparedStatement prepareMetaDataStatement(String sql,
int resultSetType, int resultSetConcurrency) throws SQLException {
return prepareStatement(sql, resultSetType, resultSetConcurrency,
resultSetHoldability, true, false);
}
/**
* Creates a default PreparedStatement
object that has
* the capability to retrieve auto-generated keys. The given constant
* tells the driver whether it should make auto-generated keys
* available for retrieval. This parameter is ignored if the SQL statement
* is not an INSERT
statement, or an SQL statement able to return
* auto-generated keys (the list of such statements is vendor-specific).
*
* Note: This method is optimized for handling
* parametric SQL statements that benefit from precompilation. If
* the driver supports precompilation,
* the method prepareStatement
will send
* the statement to the database for precompilation. Some drivers
* may not support precompilation. In this case, the statement may
* not be sent to the database until the PreparedStatement
* object is executed. This has no direct effect on users; however, it does
* affect which methods throw certain SQLExceptions.
*
* Result sets created using the returned PreparedStatement
* object will by default be type TYPE_FORWARD_ONLY
* and have a concurrency level of CONCUR_READ_ONLY
.
* The holdability of the created result sets can be determined by
* calling {@link #getHoldability}.
*
* @param sql an SQL statement that may contain one or more '?' IN
* parameter placeholders
* @param autoGeneratedKeys a flag indicating whether auto-generated keys
* should be returned; one of
* Statement.RETURN_GENERATED_KEYS
or
* Statement.NO_GENERATED_KEYS
* @return a new PreparedStatement
object, containing the
* pre-compiled SQL statement, that will have the capability of
* returning auto-generated keys
* @exception SQLException if a database access error occurs, this
* method is called on a closed connection
* or the given parameter is not a Statement
* constant indicating whether auto-generated keys should be
* returned
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method with a constant of Statement.RETURN_GENERATED_KEYS
* @since 1.4
*/
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
throws SQLException {
checkValidity();
if (autoGeneratedKeys == Statement.RETURN_GENERATED_KEYS) {
checkAutoGeneratedKeysSupport();
}
GeneratedKeysQuery query = new GeneratedKeysQuery(sql, autoGeneratedKeys);
return prepareStatement(query);
}
/**
* Creates a default PreparedStatement
object capable
* of returning the auto-generated keys designated by the given array.
* This array contains the indexes of the columns in the target
* table that contain the auto-generated keys that should be made
* available. The driver will ignore the array if the SQL statement
* is not an INSERT
statement, or an SQL statement able to return
* auto-generated keys (the list of such statements is vendor-specific).
*
* An SQL statement with or without IN parameters can be
* pre-compiled and stored in a PreparedStatement
object. This
* object can then be used to efficiently execute this statement
* multiple times.
*
* Note: This method is optimized for handling
* parametric SQL statements that benefit from precompilation. If
* the driver supports precompilation,
* the method prepareStatement
will send
* the statement to the database for precompilation. Some drivers
* may not support precompilation. In this case, the statement may
* not be sent to the database until the PreparedStatement
* object is executed. This has no direct effect on users; however, it does
* affect which methods throw certain SQLExceptions.
*
* Result sets created using the returned PreparedStatement
* object will by default be type TYPE_FORWARD_ONLY
* and have a concurrency level of CONCUR_READ_ONLY
.
* The holdability of the created result sets can be determined by
* calling {@link #getHoldability}.
*
* @param sql an SQL statement that may contain one or more '?' IN
* parameter placeholders
* @param columnIndexes an array of column indexes indicating the columns
* that should be returned from the inserted row or rows
* @return a new PreparedStatement
object, containing the
* pre-compiled statement, that is capable of returning the
* auto-generated keys designated by the given array of column
* indexes
* @exception SQLException if a database access error occurs
* or this method is called on a closed connection
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
*
* @since 1.4
*/
public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
throws SQLException {
checkValidity();
checkAutoGeneratedKeysSupport();
GeneratedKeysQuery query = new GeneratedKeysQuery(sql, columnIndexes);
return prepareStatement(query);
}
/**
* Creates a default PreparedStatement
object capable
* of returning the auto-generated keys designated by the given array.
* This array contains the names of the columns in the target
* table that contain the auto-generated keys that should be returned.
* The driver will ignore the array if the SQL statement
* is not an INSERT
statement, or an SQL statement able to return
* auto-generated keys (the list of such statements is vendor-specific).
*
* An SQL statement with or without IN parameters can be
* pre-compiled and stored in a PreparedStatement
object. This
* object can then be used to efficiently execute this statement
* multiple times.
*
* Note: This method is optimized for handling
* parametric SQL statements that benefit from precompilation. If
* the driver supports precompilation,
* the method prepareStatement
will send
* the statement to the database for precompilation. Some drivers
* may not support precompilation. In this case, the statement may
* not be sent to the database until the PreparedStatement
* object is executed. This has no direct effect on users; however, it does
* affect which methods throw certain SQLExceptions.
*
* Result sets created using the returned PreparedStatement
* object will by default be type TYPE_FORWARD_ONLY
* and have a concurrency level of CONCUR_READ_ONLY
.
* The holdability of the created result sets can be determined by
* calling {@link #getHoldability}.
*
* @param sql an SQL statement that may contain one or more '?' IN
* parameter placeholders
* @param columnNames an array of column names indicating the columns
* that should be returned from the inserted row or rows
* @return a new PreparedStatement
object, containing the
* pre-compiled statement, that is capable of returning the
* auto-generated keys designated by the given array of column
* names
* @exception SQLException if a database access error occurs
* or this method is called on a closed connection
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
*
* @since 1.4
*/
public PreparedStatement prepareStatement(String sql, String[] columnNames)
throws SQLException {
checkValidity();
checkAutoGeneratedKeysSupport();
GeneratedKeysQuery query = new GeneratedKeysQuery(sql, columnNames);
return prepareStatement(query);
}
/**
* Prepares a statement for generated keys.
*
* @param query AbstractGeneratedKeysQuery instance
* @return PreparedStatement object
* @throws SQLException if a database access error occurs
* or this method is called on a closed connection
*/
private PreparedStatement prepareStatement(AbstractGeneratedKeysQuery query) throws SQLException {
if (query.generatesKeys()) {
return prepareStatement(query.getQueryString(),
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY,
ResultSet.CLOSE_CURSORS_AT_COMMIT,
false, true);
} else {
return prepareStatement(query.getQueryString());
}
}
/**
* Helper method to check support of autoGeneratedKeys
*
* @throws SQLException If the feature is not supported, or if an error occurred retrieving the server version.
*/
protected void checkAutoGeneratedKeysSupport() throws SQLException {
GDSHelper gdsHelper;
try {
gdsHelper = getGDSHelper();
} catch (GDSException ex) {
throw new FBSQLException(ex);
}
if (gdsHelper.compareToVersion(2, 0) < 0) {
throw new FBDriverNotCapableException("This version of Firebird does not support retrieving generated keys (support was added in Firebird 2.0)");
}
}
protected synchronized PreparedStatement prepareStatement(String sql,
int resultSetType, int resultSetConcurrency, int resultSetHoldability,
boolean metaData, boolean generatedKeys) throws SQLException
{
PreparedStatement stmt;
if (resultSetHoldability == ResultSet.HOLD_CURSORS_OVER_COMMIT &&
resultSetType == ResultSet.TYPE_FORWARD_ONLY) {
addWarning(new FBSQLWarning("Holdable result set must be scrollable."));
resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
}
if (resultSetType == ResultSet.TYPE_SCROLL_SENSITIVE)
{
addWarning(new FBSQLWarning("resultSetType or resultSetConcurrency changed"));
if (resultSetType == ResultSet.TYPE_SCROLL_SENSITIVE)
resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
}
checkHoldability(resultSetType, resultSetHoldability);
try {
FBObjectListener.StatementListener coordinator = txCoordinator;
if (metaData)
coordinator = new InternalTransactionCoordinator.MetaDataTransactionCoordinator(txCoordinator);
FBObjectListener.BlobListener blobCoordinator;
if (metaData)
blobCoordinator = null;
else
blobCoordinator = txCoordinator;
stmt = FBStatementFactory.createPreparedStatement(
getGDSHelper(), sql, resultSetType, resultSetConcurrency,
resultSetHoldability, coordinator, blobCoordinator,
metaData, false, generatedKeys);
activeStatements.add(stmt);
return stmt;
} catch(GDSException ex) {
throw new FBSQLException(ex);
}
}
/**
*
* Creates a CallableStatement
object that will generate
* ResultSet
objects with the given type and concurrency.
* This method is the same as the prepareCall
method
* above, but it allows the default result set
* type and result set concurrency type to be overridden.
*
* @param resultSetType a result set type; see ResultSet.TYPE_XXX
* @param resultSetConcurrency a concurrency type; see ResultSet.CONCUR_XXX
* @return a new CallableStatement object containing the
* pre-compiled SQL statement
* @exception SQLException if a database access error occurs
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public synchronized CallableStatement prepareCall(String sql,
int resultSetType, int resultSetConcurrency) throws SQLException {
return prepareCall(sql, resultSetType, resultSetConcurrency, this.resultSetHoldability);
}
public synchronized CallableStatement prepareCall(String sql,
int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
AbstractCallableStatement stmt;
if (resultSetHoldability == ResultSet.HOLD_CURSORS_OVER_COMMIT &&
resultSetType == ResultSet.TYPE_FORWARD_ONLY) {
addWarning(new FBSQLWarning("Holdable result set must be scrollable."));
resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
}
if (resultSetType == ResultSet.TYPE_SCROLL_SENSITIVE)
{
addWarning(new FBSQLWarning("Scroll-sensitive result sets are not supported."));
resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
}
if (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) {
addWarning(new FBSQLWarning(
"Updatable result sets from stored procedures are not supported."));
resultSetConcurrency = ResultSet.CONCUR_READ_ONLY;
}
checkHoldability(resultSetType, resultSetHoldability);
if (storedProcedureMetaData == null){
storedProcedureMetaData = StoredProcedureMetaDataFactory.getInstance(this);
}
try {
stmt = FBStatementFactory.createCallableStatement(getGDSHelper(), sql, resultSetType,
resultSetConcurrency, resultSetHoldability, storedProcedureMetaData, txCoordinator, txCoordinator);
activeStatements.add(stmt);
return stmt;
} catch(GDSException ex) {
throw new FBSQLException(ex);
}
}
/**
*
* Gets the type map object associated with this connection.
* Unless the application has added an entry to the type map,
* the map returned will be empty.
*
* @return the java.util.Map
object associated
* with this Connection
object
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public Map getTypeMap() throws SQLException {
return new HashMap();
}
/**
* Installs the given type map as the type map for
* this connection. The type map will be used for the
* custom mapping of SQL structured types and distinct types.
*
* @param map the java.util.Map
object to install
* as the replacement for this Connection
* object's default type map
* @since 1.2
* @see What Is in the JDBC 2.0 API
*/
public void setTypeMap(Map map) throws SQLException {
throw new FBDriverNotCapableException();
}
/*
* Savepoint stuff.
*/
public Savepoint setSavepoint() throws SQLException {
return (Savepoint)setFirebirdSavepoint();
}
public Savepoint setSavepoint(String name) throws SQLException {
return (Savepoint)setFirebirdSavepoint(name);
}
@SuppressWarnings("deprecation")
public void rollback(Savepoint savepoint) throws SQLException {
rollback((FirebirdSavepoint)savepoint);
}
@SuppressWarnings("deprecation")
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
releaseSavepoint((FirebirdSavepoint)savepoint);
}
private int savepointCounter = 0;
private List savepoints = new LinkedList();
private synchronized int getNextSavepointCounter() {
return savepointCounter++;
}
/**
* Creates an unnamed savepoint in the current transaction and
* returns the new Savepoint
object that represents it.
*
* @return the new Savepoint
object
* @exception SQLException if a database access error occurs
* or this Connection
object is currently in
* auto-commit mode
* @see Savepoint
* @deprecated This method will be removed in Jaybird 2.3, use {@link java.sql.Connection#setSavepoint()}
*/
@Deprecated
public synchronized FirebirdSavepoint setFirebirdSavepoint() throws SQLException {
FBSavepoint savepoint = new FBSavepoint(getNextSavepointCounter());
setSavepoint(savepoint);
return savepoint;
}
/**
* Set the savepoint on the server.
*
* @param savepoint savepoint to set.
*
* @throws SQLException if something went wrong.
*/
private void setSavepoint(FBSavepoint savepoint) throws SQLException {
if (getAutoCommit()) {
throw new SQLException("Connection.setSavepoint() method cannot " +
"be used in auto-commit mode.", FBSQLException.SQL_STATE_INVALID_TX_STATE);
}
if (mc.inDistributedTransaction()) {
throw new FBSQLException("Connection enlisted in distributed transaction", FBSQLException.SQL_STATE_INVALID_TX_STATE);
}
try {
txCoordinator.ensureTransaction();
getGDSHelper().executeImmediate("SAVEPOINT " + savepoint.getServerSavepointId());
savepoints.add(savepoint);
} catch(GDSException ex) {
throw new FBSQLException(ex);
}
}
/**
* Creates a savepoint with the given name in the current transaction
* and returns the new Savepoint
object that represents it.
*
* @param name a String
containing the name of the savepoint
* @return the new Savepoint
object
* @exception SQLException if a database access error occurs
* or this Connection
object is currently in
* auto-commit mode
* @see Savepoint
* @deprecated This method will be removed in Jaybird 2.3, use {@link java.sql.Connection#setSavepoint()}
*/
@Deprecated
public synchronized FirebirdSavepoint setFirebirdSavepoint(String name) throws SQLException {
FBSavepoint savepoint = new FBSavepoint(name);
setSavepoint(savepoint);
return savepoint;
}
/**
* Undoes all changes made after the given Savepoint
object
* was set.
*
* This method should be used only when auto-commit has been disabled.
*
* @param savepoint the Savepoint
object to roll back to
* @exception SQLException if a database access error occurs,
* the Savepoint
object is no longer valid,
* or this Connection
object is currently in
* auto-commit mode
* @see Savepoint
* @see #rollback
* @deprecated This method will be removed in Jaybird 2.3, use {@link java.sql.Connection#rollback(Savepoint)}
*/
@Deprecated
public synchronized void rollback(FirebirdSavepoint savepoint) throws SQLException {
if (getAutoCommit())
throw new SQLException("Connection.setSavepoint() method cannot " +
"be used in auto-commit mode.");
if (!(savepoint instanceof FBSavepoint))
throw new SQLException(
"Specified savepoint was not obtained from this connection.");
if (mc.inDistributedTransaction()) {
throw new FBSQLException("Connection enlisted in distributed transaction", FBSQLException.SQL_STATE_INVALID_TX_STATE);
}
FBSavepoint fbSavepoint = (FBSavepoint)savepoint;
if (!fbSavepoint.isValid())
throw new SQLException("Savepoint is no longer valid.");
try {
getGDSHelper().executeImmediate(
"ROLLBACK TO " + fbSavepoint.getServerSavepointId());
} catch (GDSException ex) {
throw new FBSQLException(ex);
}
}
/**
* Removes the given Savepoint
object from the current
* transaction. Any reference to the savepoint after it have been removed
* will cause an SQLException
to be thrown.
*
* @param savepoint the Savepoint
object to be removed
* @exception SQLException if a database access error occurs or
* the given Savepoint
object is not a valid
* savepoint in the current transaction
* @deprecated This method will be removed in Jaybird 2.3, use {@link java.sql.Connection#releaseSavepoint(Savepoint)}
*/
@Deprecated
public synchronized void releaseSavepoint(FirebirdSavepoint savepoint) throws SQLException {
if (getAutoCommit())
throw new SQLException("Connection.setSavepoint() method cannot " +
"be used in auto-commit mode.");
if (!(savepoint instanceof FBSavepoint))
throw new SQLException(
"Specified savepoint was not obtained from this connection.");
FBSavepoint fbSavepoint = (FBSavepoint)savepoint;
if (!fbSavepoint.isValid())
throw new SQLException("Savepoint is no longer valid.");
try {
getGDSHelper().executeImmediate(
"RELEASE SAVEPOINT " + fbSavepoint.getServerSavepointId() + " ONLY");
} catch (GDSException ex) {
throw new FBSQLException(ex);
}
fbSavepoint.invalidate();
savepoints.remove(fbSavepoint);
}
/**
* Invalidate all savepoints.
*/
protected synchronized void invalidateSavepoints() {
Iterator iter = savepoints.iterator();
while(iter.hasNext())
((FBSavepoint)iter.next()).invalidate();
savepoints.clear();
}
//-------------------------------------------
//Borrowed from javax.resource.cci.Connection
/**
* Returns a FBLocalTransaction instance that enables a component to
* demarcate resource manager local transactions on this connection.
*/
public synchronized FirebirdLocalTransaction getLocalTransaction() {
if (localTransaction == null)
localTransaction = new FBLocalTransaction(mc, this);
return localTransaction;
}
// java.sql.Wrapper interface
public boolean isWrapperFor(Class> iface) throws SQLException {
return iface != null && iface.isAssignableFrom(FBConnection.class);
}
public T unwrap(Class iface) throws SQLException {
if (!isWrapperFor(iface))
throw new FBDriverNotCapableException();
return iface.cast(this);
}
public void setSchema(String schema) throws SQLException {
// Ignore: no schema support
checkValidity();
}
public String getSchema() throws SQLException {
checkValidity();
return null;
}
//package methods
/**
* Check if this connection is currently involved in a transaction
*/
public boolean inTransaction() throws SQLException {
try {
return getGDSHelper().inTransaction();
} catch(GDSException ex) {
throw new FBSQLException(ex);
}
}
/**
* Get the encoding that is being used for this connection.
*
* @return The name of the encoding used
*/
public String getIscEncoding() throws SQLException {
try {
return getGDSHelper().getIscEncoding();
} catch(GDSException ex) {
throw new FBSQLException(ex);
}
}
protected synchronized void addWarning(SQLWarning warning){
if (firstWarning == null)
firstWarning = warning;
else{
SQLWarning lastWarning = firstWarning;
while (lastWarning.getNextWarning() != null){
lastWarning = lastWarning.getNextWarning();
}
lastWarning.setNextWarning(warning);
}
}
/**
* Get warnings associated with this database connection.
*
* @return instance of {@link SQLWarning} that is the first warning in
* a linked list of warnings.
*/
private SQLWarning getIscWarnings() throws SQLException {
try {
SQLWarning firstWarning = null;
SQLWarning lastWarning = null;
Iterator iter = getGDSHelper().getWarnings().iterator();
while (iter.hasNext()) {
GDSException item = (GDSException)iter.next();
FBSQLWarning warning = new FBSQLWarning(item);
if (firstWarning == null) {
firstWarning = warning;
lastWarning = firstWarning;
} else {
lastWarning.setNextWarning(warning);
lastWarning = warning;
}
}
return firstWarning;
} catch(GDSException ex) {
throw new FBSQLException(ex);
}
}
/**
* Clear warnings associated with this database connection.
*/
private void clearIscWarnings() throws SQLException {
try {
getGDSHelper().clearWarnings();
} catch(GDSException ex) {
throw new FBSQLException(ex);
}
}
public GDSHelper getGDSHelper() throws GDSException {
if (mc == null)
throw new GDSException(ISCConstants.isc_arg_gds, ISCConstants.isc_req_no_trans);
return mc.getGDSHelper();
}
protected void finalize() throws Throwable {
close();
}
protected class GeneratedKeysQuery extends AbstractGeneratedKeysQuery {
protected GeneratedKeysQuery(String sql, int autoGeneratedKeys) throws SQLException {
super(sql, autoGeneratedKeys);
}
protected GeneratedKeysQuery(String sql, int[] columnIndexes) throws SQLException {
super(sql, columnIndexes);
}
protected GeneratedKeysQuery(String sql, String[] columnNames) throws SQLException {
super(sql, columnNames);
}
@Override
DatabaseMetaData getDatabaseMetaData() throws SQLException {
return getMetaData();
}
}
}