com.mysql.cj.jdbc.ConnectionImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mysql-connector-j Show documentation
Show all versions of mysql-connector-j Show documentation
JDBC Type 4 driver for MySQL.
The newest version!
/*
* Copyright (c) 2002, 2024, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by
* the Free Software Foundation.
*
* This program is designed to work with certain software that is licensed under separate terms, as designated in a particular file or component or in
* included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the
* separately licensed software that they have either included with the program or referenced in the documentation.
*
* Without limiting anything contained in the foregoing, this file, which is part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
* version 1.0, a copy of which can be found at http://oss.oracle.com/licenses/universal-foss-exception.
*
* 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 GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.mysql.cj.jdbc;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.NClob;
import java.sql.ResultSet;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLPermission;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Struct;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Stack;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import com.mysql.cj.CacheAdapter;
import com.mysql.cj.CacheAdapterFactory;
import com.mysql.cj.LicenseConfiguration;
import com.mysql.cj.Messages;
import com.mysql.cj.NativeSession;
import com.mysql.cj.NoSubInterceptorWrapper;
import com.mysql.cj.PreparedQuery;
import com.mysql.cj.QueryInfo;
import com.mysql.cj.ServerVersion;
import com.mysql.cj.Session.SessionEventListener;
import com.mysql.cj.conf.HostInfo;
import com.mysql.cj.conf.PropertyDefinitions.DatabaseTerm;
import com.mysql.cj.conf.PropertyKey;
import com.mysql.cj.conf.RuntimeProperty;
import com.mysql.cj.exceptions.CJCommunicationsException;
import com.mysql.cj.exceptions.CJException;
import com.mysql.cj.exceptions.ExceptionFactory;
import com.mysql.cj.exceptions.ExceptionInterceptor;
import com.mysql.cj.exceptions.ExceptionInterceptorChain;
import com.mysql.cj.exceptions.MysqlErrorNumbers;
import com.mysql.cj.exceptions.PasswordExpiredException;
import com.mysql.cj.exceptions.UnableToConnectException;
import com.mysql.cj.interceptors.QueryInterceptor;
import com.mysql.cj.jdbc.exceptions.SQLError;
import com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping;
import com.mysql.cj.jdbc.ha.MultiHostConnectionProxy;
import com.mysql.cj.jdbc.ha.MultiHostMySQLConnection;
import com.mysql.cj.jdbc.interceptors.ConnectionLifecycleInterceptor;
import com.mysql.cj.jdbc.result.CachedResultSetMetaData;
import com.mysql.cj.jdbc.result.CachedResultSetMetaDataImpl;
import com.mysql.cj.jdbc.result.ResultSetFactory;
import com.mysql.cj.jdbc.result.ResultSetInternalMethods;
import com.mysql.cj.jdbc.result.UpdatableResultSet;
import com.mysql.cj.log.ProfilerEvent;
import com.mysql.cj.protocol.ServerSessionStateController;
import com.mysql.cj.protocol.SocksProxySocketFactory;
import com.mysql.cj.protocol.a.NativeProtocol;
import com.mysql.cj.telemetry.TelemetryAttribute;
import com.mysql.cj.telemetry.TelemetryScope;
import com.mysql.cj.telemetry.TelemetrySpan;
import com.mysql.cj.telemetry.TelemetrySpanName;
import com.mysql.cj.util.LRUCache;
import com.mysql.cj.util.StringUtils;
import com.mysql.cj.util.Util;
/**
* A Connection represents a session with a specific database. Within the context of a Connection, SQL statements are executed and results are returned.
*
*
* A Connection's database is able to provide information describing its tables, its supported SQL grammar, its stored procedures, the capabilities of this
* connection, etc. This information is obtained with the getMetaData method.
*
*/
public class ConnectionImpl implements JdbcConnection, SessionEventListener, Serializable {
private static final long serialVersionUID = 4009476458425101761L;
private static final SQLPermission SET_NETWORK_TIMEOUT_PERM = new SQLPermission("setNetworkTimeout");
private static final SQLPermission ABORT_PERM = new SQLPermission("abort");
@Override
public String getHost() {
return this.session.getHostInfo().getHost();
}
private JdbcConnection parentProxy = null;
private JdbcConnection topProxy = null;
private MultiHostConnectionProxy realProxy = null;
private final Lock lock = new ReentrantLock();
@Override
public Lock getLock() {
return this.lock;
}
@Override
public boolean isProxySet() {
return this.topProxy != null;
}
@Override
public void setProxy(JdbcConnection proxy) {
if (this.parentProxy == null) { // Only set this once.
this.parentProxy = proxy;
}
this.topProxy = proxy;
this.realProxy = this.topProxy instanceof MultiHostMySQLConnection ? ((MultiHostMySQLConnection) proxy).getThisAsProxy() : null;
}
// this connection has to be proxied when using multi-host settings so that statements get routed to the right physical connection
// (works as "logical" connection)
private JdbcConnection getProxy() {
return this.topProxy != null ? this.topProxy : this;
}
@Override
public JdbcConnection getMultiHostSafeProxy() {
return getProxy();
}
@Override
public JdbcConnection getMultiHostParentProxy() {
return this.parentProxy;
}
@Override
public JdbcConnection getActiveMySQLConnection() {
return this;
}
@Override
public Lock getConnectionLock() {
return this.realProxy != null ? this.realProxy.getLock() : getProxy().getLock();
}
/**
* Used as a key for caching callable/prepared statements which (may) depend on current database.
*/
static class CompoundCacheKey {
final String componentOne;
final String componentTwo;
final int hashCode;
CompoundCacheKey(String partOne, String partTwo) {
this.componentOne = partOne;
this.componentTwo = partTwo;
int hc = 17;
hc = 31 * hc + (this.componentOne != null ? this.componentOne.hashCode() : 0);
hc = 31 * hc + (this.componentTwo != null ? this.componentTwo.hashCode() : 0);
this.hashCode = hc;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && CompoundCacheKey.class.isAssignableFrom(obj.getClass())) {
CompoundCacheKey another = (CompoundCacheKey) obj;
if (this.componentOne == null ? another.componentOne == null : this.componentOne.equals(another.componentOne)) {
return this.componentTwo == null ? another.componentTwo == null : this.componentTwo.equals(another.componentTwo);
}
}
return false;
}
@Override
public int hashCode() {
return this.hashCode;
}
}
/**
* Map mysql transaction isolation level name to java.sql.Connection.TRANSACTION_XXX.
*/
private static Map mapTransIsolationNameToValue = null;
static {
mapTransIsolationNameToValue = new HashMap<>(8);
mapTransIsolationNameToValue.put("READ-UNCOMMITED", TRANSACTION_READ_UNCOMMITTED);
mapTransIsolationNameToValue.put("READ-UNCOMMITTED", TRANSACTION_READ_UNCOMMITTED);
mapTransIsolationNameToValue.put("READ-COMMITTED", TRANSACTION_READ_COMMITTED);
mapTransIsolationNameToValue.put("REPEATABLE-READ", TRANSACTION_REPEATABLE_READ);
mapTransIsolationNameToValue.put("SERIALIZABLE", TRANSACTION_SERIALIZABLE);
}
protected static Map, ?> roundRobinStatsMap;
private List connectionLifecycleInterceptors;
private static final int DEFAULT_RESULT_SET_TYPE = ResultSet.TYPE_FORWARD_ONLY;
private static final int DEFAULT_RESULT_SET_CONCURRENCY = ResultSet.CONCUR_READ_ONLY;
/**
* Creates a connection instance.
*
* @param hostInfo
* {@link HostInfo} instance
* @return new {@link ConnectionImpl} instance
* @throws SQLException
* if a database access error occurs
*/
public static JdbcConnection getInstance(HostInfo hostInfo) throws SQLException {
return new ConnectionImpl(hostInfo);
}
/** A cache of SQL to parsed prepared statement parameters. */
private CacheAdapter queryInfoCache;
/** The database we're currently using. */
private String database = null;
/** Internal DBMD to use for various database-version specific features */
private DatabaseMetaData dbmd = null;
private NativeSession session = null;
/** Is this connection associated with a global tx? */
private boolean isInGlobalTx = false;
/** isolation level */
private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED;
/**
* An array of currently open statements.
* Copy-on-write used here to avoid ConcurrentModificationException when statements unregister themselves while we iterate over the list.
*/
private final CopyOnWriteArrayList openStatements = new CopyOnWriteArrayList<>();
private LRUCache parsedCallableStatementCache;
private final Lock parsedCallableStatementCacheLock = new ReentrantLock();
/** The password we used */
private String password = null;
/** Properties for this connection specified by user */
protected Properties props = null;
/** Are we in read-only mode? */
private boolean readOnly = false;
/** Cache of ResultSet metadata */
protected LRUCache resultSetMetadataCache;
protected final Lock resultSetMetadataCacheLock = new ReentrantLock();
/**
* The type map for UDTs (not implemented, but used by some third-party
* vendors, most notably IBM WebSphere)
*/
private Map> typeMap;
/** The user we're connected as */
private String user = null;
private LRUCache serverSideStatementCheckCache;
private final Lock serverSideStatementCheckCacheLock = new ReentrantLock();
private LRUCache serverSideStatementCache;
private HostInfo origHostInfo;
private String origHostToConnectTo;
// we don't want to be able to publicly clone this...
private int origPortToConnectTo;
private List queryInterceptors;
protected JdbcPropertySet propertySet;
private RuntimeProperty autoReconnectForPools;
private RuntimeProperty cachePrepStmts;
private RuntimeProperty autoReconnect;
private RuntimeProperty useUsageAdvisor;
private RuntimeProperty reconnectAtTxEnd;
private RuntimeProperty emulateUnsupportedPstmts;
private RuntimeProperty ignoreNonTxTables;
private RuntimeProperty pedantic;
private RuntimeProperty prepStmtCacheSqlLimit;
private RuntimeProperty useLocalSessionState;
private RuntimeProperty useServerPrepStmts;
private RuntimeProperty processEscapeCodesForPrepStmts;
private RuntimeProperty useLocalTransactionState;
private RuntimeProperty disconnectOnExpiredPasswords;
private RuntimeProperty readOnlyPropagatesToServer;
protected ResultSetFactory nullStatementResultSetFactory;
TelemetrySpan connectionSpan = null;
/**
* '
* For the delegate only
*/
protected ConnectionImpl() {
}
/**
* Creates a connection to a MySQL Server.
*
* @param hostInfo
* the {@link HostInfo} instance that contains the host, user and connections attributes for this connection
* @exception SQLException
* if a database access error occurs
*/
public ConnectionImpl(HostInfo hostInfo) throws SQLException {
try {
// Stash away for later, used to clone this connection for Statement.cancel and Statement.setQueryTimeout().
this.origHostInfo = hostInfo;
this.origHostToConnectTo = hostInfo.getHost();
this.origPortToConnectTo = hostInfo.getPort();
this.database = hostInfo.getDatabase();
this.user = hostInfo.getUser();
this.password = hostInfo.getPassword();
this.props = hostInfo.exposeAsProperties();
this.propertySet = new JdbcPropertySetImpl();
this.propertySet.initializeProperties(this.props);
// We need Session ASAP to get access to central driver functionality
this.nullStatementResultSetFactory = new ResultSetFactory(this, null);
this.session = new NativeSession(hostInfo, this.propertySet);
this.session.addListener(this); // listen for session status changes
} catch (CJException e) {
throw SQLExceptionsMapping.translateException(e, getExceptionInterceptor());
}
this.connectionSpan = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.CONNECTION_CREATE);
this.session.getTelemetryHandler().addLinkTarget(this.connectionSpan);
try (TelemetryScope scope = this.connectionSpan.makeCurrent()) {
this.connectionSpan.setAttribute(TelemetryAttribute.DB_CONNECTION_STRING, getURL());
this.connectionSpan.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
this.connectionSpan.setAttribute(TelemetryAttribute.DB_USER, getUser());
this.connectionSpan.setAttribute(TelemetryAttribute.SERVER_ADDRESS, this.origHostToConnectTo);
this.connectionSpan.setAttribute(TelemetryAttribute.SERVER_PORT, this.origPortToConnectTo);
this.connectionSpan.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
this.connectionSpan.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
try {
String exceptionInterceptorClasses = this.propertySet.getStringProperty(PropertyKey.exceptionInterceptors).getStringValue();
if (exceptionInterceptorClasses != null && !"".equals(exceptionInterceptorClasses)) {
this.exceptionInterceptor = new ExceptionInterceptorChain(exceptionInterceptorClasses, this.props, this.session.getLog());
}
// we can't cache fixed values here because properties are still not initialized with user provided values
this.autoReconnectForPools = this.propertySet.getBooleanProperty(PropertyKey.autoReconnectForPools);
this.cachePrepStmts = this.propertySet.getBooleanProperty(PropertyKey.cachePrepStmts);
this.autoReconnect = this.propertySet.getBooleanProperty(PropertyKey.autoReconnect);
this.useUsageAdvisor = this.propertySet.getBooleanProperty(PropertyKey.useUsageAdvisor);
this.reconnectAtTxEnd = this.propertySet.getBooleanProperty(PropertyKey.reconnectAtTxEnd);
this.emulateUnsupportedPstmts = this.propertySet.getBooleanProperty(PropertyKey.emulateUnsupportedPstmts);
this.ignoreNonTxTables = this.propertySet.getBooleanProperty(PropertyKey.ignoreNonTxTables);
this.pedantic = this.propertySet.getBooleanProperty(PropertyKey.pedantic);
this.prepStmtCacheSqlLimit = this.propertySet.getIntegerProperty(PropertyKey.prepStmtCacheSqlLimit);
this.useLocalSessionState = this.propertySet.getBooleanProperty(PropertyKey.useLocalSessionState);
this.useServerPrepStmts = this.propertySet.getBooleanProperty(PropertyKey.useServerPrepStmts);
this.processEscapeCodesForPrepStmts = this.propertySet.getBooleanProperty(PropertyKey.processEscapeCodesForPrepStmts);
this.useLocalTransactionState = this.propertySet.getBooleanProperty(PropertyKey.useLocalTransactionState);
this.disconnectOnExpiredPasswords = this.propertySet.getBooleanProperty(PropertyKey.disconnectOnExpiredPasswords);
this.readOnlyPropagatesToServer = this.propertySet.getBooleanProperty(PropertyKey.readOnlyPropagatesToServer);
if (this.cachePrepStmts.getValue()) {
createPreparedStatementCaches();
}
if (this.propertySet.getBooleanProperty(PropertyKey.cacheCallableStmts).getValue()) {
this.parsedCallableStatementCache = new LRUCache<>(this.propertySet.getIntegerProperty(PropertyKey.callableStmtCacheSize).getValue());
}
if (this.propertySet.getBooleanProperty(PropertyKey.allowMultiQueries).getValue()) {
this.propertySet.getProperty(PropertyKey.cacheResultSetMetadata).setValue(false); // we don't handle this yet
}
if (this.propertySet.getBooleanProperty(PropertyKey.cacheResultSetMetadata).getValue()) {
this.resultSetMetadataCache = new LRUCache<>(this.propertySet.getIntegerProperty(PropertyKey.metadataCacheSize).getValue());
}
if (this.propertySet.getStringProperty(PropertyKey.socksProxyHost).getStringValue() != null) {
this.propertySet.getProperty(PropertyKey.socketFactory).setValue(SocksProxySocketFactory.class.getName());
}
this.dbmd = getMetaData(false, false);
initializeSafeQueryInterceptors();
} catch (CJException e) {
throw SQLExceptionsMapping.translateException(e, getExceptionInterceptor());
}
try {
createNewIO(false);
unSafeQueryInterceptors();
AbandonedConnectionCleanupThread.trackConnection(this, getSession().getNetworkResources());
SocketAddress socketAddress = this.session.getRemoteSocketAddress();
if (InetSocketAddress.class.isInstance(socketAddress)) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
this.connectionSpan.setAttribute(TelemetryAttribute.NETWORK_PEER_ADDRESS, inetSocketAddress.getHostName());
this.connectionSpan.setAttribute(TelemetryAttribute.NETWORK_PEER_PORT, inetSocketAddress.getPort());
this.connectionSpan.setAttribute(TelemetryAttribute.NETWORK_TRANSPORT, TelemetryAttribute.NETWORK_TRANSPORT_TCP);
}
if (this.propertySet.getStringProperty(PropertyKey.socketFactory).getValue().equalsIgnoreCase("com.mysql.cj.protocol.NamedPipeSocketFactory")) {
this.connectionSpan.setAttribute(TelemetryAttribute.NETWORK_TRANSPORT, TelemetryAttribute.NETWORK_TRANSPORT_PIPE);
} else if (StringUtils.indexOfIgnoreCase(socketAddress.getClass().getName(), "UNIXSocket") >= 0) {
this.connectionSpan.setAttribute(TelemetryAttribute.NETWORK_TRANSPORT, TelemetryAttribute.NETWORK_TRANSPORT_UNIX);
}
} catch (SQLException ex) {
cleanup(ex);
// don't clobber SQL exceptions
throw ex;
} catch (Exception ex) {
cleanup(ex);
throw SQLError.createSQLException(
this.propertySet.getBooleanProperty(PropertyKey.paranoid).getValue() ? Messages.getString("Connection.0")
: Messages.getString("Connection.1",
new Object[] { this.session.getHostInfo().getHost(), this.session.getHostInfo().getPort() }),
MysqlErrorNumbers.SQLSTATE_MYSQL_COMMUNICATION_LINK_FAILURE, ex, getExceptionInterceptor());
}
} catch (Throwable t) {
this.connectionSpan.setError(t);
throw t;
} finally {
this.connectionSpan.end();
}
}
@Override
public JdbcPropertySet getPropertySet() {
return this.propertySet;
}
@Override
public void unSafeQueryInterceptors() throws SQLException {
this.queryInterceptors = this.queryInterceptors.stream().map(NoSubInterceptorWrapper.class::cast).map(NoSubInterceptorWrapper::getUnderlyingInterceptor)
.collect(Collectors.toCollection(LinkedList::new));
if (this.session != null) {
this.session.setQueryInterceptors(this.queryInterceptors);
}
}
@Override
public void initializeSafeQueryInterceptors() throws SQLException {
this.queryInterceptors = Util
.loadClasses(QueryInterceptor.class, this.propertySet.getStringProperty(PropertyKey.queryInterceptors).getStringValue(),
"MysqlIo.BadQueryInterceptor", getExceptionInterceptor())
.stream().map(o -> new NoSubInterceptorWrapper(o.init(this, this.props, this.session.getLog())))
.collect(Collectors.toCollection(LinkedList::new));
}
@Override
public List getQueryInterceptorsInstances() {
return this.queryInterceptors;
}
private boolean canHandleAsServerPreparedStatement(String sql) throws SQLException {
if (sql == null || sql.length() == 0) {
return true;
}
if (!this.useServerPrepStmts.getValue()) {
return false;
}
boolean allowMultiQueries = this.propertySet.getBooleanProperty(PropertyKey.allowMultiQueries).getValue()
|| this.propertySet.getBooleanProperty(PropertyKey.rewriteBatchedStatements).getValue();
if (this.cachePrepStmts.getValue()) {
this.serverSideStatementCheckCacheLock.lock();
try {
Boolean flag = this.serverSideStatementCheckCache.get(sql);
if (flag != null) {
return flag.booleanValue();
}
boolean canHandle = StringUtils.canHandleAsServerPreparedStatementNoCache(sql, getServerVersion(), allowMultiQueries,
this.session.getServerSession().isNoBackslashEscapesSet(), this.session.getServerSession().useAnsiQuotedIdentifiers());
if (sql.length() < this.prepStmtCacheSqlLimit.getValue()) {
this.serverSideStatementCheckCache.put(sql, canHandle ? Boolean.TRUE : Boolean.FALSE);
}
return canHandle;
} finally {
this.serverSideStatementCheckCacheLock.unlock();
}
}
return StringUtils.canHandleAsServerPreparedStatementNoCache(sql, getServerVersion(), allowMultiQueries,
this.session.getServerSession().isNoBackslashEscapesSet(), this.session.getServerSession().useAnsiQuotedIdentifiers());
}
@Override
public void changeUser(String userName, String newPassword) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
if (userName == null || userName.equals("")) {
userName = "";
}
if (newPassword == null) {
newPassword = "";
}
try {
this.session.changeUser(userName, newPassword, this.database);
} catch (CJException ex) {
// After Bug#16241992 fix the server doesn't return to previous credentials if COM_CHANGE_USER attempt failed.
if ("28000".equals(ex.getSQLState())) {
cleanup(ex);
}
throw ex;
}
this.user = userName;
this.password = newPassword;
this.session.getServerSession().getCharsetSettings().configurePostHandshake(true);
this.session.setSessionVariables();
handleAutoCommitDefaults();
setupServerForTruncationChecks();
} finally {
connectionLock.unlock();
}
}
@Override
public void checkClosed() {
this.session.checkClosed();
}
@Override
public void throwConnectionClosedException() throws SQLException {
SQLException ex = SQLError.createSQLException(Messages.getString("Connection.2"),
MysqlErrorNumbers.SQLSTATE_CONNECTION_EXCEPTION_CONNECTION_DOES_NOT_EXIST, getExceptionInterceptor());
if (this.session.getForceClosedReason() != null) {
ex.initCause(this.session.getForceClosedReason());
}
throw ex;
}
/**
* Set transaction isolation level to the value received from server if any.
* Is called by connectionInit(...)
*/
private void checkTransactionIsolationLevel() {
String s = this.session.getServerSession().getServerVariable("transaction_isolation");
if (s == null) {
s = this.session.getServerSession().getServerVariable("tx_isolation");
}
if (s != null) {
Integer intTI = mapTransIsolationNameToValue.get(s);
if (intTI != null) {
this.isolationLevel = intTI.intValue();
}
}
}
@Override
public void abortInternal() throws SQLException {
this.session.forceClose();
}
@Override
public void cleanup(Throwable whyCleanedUp) {
try {
if (this.session != null) {
if (isClosed()) {
this.session.forceClose();
} else {
doClose(whyCleanedUp, CloseOption.IMPLICIT, CloseOption.PROPAGATE);
}
}
} catch (SQLException | CJException sqlEx) {
// ignore, we're going away.
}
}
@Override
public void clearWarnings() throws SQLException {
// firstWarning = null;
}
@Override
public java.sql.PreparedStatement clientPrepareStatement(String sql) throws SQLException {
return clientPrepareStatement(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY);
}
@Override
public java.sql.PreparedStatement clientPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException {
java.sql.PreparedStatement pStmt = clientPrepareStatement(sql);
((ClientPreparedStatement) pStmt).setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
return pStmt;
}
@Override
public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true);
}
public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, boolean processEscapeCodesIfNeeded)
throws SQLException {
String nativeSql = processEscapeCodesIfNeeded && this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql;
ClientPreparedStatement pStmt = null;
if (this.cachePrepStmts.getValue()) {
QueryInfo pStmtInfo = this.queryInfoCache.get(nativeSql);
if (pStmtInfo == null) {
pStmt = ClientPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database);
this.queryInfoCache.put(nativeSql, pStmt.getQueryInfo());
} else {
pStmt = ClientPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, pStmtInfo);
}
} else {
pStmt = ClientPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database);
}
pStmt.setResultSetType(resultSetType);
pStmt.setResultSetConcurrency(resultSetConcurrency);
return pStmt;
}
@Override
public java.sql.PreparedStatement clientPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException {
ClientPreparedStatement pStmt = (ClientPreparedStatement) clientPrepareStatement(sql);
pStmt.setRetrieveGeneratedKeys(autoGenKeyIndexes != null && autoGenKeyIndexes.length > 0);
return pStmt;
}
@Override
public java.sql.PreparedStatement clientPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException {
ClientPreparedStatement pStmt = (ClientPreparedStatement) clientPrepareStatement(sql);
pStmt.setRetrieveGeneratedKeys(autoGenKeyColNames != null && autoGenKeyColNames.length > 0);
return pStmt;
}
@Override
public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true);
}
@Override
public void close() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
if (this.connectionLifecycleInterceptors != null) {
for (ConnectionLifecycleInterceptor cli : this.connectionLifecycleInterceptors) {
cli.close();
}
}
doClose(null, CloseOption.ROLLBACK, CloseOption.PROPAGATE);
} finally {
connectionLock.unlock();
}
}
/**
* Closes all currently open statements.
*
* @throws SQLException
* if a database access error occurs
*/
private void closeAllOpenStatements() throws SQLException {
SQLException postponedException = null;
for (JdbcStatement stmt : this.openStatements) {
try {
stmt.doClose(CloseOption.IMPLICIT, CloseOption.PROPAGATE, CloseOption.NO_CACHE);
} catch (SQLException sqlEx) {
postponedException = sqlEx; // throw it later, cleanup all statements first
}
}
if (postponedException != null) {
throw postponedException;
}
}
private void closeStatement(java.sql.Statement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException sqlEx) {
// ignore
}
stmt = null;
}
}
@Override
public void commit() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
try {
if (this.connectionLifecycleInterceptors != null) {
IterateBlock iter = new IterateBlock(
this.connectionLifecycleInterceptors.iterator()) {
@Override
void forEach(ConnectionLifecycleInterceptor each) throws SQLException {
if (!each.commit()) {
this.stopIterating = true;
}
}
};
iter.doForAll();
if (!iter.fullIteration()) {
return;
}
}
if (this.session.getServerSession().isAutoCommit()) {
throw SQLError.createSQLException(Messages.getString("Connection.3"), getExceptionInterceptor());
}
if (this.useLocalTransactionState.getValue()) {
if (!this.session.getServerSession().inTransactionOnServer()) {
return; // effectively a no-op
}
}
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.ROLLBACK);
try (TelemetryScope scope = span.makeCurrent()) {
span.setAttribute(TelemetryAttribute.DB_NAME, getDatabase());
span.setAttribute(TelemetryAttribute.DB_OPERATION, TelemetryAttribute.OPERATION_ROLLBACK);
span.setAttribute(TelemetryAttribute.DB_STATEMENT, TelemetryAttribute.OPERATION_ROLLBACK);
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, getUser());
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
this.session.execSQL(null, "COMMIT", -1, null, false, this.nullStatementResultSetFactory, null, false);
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
} catch (SQLException sqlException) {
if (MysqlErrorNumbers.SQLSTATE_MYSQL_COMMUNICATION_LINK_FAILURE.equals(sqlException.getSQLState())) {
throw SQLError.createSQLException(Messages.getString("Connection.4"),
MysqlErrorNumbers.SQLSTATE_CONNECTION_EXCEPTION_TRANSACTION_RESOLUTION_UNKNOWN, getExceptionInterceptor());
}
throw sqlException;
} finally {
this.session.setNeedsPing(this.reconnectAtTxEnd.getValue());
}
} finally {
connectionLock.unlock();
}
return;
}
@Override
public void createNewIO(boolean isForReconnect) {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
// Synchronization Not needed for *new* connections, but definitely for connections going through fail-over, since we might get the new connection
// up and running *enough* to start sending cached or still-open server-side prepared statements over to the backend before we get a chance to
// re-prepare them...
try {
if (!this.autoReconnect.getValue()) {
connectOneTryOnly(isForReconnect);
return;
}
connectWithRetries(isForReconnect);
} catch (SQLException ex) {
throw ExceptionFactory.createException(UnableToConnectException.class, ex.getMessage(), ex);
}
} finally {
connectionLock.unlock();
}
}
private void connectWithRetries(boolean isForReconnect) throws SQLException {
double timeout = this.propertySet.getIntegerProperty(PropertyKey.initialTimeout).getValue();
boolean connectionGood = false;
Exception connectionException = null;
for (int attemptCount = 0; attemptCount < this.propertySet.getIntegerProperty(PropertyKey.maxReconnects).getValue()
&& !connectionGood; attemptCount++) {
try {
this.session.forceClose();
JdbcConnection c = getProxy();
this.session.connect(this.origHostInfo, this.user, this.password, this.database, getLoginTimeout(), c);
pingInternal(false, 0);
boolean oldAutoCommit;
int oldIsolationLevel;
boolean oldReadOnly;
String oldDb;
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
// save state from old connection
oldAutoCommit = getAutoCommit();
oldIsolationLevel = this.isolationLevel;
oldReadOnly = isReadOnly(false);
oldDb = getDatabase();
this.session.setQueryInterceptors(this.queryInterceptors);
} finally {
connectionLock.unlock();
}
// Server properties might be different from previous connection, so initialize again...
initializePropsFromServer();
if (isForReconnect) {
// Restore state from old connection
setAutoCommit(oldAutoCommit);
setTransactionIsolation(oldIsolationLevel);
setDatabase(oldDb);
setReadOnly(oldReadOnly);
}
connectionGood = true;
break;
} catch (UnableToConnectException rejEx) {
close();
this.session.getProtocol().getSocketConnection().forceClose();
} catch (Exception e) {
connectionException = e;
connectionGood = false;
}
if (connectionGood) {
break;
}
if (attemptCount > 0) {
try {
Thread.sleep((long) timeout * 1000);
} catch (InterruptedException IE) {
// ignore
}
}
} // end attempts for a single host
if (!connectionGood) {
// We've really failed!
SQLException chainedEx = SQLError.createSQLException(
Messages.getString("Connection.UnableToConnectWithRetries",
new Object[] { this.propertySet.getIntegerProperty(PropertyKey.maxReconnects).getValue() }),
MysqlErrorNumbers.SQLSTATE_CONNECTION_EXCEPTION_SQL_CLIENT_UNABLE_TO_ESTABLISH_SQL_CONNECTION, connectionException,
getExceptionInterceptor());
throw chainedEx;
}
if (this.propertySet.getBooleanProperty(PropertyKey.paranoid).getValue() && !this.autoReconnect.getValue()) {
this.password = null;
this.user = null;
}
if (isForReconnect) {
//
// Retrieve any 'lost' prepared statements if re-connecting
//
Iterator statementIter = this.openStatements.iterator();
//
// We build a list of these outside the map of open statements, because in the process of re-preparing, we might end up having to close a prepared
// statement, thus removing it from the map, and generating a ConcurrentModificationException
//
Stack serverPreparedStatements = null;
while (statementIter.hasNext()) {
JdbcStatement statementObj = statementIter.next();
if (statementObj instanceof ServerPreparedStatement) {
if (serverPreparedStatements == null) {
serverPreparedStatements = new Stack<>();
}
serverPreparedStatements.add(statementObj);
}
}
if (serverPreparedStatements != null) {
while (!serverPreparedStatements.isEmpty()) {
((ServerPreparedStatement) serverPreparedStatements.pop()).rePrepare();
}
}
}
}
private void connectOneTryOnly(boolean isForReconnect) throws SQLException {
Exception connectionNotEstablishedBecause = null;
try {
JdbcConnection c = getProxy();
this.session.connect(this.origHostInfo, this.user, this.password, this.database, getLoginTimeout(), c);
// save state from old connection
boolean oldAutoCommit = getAutoCommit();
int oldIsolationLevel = this.isolationLevel;
boolean oldReadOnly = isReadOnly(false);
String oldDb = getDatabase();
this.session.setQueryInterceptors(this.queryInterceptors);
// Server properties might be different from previous connection, so initialize again...
initializePropsFromServer();
if (isForReconnect) {
// Restore state from old connection
setAutoCommit(oldAutoCommit);
setTransactionIsolation(oldIsolationLevel);
setDatabase(oldDb);
setReadOnly(oldReadOnly);
}
return;
} catch (UnableToConnectException rejEx) {
close();
NativeProtocol protocol = this.session.getProtocol();
if (protocol != null) {
protocol.getSocketConnection().forceClose();
}
throw rejEx;
} catch (Exception e) {
if ((e instanceof PasswordExpiredException
|| e instanceof SQLException && ((SQLException) e).getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD)
&& !this.disconnectOnExpiredPasswords.getValue()) {
return;
}
if (this.session != null) {
this.session.forceClose();
}
connectionNotEstablishedBecause = e;
if (e instanceof SQLException) {
throw (SQLException) e;
}
if (e.getCause() != null && e.getCause() instanceof SQLException) {
throw (SQLException) e.getCause();
}
if (e instanceof CJException) {
throw (CJException) e;
}
SQLException chainedEx = SQLError.createSQLException(Messages.getString("Connection.UnableToConnect"),
MysqlErrorNumbers.SQLSTATE_CONNECTION_EXCEPTION_SQL_CLIENT_UNABLE_TO_ESTABLISH_SQL_CONNECTION, getExceptionInterceptor());
chainedEx.initCause(connectionNotEstablishedBecause);
throw chainedEx;
}
}
private int getLoginTimeout() {
int loginTimeoutSecs = DriverManager.getLoginTimeout();
if (loginTimeoutSecs <= 0) {
return 0;
}
return loginTimeoutSecs * 1000;
}
private void createPreparedStatementCaches() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
int cacheSize = this.propertySet.getIntegerProperty(PropertyKey.prepStmtCacheSize).getValue();
String queryInfoCacheFactory = this.propertySet.getStringProperty(PropertyKey.queryInfoCacheFactory).getValue();
@SuppressWarnings("unchecked")
CacheAdapterFactory cacheFactory = Util.getInstance(CacheAdapterFactory.class, queryInfoCacheFactory, null, null,
getExceptionInterceptor());
this.queryInfoCache = cacheFactory.getInstance(connectionLock, this.origHostInfo.getDatabaseUrl(), cacheSize,
this.prepStmtCacheSqlLimit.getValue());
if (this.useServerPrepStmts.getValue()) {
this.serverSideStatementCheckCache = new LRUCache<>(cacheSize);
this.serverSideStatementCache = new LRUCache(cacheSize) {
private static final long serialVersionUID = 7692318650375988114L;
@Override
protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
if (this.maxElements <= 1) {
return false;
}
boolean removeIt = super.removeEldestEntry(eldest);
if (removeIt) {
ServerPreparedStatement ps = eldest.getValue();
ps.isCached = false;
ps.setClosed(false);
try {
ps.doClose(CloseOption.PROPAGATE, CloseOption.NO_CACHE);
} catch (SQLException sqlEx) {
// punt
}
}
return removeIt;
}
};
}
} finally {
connectionLock.unlock();
}
}
@Override
public java.sql.Statement createStatement() throws SQLException {
return createStatement(DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY);
}
@Override
public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
StatementImpl stmt = new StatementImpl(getMultiHostSafeProxy(), this.database);
stmt.setResultSetType(resultSetType);
stmt.setResultSetConcurrency(resultSetConcurrency);
return stmt;
}
@Override
public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
if (this.pedantic.getValue()) {
if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
throw SQLError.createSQLException("HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
MysqlErrorNumbers.SQLSTATE_CONNJ_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
}
return createStatement(resultSetType, resultSetConcurrency);
}
@Override
public int getActiveStatementCount() {
return this.openStatements.size();
}
@Override
public boolean getAutoCommit() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
return this.session.getServerSession().isAutoCommit();
} finally {
connectionLock.unlock();
}
}
@Override
public String getCatalog() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
return this.propertySet.getEnumProperty(PropertyKey.databaseTerm).getValue() == DatabaseTerm.SCHEMA ? null : this.database;
} finally {
connectionLock.unlock();
}
}
@Override
public String getCharacterSetMetadata() {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
return this.session.getServerSession().getCharsetSettings().getMetadataEncoding();
} finally {
connectionLock.unlock();
}
}
@Override
public int getHoldability() throws SQLException {
return java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT;
}
@Override
public long getId() {
return this.session.getThreadId();
}
/**
* NOT JDBC-Compliant, but clients can use this method to determine how long
* this connection has been idle. This time (reported in milliseconds) is
* updated once a query has completed.
*
* @return number of ms that this connection has been idle, 0 if the driver
* is busy retrieving results.
*/
@Override
public long getIdleFor() {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
return this.session.getIdleFor();
} finally {
connectionLock.unlock();
}
}
@Override
public java.sql.DatabaseMetaData getMetaData() throws SQLException {
return getMetaData(true, true);
}
private java.sql.DatabaseMetaData getMetaData(boolean checkClosed, boolean checkForInfoSchema) throws SQLException {
if (checkClosed) {
checkClosed();
}
com.mysql.cj.jdbc.DatabaseMetaData dbmeta = com.mysql.cj.jdbc.DatabaseMetaData.getInstance(getMultiHostSafeProxy(), this.database, checkForInfoSchema,
this.nullStatementResultSetFactory);
if (getSession() != null && getSession().getProtocol() != null) {
dbmeta.setMetadataEncoding(getSession().getServerSession().getCharsetSettings().getMetadataEncoding());
dbmeta.setMetadataCollationIndex(getSession().getServerSession().getCharsetSettings().getMetadataCollationIndex());
}
return dbmeta;
}
@Override
public java.sql.Statement getMetadataSafeStatement() throws SQLException {
return getMetadataSafeStatement(0);
}
public java.sql.Statement getMetadataSafeStatement(int maxRows) throws SQLException {
java.sql.Statement stmt = createStatement();
stmt.setMaxRows(maxRows == -1 ? 0 : maxRows);
stmt.setEscapeProcessing(false);
if (stmt.getFetchSize() != 0) {
stmt.setFetchSize(0);
}
return stmt;
}
@Override
public ServerVersion getServerVersion() {
return this.session.getServerSession().getServerVersion();
}
@Override
public int getTransactionIsolation() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
if (!this.useLocalSessionState.getValue()) {
String s = this.session.queryServerVariable(
versionMeetsMinimum(8, 0, 3) || versionMeetsMinimum(5, 7, 20) && !versionMeetsMinimum(8, 0, 0) ? "@@session.transaction_isolation"
: "@@session.tx_isolation");
if (s != null) {
Integer intTI = mapTransIsolationNameToValue.get(s);
if (intTI != null) {
this.isolationLevel = intTI.intValue();
return this.isolationLevel;
}
throw SQLError.createSQLException(Messages.getString("Connection.12", new Object[] { s }), MysqlErrorNumbers.SQLSTATE_CONNJ_GENERAL_ERROR,
getExceptionInterceptor());
}
throw SQLError.createSQLException(Messages.getString("Connection.13"), MysqlErrorNumbers.SQLSTATE_CONNJ_GENERAL_ERROR,
getExceptionInterceptor());
}
return this.isolationLevel;
} finally {
connectionLock.unlock();
}
}
@Override
public java.util.Map> getTypeMap() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
if (this.typeMap == null) {
this.typeMap = new HashMap<>();
}
return this.typeMap;
} finally {
connectionLock.unlock();
}
}
@Override
public String getURL() {
return this.origHostInfo.getDatabaseUrl();
}
@Override
public String getUser() {
return this.user;
}
@Override
public SQLWarning getWarnings() throws SQLException {
return null;
}
@Override
public boolean hasSameProperties(JdbcConnection c) {
return this.props.equals(c.getProperties());
}
@Override
public Properties getProperties() {
return this.props;
}
/**
* Sets varying properties that depend on server information. Called once we have connected to the server.
*
* @throws SQLException
* if a database access error occurs
*/
private void initializePropsFromServer() throws SQLException {
String connectionInterceptorClasses = this.propertySet.getStringProperty(PropertyKey.connectionLifecycleInterceptors).getStringValue();
this.connectionLifecycleInterceptors = null;
if (connectionInterceptorClasses != null) {
try {
this.connectionLifecycleInterceptors = Util
.loadClasses(ConnectionLifecycleInterceptor.class, connectionInterceptorClasses, "Connection.badLifecycleInterceptor",
getExceptionInterceptor())
.stream().map(i -> i.init(this, this.props, this.session.getLog())).collect(Collectors.toCollection(LinkedList::new));
} catch (CJException e) {
throw SQLExceptionsMapping.translateException(e, getExceptionInterceptor());
}
}
this.session.setSessionVariables();
this.session.loadServerVariables(getConnectionLock(), this.dbmd.getDriverVersion());
this.autoIncrementIncrement = this.session.getServerSession().getServerVariable("auto_increment_increment", 1);
try {
LicenseConfiguration.checkLicenseType(this.session.getServerSession().getServerVariables());
} catch (CJException e) {
throw SQLError.createSQLException(e.getMessage(), MysqlErrorNumbers.SQLSTATE_CONNECTION_EXCEPTION_SQL_CLIENT_UNABLE_TO_ESTABLISH_SQL_CONNECTION,
getExceptionInterceptor());
}
this.session.getProtocol().initServerSession();
checkTransactionIsolationLevel();
handleAutoCommitDefaults();
((com.mysql.cj.jdbc.DatabaseMetaData) this.dbmd).setMetadataEncoding(this.session.getServerSession().getCharsetSettings().getMetadataEncoding());
((com.mysql.cj.jdbc.DatabaseMetaData) this.dbmd)
.setMetadataCollationIndex(this.session.getServerSession().getCharsetSettings().getMetadataCollationIndex());
//
// Server can do this more efficiently for us
//
setupServerForTruncationChecks();
}
/**
* Resets a default auto-commit value of 0 to 1, as required by JDBC specification.
* Takes into account that the default auto-commit value of 0 may have been changed on the server via init_connect.
*
* @throws SQLException
* if a database access error occurs
*/
private void handleAutoCommitDefaults() throws SQLException {
boolean resetAutoCommitDefault = false;
// Server Bug#66884 (SERVER_STATUS is always initiated with SERVER_STATUS_AUTOCOMMIT=1) invalidates "elideSetAutoCommits" feature.
// TODO Turn this feature back on as soon as the server bug is fixed. Consider making it version specific.
// if (!getPropertySet().getBooleanReadableProperty(PropertyKey.elideSetAutoCommits).getValue()) {
String initConnectValue = this.session.getServerSession().getServerVariable("init_connect");
if (initConnectValue != null && initConnectValue.length() > 0) {
// auto-commit might have changed
String s = this.session.queryServerVariable("@@session.autocommit");
if (s != null) {
this.session.getServerSession().setAutoCommit(Boolean.parseBoolean(s));
if (!this.session.getServerSession().isAutoCommit()) {
resetAutoCommitDefault = true;
}
}
} else {
// reset it anyway, the server may have been initialized with --autocommit=0
resetAutoCommitDefault = true;
}
//} else if (getSession().isSetNeededForAutoCommitMode(true)) {
// // we're not in standard autocommit=true mode
// this.session.setAutoCommit(false);
// resetAutoCommitDefault = true;
//}
if (resetAutoCommitDefault) {
try {
setAutoCommit(true); // required by JDBC spec
} catch (SQLException ex) {
if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
}
}
}
@Override
public boolean isClosed() {
return this.session.isClosed();
}
@Override
public boolean isInGlobalTx() {
return this.isInGlobalTx;
}
@Override
public boolean isSourceConnection() {
return false; // handled higher up
}
@Override
public boolean isReadOnly() throws SQLException {
return isReadOnly(true);
}
@Override
public boolean isReadOnly(boolean useSessionStatus) throws SQLException {
if (useSessionStatus && !this.session.isClosed() && versionMeetsMinimum(5, 6, 5) && !this.useLocalSessionState.getValue()
&& this.readOnlyPropagatesToServer.getValue()) {
String s = this.session.queryServerVariable(
versionMeetsMinimum(8, 0, 3) || versionMeetsMinimum(5, 7, 20) && !versionMeetsMinimum(8, 0, 0) ? "@@session.transaction_read_only"
: "@@session.tx_read_only");
if (s != null) {
return Integer.parseInt(s) != 0; // mysql has a habit of tri+ state booleans
}
}
return this.readOnly;
}
@Override
public boolean isSameResource(JdbcConnection otherConnection) {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
if (otherConnection == null) {
return false;
}
boolean directCompare = true;
String otherHost = ((ConnectionImpl) otherConnection).origHostToConnectTo;
String otherOrigDatabase = ((ConnectionImpl) otherConnection).origHostInfo.getDatabase();
String otherCurrentDb = ((ConnectionImpl) otherConnection).database;
if (!StringUtils.nullSafeEqual(otherHost, this.origHostToConnectTo)) {
directCompare = false;
} else if (otherHost != null && otherHost.indexOf(',') == -1 && otherHost.indexOf(':') == -1) {
// need to check port numbers
directCompare = ((ConnectionImpl) otherConnection).origPortToConnectTo == this.origPortToConnectTo;
}
if (directCompare) {
if (!StringUtils.nullSafeEqual(otherOrigDatabase, this.origHostInfo.getDatabase())
|| !StringUtils.nullSafeEqual(otherCurrentDb, this.database)) {
directCompare = false;
}
}
if (directCompare) {
return true;
}
// Has the user explicitly set a resourceId?
String otherResourceId = ((ConnectionImpl) otherConnection).getPropertySet().getStringProperty(PropertyKey.resourceId).getValue();
String myResourceId = this.propertySet.getStringProperty(PropertyKey.resourceId).getValue();
if (otherResourceId != null || myResourceId != null) {
directCompare = StringUtils.nullSafeEqual(otherResourceId, myResourceId);
if (directCompare) {
return true;
}
}
return false;
} finally {
connectionLock.unlock();
}
}
private int autoIncrementIncrement = 0;
@Override
public int getAutoIncrementIncrement() {
return this.autoIncrementIncrement;
}
@Override
public boolean lowerCaseTableNames() {
return this.session.getServerSession().isLowerCaseTableNames();
}
@Override
public String nativeSQL(String sql) throws SQLException {
if (sql == null) {
return null;
}
Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, getMultiHostSafeProxy().getSession().getServerSession().getSessionTimeZone(),
getMultiHostSafeProxy().getSession().getServerSession().getCapabilities().serverSupportsFracSecs(),
getMultiHostSafeProxy().getSession().getServerSession().isServerTruncatesFracSecs(), getExceptionInterceptor());
if (escapedSqlResult instanceof String) {
return (String) escapedSqlResult;
}
return ((EscapeProcessorResult) escapedSqlResult).escapedSql;
}
private CallableStatement parseCallableStatement(String sql) throws SQLException {
Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, getMultiHostSafeProxy().getSession().getServerSession().getSessionTimeZone(),
getMultiHostSafeProxy().getSession().getServerSession().getCapabilities().serverSupportsFracSecs(),
getMultiHostSafeProxy().getSession().getServerSession().isServerTruncatesFracSecs(), getExceptionInterceptor());
boolean isFunctionCall = false;
String parsedSql = null;
if (escapedSqlResult instanceof EscapeProcessorResult) {
parsedSql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
isFunctionCall = ((EscapeProcessorResult) escapedSqlResult).callingStoredFunction;
} else {
parsedSql = (String) escapedSqlResult;
isFunctionCall = false;
}
return CallableStatement.getInstance(getMultiHostSafeProxy(), parsedSql, this.database, isFunctionCall);
}
@Override
public void ping() throws SQLException {
pingInternal(true, 0);
}
@Override
public void pingInternal(boolean checkForClosedConnection, int timeoutMillis) throws SQLException {
this.session.ping(checkForClosedConnection, timeoutMillis);
}
@Override
public java.sql.CallableStatement prepareCall(String sql) throws SQLException {
return prepareCall(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY);
}
@Override
public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.ROUTINE_PREPARE);
try (TelemetryScope scope = span.makeCurrent()) {
CallableStatement cStmt = null;
if (!this.propertySet.getBooleanProperty(PropertyKey.cacheCallableStmts).getValue()) {
cStmt = parseCallableStatement(sql);
} else {
this.parsedCallableStatementCacheLock.lock();
try {
CompoundCacheKey key = new CompoundCacheKey(getDatabase(), sql);
CallableStatement.CallableStatementParamInfo cachedParamInfo = this.parsedCallableStatementCache.get(key);
if (cachedParamInfo != null) {
cStmt = CallableStatement.getInstance(getMultiHostSafeProxy(), cachedParamInfo);
} else {
cStmt = parseCallableStatement(sql);
cachedParamInfo = cStmt.paramInfo;
this.parsedCallableStatementCache.put(key, cachedParamInfo);
}
} finally {
this.parsedCallableStatementCacheLock.unlock();
}
}
cStmt.setResultSetType(resultSetType);
cStmt.setResultSetConcurrency(resultSetConcurrency);
String dbOperation = cStmt.getQueryInfo().getStatementKeyword();
span.setAttribute(TelemetryAttribute.DB_NAME, getDatabase());
span.setAttribute(TelemetryAttribute.DB_OPERATION, dbOperation);
span.setAttribute(TelemetryAttribute.DB_STATEMENT, dbOperation + TelemetryAttribute.STATEMENT_SUFFIX);
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, getUser());
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
return cStmt;
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
} finally {
connectionLock.unlock();
}
}
@Override
public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
if (this.pedantic.getValue()) {
if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
throw SQLError.createSQLException(Messages.getString("Connection.17"), MysqlErrorNumbers.SQLSTATE_CONNJ_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
}
CallableStatement cStmt = (com.mysql.cj.jdbc.CallableStatement) prepareCall(sql, resultSetType, resultSetConcurrency);
return cStmt;
}
@Override
public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException {
return prepareStatement(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY);
}
@Override
public java.sql.PreparedStatement prepareStatement(String sql, int autoGenKeyIndex) throws SQLException {
java.sql.PreparedStatement pStmt = prepareStatement(sql);
((ClientPreparedStatement) pStmt).setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
return pStmt;
}
@Override
public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.STMT_PREPARE);
try (TelemetryScope scope = span.makeCurrent()) {
//
// FIXME: Create warnings if can't create results of the given type or concurrency
//
ClientPreparedStatement pStmt = null;
boolean canServerPrepare = true;
String nativeSql = this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql;
if (this.useServerPrepStmts.getValue() && this.emulateUnsupportedPstmts.getValue()) {
canServerPrepare = canHandleAsServerPreparedStatement(nativeSql);
}
if (this.useServerPrepStmts.getValue() && canServerPrepare) {
if (this.cachePrepStmts.getValue()) {
this.serverSideStatementCheckCacheLock.lock();
try {
pStmt = this.serverSideStatementCache.remove(new CompoundCacheKey(this.database, sql));
if (pStmt != null) {
((com.mysql.cj.jdbc.ServerPreparedStatement) pStmt).setClosed(false);
pStmt.clearParameters();
pStmt.setResultSetType(resultSetType);
pStmt.setResultSetConcurrency(resultSetConcurrency);
}
if (pStmt == null) {
try {
pStmt = ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, resultSetType,
resultSetConcurrency);
if (sql.length() < this.prepStmtCacheSqlLimit.getValue()) {
((com.mysql.cj.jdbc.ServerPreparedStatement) pStmt).isCacheable = true;
}
pStmt.setResultSetType(resultSetType);
pStmt.setResultSetConcurrency(resultSetConcurrency);
} catch (SQLException sqlEx) {
// Punt, if necessary
if (this.emulateUnsupportedPstmts.getValue()) {
pStmt = (ClientPreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
if (sql.length() < this.prepStmtCacheSqlLimit.getValue()) {
this.serverSideStatementCheckCache.put(sql, Boolean.FALSE);
}
} else {
throw sqlEx;
}
}
}
} finally {
this.serverSideStatementCheckCacheLock.unlock();
}
} else {
try {
pStmt = ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, resultSetType, resultSetConcurrency);
pStmt.setResultSetType(resultSetType);
pStmt.setResultSetConcurrency(resultSetConcurrency);
} catch (SQLException sqlEx) {
// Punt, if necessary
if (this.emulateUnsupportedPstmts.getValue()) {
pStmt = (ClientPreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
} else {
throw sqlEx;
}
}
}
} else {
pStmt = (ClientPreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
}
String dbOperation = pStmt.getQueryInfo().getStatementKeyword();
span.setAttribute(TelemetryAttribute.DB_NAME, getDatabase());
span.setAttribute(TelemetryAttribute.DB_OPERATION, dbOperation);
span.setAttribute(TelemetryAttribute.DB_STATEMENT, dbOperation + TelemetryAttribute.STATEMENT_SUFFIX);
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, getUser());
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
return pStmt;
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
} finally {
connectionLock.unlock();
}
}
@Override
public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
if (this.pedantic.getValue()) {
if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
throw SQLError.createSQLException(Messages.getString("Connection.17"), MysqlErrorNumbers.SQLSTATE_CONNJ_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
}
return prepareStatement(sql, resultSetType, resultSetConcurrency);
}
@Override
public java.sql.PreparedStatement prepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException {
java.sql.PreparedStatement pStmt = prepareStatement(sql);
((ClientPreparedStatement) pStmt).setRetrieveGeneratedKeys(autoGenKeyIndexes != null && autoGenKeyIndexes.length > 0);
return pStmt;
}
@Override
public java.sql.PreparedStatement prepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException {
java.sql.PreparedStatement pStmt = prepareStatement(sql);
((ClientPreparedStatement) pStmt).setRetrieveGeneratedKeys(autoGenKeyColNames != null && autoGenKeyColNames.length > 0);
return pStmt;
}
/**
* Close the connection and release resources. By default the close is considered explicit, does not roll back the transaction, does not propagate to
* dependents and is processed normally (not forced).
*/
@Override
public void doClose(Throwable reason, CloseOption... options) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
if (isClosed()) {
return;
}
SQLException sqlEx = null;
this.session.setForceClosedReason(reason);
try {
if (this.propertySet.getBooleanProperty(PropertyKey.gatherPerfMetrics).getValue()) {
this.session.getProtocol().getMetricsHolder().reportMetrics(this.session.getLog());
}
if (this.useUsageAdvisor.getValue()) {
if (CloseOption.IMPLICIT.in(options)) {
this.session.getProfilerEventHandler().processEvent(ProfilerEvent.TYPE_USAGE, this.session, null, null, 0, new Throwable(),
Messages.getString("Connection.18"));
}
if (System.currentTimeMillis() - this.session.getConnectionCreationTimeMillis() < 500) {
this.session.getProfilerEventHandler().processEvent(ProfilerEvent.TYPE_USAGE, this.session, null, null, 0, new Throwable(),
Messages.getString("Connection.19"));
}
}
if (!getAutoCommit() && CloseOption.ROLLBACK.in(options)) {
try {
rollback();
} catch (SQLException e) {
sqlEx = e;
}
}
if (CloseOption.PROPAGATE.in(options)) {
try {
closeAllOpenStatements();
} catch (SQLException ex) {
sqlEx = ex;
}
}
if (CloseOption.FORCED.in(options)) {
this.session.forceClose();
} else {
this.session.quit();
}
if (this.queryInterceptors != null) {
this.queryInterceptors.forEach(QueryInterceptor::destroy);
}
if (this.exceptionInterceptor != null) {
this.exceptionInterceptor.destroy();
}
} finally {
this.openStatements.clear();
this.queryInterceptors = null;
this.exceptionInterceptor = null;
this.nullStatementResultSetFactory = null;
this.session.getTelemetryHandler().removeLinkTarget(this.connectionSpan);
}
if (sqlEx != null) {
throw sqlEx;
}
} finally {
connectionLock.unlock();
}
}
@Override
public void recachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
if (this.cachePrepStmts.getValue() && pstmt.isPoolable()) {
this.serverSideStatementCheckCacheLock.lock();
try {
Object oldServerPrepStmt = this.serverSideStatementCache.put(
new CompoundCacheKey(pstmt.getCurrentDatabase(), ((PreparedQuery) pstmt.getQuery()).getOriginalSql()),
(ServerPreparedStatement) pstmt);
if (oldServerPrepStmt != null && oldServerPrepStmt != pstmt) {
((ServerPreparedStatement) oldServerPrepStmt).isCached = false;
((ServerPreparedStatement) oldServerPrepStmt).setClosed(false);
((ServerPreparedStatement) oldServerPrepStmt).doClose(CloseOption.PROPAGATE, CloseOption.NO_CACHE);
}
} finally {
this.serverSideStatementCheckCacheLock.unlock();
}
}
} finally {
connectionLock.unlock();
}
}
@Override
public void decachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
if (this.cachePrepStmts.getValue()) {
this.serverSideStatementCheckCacheLock.lock();
try {
this.serverSideStatementCache.remove(new CompoundCacheKey(pstmt.getCurrentDatabase(), ((PreparedQuery) pstmt.getQuery()).getOriginalSql()));
} finally {
this.serverSideStatementCheckCacheLock.unlock();
}
}
} finally {
connectionLock.unlock();
}
}
@Override
public void registerStatement(JdbcStatement stmt) {
this.openStatements.addIfAbsent(stmt);
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
StringBuilder releaseSavepointQuery = new StringBuilder("RELEASE SAVEPOINT ");
releaseSavepointQuery
.append(StringUtils.quoteIdentifier(savepoint.getSavepointName(), this.session.getIdentifierQuoteString(), this.pedantic.getValue()));
java.sql.Statement stmt = null;
try {
stmt = getMetadataSafeStatement();
stmt.executeUpdate(releaseSavepointQuery.toString());
} finally {
closeStatement(stmt);
}
} finally {
connectionLock.unlock();
}
}
@Override
public void resetServerState() throws SQLException {
if (!this.propertySet.getBooleanProperty(PropertyKey.paranoid).getValue() && this.session != null) {
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.CONNECTION_RESET);
try (TelemetryScope scope = span.makeCurrent()) {
span.setAttribute(TelemetryAttribute.DB_CONNECTION_STRING, getURL());
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, getUser());
span.setAttribute(TelemetryAttribute.SERVER_ADDRESS, this.origHostToConnectTo);
span.setAttribute(TelemetryAttribute.SERVER_PORT, this.origPortToConnectTo);
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
this.session.getServerSession().getCharsetSettings().configurePreHandshake(true);
this.session.resetSessionState();
this.session.getServerSession().getCharsetSettings().configurePostHandshake(true);
this.session.setSessionVariables();
handleAutoCommitDefaults();
setupServerForTruncationChecks();
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
}
}
@Override
public void rollback() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
try {
if (this.connectionLifecycleInterceptors != null) {
IterateBlock iter = new IterateBlock(
this.connectionLifecycleInterceptors.iterator()) {
@Override
void forEach(ConnectionLifecycleInterceptor each) throws SQLException {
if (!each.rollback()) {
this.stopIterating = true;
}
}
};
iter.doForAll();
if (!iter.fullIteration()) {
return;
}
}
if (this.session.getServerSession().isAutoCommit()) {
throw SQLError.createSQLException(Messages.getString("Connection.20"),
MysqlErrorNumbers.SQLSTATE_CONNECTION_EXCEPTION_CONNECTION_DOES_NOT_EXIST, getExceptionInterceptor());
}
try {
rollbackNoChecks();
} catch (SQLException sqlEx) {
// We ignore non-transactional tables if told to do so
if (this.ignoreNonTxTables.getInitialValue() && sqlEx.getErrorCode() == MysqlErrorNumbers.ER_WARNING_NOT_COMPLETE_ROLLBACK) {
return;
}
throw sqlEx;
}
} catch (SQLException sqlException) {
if (MysqlErrorNumbers.SQLSTATE_MYSQL_COMMUNICATION_LINK_FAILURE.equals(sqlException.getSQLState())) {
throw SQLError.createSQLException(Messages.getString("Connection.21"),
MysqlErrorNumbers.SQLSTATE_CONNECTION_EXCEPTION_TRANSACTION_RESOLUTION_UNKNOWN, getExceptionInterceptor());
}
throw sqlException;
} finally {
this.session.setNeedsPing(this.reconnectAtTxEnd.getValue());
}
} finally {
connectionLock.unlock();
}
}
@Override
public void rollback(final Savepoint savepoint) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
try {
if (this.connectionLifecycleInterceptors != null) {
IterateBlock iter = new IterateBlock(
this.connectionLifecycleInterceptors.iterator()) {
@Override
void forEach(ConnectionLifecycleInterceptor each) throws SQLException {
if (!each.rollback(savepoint)) {
this.stopIterating = true;
}
}
};
iter.doForAll();
if (!iter.fullIteration()) {
return;
}
}
StringBuilder rollbackQuery = new StringBuilder("ROLLBACK TO SAVEPOINT ");
rollbackQuery
.append(StringUtils.quoteIdentifier(savepoint.getSavepointName(), this.session.getIdentifierQuoteString(), this.pedantic.getValue()));
java.sql.Statement stmt = null;
try {
stmt = getMetadataSafeStatement();
stmt.executeUpdate(rollbackQuery.toString());
} catch (SQLException sqlEx) {
int errno = sqlEx.getErrorCode();
if (errno == 1181) {
String msg = sqlEx.getMessage();
if (msg != null) {
int indexOfError153 = msg.indexOf("153");
if (indexOfError153 != -1) {
throw SQLError.createSQLException(Messages.getString("Connection.22", new Object[] { savepoint.getSavepointName() }),
MysqlErrorNumbers.SQLSTATE_CONNJ_ILLEGAL_ARGUMENT, errno, getExceptionInterceptor());
}
}
}
// We ignore non-transactional tables if told to do so
if (this.ignoreNonTxTables.getValue() && sqlEx.getErrorCode() != MysqlErrorNumbers.ER_WARNING_NOT_COMPLETE_ROLLBACK) {
throw sqlEx;
}
if (MysqlErrorNumbers.SQLSTATE_MYSQL_COMMUNICATION_LINK_FAILURE.equals(sqlEx.getSQLState())) {
throw SQLError.createSQLException(Messages.getString("Connection.23"),
MysqlErrorNumbers.SQLSTATE_CONNECTION_EXCEPTION_TRANSACTION_RESOLUTION_UNKNOWN, getExceptionInterceptor());
}
throw sqlEx;
} finally {
closeStatement(stmt);
}
} finally {
this.session.setNeedsPing(this.reconnectAtTxEnd.getValue());
}
} finally {
connectionLock.unlock();
}
}
private void rollbackNoChecks() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
if (this.useLocalTransactionState.getValue()) {
if (!this.session.getServerSession().inTransactionOnServer()) {
return; // effectively a no-op
}
}
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.ROLLBACK);
try (TelemetryScope scope = span.makeCurrent()) {
span.setAttribute(TelemetryAttribute.DB_NAME, getDatabase());
span.setAttribute(TelemetryAttribute.DB_OPERATION, TelemetryAttribute.OPERATION_ROLLBACK);
span.setAttribute(TelemetryAttribute.DB_STATEMENT, TelemetryAttribute.OPERATION_ROLLBACK);
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, getUser());
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
this.session.execSQL(null, "ROLLBACK", -1, null, false, this.nullStatementResultSetFactory, null, false);
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
} finally {
connectionLock.unlock();
}
}
@Override
public java.sql.PreparedStatement serverPrepareStatement(String sql) throws SQLException {
String nativeSql = this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql;
return ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, getDatabase(), DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY);
}
@Override
public java.sql.PreparedStatement serverPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException {
String nativeSql = this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql;
ClientPreparedStatement pStmt = ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, getDatabase(), DEFAULT_RESULT_SET_TYPE,
DEFAULT_RESULT_SET_CONCURRENCY);
pStmt.setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
return pStmt;
}
@Override
public java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
String nativeSql = this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql;
return ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, getDatabase(), resultSetType, resultSetConcurrency);
}
@Override
public java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
if (this.pedantic.getValue()) {
if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
throw SQLError.createSQLException(Messages.getString("Connection.17"), MysqlErrorNumbers.SQLSTATE_CONNJ_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
}
return serverPrepareStatement(sql, resultSetType, resultSetConcurrency);
}
@Override
public java.sql.PreparedStatement serverPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException {
ClientPreparedStatement pStmt = (ClientPreparedStatement) serverPrepareStatement(sql);
pStmt.setRetrieveGeneratedKeys(autoGenKeyIndexes != null && autoGenKeyIndexes.length > 0);
return pStmt;
}
@Override
public java.sql.PreparedStatement serverPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException {
ClientPreparedStatement pStmt = (ClientPreparedStatement) serverPrepareStatement(sql);
pStmt.setRetrieveGeneratedKeys(autoGenKeyColNames != null && autoGenKeyColNames.length > 0);
return pStmt;
}
@Override
public void setAutoCommit(final boolean autoCommitFlag) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
if (this.connectionLifecycleInterceptors != null) {
IterateBlock iter = new IterateBlock(
this.connectionLifecycleInterceptors.iterator()) {
@Override
void forEach(ConnectionLifecycleInterceptor each) throws SQLException {
if (!each.setAutoCommit(autoCommitFlag)) {
this.stopIterating = true;
}
}
};
iter.doForAll();
if (!iter.fullIteration()) {
return;
}
}
if (this.autoReconnectForPools.getValue()) {
this.autoReconnect.setValue(true);
}
boolean isAutoCommit = this.session.getServerSession().isAutoCommit();
try {
boolean needsSetOnServer = true;
if (this.useLocalSessionState.getValue() && isAutoCommit == autoCommitFlag) {
needsSetOnServer = false;
} else if (!this.autoReconnect.getValue()) {
needsSetOnServer = getSession().isSetNeededForAutoCommitMode(autoCommitFlag);
}
// this internal value must be set first as failover depends on it being set to true to fail over (which is done by most app servers and
// connection pools at the end of a transaction), and the driver issues an implicit set based on this value when it (re)-connects to a
// server so the value holds across connections
this.session.getServerSession().setAutoCommit(autoCommitFlag);
if (needsSetOnServer) {
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.SET_VARIABLE, "autocommit");
try (TelemetryScope scope = span.makeCurrent()) {
span.setAttribute(TelemetryAttribute.DB_NAME, getDatabase());
span.setAttribute(TelemetryAttribute.DB_OPERATION, TelemetryAttribute.OPERATION_SET);
span.setAttribute(TelemetryAttribute.DB_STATEMENT, TelemetryAttribute.OPERATION_SET + TelemetryAttribute.STATEMENT_SUFFIX);
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, getUser());
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
this.session.execSQL(null, autoCommitFlag ? "SET autocommit=1" : "SET autocommit=0", -1, null, false,
this.nullStatementResultSetFactory, null, false);
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
}
} catch (CJCommunicationsException e) {
throw e;
} catch (CJException e) {
// Reset to current autocommit value in case of an exception different than a communication exception occurs.
this.session.getServerSession().setAutoCommit(isAutoCommit);
// Update the stacktrace.
throw SQLError.createSQLException(e.getMessage(), e.getSQLState(), e.getVendorCode(), e.isTransient(), e, getExceptionInterceptor());
} finally {
if (this.autoReconnectForPools.getValue()) {
this.autoReconnect.setValue(false);
}
}
return;
} finally {
connectionLock.unlock();
}
}
@Override
public void setCatalog(final String catalog) throws SQLException {
if (this.propertySet.getEnumProperty(PropertyKey.databaseTerm).getValue() == DatabaseTerm.CATALOG) {
setDatabase(catalog);
}
}
@Override
public void setDatabase(final String db) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
if (db == null) {
throw SQLError.createSQLException("Database can not be null", MysqlErrorNumbers.SQLSTATE_CONNJ_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
if (this.connectionLifecycleInterceptors != null) {
IterateBlock iter = new IterateBlock(
this.connectionLifecycleInterceptors.iterator()) {
@Override
void forEach(ConnectionLifecycleInterceptor each) throws SQLException {
if (!each.setDatabase(db)) {
this.stopIterating = true;
}
}
};
iter.doForAll();
if (!iter.fullIteration()) {
return;
}
}
if (this.useLocalSessionState.getValue()) {
if (this.session.getServerSession().isLowerCaseTableNames()) {
if (this.database.equalsIgnoreCase(db)) {
return;
}
} else if (this.database.equals(db)) {
return;
}
}
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.USE_DATABASE);
try (TelemetryScope scope = span.makeCurrent()) {
span.setAttribute(TelemetryAttribute.DB_NAME, db);
span.setAttribute(TelemetryAttribute.DB_OPERATION, TelemetryAttribute.OPERATION_USE);
span.setAttribute(TelemetryAttribute.DB_STATEMENT, TelemetryAttribute.OPERATION_USE + TelemetryAttribute.STATEMENT_SUFFIX);
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, getUser());
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
String quotedId = this.session.getIdentifierQuoteString();
StringBuilder query = new StringBuilder("USE ");
query.append(StringUtils.quoteIdentifier(db, quotedId, this.pedantic.getValue()));
this.session.execSQL(null, query.toString(), -1, null, false, this.nullStatementResultSetFactory, null, false);
this.database = db;
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
} finally {
connectionLock.unlock();
}
}
@Override
public String getDatabase() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
return this.database;
} finally {
connectionLock.unlock();
}
}
@Override
public void setFailedOver(boolean flag) {
// handled higher up
}
@Override
public void setHoldability(int arg0) throws SQLException {
// do nothing
}
@Override
public void setInGlobalTx(boolean flag) {
this.isInGlobalTx = flag;
}
@Override
public void setReadOnly(boolean readOnlyFlag) throws SQLException {
setReadOnlyInternal(readOnlyFlag);
}
@Override
public void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
// note this this is safe even inside a transaction
if (this.readOnlyPropagatesToServer.getValue() && versionMeetsMinimum(5, 6, 5)) {
if (!this.useLocalSessionState.getValue() || readOnlyFlag != this.readOnly) {
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.SET_TRANSACTION_ACCESS_MODE,
readOnlyFlag ? "read-only" : "read-write");
try (TelemetryScope scope = span.makeCurrent()) {
span.setAttribute(TelemetryAttribute.DB_NAME, getDatabase());
span.setAttribute(TelemetryAttribute.DB_OPERATION, TelemetryAttribute.OPERATION_SET);
span.setAttribute(TelemetryAttribute.DB_STATEMENT, TelemetryAttribute.OPERATION_SET + TelemetryAttribute.STATEMENT_SUFFIX);
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, getUser());
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
this.session.execSQL(null, "SET SESSION TRANSACTION " + (readOnlyFlag ? "READ ONLY" : "READ WRITE"), -1, null, false,
this.nullStatementResultSetFactory, null, false);
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
}
}
this.readOnly = readOnlyFlag;
} finally {
connectionLock.unlock();
}
}
@Override
public java.sql.Savepoint setSavepoint() throws SQLException {
MysqlSavepoint savepoint = new MysqlSavepoint(getExceptionInterceptor());
setSavepoint(savepoint);
return savepoint;
}
private void setSavepoint(MysqlSavepoint savepoint) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
StringBuilder savePointQuery = new StringBuilder("SAVEPOINT ");
savePointQuery.append(StringUtils.quoteIdentifier(savepoint.getSavepointName(), this.session.getIdentifierQuoteString(), this.pedantic.getValue()));
java.sql.Statement stmt = null;
try {
stmt = getMetadataSafeStatement();
stmt.executeUpdate(savePointQuery.toString());
} finally {
closeStatement(stmt);
}
} finally {
connectionLock.unlock();
}
}
@Override
public java.sql.Savepoint setSavepoint(String name) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
MysqlSavepoint savepoint = new MysqlSavepoint(name, getExceptionInterceptor());
setSavepoint(savepoint);
return savepoint;
} finally {
connectionLock.unlock();
}
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
String sql = null;
boolean shouldSendSet = false;
if (this.propertySet.getBooleanProperty(PropertyKey.alwaysSendSetIsolation).getValue()) {
shouldSendSet = true;
} else if (level != this.isolationLevel) {
shouldSendSet = true;
}
if (this.useLocalSessionState.getValue()) {
shouldSendSet = this.isolationLevel != level;
}
if (shouldSendSet) {
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.SET_TRANSACTION_ISOLATION);
try (TelemetryScope scope = span.makeCurrent()) {
span.setAttribute(TelemetryAttribute.DB_NAME, getDatabase());
span.setAttribute(TelemetryAttribute.DB_OPERATION, TelemetryAttribute.OPERATION_SET);
span.setAttribute(TelemetryAttribute.DB_STATEMENT, TelemetryAttribute.OPERATION_SET + TelemetryAttribute.STATEMENT_SUFFIX);
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, getUser());
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
switch (level) {
case java.sql.Connection.TRANSACTION_NONE:
throw SQLError.createSQLException(Messages.getString("Connection.24"), getExceptionInterceptor());
case java.sql.Connection.TRANSACTION_READ_COMMITTED:
sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED";
break;
case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED:
sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
break;
case java.sql.Connection.TRANSACTION_REPEATABLE_READ:
sql = "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";
break;
case java.sql.Connection.TRANSACTION_SERIALIZABLE:
sql = "SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE";
break;
default:
throw SQLError.createSQLException(Messages.getString("Connection.25", new Object[] { level }),
MysqlErrorNumbers.SQLSTATE_CONNJ_DRIVER_NOT_CAPABLE, getExceptionInterceptor());
}
this.session.execSQL(null, sql, -1, null, false, this.nullStatementResultSetFactory, null, false);
this.isolationLevel = level;
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
}
} finally {
connectionLock.unlock();
}
}
@Override
public void setTypeMap(java.util.Map> map) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
this.typeMap = map;
} finally {
connectionLock.unlock();
}
}
private void setupServerForTruncationChecks() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
RuntimeProperty jdbcCompliantTruncation = this.propertySet.getProperty(PropertyKey.jdbcCompliantTruncation);
if (jdbcCompliantTruncation.getValue()) {
String currentSqlMode = this.session.getServerSession().getServerVariable("sql_mode");
boolean strictTransTablesIsSet = StringUtils.indexOfIgnoreCase(currentSqlMode, "STRICT_TRANS_TABLES") != -1;
if (currentSqlMode == null || currentSqlMode.length() == 0 || !strictTransTablesIsSet) {
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.SET_VARIABLE, "sql_mode");
try (TelemetryScope scope = span.makeCurrent()) {
span.setAttribute(TelemetryAttribute.DB_NAME, getDatabase());
span.setAttribute(TelemetryAttribute.DB_OPERATION, TelemetryAttribute.OPERATION_SET);
span.setAttribute(TelemetryAttribute.DB_STATEMENT, TelemetryAttribute.OPERATION_SET + TelemetryAttribute.STATEMENT_SUFFIX);
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, getUser());
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
StringBuilder commandBuf = new StringBuilder("SET sql_mode='");
if (currentSqlMode != null && currentSqlMode.length() > 0) {
commandBuf.append(currentSqlMode);
commandBuf.append(",");
}
commandBuf.append("STRICT_TRANS_TABLES'");
this.session.execSQL(null, commandBuf.toString(), -1, null, false, this.nullStatementResultSetFactory, null, false);
jdbcCompliantTruncation.setValue(false); // server's handling this for us now
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
} else if (strictTransTablesIsSet) {
// We didn't set it, but someone did, so we piggy back on it
jdbcCompliantTruncation.setValue(false); // server's handling this for us now
}
}
} finally {
connectionLock.unlock();
}
}
@Override
public void shutdownServer() throws SQLException {
try {
this.session.shutdownServer();
} catch (CJException ex) {
SQLException sqlEx = SQLError.createSQLException(Messages.getString("Connection.UnhandledExceptionDuringShutdown"),
MysqlErrorNumbers.SQLSTATE_CONNJ_GENERAL_ERROR, getExceptionInterceptor());
sqlEx.initCause(ex);
throw sqlEx;
}
}
@Override
public void unregisterStatement(JdbcStatement stmt) {
this.openStatements.remove(stmt);
}
public boolean versionMeetsMinimum(int major, int minor, int subminor) {
return this.session.versionMeetsMinimum(major, minor, subminor);
}
@Override
public CachedResultSetMetaData getCachedMetaData(String sql) {
if (this.resultSetMetadataCache != null) {
this.resultSetMetadataCacheLock.lock();
try {
return this.resultSetMetadataCache.get(sql);
} finally {
this.resultSetMetadataCacheLock.unlock();
}
}
return null; // no cache exists
}
@Override
public void initializeResultsMetadataFromCache(String sql, CachedResultSetMetaData cachedMetaData, ResultSetInternalMethods resultSet) throws SQLException {
if (cachedMetaData == null) {
// read from results
cachedMetaData = new CachedResultSetMetaDataImpl();
// assume that users will use named-based lookups
resultSet.getColumnDefinition().buildIndexMapping();
resultSet.initializeWithMetadata();
if (resultSet instanceof UpdatableResultSet) {
((UpdatableResultSet) resultSet).checkUpdatability();
}
resultSet.populateCachedMetaData(cachedMetaData);
this.resultSetMetadataCache.put(sql, cachedMetaData);
} else {
resultSet.getColumnDefinition().initializeFrom(cachedMetaData);
resultSet.initializeWithMetadata();
if (resultSet instanceof UpdatableResultSet) {
((UpdatableResultSet) resultSet).checkUpdatability();
}
}
}
@Override
public String getStatementComment() {
return this.session.getQueryComment();
}
@Override
public void setStatementComment(String comment) {
this.session.setQueryComment(comment);
}
@Override
public void transactionBegun() {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
if (this.connectionLifecycleInterceptors != null) {
this.connectionLifecycleInterceptors.stream().forEach(ConnectionLifecycleInterceptor::transactionBegun);
}
} finally {
connectionLock.unlock();
}
}
@Override
public void transactionCompleted() {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
if (this.connectionLifecycleInterceptors != null) {
this.connectionLifecycleInterceptors.stream().forEach(ConnectionLifecycleInterceptor::transactionCompleted);
}
} finally {
connectionLock.unlock();
}
}
@Override
public boolean storesLowerCaseTableName() {
return this.session.getServerSession().storesLowerCaseTableNames();
}
private ExceptionInterceptor exceptionInterceptor;
@Override
public ExceptionInterceptor getExceptionInterceptor() {
return this.exceptionInterceptor;
}
@Override
public boolean isServerLocal() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
try {
return this.session.isServerLocal(getSession());
} catch (CJException ex) {
SQLException sqlEx = SQLExceptionsMapping.translateException(ex, getExceptionInterceptor());
throw sqlEx;
}
} finally {
connectionLock.unlock();
}
}
@Override
public int getSessionMaxRows() {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
return this.session.getSessionMaxRows();
} finally {
connectionLock.unlock();
}
}
@Override
public void setSessionMaxRows(int max) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
if (this.session.getSessionMaxRows() != max) {
TelemetrySpan span = this.session.getTelemetryHandler().startSpan(TelemetrySpanName.SET_VARIABLE, "sql_select_limit");
try (TelemetryScope scope = span.makeCurrent()) {
span.setAttribute(TelemetryAttribute.DB_NAME, getDatabase());
span.setAttribute(TelemetryAttribute.DB_OPERATION, TelemetryAttribute.OPERATION_SET);
span.setAttribute(TelemetryAttribute.DB_STATEMENT, TelemetryAttribute.OPERATION_SET + TelemetryAttribute.STATEMENT_SUFFIX);
span.setAttribute(TelemetryAttribute.DB_SYSTEM, TelemetryAttribute.DB_SYSTEM_DEFAULT);
span.setAttribute(TelemetryAttribute.DB_USER, getUser());
span.setAttribute(TelemetryAttribute.THREAD_ID, Thread.currentThread().getId());
span.setAttribute(TelemetryAttribute.THREAD_NAME, Thread.currentThread().getName());
this.session.setSessionMaxRows(max);
this.session.execSQL(null,
"SET sql_select_limit=" + (this.session.getSessionMaxRows() == -1 ? "DEFAULT" : this.session.getSessionMaxRows()), -1, null, false,
this.nullStatementResultSetFactory, null, false);
} catch (Throwable t) {
span.setError(t);
throw t;
} finally {
span.end();
}
}
} finally {
connectionLock.unlock();
}
}
@Override
public void setSchema(String schema) throws SQLException {
checkClosed();
if (this.propertySet.getEnumProperty(PropertyKey.databaseTerm).getValue() == DatabaseTerm.SCHEMA) {
setDatabase(schema);
}
}
@Override
public String getSchema() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
return this.propertySet.getEnumProperty(PropertyKey.databaseTerm).getValue() == DatabaseTerm.SCHEMA ? this.database : null;
} finally {
connectionLock.unlock();
}
}
@Override
public void abort(Executor executor) throws SQLException {
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
sec.checkPermission(ABORT_PERM);
}
if (executor == null) {
throw SQLError.createSQLException(Messages.getString("Connection.26"), MysqlErrorNumbers.SQLSTATE_CONNJ_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
executor.execute(() -> {
try {
abortInternal();
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
}
@Override
public void setNetworkTimeout(Executor executor, final int milliseconds) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
sec.checkPermission(SET_NETWORK_TIMEOUT_PERM);
}
if (executor == null) {
throw SQLError.createSQLException(Messages.getString("Connection.26"), MysqlErrorNumbers.SQLSTATE_CONNJ_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
checkClosed();
executor.execute(new NetworkTimeoutSetter(this, milliseconds));
} finally {
connectionLock.unlock();
}
}
private static class NetworkTimeoutSetter implements Runnable {
private final WeakReference connRef;
private final int milliseconds;
public NetworkTimeoutSetter(JdbcConnection conn, int milliseconds) {
this.connRef = new WeakReference<>(conn);
this.milliseconds = milliseconds;
}
@Override
public void run() {
JdbcConnection conn = this.connRef.get();
if (conn != null) {
Lock connectionLock = conn.getConnectionLock();
connectionLock.lock();
try {
((NativeSession) conn.getSession()).setSocketTimeout(this.milliseconds);
} finally {
connectionLock.unlock();
}
}
}
}
@Override
public int getNetworkTimeout() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
checkClosed();
return this.session.getSocketTimeout();
} finally {
connectionLock.unlock();
}
}
@Override
public Clob createClob() {
return new com.mysql.cj.jdbc.Clob(getExceptionInterceptor());
}
@Override
public Blob createBlob() {
return new com.mysql.cj.jdbc.Blob(getExceptionInterceptor());
}
@Override
public NClob createNClob() {
return new com.mysql.cj.jdbc.NClob(getExceptionInterceptor());
}
@Override
public SQLXML createSQLXML() throws SQLException {
return new MysqlSQLXML(getExceptionInterceptor());
}
@Override
public boolean isValid(int timeout) throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
if (isClosed()) {
return false;
}
try {
try {
pingInternal(false, timeout * 1000);
} catch (Throwable t) {
try {
abortInternal();
} catch (Throwable ignoreThrown) {
// we're dead now anyway
}
return false;
}
} catch (Throwable t) {
return false;
}
return true;
} finally {
connectionLock.unlock();
}
}
private ClientInfoProvider infoProvider;
@Override
public ClientInfoProvider getClientInfoProviderImpl() throws SQLException {
Lock connectionLock = getConnectionLock();
connectionLock.lock();
try {
if (this.infoProvider == null) {
String clientInfoProvider = this.propertySet.getStringProperty(PropertyKey.clientInfoProvider).getStringValue();
try {
this.infoProvider = Util.getInstance(ClientInfoProvider.class, clientInfoProvider, null, null, getExceptionInterceptor());
} catch (CJException e1) {
if (ClassNotFoundException.class.isInstance(e1.getCause())) {
// Retry with package name prepended.
try {
this.infoProvider = Util.getInstance(ClientInfoProvider.class, "com.mysql.cj.jdbc." + clientInfoProvider, null, null,
getExceptionInterceptor());
} catch (CJException e2) {
throw SQLExceptionsMapping.translateException(e1, getExceptionInterceptor());
}
} else {
throw SQLExceptionsMapping.translateException(e1, getExceptionInterceptor());
}
}
this.infoProvider.initialize(this, this.props);
}
return this.infoProvider;
} finally {
connectionLock.unlock();
}
}
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
try {
getClientInfoProviderImpl().setClientInfo(this, name, value);
} catch (SQLClientInfoException ciEx) {
throw ciEx;
} catch (SQLException | CJException sqlEx) {
SQLClientInfoException clientInfoEx = new SQLClientInfoException();
clientInfoEx.initCause(sqlEx);
throw clientInfoEx;
}
}
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
try {
getClientInfoProviderImpl().setClientInfo(this, properties);
} catch (SQLClientInfoException ciEx) {
throw ciEx;
} catch (SQLException | CJException sqlEx) {
SQLClientInfoException clientInfoEx = new SQLClientInfoException();
clientInfoEx.initCause(sqlEx);
throw clientInfoEx;
}
}
@Override
public String getClientInfo(String name) throws SQLException {
return getClientInfoProviderImpl().getClientInfo(this, name);
}
@Override
public Properties getClientInfo() throws SQLException {
return getClientInfoProviderImpl().getClientInfo(this);
}
@Override
public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException {
throw SQLError.createSQLFeatureNotSupportedException();
}
@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
throw SQLError.createSQLFeatureNotSupportedException();
}
@Override
public T unwrap(java.lang.Class iface) throws java.sql.SQLException {
try {
// This works for classes that aren't actually wrapping
// anything
return iface.cast(this);
} catch (ClassCastException cce) {
throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), MysqlErrorNumbers.SQLSTATE_CONNJ_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
}
@Override
public boolean isWrapperFor(Class> iface) throws SQLException {
// This works for classes that aren't actually wrapping
// anything
return iface.isInstance(this);
}
@Override
public NativeSession getSession() {
return this.session;
}
@Override
public String getHostPortPair() {
return this.origHostInfo.getHostPortPair();
}
@Override
public void handleNormalClose() {
try {
close();
} catch (SQLException e) {
ExceptionFactory.createException(e.getMessage(), e);
}
}
@Override
public void handleReconnect() {
createNewIO(true);
}
@Override
public void handleCleanup(Throwable whyCleanedUp) {
cleanup(whyCleanedUp);
}
@Override
public ServerSessionStateController getServerSessionStateController() {
return this.session.getServerSession().getServerSessionStateController();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy