All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mysql.cj.jdbc.ConnectionImpl Maven / Gradle / Ivy

There is a newer version: 1.0.0-beta2
Show newest version
/*
 * 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); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy