com.mysql.cj.jdbc.ConnectionImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mysql-connector-java
Show all versions of mysql-connector-java
MySQL JDBC Type 4 driver
/*
Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
The MySQL Connector/J is licensed under the terms of the GPLv2
, like most MySQL Connectors.
There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
this software, see the FOSS License Exception
.
This program is free software; you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation; version 2
of the License.
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 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.lang.reflect.InvocationHandler;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
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.ResultSetMetaData;
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.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Stack;
import java.util.Timer;
import java.util.TreeMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import com.mysql.cj.api.CacheAdapter;
import com.mysql.cj.api.CacheAdapterFactory;
import com.mysql.cj.api.ProfilerEvent;
import com.mysql.cj.api.conf.ModifiableProperty;
import com.mysql.cj.api.conf.ReadableProperty;
import com.mysql.cj.api.exceptions.ExceptionInterceptor;
import com.mysql.cj.api.io.ServerSession;
import com.mysql.cj.api.jdbc.ClientInfoProvider;
import com.mysql.cj.api.jdbc.JdbcConnection;
import com.mysql.cj.api.jdbc.Statement;
import com.mysql.cj.api.jdbc.interceptors.ConnectionLifecycleInterceptor;
import com.mysql.cj.api.jdbc.interceptors.StatementInterceptor;
import com.mysql.cj.api.jdbc.result.ResultSetInternalMethods;
import com.mysql.cj.api.log.Log;
import com.mysql.cj.api.mysqla.io.PacketPayload;
import com.mysql.cj.api.mysqla.result.ColumnDefinition;
import com.mysql.cj.core.CharsetMapping;
import com.mysql.cj.core.Constants;
import com.mysql.cj.core.LicenseConfiguration;
import com.mysql.cj.core.Messages;
import com.mysql.cj.core.ServerVersion;
import com.mysql.cj.core.conf.PropertyDefinitions;
import com.mysql.cj.core.conf.url.HostInfo;
import com.mysql.cj.core.exceptions.CJException;
import com.mysql.cj.core.exceptions.ConnectionIsClosedException;
import com.mysql.cj.core.exceptions.ExceptionFactory;
import com.mysql.cj.core.exceptions.MysqlErrorNumbers;
import com.mysql.cj.core.exceptions.PasswordExpiredException;
import com.mysql.cj.core.exceptions.UnableToConnectException;
import com.mysql.cj.core.exceptions.WrongArgumentException;
import com.mysql.cj.core.io.SocksProxySocketFactory;
import com.mysql.cj.core.log.LogFactory;
import com.mysql.cj.core.log.StandardLogger;
import com.mysql.cj.core.profiler.ProfilerEventHandlerFactory;
import com.mysql.cj.core.profiler.ProfilerEventImpl;
import com.mysql.cj.core.util.LRUCache;
import com.mysql.cj.core.util.LogUtils;
import com.mysql.cj.core.util.StringUtils;
import com.mysql.cj.core.util.Util;
import com.mysql.cj.jdbc.PreparedStatement.ParseInfo;
import com.mysql.cj.jdbc.exceptions.CommunicationsException;
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.NoSubInterceptorWrapper;
import com.mysql.cj.jdbc.io.ResultSetFactory;
import com.mysql.cj.jdbc.result.CachedResultSetMetaData;
import com.mysql.cj.jdbc.result.UpdatableResultSet;
import com.mysql.cj.jdbc.util.ResultSetUtil;
import com.mysql.cj.mysqla.MysqlaConstants;
import com.mysql.cj.mysqla.MysqlaSession;
import com.mysql.cj.mysqla.MysqlaUtils;
/**
* 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 extends AbstractJdbcConnection implements JdbcConnection {
private static final long serialVersionUID = 2877471301981509474L;
private static final SQLPermission SET_NETWORK_TIMEOUT_PERM = new SQLPermission("setNetworkTimeout");
private static final SQLPermission ABORT_PERM = new SQLPermission("abort");
public String getHost() {
return this.session.getHostInfo().getHost();
}
private JdbcConnection proxy = null;
private InvocationHandler realProxy = null;
public boolean isProxySet() {
return this.proxy != null;
}
public void setProxy(JdbcConnection proxy) {
this.proxy = proxy;
this.realProxy = this.proxy 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.proxy != null) ? this.proxy : (JdbcConnection) this;
}
public JdbcConnection getMultiHostSafeProxy() {
return this.getProxy();
}
public Object getConnectionMutex() {
return (this.realProxy != null) ? this.realProxy : getProxy();
}
public class ExceptionInterceptorChain implements ExceptionInterceptor {
List interceptors;
ExceptionInterceptorChain(String interceptorClasses) {
this.interceptors = Util. loadClasses(interceptorClasses, "Connection.BadExceptionInterceptor", this).stream()
.map(o -> o.init(ConnectionImpl.this.props, ConnectionImpl.this.getSession().getLog())).collect(Collectors.toList());
}
void addRingZero(ExceptionInterceptor interceptor) throws SQLException {
this.interceptors.add(0, interceptor);
}
public Exception interceptException(Exception sqlEx) {
if (this.interceptors != null) {
Iterator iter = this.interceptors.iterator();
while (iter.hasNext()) {
sqlEx = iter.next().interceptException(sqlEx);
}
}
return sqlEx;
}
public void destroy() {
if (this.interceptors != null) {
Iterator iter = this.interceptors.iterator();
while (iter.hasNext()) {
iter.next().destroy();
}
}
}
public ExceptionInterceptor init(Properties properties, Log log) {
if (this.interceptors != null) {
Iterator iter = this.interceptors.iterator();
while (iter.hasNext()) {
iter.next().init(properties, log);
}
}
return this;
}
public List getInterceptors() {
return this.interceptors;
}
}
/**
* Used as a key for caching callable statements which (may) depend on
* current catalog...In 5.0.x, they don't (currently), but stored procedure
* names soon will, so current catalog is a (hidden) component of the name.
*/
static class CompoundCacheKey {
String componentOne;
String componentTwo;
int hashCode;
CompoundCacheKey(String partOne, String partTwo) {
this.componentOne = partOne;
this.componentTwo = partTwo;
// Handle first component (in most cases, currentCatalog being NULL....
this.hashCode = (((this.componentOne != null) ? this.componentOne : "") + this.componentTwo).hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof CompoundCacheKey) {
CompoundCacheKey another = (CompoundCacheKey) obj;
boolean firstPartEqual = false;
if (this.componentOne == null) {
firstPartEqual = (another.componentOne == null);
} else {
firstPartEqual = this.componentOne.equals(another.componentOne);
}
return (firstPartEqual && 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();
private final static int HISTOGRAM_BUCKETS = 20;
/**
* Map mysql transaction isolation level name to
* java.sql.Connection.TRANSACTION_XXX
*/
private static Map mapTransIsolationNameToValue = null;
protected static Map roundRobinStatsMap;
/**
* Actual collation index to collation name map for given server URLs.
*/
private static final Map> dynamicIndexToCollationMapByUrl = new HashMap>();
/**
* Actual collation index to mysql charset name map for given server URLs.
*/
private static final Map> dynamicIndexToCharsetMapByUrl = new HashMap>();
/**
* Actual collation index to mysql charset name map of user defined charsets for given server URLs.
*/
private static final Map> customIndexToCharsetMapByUrl = new HashMap>();
/**
* Actual mysql charset name to mblen map of user defined charsets for given server URLs.
*/
private static final Map> customCharsetToMblenMapByUrl = new HashMap>();
private CacheAdapter> serverConfigCache;
private long queryTimeCount;
private double queryTimeSum;
private double queryTimeSumSquares;
private double queryTimeMean;
private transient Timer cancelTimer;
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);
}
protected static SQLException appendMessageToException(SQLException sqlEx, String messageToAppend, ExceptionInterceptor interceptor) {
String origMessage = sqlEx.getMessage();
String sqlState = sqlEx.getSQLState();
int vendorErrorCode = sqlEx.getErrorCode();
StringBuilder messageBuf = new StringBuilder(origMessage.length() + messageToAppend.length());
messageBuf.append(origMessage);
messageBuf.append(messageToAppend);
SQLException sqlExceptionWithNewMessage = SQLError.createSQLException(messageBuf.toString(), sqlState, vendorErrorCode, interceptor);
sqlExceptionWithNewMessage.setStackTrace(sqlEx.getStackTrace());
return sqlExceptionWithNewMessage;
}
public Timer getCancelTimer() {
synchronized (getConnectionMutex()) {
if (this.cancelTimer == null) {
this.cancelTimer = new Timer("MySQL Statement Cancellation Timer", Boolean.TRUE);
}
return this.cancelTimer;
}
}
/**
* Creates a connection instance
*/
public static JdbcConnection getInstance(HostInfo hostInfo) throws SQLException {
return new ConnectionImpl(hostInfo);
}
private static final Random random = new Random();
/**
* @param url
* @param hostList
*/
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);
}
/** Are we in autoCommit mode? */
private boolean autoCommit = true;
/** A cache of SQL to parsed prepared statement parameters. */
private CacheAdapter cachedPreparedStatementParams;
/** The point in time when this connection was created */
private long connectionCreationTimeMillis = 0;
/** ID used when profiling */
private long connectionId;
/** The database we're currently using (called Catalog in JDBC terms). */
private String database = null;
/** Internal DBMD to use for various database-version specific features */
private DatabaseMetaData dbmd = null;
/** Why was this connection implicitly closed, if known? (for diagnostics) */
private Throwable forceClosedReason;
private MysqlaSession session = null;
/** Has this connection been closed? */
private boolean isClosed = true;
/** 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 last query finish? */
private long lastQueryFinishedTime = 0;
/**
* If gathering metrics, what was the execution time of the longest query so
* far ?
*/
private long longestQueryTimeMs = 0;
/** Is the server configured to use lower-case table names only? */
private boolean lowerCaseTableNames = false;
/** When did the master fail? */
// private long masterFailTimeMillis = 0L;
private long maximumNumberTablesAccessed = 0;
/** When was the last time we reported metrics? */
private long metricsLastReportedMs;
private long minimumNumberTablesAccessed = Long.MAX_VALUE;
/** Does this connection need to be tested? */
private boolean needsPing = false;
private boolean noBackslashEscapes = false;
private long numberOfPreparedExecutes = 0;
private long numberOfPrepares = 0;
private long numberOfQueriesIssued = 0;
private long numberOfResultSetsCreated = 0;
private long[] numTablesMetricsHistBreakpoints;
private int[] numTablesMetricsHistCounts;
private long[] oldHistBreakpoints = null;
private int[] oldHistCounts = null;
/**
* 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;
private long[] perfMetricsHistBreakpoints;
private int[] perfMetricsHistCounts;
/** Point of origin where this Connection was created */
private String pointOfOrigin;
/** Properties for this connection specified by user */
protected Properties props = null;
/** Should we retrieve 'info' messages from the server? */
private boolean readInfoMsg = false;
/** Are we in read-only mode? */
private boolean readOnly = false;
/** Cache of ResultSet metadata */
protected LRUCache resultSetMetadataCache;
private long shortestQueryTimeMs = Long.MAX_VALUE;
private double totalQueryTimeMs = 0;
/**
* The type map for UDTs (not implemented, but used by some third-party
* vendors, most notably IBM WebSphere)
*/
private Map> typeMap;
/** Has ANSI_QUOTES been enabled on the server? */
private boolean useAnsiQuotes = false;
/** The user we're connected as */
private String user = null;
/**
* Should we use server-side prepared statements? (auto-detected, but can be
* disabled by user)
*/
private boolean useServerPreparedStmts = false;
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;
/**
* The comment (if any) that we'll prepend to all statements
* sent to the server (to show up in "SHOW PROCESSLIST")
*/
private String statementComment = null;
private boolean storesLowerCaseTableName;
private List statementInterceptors;
/**
* If a CharsetEncoder is required for escaping. Needed for SJIS and related
* problems with \u00A5.
*/
private boolean requiresEscapingEncoder;
private ModifiableProperty characterEncoding;
private ReadableProperty autoReconnectForPools;
private ReadableProperty cachePrepStmts;
private ReadableProperty cacheServerConfiguration;
private ModifiableProperty autoReconnect;
private ModifiableProperty profileSQL;
private ReadableProperty useUsageAdvisor;
private ReadableProperty reconnectAtTxEnd;
private ReadableProperty useOldUTF8Behavior;
private ReadableProperty maintainTimeStats;
private ReadableProperty emulateUnsupportedPstmts;
private ReadableProperty gatherPerfMetrics;
private ReadableProperty ignoreNonTxTables;
private ReadableProperty pedantic;
private ReadableProperty prepStmtCacheSqlLimit;
private ReadableProperty useLocalSessionState;
private ReadableProperty useServerPrepStmts;
private ReadableProperty processEscapeCodesForPrepStmts;
private ReadableProperty useLocalTransactionState;
protected ModifiableProperty maxAllowedPacket;
private ReadableProperty disconnectOnExpiredPasswords;
private ReadableProperty 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 {
this.connectionCreationTimeMillis = System.currentTimeMillis();
// 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();
// We need Session ASAP to get access to central driver functionality
this.nullStatementResultSetFactory = new ResultSetFactory(this, null);
this.session = new MysqlaSession(hostInfo, getPropertySet());
// we can't cache fixed values here because properties are still not initialized with user provided values
this.characterEncoding = getPropertySet().getJdbcModifiableProperty(PropertyDefinitions.PNAME_characterEncoding);
this.autoReconnectForPools = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_autoReconnectForPools);
this.cachePrepStmts = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_cachePrepStmts);
this.cacheServerConfiguration = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_cacheServerConfiguration);
this.autoReconnect = getPropertySet(). getModifiableProperty(PropertyDefinitions.PNAME_autoReconnect);
this.profileSQL = getPropertySet().getModifiableProperty(PropertyDefinitions.PNAME_profileSQL);
this.useUsageAdvisor = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useUsageAdvisor);
this.reconnectAtTxEnd = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_reconnectAtTxEnd);
this.useOldUTF8Behavior = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useOldUTF8Behavior);
this.maintainTimeStats = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_maintainTimeStats);
this.emulateUnsupportedPstmts = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_emulateUnsupportedPstmts);
this.gatherPerfMetrics = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_gatherPerfMetrics);
this.ignoreNonTxTables = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_ignoreNonTxTables);
this.pedantic = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_pedantic);
this.prepStmtCacheSqlLimit = getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_prepStmtCacheSqlLimit);
this.useLocalSessionState = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useLocalSessionState);
this.useServerPrepStmts = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useServerPrepStmts);
this.processEscapeCodesForPrepStmts = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_processEscapeCodesForPrepStmts);
this.useLocalTransactionState = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useLocalTransactionState);
this.maxAllowedPacket = getPropertySet().getModifiableProperty(PropertyDefinitions.PNAME_maxAllowedPacket);
this.disconnectOnExpiredPasswords = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_disconnectOnExpiredPasswords);
this.readOnlyPropagatesToServer = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_readOnlyPropagatesToServer);
this.database = hostInfo.getDatabase();
this.user = StringUtils.isNullOrEmpty(hostInfo.getUser()) ? "" : hostInfo.getUser();
this.password = StringUtils.isNullOrEmpty(hostInfo.getPassword()) ? "" : hostInfo.getPassword();
this.props = hostInfo.exposeAsProperties();
initializeDriverProperties(this.props);
this.pointOfOrigin = this.useUsageAdvisor.getValue() ? LogUtils.findCallingClassAndMethod(new Throwable()) : "";
this.dbmd = getMetaData(false, false);
initializeSafeStatementInterceptors();
} catch (CJException e1) {
throw SQLExceptionsMapping.translateException(e1, getExceptionInterceptor());
}
try {
createNewIO(false);
unSafeStatementInterceptors();
NonRegisteringDriver.trackConnection(this);
} catch (SQLException ex) {
cleanup(ex);
// don't clobber SQL exceptions
throw ex;
} catch (Exception ex) {
cleanup(ex);
throw SQLError
.createSQLException(
this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_paranoid).getValue() ? Messages.getString("Connection.0")
: Messages.getString("Connection.1",
new Object[] { this.session.getHostInfo().getHost(), this.session.getHostInfo().getPort() }),
SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, ex, getExceptionInterceptor());
}
}
public void unSafeStatementInterceptors() throws SQLException {
this.statementInterceptors = this.statementInterceptors.stream().map(u -> ((NoSubInterceptorWrapper) u).getUnderlyingInterceptor())
.collect(Collectors.toList());
if (this.session != null) {
this.session.setStatementInterceptors(this.statementInterceptors);
}
}
public void initializeSafeStatementInterceptors() throws SQLException {
this.isClosed = false;
this.statementInterceptors = Util
. loadClasses(
getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_statementInterceptors).getStringValue(),
"MysqlIo.BadStatementInterceptor", getExceptionInterceptor())
.stream().map(o -> new NoSubInterceptorWrapper(o.init(this, this.props, this.session.getLog()))).collect(Collectors.toList());
}
public List getStatementInterceptorsInstances() {
return this.statementInterceptors;
}
private void addToHistogram(int[] histogramCounts, long[] histogramBreakpoints, long value, int numberOfTimes, long currentLowerBound,
long currentUpperBound) {
if (histogramCounts == null) {
createInitialHistogram(histogramBreakpoints, currentLowerBound, currentUpperBound);
} else {
for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
if (histogramBreakpoints[i] >= value) {
histogramCounts[i] += numberOfTimes;
break;
}
}
}
}
private void addToPerformanceHistogram(long value, int numberOfTimes) {
checkAndCreatePerformanceHistogram();
addToHistogram(this.perfMetricsHistCounts, this.perfMetricsHistBreakpoints, value, numberOfTimes,
this.shortestQueryTimeMs == Long.MAX_VALUE ? 0 : this.shortestQueryTimeMs, this.longestQueryTimeMs);
}
private void addToTablesAccessedHistogram(long value, int numberOfTimes) {
checkAndCreateTablesAccessedHistogram();
addToHistogram(this.numTablesMetricsHistCounts, this.numTablesMetricsHistBreakpoints, value, numberOfTimes,
this.minimumNumberTablesAccessed == Long.MAX_VALUE ? 0 : this.minimumNumberTablesAccessed, this.maximumNumberTablesAccessed);
}
/**
* Builds the map needed for 4.1.0 and newer servers that maps field-level
* charset/collation info to a java character encoding name.
*
* @throws SQLException
*/
private void buildCollationMapping() throws SQLException {
Map indexToCharset = null;
Map sortedCollationMap = null;
Map customCharset = null;
Map customMblen = null;
if (this.cacheServerConfiguration.getValue()) {
synchronized (dynamicIndexToCharsetMapByUrl) {
indexToCharset = dynamicIndexToCharsetMapByUrl.get(getURL());
sortedCollationMap = dynamicIndexToCollationMapByUrl.get(getURL());
customCharset = customIndexToCharsetMapByUrl.get(getURL());
customMblen = customCharsetToMblenMapByUrl.get(getURL());
}
}
if (indexToCharset == null) {
indexToCharset = new HashMap();
if (getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_detectCustomCollations).getValue()) {
java.sql.Statement stmt = null;
java.sql.ResultSet results = null;
try {
sortedCollationMap = new TreeMap();
customCharset = new HashMap();
customMblen = new HashMap();
stmt = getMetadataSafeStatement();
try {
results = stmt.executeQuery("SHOW COLLATION");
ResultSetUtil.resultSetToMap(sortedCollationMap, results, 3, 2);
} catch (PasswordExpiredException ex) {
if (this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
} catch (SQLException ex) {
if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
}
for (Iterator> indexIter = sortedCollationMap.entrySet().iterator(); indexIter.hasNext();) {
Map.Entry indexEntry = indexIter.next();
int collationIndex = indexEntry.getKey().intValue();
String charsetName = indexEntry.getValue();
indexToCharset.put(collationIndex, charsetName);
// if no static map for charsetIndex or server has a different mapping then our static map, adding it to custom map
if (collationIndex >= CharsetMapping.MAP_SIZE
|| !charsetName.equals(CharsetMapping.getMysqlCharsetNameForCollationIndex(collationIndex))) {
customCharset.put(collationIndex, charsetName);
}
// if no static map for charsetName adding to custom map
if (!CharsetMapping.CHARSET_NAME_TO_CHARSET.containsKey(charsetName)) {
customMblen.put(charsetName, null);
}
}
// if there is a number of custom charsets we should execute SHOW CHARACTER SET to know theirs mblen
if (customMblen.size() > 0) {
try {
results = stmt.executeQuery("SHOW CHARACTER SET");
while (results.next()) {
String charsetName = results.getString("Charset");
if (customMblen.containsKey(charsetName)) {
customMblen.put(charsetName, results.getInt("Maxlen"));
}
}
} catch (PasswordExpiredException ex) {
if (this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
} catch (SQLException ex) {
if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
}
}
if (this.cacheServerConfiguration.getValue()) {
synchronized (dynamicIndexToCharsetMapByUrl) {
dynamicIndexToCharsetMapByUrl.put(getURL(), indexToCharset);
dynamicIndexToCollationMapByUrl.put(getURL(), sortedCollationMap);
customIndexToCharsetMapByUrl.put(getURL(), customCharset);
customCharsetToMblenMapByUrl.put(getURL(), customMblen);
}
}
} catch (SQLException ex) {
throw ex;
} catch (RuntimeException ex) {
SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
sqlEx.initCause(ex);
throw sqlEx;
} finally {
if (results != null) {
try {
results.close();
} catch (java.sql.SQLException sqlE) {
// ignore
}
}
if (stmt != null) {
try {
stmt.close();
} catch (java.sql.SQLException sqlE) {
// ignore
}
}
}
} else {
for (int i = 1; i < CharsetMapping.MAP_SIZE; i++) {
indexToCharset.put(i, CharsetMapping.getMysqlCharsetNameForCollationIndex(i));
}
if (this.cacheServerConfiguration.getValue()) {
synchronized (dynamicIndexToCharsetMapByUrl) {
dynamicIndexToCharsetMapByUrl.put(getURL(), indexToCharset);
}
}
}
}
this.session.setCharsetMaps(indexToCharset, customCharset, customMblen);
}
private boolean canHandleAsServerPreparedStatement(String sql) throws SQLException {
if (sql == null || sql.length() == 0) {
return true;
}
if (!this.useServerPreparedStmts) {
return false;
}
if (this.cachePrepStmts.getValue()) {
synchronized (this.serverSideStatementCheckCache) {
Boolean flag = (Boolean) this.serverSideStatementCheckCache.get(sql);
if (flag != null) {
return flag.booleanValue();
}
boolean canHandle = StringUtils.canHandleAsServerPreparedStatementNoCache(sql, getServerVersion());
if (sql.length() < this.prepStmtCacheSqlLimit.getValue()) {
this.serverSideStatementCheckCache.put(sql, canHandle ? Boolean.TRUE : Boolean.FALSE);
}
return canHandle;
}
}
return StringUtils.canHandleAsServerPreparedStatementNoCache(sql, getServerVersion());
}
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;
configureClientCharacterSet(true);
setSessionVariables();
setupServerForTruncationChecks();
}
}
private void checkAndCreatePerformanceHistogram() {
if (this.perfMetricsHistCounts == null) {
this.perfMetricsHistCounts = new int[HISTOGRAM_BUCKETS];
}
if (this.perfMetricsHistBreakpoints == null) {
this.perfMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS];
}
}
private void checkAndCreateTablesAccessedHistogram() {
if (this.numTablesMetricsHistCounts == null) {
this.numTablesMetricsHistCounts = new int[HISTOGRAM_BUCKETS];
}
if (this.numTablesMetricsHistBreakpoints == null) {
this.numTablesMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS];
}
}
public void checkClosed() {
if (this.isClosed) {
throw ExceptionFactory.createException(ConnectionIsClosedException.class, Messages.getString("Connection.2"), this.forceClosedReason,
getExceptionInterceptor());
}
}
public void throwConnectionClosedException() throws SQLException {
SQLException ex = SQLError.createSQLException(Messages.getString("Connection.2"), SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor());
if (this.forceClosedReason != null) {
ex.initCause(this.forceClosedReason);
}
throw ex;
}
/**
* Set transaction isolation level to the value received from server if any.
* Is called by connectionInit(...)
*
* @throws SQLException
*/
private void checkTransactionIsolationLevel() throws SQLException {
String s = this.session.getServerVariable("tx_isolation");
if (s != null) {
Integer intTI = mapTransIsolationNameToValue.get(s);
if (intTI != null) {
this.isolationLevel = intTI.intValue();
}
}
}
/**
* Clobbers the physical network connection and marks
* this connection as closed.
*
* @throws SQLException
*/
public void abortInternal() throws SQLException {
this.session.abortInternal();
this.isClosed = true;
}
/**
* Destroys this connection and any underlying resources
*
* @param fromWhere
* @param whyCleanedUp
*/
private 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.
}
this.isClosed = true;
}
@Deprecated
public void clearHasTriedMaster() {
this.hasTriedMasterFlag = false;
}
/**
* After this call, getWarnings returns null until a new warning is reported
* for this connection.
*
* @exception SQLException
* if a database access error occurs
*/
public void clearWarnings() throws SQLException {
// firstWarning = null;
}
/**
* @param sql
* @throws SQLException
*/
public java.sql.PreparedStatement clientPrepareStatement(String sql) throws SQLException {
return clientPrepareStatement(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY);
}
public java.sql.PreparedStatement clientPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException {
java.sql.PreparedStatement pStmt = clientPrepareStatement(sql);
((com.mysql.cj.jdbc.PreparedStatement) pStmt).setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
return pStmt;
}
/**
* @param sql
* @param resultSetType
* @param resultSetConcurrency
* @throws SQLException
*/
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;
PreparedStatement pStmt = null;
if (this.cachePrepStmts.getValue()) {
PreparedStatement.ParseInfo pStmtInfo = this.cachedPreparedStatementParams.get(nativeSql);
if (pStmtInfo == null) {
pStmt = com.mysql.cj.jdbc.PreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database);
this.cachedPreparedStatementParams.put(nativeSql, pStmt.getParseInfo());
} else {
pStmt = com.mysql.cj.jdbc.PreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, pStmtInfo);
}
} else {
pStmt = com.mysql.cj.jdbc.PreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database);
}
pStmt.setResultSetType(resultSetType);
pStmt.setResultSetConcurrency(resultSetConcurrency);
return pStmt;
}
public java.sql.PreparedStatement clientPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException {
PreparedStatement pStmt = (PreparedStatement) clientPrepareStatement(sql);
pStmt.setRetrieveGeneratedKeys((autoGenKeyIndexes != null) && (autoGenKeyIndexes.length > 0));
return pStmt;
}
public java.sql.PreparedStatement clientPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException {
PreparedStatement pStmt = (PreparedStatement) clientPrepareStatement(sql);
pStmt.setRetrieveGeneratedKeys((autoGenKeyColNames != null) && (autoGenKeyColNames.length > 0));
return pStmt;
}
public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true);
}
// --------------------------JDBC 2.0-----------------------------
/**
* In some cases, it is desirable to immediately release a Connection's
* database and JDBC resources instead of waiting for them to be
* automatically released (cant think why off the top of my head) Note:
* A Connection is automatically closed when it is garbage collected.
* Certain fatal errors also result in a closed connection.
*
* @exception SQLException
* if a database access error occurs
*/
public void close() throws SQLException {
synchronized (getConnectionMutex()) {
if (this.connectionLifecycleInterceptors != null) {
new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {
@Override
void forEach(ConnectionLifecycleInterceptor each) throws SQLException {
each.close();
}
}.doForAll();
}
realClose(true, true, false, null);
}
}
/**
* Closes all currently open statements.
*
* @throws SQLException
*/
private void closeAllOpenStatements() throws SQLException {
SQLException postponedException = null;
for (Statement 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;
}
}
/**
* The method commit() makes all changes made since the previous
* commit/rollback permanent and releases any database locks currently held
* by the Connection. This method should only be used when auto-commit has
* been disabled.
*
* Note: MySQL does not support transactions, so this method is a no-op.
*
*
* @exception SQLException
* if a database access error occurs
* @see setAutoCommit
*/
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.autoCommit) {
throw SQLError.createSQLException(Messages.getString("Connection.3"), getExceptionInterceptor());
}
if (this.useLocalTransactionState.getValue()) {
if (!this.session.inTransactionOnServer()) {
return; // effectively a no-op
}
}
execSQL(null, "commit", -1, null, false, this.database, null, false);
} catch (SQLException sqlException) {
if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlException.getSQLState())) {
throw SQLError.createSQLException(Messages.getString("Connection.4"), SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN,
getExceptionInterceptor());
}
throw sqlException;
} finally {
this.needsPing = this.reconnectAtTxEnd.getValue();
}
}
return;
}
/**
* Configures client-side properties for character set information.
*
* @throws SQLException
* if unable to configure the specified character set.
*/
private void configureCharsetProperties() throws SQLException {
if (this.characterEncoding.getValue() != null) {
// Attempt to use the encoding, and bail out if it can't be used
try {
String testString = "abc";
StringUtils.getBytes(testString, this.characterEncoding.getValue());
} catch (WrongArgumentException waEx) {
// Try the MySQL character encoding, then....
String oldEncoding = this.characterEncoding.getValue();
try {
this.characterEncoding.setValue(CharsetMapping.getJavaEncodingForMysqlCharset(oldEncoding));
} catch (RuntimeException ex) {
throw SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, ex, null);
}
if (this.characterEncoding.getValue() == null) {
throw SQLError.createSQLException(Messages.getString("Connection.5", new Object[] { oldEncoding }),
SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
}
try {
String testString = "abc";
StringUtils.getBytes(testString, this.characterEncoding.getValue());
} catch (WrongArgumentException encodingEx) {
throw SQLError.createSQLException(encodingEx.getMessage(), SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
}
}
}
}
/**
* Sets up client character set for MySQL-4.1 and newer if the user This
* must be done before any further communication with the server!
*
* @return true if this routine actually configured the client character
* set, or false if the driver needs to use 'older' methods to
* detect the character set, as it is connected to a MySQL server
* older than 4.1.0
* @throws SQLException
* if an exception happens while sending 'SET NAMES' to the
* server, or the server sends character set information that
* the client doesn't know about.
*/
private boolean configureClientCharacterSet(boolean dontCheckServerMatch) throws SQLException {
String realJavaEncoding = this.characterEncoding.getValue();
ModifiableProperty characterSetResults = getPropertySet().getModifiableProperty(PropertyDefinitions.PNAME_characterSetResults);
boolean characterSetAlreadyConfigured = false;
try {
characterSetAlreadyConfigured = true;
configureCharsetProperties();
realJavaEncoding = this.characterEncoding.getValue(); // we need to do this again to grab this for versions > 4.1.0
try {
// Fault injection for testing server character set indices
if (this.props != null && this.props.getProperty(PropertyDefinitions.PNAME_testsuite_faultInjection_serverCharsetIndex) != null) {
this.session.setServerDefaultCollationIndex(
Integer.parseInt(this.props.getProperty(PropertyDefinitions.PNAME_testsuite_faultInjection_serverCharsetIndex)));
}
String serverEncodingToSet = CharsetMapping.getJavaEncodingForCollationIndex(this.session.getServerDefaultCollationIndex());
if (serverEncodingToSet == null || serverEncodingToSet.length() == 0) {
if (realJavaEncoding != null) {
// user knows best, try it
this.characterEncoding.setValue(realJavaEncoding);
} else {
throw SQLError.createSQLException(Messages.getString("Connection.6", new Object[] { this.session.getServerDefaultCollationIndex() }),
SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
}
}
// "latin1" on MySQL-4.1.0+ is actually CP1252, not ISO8859_1
if ("ISO8859_1".equalsIgnoreCase(serverEncodingToSet)) {
serverEncodingToSet = "Cp1252";
}
if ("UnicodeBig".equalsIgnoreCase(serverEncodingToSet) || "UTF-16".equalsIgnoreCase(serverEncodingToSet)
|| "UTF-16LE".equalsIgnoreCase(serverEncodingToSet) || "UTF-32".equalsIgnoreCase(serverEncodingToSet)) {
serverEncodingToSet = "UTF-8";
}
this.characterEncoding.setValue(serverEncodingToSet);
} catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
if (realJavaEncoding != null) {
// user knows best, try it
this.characterEncoding.setValue(realJavaEncoding);
} else {
throw SQLError.createSQLException(Messages.getString("Connection.6", new Object[] { this.session.getServerDefaultCollationIndex() }),
SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
}
} catch (SQLException ex) {
throw ex;
} catch (RuntimeException ex) {
SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
sqlEx.initCause(ex);
throw sqlEx;
}
if (this.characterEncoding.getValue() == null) {
// punt?
this.characterEncoding.setValue("ISO8859_1");
}
if (realJavaEncoding != null) {
//
// Now, inform the server what character set we will be using from now-on...
//
if (realJavaEncoding.equalsIgnoreCase("UTF-8") || realJavaEncoding.equalsIgnoreCase("UTF8")) {
// charset names are case-sensitive
boolean useutf8mb4 = CharsetMapping.UTF8MB4_INDEXES.contains(this.session.getServerDefaultCollationIndex());
if (!this.useOldUTF8Behavior.getValue()) {
if (dontCheckServerMatch || !this.session.characterSetNamesMatches("utf8") || (!this.session.characterSetNamesMatches("utf8mb4"))) {
execSQL(null, "SET NAMES " + (useutf8mb4 ? "utf8mb4" : "utf8"), -1, null, false, this.database, null, false);
this.session.getServerVariables().put("character_set_client", useutf8mb4 ? "utf8mb4" : "utf8");
this.session.getServerVariables().put("character_set_connection", useutf8mb4 ? "utf8mb4" : "utf8");
}
} else {
execSQL(null, "SET NAMES latin1", -1, null, false, this.database, null, false);
this.session.getServerVariables().put("character_set_client", "latin1");
this.session.getServerVariables().put("character_set_connection", "latin1");
}
this.characterEncoding.setValue(realJavaEncoding);
} /* not utf-8 */else {
String mysqlCharsetName = CharsetMapping.getMysqlCharsetForJavaEncoding(realJavaEncoding.toUpperCase(Locale.ENGLISH), getServerVersion());
if (mysqlCharsetName != null) {
if (dontCheckServerMatch || !this.session.characterSetNamesMatches(mysqlCharsetName)) {
execSQL(null, "SET NAMES " + mysqlCharsetName, -1, null, false, this.database, null, false);
this.session.getServerVariables().put("character_set_client", mysqlCharsetName);
this.session.getServerVariables().put("character_set_connection", mysqlCharsetName);
}
}
// Switch driver's encoding now, since the server knows what we're sending...
//
this.characterEncoding.setValue(realJavaEncoding);
}
} else if (this.characterEncoding.getValue() != null) {
// Tell the server we'll use the server default charset to send our queries from now on....
String mysqlCharsetName = getSession().getServerCharset();
if (this.useOldUTF8Behavior.getValue()) {
mysqlCharsetName = "latin1";
}
boolean ucs2 = false;
if ("ucs2".equalsIgnoreCase(mysqlCharsetName) || "utf16".equalsIgnoreCase(mysqlCharsetName) || "utf16le".equalsIgnoreCase(mysqlCharsetName)
|| "utf32".equalsIgnoreCase(mysqlCharsetName)) {
mysqlCharsetName = "utf8";
ucs2 = true;
if (characterSetResults.getValue() == null) {
characterSetResults.setValue("UTF-8");
}
}
if (dontCheckServerMatch || !this.session.characterSetNamesMatches(mysqlCharsetName) || ucs2) {
try {
execSQL(null, "SET NAMES " + mysqlCharsetName, -1, null, false, this.database, null, false);
this.session.getServerVariables().put("character_set_client", mysqlCharsetName);
this.session.getServerVariables().put("character_set_connection", mysqlCharsetName);
} catch (PasswordExpiredException ex) {
if (this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
} catch (SQLException ex) {
if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
}
}
realJavaEncoding = this.characterEncoding.getValue();
}
//
// We know how to deal with any charset coming back from the database, so tell the server not to do conversion if the user hasn't 'forced' a
// result-set character set
//
String onServer = null;
boolean isNullOnServer = false;
if (this.session.getServerVariables() != null) {
onServer = this.session.getServerVariable("character_set_results");
isNullOnServer = onServer == null || "NULL".equalsIgnoreCase(onServer) || onServer.length() == 0;
}
if (characterSetResults.getValue() == null) {
//
// Only send if needed, if we're caching server variables we -have- to send, because we don't know what it was before we cached them.
//
if (!isNullOnServer) {
try {
execSQL(null, "SET character_set_results = NULL", -1, null, false, this.database, null, false);
} catch (PasswordExpiredException ex) {
if (this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
} catch (SQLException ex) {
if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
}
this.session.getServerVariables().put(ServerSession.JDBC_LOCAL_CHARACTER_SET_RESULTS, null);
} else {
this.session.getServerVariables().put(ServerSession.JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer);
}
} else {
if (this.useOldUTF8Behavior.getValue()) {
try {
execSQL(null, "SET NAMES latin1", -1, null, false, this.database, null, false);
this.session.getServerVariables().put("character_set_client", "latin1");
this.session.getServerVariables().put("character_set_connection", "latin1");
} catch (PasswordExpiredException ex) {
if (this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
} catch (SQLException ex) {
if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
}
}
String charsetResults = characterSetResults.getValue();
String mysqlEncodingName = null;
if ("UTF-8".equalsIgnoreCase(charsetResults) || "UTF8".equalsIgnoreCase(charsetResults)) {
mysqlEncodingName = "utf8";
} else if ("null".equalsIgnoreCase(charsetResults)) {
mysqlEncodingName = "NULL";
} else {
mysqlEncodingName = CharsetMapping.getMysqlCharsetForJavaEncoding(charsetResults.toUpperCase(Locale.ENGLISH), getServerVersion());
}
//
// Only change the value if needed
//
if (mysqlEncodingName == null) {
throw SQLError.createSQLException(Messages.getString("Connection.7", new Object[] { charsetResults }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
if (!mysqlEncodingName.equalsIgnoreCase(this.session.getServerVariable("character_set_results"))) {
StringBuilder setBuf = new StringBuilder("SET character_set_results = ".length() + mysqlEncodingName.length());
setBuf.append("SET character_set_results = ").append(mysqlEncodingName);
try {
execSQL(null, setBuf.toString(), -1, null, false, this.database, null, false);
} catch (PasswordExpiredException ex) {
if (this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
} catch (SQLException ex) {
if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
}
this.session.getServerVariables().put(ServerSession.JDBC_LOCAL_CHARACTER_SET_RESULTS, mysqlEncodingName);
// We have to set errorMessageEncoding according to new value of charsetResults for server version 5.5 and higher
this.session.setErrorMessageEncoding(charsetResults);
} else {
this.session.getServerVariables().put(ServerSession.JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer);
}
}
String connectionCollation = getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_connectionCollation).getStringValue();
if (connectionCollation != null) {
StringBuilder setBuf = new StringBuilder("SET collation_connection = ".length() + connectionCollation.length());
setBuf.append("SET collation_connection = ").append(connectionCollation);
try {
execSQL(null, setBuf.toString(), -1, null, false, this.database, null, false);
} catch (PasswordExpiredException ex) {
if (this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
} catch (SQLException ex) {
if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || this.disconnectOnExpiredPasswords.getValue()) {
throw ex;
}
}
}
} finally {
// Failsafe, make sure that the driver's notion of character encoding matches what the user has specified.
//
this.characterEncoding.setValue(realJavaEncoding);
}
/**
* Check if we need a CharsetEncoder for escaping codepoints that are
* transformed to backslash (0x5c) in the connection encoding.
*/
try {
CharsetEncoder enc = Charset.forName(this.characterEncoding.getValue()).newEncoder();
CharBuffer cbuf = CharBuffer.allocate(1);
ByteBuffer bbuf = ByteBuffer.allocate(1);
cbuf.put("\u00a5");
cbuf.position(0);
enc.encode(cbuf, bbuf, true);
if (bbuf.get(0) == '\\') {
this.requiresEscapingEncoder = true;
} else {
cbuf.clear();
bbuf.clear();
cbuf.put("\u20a9");
cbuf.position(0);
enc.encode(cbuf, bbuf, true);
if (bbuf.get(0) == '\\') {
this.requiresEscapingEncoder = true;
}
}
} catch (java.nio.charset.UnsupportedCharsetException ucex) {
// fallback to String API
byte bbuf[] = StringUtils.getBytes("\u00a5", this.characterEncoding.getValue());
if (bbuf[0] == '\\') {
this.requiresEscapingEncoder = true;
} else {
bbuf = StringUtils.getBytes("\u20a9", this.characterEncoding.getValue());
if (bbuf[0] == '\\') {
this.requiresEscapingEncoder = true;
}
}
}
return characterSetAlreadyConfigured;
}
private void createInitialHistogram(long[] breakpoints, long lowerBound, long upperBound) {
double bucketSize = (((double) upperBound - (double) lowerBound) / HISTOGRAM_BUCKETS) * 1.25;
if (bucketSize < 1) {
bucketSize = 1;
}
for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
breakpoints[i] = lowerBound;
lowerBound += bucketSize;
}
}
/**
* Creates an IO channel to the server
*
* @param isForReconnect
* is this request for a re-connect
* @return a new MysqlIO instance connected to a server
* @throws SQLException
* if a database access error occurs
* @throws CommunicationsException
*/
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 {
Properties mergedProps = getPropertySet().exposeAsProperties(this.props);
if (!this.autoReconnect.getValue()) {
connectOneTryOnly(isForReconnect, mergedProps);
return;
}
connectWithRetries(isForReconnect, mergedProps);
} catch (SQLException ex) {
throw ExceptionFactory.createException(UnableToConnectException.class, ex.getMessage(), ex);
}
}
}
private void connectWithRetries(boolean isForReconnect, Properties mergedProps) throws SQLException {
double timeout = getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_initialTimeout).getValue();
boolean connectionGood = false;
Exception connectionException = null;
for (int attemptCount = 0; (attemptCount < getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_maxReconnects).getValue())
&& !connectionGood; attemptCount++) {
try {
this.session.forceClose();
this.session.connect(getProxy(), this.origHostInfo, mergedProps, this.user, this.password, this.database,
DriverManager.getLoginTimeout() * 1000);
pingInternal(false, 0);
boolean oldAutoCommit;
int oldIsolationLevel;
boolean oldReadOnly;
String oldCatalog;
synchronized (getConnectionMutex()) {
this.connectionId = this.session.getThreadId();
this.isClosed = false;
// save state from old connection
oldAutoCommit = getAutoCommit();
oldIsolationLevel = this.isolationLevel;
oldReadOnly = isReadOnly(false);
oldCatalog = getCatalog();
this.session.setStatementInterceptors(this.statementInterceptors);
}
// Server properties might be different from previous connection, so initialize again...
initializePropsFromServer();
if (isForReconnect) {
// Restore state from old connection
setAutoCommit(oldAutoCommit);
setTransactionIsolation(oldIsolationLevel);
setCatalog(oldCatalog);
setReadOnly(oldReadOnly);
}
connectionGood = true;
break;
} 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[] { getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_maxReconnects).getValue() }),
SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, connectionException, getExceptionInterceptor());
throw chainedEx;
}
if (this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_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()) {
Statement 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, Properties mergedProps) throws SQLException {
Exception connectionNotEstablishedBecause = null;
try {
this.session.connect(getProxy(), this.origHostInfo, mergedProps, this.user, this.password, this.database, DriverManager.getLoginTimeout() * 1000);
this.connectionId = this.session.getThreadId();
this.isClosed = false;
// save state from old connection
boolean oldAutoCommit = getAutoCommit();
int oldIsolationLevel = this.isolationLevel;
boolean oldReadOnly = isReadOnly(false);
String oldCatalog = getCatalog();
this.session.setStatementInterceptors(this.statementInterceptors);
// Server properties might be different from previous connection, so initialize again...
initializePropsFromServer();
if (isForReconnect) {
// Restore state from old connection
setAutoCommit(oldAutoCommit);
setTransactionIsolation(oldIsolationLevel);
setCatalog(oldCatalog);
setReadOnly(oldReadOnly);
}
return;
} 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"),
SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor());
chainedEx.initCause(connectionNotEstablishedBecause);
throw chainedEx;
}
}
private void createPreparedStatementCaches() throws SQLException {
synchronized (getConnectionMutex()) {
int cacheSize = getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_prepStmtCacheSize).getValue();
String parseInfoCacheFactory = getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_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(), this.props);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
SQLException sqlEx = SQLError.createSQLException(Messages.getString("Connection.CantFindCacheFactory",
new Object[] { parseInfoCacheFactory, PropertyDefinitions.PNAME_parseInfoCacheFactory }), getExceptionInterceptor());
sqlEx.initCause(e);
throw sqlEx;
} catch (Exception e) {
SQLException sqlEx = SQLError.createSQLException(Messages.getString("Connection.CantLoadCacheFactory",
new Object[] { parseInfoCacheFactory, PropertyDefinitions.PNAME_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