com.mysql.cj.jdbc.ConnectionImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2002, 2020, 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 also distributed with certain software (including but not
* limited to OpenSSL) 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 included with MySQL.
*
* 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.lang.reflect.InvocationHandler;
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.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Stack;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
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.ParseInfo;
import com.mysql.cj.PreparedQuery;
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.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.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.log.StandardLogger;
import com.mysql.cj.protocol.SocksProxySocketFactory;
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 InvocationHandler realProxy = null;
@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 : (JdbcConnection) this;
}
@Override
public JdbcConnection getMultiHostSafeProxy() {
return this.getProxy();
}
@Override
public JdbcConnection getMultiHostParentProxy() {
return this.parentProxy;
}
@Override
public JdbcConnection getActiveMySQLConnection() {
return this;
}
@Override
public Object getConnectionMutex() {
return (this.realProxy != null) ? this.realProxy : getProxy();
}
/**
* Used as a key for caching callable statements which (may) depend on
* current database...In 5.0.x, they don't (currently), but stored procedure
* names soon will, so current database is a (hidden) component of the name.
*/
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;
}
}
/**
* The mapping between MySQL charset names and Java charset names.
* Initialized by loadCharacterSetMapping()
*/
public static Map, ?> charsetMap;
/** Default logger class name */
protected static final String DEFAULT_LOGGER_CLASS = StandardLogger.class.getName();
/**
* Map mysql transaction isolation level name to
* java.sql.Connection.TRANSACTION_XXX
*/
private static Map mapTransIsolationNameToValue = null;
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;
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);
}
/**
* 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);
}
private static final Random random = new Random();
/**
* @param url
* connection URL
* @param hostList
* hosts list
* @return index in a host list
*/
protected static synchronized int getNextRoundRobinHostIndex(String url, List> hostList) {
// we really do "random" here, because you don't get even distribution when this is coupled with connection pools
int indexRange = hostList.size();
int index = random.nextInt(indexRange);
return index;
}
private static boolean nullSafeCompare(String s1, String s2) {
if (s1 == null && s2 == null) {
return true;
}
if (s1 == null && s2 != null) {
return false;
}
return s1 != null && s1.equals(s2);
}
/** A cache of SQL to parsed prepared statement parameters. */
private CacheAdapter cachedPreparedStatementParams;
/** 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;
/** When did the master fail? */
// private long masterFailTimeMillis = 0L;
/**
* 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;
/** 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;
/**
* 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 LRUCache serverSideStatementCache;
private HostInfo origHostInfo;
private String origHostToConnectTo;
// we don't want to be able to publicly clone this...
private int origPortToConnectTo;
/*
* For testing failover scenarios
*/
private boolean hasTriedMasterFlag = false;
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;
/**
* '
* 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 = StringUtils.isNullOrEmpty(hostInfo.getUser()) ? "" : hostInfo.getUser();
this.password = StringUtils.isNullOrEmpty(hostInfo.getPassword()) ? "" : 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
// 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);
String exceptionInterceptorClasses = this.propertySet.getStringProperty(PropertyKey.exceptionInterceptors).getStringValue();
if (exceptionInterceptorClasses != null && !"".equals(exceptionInterceptorClasses)) {
this.exceptionInterceptor = new ExceptionInterceptorChain(exceptionInterceptorClasses, this.props, this.session.getLog());
}
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 e1) {
throw SQLExceptionsMapping.translateException(e1, getExceptionInterceptor());
}
try {
createNewIO(false);
unSafeQueryInterceptors();
AbandonedConnectionCleanupThread.trackConnection(this, this.getSession().getNetworkResources());
} 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.SQL_STATE_COMMUNICATION_LINK_FAILURE, ex, getExceptionInterceptor());
}
}
@Override
public JdbcPropertySet getPropertySet() {
return this.propertySet;
}
@Override
public void unSafeQueryInterceptors() throws SQLException {
this.queryInterceptors = this.queryInterceptors.stream().map(u -> ((NoSubInterceptorWrapper) u).getUnderlyingInterceptor())
.collect(Collectors.toList());
if (this.session != null) {
this.session.setQueryInterceptors(this.queryInterceptors);
}
}
@Override
public void initializeSafeQueryInterceptors() throws SQLException {
this.queryInterceptors = Util
.loadClasses(this.propertySet.getStringProperty(PropertyKey.queryInterceptors).getStringValue(),
"MysqlIo.BadQueryInterceptor", getExceptionInterceptor())
.stream().map(o -> new NoSubInterceptorWrapper(o.init(this, this.props, this.session.getLog()))).collect(Collectors.toList());
}
@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();
if (this.cachePrepStmts.getValue()) {
synchronized (this.serverSideStatementCheckCache) {
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;
}
}
return StringUtils.canHandleAsServerPreparedStatementNoCache(sql, getServerVersion(), allowMultiQueries,
this.session.getServerSession().isNoBackslashEscapesSet(), this.session.getServerSession().useAnsiQuotedIdentifiers());
}
@Override
public void changeUser(String userName, String newPassword) throws SQLException {
synchronized (getConnectionMutex()) {
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.configureClientCharacterSet(true);
this.session.setSessionVariables();
setupServerForTruncationChecks();
}
}
@Override
public void checkClosed() {
this.session.checkClosed();
}
@Override
public void throwConnectionClosedException() throws SQLException {
SQLException ex = SQLError.createSQLException(Messages.getString("Connection.2"), MysqlErrorNumbers.SQL_STATE_CONNECTION_NOT_OPEN,
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 {
realClose(false, false, false, whyCleanedUp);
}
}
} catch (SQLException | CJException sqlEx) {
// ignore, we're going away.
}
}
@Deprecated
@Override
public void clearHasTriedMaster() {
this.hasTriedMasterFlag = false;
}
@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()) {
ParseInfo pStmtInfo = this.cachedPreparedStatementParams.get(nativeSql);
if (pStmtInfo == null) {
pStmt = ClientPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database);
this.cachedPreparedStatementParams.put(nativeSql, pStmt.getParseInfo());
} 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 {
synchronized (getConnectionMutex()) {
if (this.connectionLifecycleInterceptors != null) {
for (ConnectionLifecycleInterceptor cli : this.connectionLifecycleInterceptors) {
cli.close();
}
}
realClose(true, true, false, null);
}
}
@Override
public void normalClose() {
try {
close();
} catch (SQLException e) {
ExceptionFactory.createException(e.getMessage(), e);
}
}
/**
* 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 {
((StatementImpl) stmt).realClose(false, true);
} 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 {
synchronized (getConnectionMutex()) {
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
}
}
this.session.execSQL(null, "commit", -1, null, false, this.nullStatementResultSetFactory, null, false);
} catch (SQLException sqlException) {
if (MysqlErrorNumbers.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlException.getSQLState())) {
throw SQLError.createSQLException(Messages.getString("Connection.4"), MysqlErrorNumbers.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN,
getExceptionInterceptor());
}
throw sqlException;
} finally {
this.session.setNeedsPing(this.reconnectAtTxEnd.getValue());
}
}
return;
}
@Override
public void createNewIO(boolean isForReconnect) {
synchronized (getConnectionMutex()) {
// Synchronization Not needed for *new* connections, but defintely 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);
}
}
}
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;
synchronized (getConnectionMutex()) {
// save state from old connection
oldAutoCommit = getAutoCommit();
oldIsolationLevel = this.isolationLevel;
oldReadOnly = isReadOnly(false);
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);
}
connectionGood = true;
break;
} catch (UnableToConnectException rejEx) {
close();
this.session.getProtocol().getSocketConnection().forceClose();
} catch (Exception EEE) {
connectionException = EEE;
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.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, 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();
this.session.getProtocol().getSocketConnection().forceClose();
throw rejEx;
} catch (Exception EEE) {
if ((EEE instanceof PasswordExpiredException
|| EEE instanceof SQLException && ((SQLException) EEE).getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD)
&& !this.disconnectOnExpiredPasswords.getValue()) {
return;
}
if (this.session != null) {
this.session.forceClose();
}
connectionNotEstablishedBecause = EEE;
if (EEE instanceof SQLException) {
throw (SQLException) EEE;
}
if (EEE.getCause() != null && EEE.getCause() instanceof SQLException) {
throw (SQLException) EEE.getCause();
}
if (EEE instanceof CJException) {
throw (CJException) EEE;
}
SQLException chainedEx = SQLError.createSQLException(Messages.getString("Connection.UnableToConnect"),
MysqlErrorNumbers.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, 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 {
synchronized (getConnectionMutex()) {
int cacheSize = this.propertySet.getIntegerProperty(PropertyKey.prepStmtCacheSize).getValue();
String parseInfoCacheFactory = this.propertySet.getStringProperty(PropertyKey.parseInfoCacheFactory).getValue();
try {
Class> factoryClass;
factoryClass = Class.forName(parseInfoCacheFactory);
@SuppressWarnings("unchecked")
CacheAdapterFactory cacheFactory = ((CacheAdapterFactory) factoryClass.newInstance());
this.cachedPreparedStatementParams = cacheFactory.getInstance(this, this.origHostInfo.getDatabaseUrl(), cacheSize,
this.prepStmtCacheSqlLimit.getValue());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
SQLException sqlEx = SQLError.createSQLException(
Messages.getString("Connection.CantFindCacheFactory", new Object[] { parseInfoCacheFactory, PropertyKey.parseInfoCacheFactory }),
getExceptionInterceptor());
sqlEx.initCause(e);
throw sqlEx;
} catch (Exception e) {
SQLException sqlEx = SQLError.createSQLException(
Messages.getString("Connection.CantLoadCacheFactory", new Object[] { parseInfoCacheFactory, PropertyKey.parseInfoCacheFactory }),
getExceptionInterceptor());
sqlEx.initCause(e);
throw sqlEx;
}
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.realClose(true, true);
} catch (SQLException sqlEx) {
// punt
}
}
return removeIt;
}
};
}
}
}
@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.SQL_STATE_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
}
return createStatement(resultSetType, resultSetConcurrency);
}
@Override
public int getActiveStatementCount() {
return this.openStatements.size();
}
@Override
public boolean getAutoCommit() throws SQLException {
synchronized (getConnectionMutex()) {
return this.session.getServerSession().isAutoCommit();
}
}
@Override
public String getCatalog() throws SQLException {
synchronized (getConnectionMutex()) {
return this.propertySet.getEnumProperty(PropertyKey.databaseTerm).getValue() == DatabaseTerm.SCHEMA ? null : this.database;
}
}
@Override
public String getCharacterSetMetadata() {
synchronized (getConnectionMutex()) {
return this.session.getServerSession().getCharacterSetMetadata();
}
}
@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() {
synchronized (getConnectionMutex()) {
return this.session.getIdleFor();
}
}
@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().getCharacterSetMetadata());
dbmeta.setMetadataCollationIndex(getSession().getServerSession().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 {
synchronized (getConnectionMutex()) {
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.SQL_STATE_GENERAL_ERROR,
getExceptionInterceptor());
}
throw SQLError.createSQLException(Messages.getString("Connection.13"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
}
return this.isolationLevel;
}
}
@Override
public java.util.Map> getTypeMap() throws SQLException {
synchronized (getConnectionMutex()) {
if (this.typeMap == null) {
this.typeMap = new HashMap<>();
}
return this.typeMap;
}
}
@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;
}
@Deprecated
@Override
public boolean hasTriedMaster() {
return this.hasTriedMasterFlag;
}
/**
* 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(
this.propertySet.getStringProperty(PropertyKey.connectionLifecycleInterceptors).getStringValue(),
"Connection.badLifecycleInterceptor", getExceptionInterceptor())
.stream().map(o -> o.init(this, this.props, this.session.getLog())).collect(Collectors.toList());
} catch (CJException e) {
throw SQLExceptionsMapping.translateException(e, getExceptionInterceptor());
}
}
this.session.setSessionVariables();
this.session.loadServerVariables(this.getConnectionMutex(), this.dbmd.getDriverVersion());
this.autoIncrementIncrement = this.session.getServerSession().getServerVariable("auto_increment_increment", 1);
this.session.buildCollationMapping();
try {
LicenseConfiguration.checkLicenseType(this.session.getServerSession().getServerVariables());
} catch (CJException e) {
throw SQLError.createSQLException(e.getMessage(), MysqlErrorNumbers.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor());
}
this.session.getProtocol().initServerSession();
checkTransactionIsolationLevel();
this.session.checkForCharsetMismatch();
this.session.configureClientCharacterSet(false);
handleAutoCommitDefaults();
//
// We need to figure out what character set metadata and error messages will be returned in, and then map them to Java encoding names
//
// We've already set it, and it might be different than what was originally on the server, which is why we use the "special" key to retrieve it
this.session.getServerSession().configureCharacterSets();
((com.mysql.cj.jdbc.DatabaseMetaData) this.dbmd).setMetadataEncoding(getSession().getServerSession().getCharacterSetMetadata());
((com.mysql.cj.jdbc.DatabaseMetaData) this.dbmd).setMetadataCollationIndex(getSession().getServerSession().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 isMasterConnection() {
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) {
synchronized (getConnectionMutex()) {
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 (!nullSafeCompare(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 (!nullSafeCompare(otherOrigDatabase, this.origHostInfo.getDatabase()) || !nullSafeCompare(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 = nullSafeCompare(otherResourceId, myResourceId);
if (directCompare) {
return true;
}
}
return false;
}
}
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().getServerTimeZone(),
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().getServerTimeZone(),
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 {
CallableStatement cStmt = null;
if (!this.propertySet.getBooleanProperty(PropertyKey.cacheCallableStmts).getValue()) {
cStmt = parseCallableStatement(sql);
} else {
synchronized (this.parsedCallableStatementCache) {
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);
synchronized (cStmt) {
cachedParamInfo = cStmt.paramInfo;
}
this.parsedCallableStatementCache.put(key, cachedParamInfo);
}
}
}
cStmt.setResultSetType(resultSetType);
cStmt.setResultSetConcurrency(resultSetConcurrency);
return cStmt;
}
@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.SQL_STATE_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 {
synchronized (getConnectionMutex()) {
checkClosed();
//
// 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()) {
synchronized (this.serverSideStatementCache) {
pStmt = this.serverSideStatementCache.remove(new CompoundCacheKey(this.database, sql));
if (pStmt != null) {
((com.mysql.cj.jdbc.ServerPreparedStatement) pStmt).setClosed(false);
pStmt.clearParameters();
}
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;
}
}
}
}
} 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);
}
return pStmt;
}
}
@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.SQL_STATE_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;
}
@Override
public void realClose(boolean calledExplicitly, boolean issueRollback, boolean skipLocalTeardown, Throwable reason) throws SQLException {
SQLException sqlEx = null;
if (this.isClosed()) {
return;
}
this.session.setForceClosedReason(reason);
try {
if (!skipLocalTeardown) {
if (!getAutoCommit() && issueRollback) {
try {
rollback();
} catch (SQLException ex) {
sqlEx = ex;
}
}
if (this.propertySet.getBooleanProperty(PropertyKey.gatherPerfMetrics).getValue()) {
this.session.getProtocol().getMetricsHolder().reportMetrics(this.session.getLog());
}
if (this.useUsageAdvisor.getValue()) {
if (!calledExplicitly) {
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"));
}
}
try {
closeAllOpenStatements();
} catch (SQLException ex) {
sqlEx = ex;
}
this.session.quit();
} else {
this.session.forceClose();
}
if (this.queryInterceptors != null) {
for (int i = 0; i < this.queryInterceptors.size(); i++) {
this.queryInterceptors.get(i).destroy();
}
}
if (this.exceptionInterceptor != null) {
this.exceptionInterceptor.destroy();
}
} finally {
this.openStatements.clear();
this.queryInterceptors = null;
this.exceptionInterceptor = null;
this.nullStatementResultSetFactory = null;
}
if (sqlEx != null) {
throw sqlEx;
}
}
@Override
public void recachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException {
synchronized (getConnectionMutex()) {
if (this.cachePrepStmts.getValue() && pstmt.isPoolable()) {
synchronized (this.serverSideStatementCache) {
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).realClose(true, true);
}
}
}
}
}
@Override
public void decachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException {
synchronized (getConnectionMutex()) {
if (this.cachePrepStmts.getValue()) {
synchronized (this.serverSideStatementCache) {
this.serverSideStatementCache
.remove(new CompoundCacheKey(pstmt.getCurrentDatabase(), ((PreparedQuery>) pstmt.getQuery()).getOriginalSql()));
}
}
}
}
@Override
public void registerStatement(JdbcStatement stmt) {
this.openStatements.addIfAbsent(stmt);
}
@Override
public void releaseSavepoint(Savepoint arg0) throws SQLException {
// this is a no-op
}
@Override
public void resetServerState() throws SQLException {
if (!this.propertySet.getBooleanProperty(PropertyKey.paranoid).getValue() && (this.session != null)) {
changeUser(this.user, this.password);
}
}
@Override
public void rollback() throws SQLException {
synchronized (getConnectionMutex()) {
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.SQL_STATE_CONNECTION_NOT_OPEN,
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.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlException.getSQLState())) {
throw SQLError.createSQLException(Messages.getString("Connection.21"), MysqlErrorNumbers.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN,
getExceptionInterceptor());
}
throw sqlException;
} finally {
this.session.setNeedsPing(this.reconnectAtTxEnd.getValue());
}
}
}
@Override
public void rollback(final Savepoint savepoint) throws SQLException {
synchronized (getConnectionMutex()) {
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('`');
rollbackQuery.append(savepoint.getSavepointName());
rollbackQuery.append('`');
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.SQL_STATE_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.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlEx.getSQLState())) {
throw SQLError.createSQLException(Messages.getString("Connection.23"), MysqlErrorNumbers.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN,
getExceptionInterceptor());
}
throw sqlEx;
} finally {
closeStatement(stmt);
}
} finally {
this.session.setNeedsPing(this.reconnectAtTxEnd.getValue());
}
}
}
private void rollbackNoChecks() throws SQLException {
synchronized (getConnectionMutex()) {
if (this.useLocalTransactionState.getValue()) {
if (!this.session.getServerSession().inTransactionOnServer()) {
return; // effectively a no-op
}
}
this.session.execSQL(null, "rollback", -1, null, false, this.nullStatementResultSetFactory, null, false);
}
}
@Override
public java.sql.PreparedStatement serverPrepareStatement(String sql) throws SQLException {
String nativeSql = this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql;
return ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.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, this.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, this.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.SQL_STATE_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 {
synchronized (getConnectionMutex()) {
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);
}
try {
boolean needsSetOnServer = true;
if (this.useLocalSessionState.getValue() && this.session.getServerSession().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) {
this.session.execSQL(null, autoCommitFlag ? "SET autocommit=1" : "SET autocommit=0", -1, null, false, this.nullStatementResultSetFactory,
null, false);
}
} finally {
if (this.autoReconnectForPools.getValue()) {
this.autoReconnect.setValue(false);
}
}
return;
}
}
@Override
public void setCatalog(final String catalog) throws SQLException {
if (this.propertySet.getEnumProperty(PropertyKey.databaseTerm).getValue() == DatabaseTerm.CATALOG) {
setDatabase(catalog);
}
}
public void setDatabase(final String db) throws SQLException {
synchronized (getConnectionMutex()) {
checkClosed();
if (db == null) {
throw SQLError.createSQLException("Database can not be null", MysqlErrorNumbers.SQL_STATE_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;
}
}
}
String quotedId = this.session.getIdentifierQuoteString();
if ((quotedId == null) || quotedId.equals(" ")) {
quotedId = "";
}
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;
}
}
@Override
public String getDatabase() throws SQLException {
synchronized (getConnectionMutex()) {
return this.database;
}
}
@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 {
synchronized (getConnectionMutex()) {
// note this this is safe even inside a transaction
if (this.readOnlyPropagatesToServer.getValue() && versionMeetsMinimum(5, 6, 5)) {
if (!this.useLocalSessionState.getValue() || (readOnlyFlag != this.readOnly)) {
this.session.execSQL(null, "set session transaction " + (readOnlyFlag ? "read only" : "read write"), -1, null, false,
this.nullStatementResultSetFactory, null, false);
}
}
this.readOnly = readOnlyFlag;
}
}
@Override
public java.sql.Savepoint setSavepoint() throws SQLException {
MysqlSavepoint savepoint = new MysqlSavepoint(getExceptionInterceptor());
setSavepoint(savepoint);
return savepoint;
}
private void setSavepoint(MysqlSavepoint savepoint) throws SQLException {
synchronized (getConnectionMutex()) {
checkClosed();
StringBuilder savePointQuery = new StringBuilder("SAVEPOINT ");
savePointQuery.append('`');
savePointQuery.append(savepoint.getSavepointName());
savePointQuery.append('`');
java.sql.Statement stmt = null;
try {
stmt = getMetadataSafeStatement();
stmt.executeUpdate(savePointQuery.toString());
} finally {
closeStatement(stmt);
}
}
}
@Override
public java.sql.Savepoint setSavepoint(String name) throws SQLException {
synchronized (getConnectionMutex()) {
MysqlSavepoint savepoint = new MysqlSavepoint(name, getExceptionInterceptor());
setSavepoint(savepoint);
return savepoint;
}
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
synchronized (getConnectionMutex()) {
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) {
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.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor());
}
this.session.execSQL(null, sql, -1, null, false, this.nullStatementResultSetFactory, null, false);
this.isolationLevel = level;
}
}
}
@Override
public void setTypeMap(java.util.Map> map) throws SQLException {
synchronized (getConnectionMutex()) {
this.typeMap = map;
}
}
private void setupServerForTruncationChecks() throws SQLException {
synchronized (getConnectionMutex()) {
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) {
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
} 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
}
}
}
}
@Override
public void shutdownServer() throws SQLException {
try {
this.session.shutdownServer();
} catch (CJException ex) {
SQLException sqlEx = SQLError.createSQLException(Messages.getString("Connection.UnhandledExceptionDuringShutdown"),
MysqlErrorNumbers.SQL_STATE_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) {
synchronized (this.resultSetMetadataCache) {
return this.resultSetMetadataCache.get(sql);
}
}
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.getProtocol().getQueryComment();
}
@Override
public void setStatementComment(String comment) {
this.session.getProtocol().setQueryComment(comment);
}
@Override
public void transactionBegun() {
synchronized (getConnectionMutex()) {
if (this.connectionLifecycleInterceptors != null) {
this.connectionLifecycleInterceptors.stream().forEach(ConnectionLifecycleInterceptor::transactionBegun);
}
}
}
@Override
public void transactionCompleted() {
synchronized (getConnectionMutex()) {
if (this.connectionLifecycleInterceptors != null) {
this.connectionLifecycleInterceptors.stream().forEach(ConnectionLifecycleInterceptor::transactionCompleted);
}
}
}
@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 {
synchronized (getConnectionMutex()) {
try {
return this.session.isServerLocal(this.getSession());
} catch (CJException ex) {
SQLException sqlEx = SQLExceptionsMapping.translateException(ex, getExceptionInterceptor());
throw sqlEx;
}
}
}
@Override
public int getSessionMaxRows() {
synchronized (getConnectionMutex()) {
return this.session.getSessionMaxRows();
}
}
@Override
public void setSessionMaxRows(int max) throws SQLException {
synchronized (getConnectionMutex()) {
if (this.session.getSessionMaxRows() != max) {
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);
}
}
}
@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 {
synchronized (getConnectionMutex()) {
checkClosed();
return this.propertySet.getEnumProperty(PropertyKey.databaseTerm).getValue() == DatabaseTerm.SCHEMA ? this.database : null;
}
}
@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.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
executor.execute(new Runnable() {
@Override
public void run() {
try {
abortInternal();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
});
}
@Override
public void setNetworkTimeout(Executor executor, final int milliseconds) throws SQLException {
synchronized (getConnectionMutex()) {
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
sec.checkPermission(SET_NETWORK_TIMEOUT_PERM);
}
if (executor == null) {
throw SQLError.createSQLException(Messages.getString("Connection.26"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
checkClosed();
executor.execute(new NetworkTimeoutSetter(this, milliseconds));
}
}
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) {
synchronized (conn.getConnectionMutex()) {
((NativeSession) conn.getSession()).setSocketTimeout(this.milliseconds);
}
}
}
}
@Override
public int getNetworkTimeout() throws SQLException {
synchronized (getConnectionMutex()) {
checkClosed();
return this.session.getSocketTimeout();
}
}
@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 {
synchronized (getConnectionMutex()) {
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;
}
}
private ClientInfoProvider infoProvider;
@Override
public ClientInfoProvider getClientInfoProviderImpl() throws SQLException {
synchronized (getConnectionMutex()) {
if (this.infoProvider == null) {
String clientInfoProvider = this.propertySet.getStringProperty(PropertyKey.clientInfoProvider).getStringValue();
try {
try {
this.infoProvider = (ClientInfoProvider) Util.getInstance(clientInfoProvider, new Class>[0], new Object[0],
getExceptionInterceptor());
} catch (CJException ex1) {
try {
// try with package name prepended
this.infoProvider = (ClientInfoProvider) Util.getInstance("com.mysql.cj.jdbc." + clientInfoProvider, new Class>[0], new Object[0],
getExceptionInterceptor());
} catch (CJException ex2) {
throw SQLExceptionsMapping.translateException(ex1, getExceptionInterceptor());
}
}
} catch (ClassCastException ex) {
throw SQLError.createSQLException(Messages.getString("Connection.ClientInfoNotImplemented", new Object[] { clientInfoProvider }),
MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
this.infoProvider.initialize(this, this.props);
}
return this.infoProvider;
}
}
@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.SQL_STATE_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);
}
}