[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#861707: marked as done (unblock: mysql-connector-java/5.1.42-1)



Your message dated Mon, 08 May 2017 16:33:54 +0000
with message-id <E1d7lbe-0004j8-Tk@respighi.debian.org>
and subject line unblock mysql-connector-java
has caused the Debian Bug report #861707,
regarding unblock: mysql-connector-java/5.1.42-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
861707: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=861707
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Hi,

This is a pre-upload request to update the mysql-connector-java package
to the version 5.1.42-1 in stretch. This update is required to fix two
vulnerabilities (CVE-2017-3586 and CVE-2017-3589). Upstream hasn't
provided enough information to patch only the vulnerabilities, but the
branch 5.1.x is in maintenance mode, well tested and stable.

The detailed changelog is available here:

https://dev.mysql.com/doc/relnotes/connector-j/5.1/en/news-5-1-42.html

Thank you,

Emmanuel Bourg

unblock mysql-connector-java/5.1.42-1
diff --git a/CHANGES b/CHANGES
index bdbbcbd..cd1d31a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,41 @@
 # Changelog
-# $Id$
+# https://dev.mysql.com/doc/relnotes/connector-j/5.1/en/
+
+Version 5.1.42
+
+  - Fix for Bug#73775 (19531384), DBMD.getProcedureColumns()/.getFunctionColumns() fail to filter by columnPattern.
+
+  - Fix for Bug#84324 (25321524), CallableStatement.extractProcedureName() not work when catalog name with dash.
+
+  - Fix for Bug#79561 (22333996), NullPointerException when calling a fully qualified stored procedure.
+
+  - Fix for Bug#84783 (25490163), query timeout is not working(thread hang).
+
+  - Fix for Bug#70704 (17653733), Deadlock using UpdatableResultSet.
+
+  - Fix for Bug#66430 (16714868), setCatalog on connection leaves ServerPreparedStatement cache for old catalog.
+
+  - Fix for Bug#83662 (25048406), NullPointerException while reading NULL boolean value from DB.
+
+  - Fix for Bug#83368 (24841670), 5.1.40 regression: wasNull not updated when calling getInt for a bit column.
+
+  - Added static mapping for utf8mb4_ja_0900_as_cs collation.
+
+  - Fix for Bug#84189 (25250938), Allow null when extracting java.time.* classes from ResultSet.
+    Thanks to Martin Desharnais for his contribution.
+
+  - Fix for Bug#70808 (17757070), Set sessionVariables in a single query.
+
+  - Fix for Bug#77192 (21170603), Description for the Property replicationConnetionGroup Missing from the Manual.
+
+  - Fix for Bug#83834 (25101890), Typo in Connector/J error message.
+
+  - Fix for Bug#25636947, CONNECTION USING MYSQL CLIENT FAILS IF WE USE THE SSL CERTIFICATES FROM C/J SRC.
+
+  - Fix for Bug#25687718, INCORRECT TIME ZONE IDENTIFIER IN STATEMENTREGRESSIONTEST.
+
+  - Fix for Bug#25556597, RESULTSETTEST.TESTPADDING UNIT TEST IS FAILING IN 5.1.41 RELEASE PACKAGE.
+
 02-28-17 - Version 5.1.41
 
   - Fix for Bug#25517837, CONNECT PERFORMNACE DEGRADED BY 10% IN 5.1.41.
diff --git a/README b/README
index 2c05dee..2f52392 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-MySQL Connector/J 5.1.41
+MySQL Connector/J 5.1.42
 
 This is a release of MySQL Connector/J, Oracle's dual-
 license JDBC Driver for MySQL. For the avoidance of
diff --git a/README.txt b/README.txt
index 9e93489..f21de5b 100644
--- a/README.txt
+++ b/README.txt
@@ -1,4 +1,4 @@
-MySQL Connector/J 5.1.41
+MySQL Connector/J 5.1.42
 
 This is a release of MySQL Connector/J, Oracle's dual-
 license JDBC Driver for MySQL. For the avoidance of
diff --git a/build.xml b/build.xml
index 4edd5ca..1566e11 100644
--- a/build.xml
+++ b/build.xml
@@ -1,6 +1,6 @@
 <?xml version='1.0'?>
 <!--
-  Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -158,7 +158,7 @@ com.mysql.jdbc.noCleanBetweenCompiles=yes
 
     <property name="major_version" value="5" />
     <property name="minor_version" value="1" />
-    <property name="subminor_version" value="41" />
+    <property name="subminor_version" value="42" />
     <property name="version_status" value="" />
 
     <property name="version" value="${major_version}.${minor_version}.${subminor_version}${version_status}" />
diff --git a/debian/changelog b/debian/changelog
index 581df76..3fc8cbd 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+mysql-connector-java (5.1.42-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream release
+    - Fixes CVE-2017-3586 and CVE-2017-3589
+    - Refreshed the patches
+
+ -- Emmanuel Bourg <ebourg@apache.org>  Wed, 03 May 2017 00:54:54 +0200
+
 mysql-connector-java (5.1.41-1) unstable; urgency=medium
 
   * Team upload.
diff --git a/debian/patches/0002-java6-compilation-compat.patch b/debian/patches/0002-java6-compilation-compat.patch
index b6a6471..04c65ba 100644
--- a/debian/patches/0002-java6-compilation-compat.patch
+++ b/debian/patches/0002-java6-compilation-compat.patch
@@ -76,7 +76,7 @@ Subject: [PATCH] Java 8 compatibility
  }
 --- a/src/com/mysql/jdbc/CallableStatement.java
 +++ b/src/com/mysql/jdbc/CallableStatement.java
-@@ -2383,6 +2383,90 @@
+@@ -2396,6 +2396,90 @@
          }
      }
  
@@ -228,7 +228,7 @@ Subject: [PATCH] Java 8 compatibility
  }
 --- a/src/com/mysql/jdbc/DatabaseMetaData.java
 +++ b/src/com/mysql/jdbc/DatabaseMetaData.java
-@@ -7880,6 +7880,22 @@
+@@ -7878,6 +7878,22 @@
          return pStmt;
      }
  
@@ -345,7 +345,7 @@ Subject: [PATCH] Java 8 compatibility
       * Same as PreparedStatement.executeUpdate() but returns long instead of int.
 --- a/src/com/mysql/jdbc/ResultSetImpl.java
 +++ b/src/com/mysql/jdbc/ResultSetImpl.java
-@@ -7908,4 +7908,200 @@
+@@ -7890,4 +7890,200 @@
      protected ExceptionInterceptor getExceptionInterceptor() {
          return this.exceptionInterceptor;
      }
diff --git a/src/com/mysql/fabric/jdbc/FabricMySQLConnectionProxy.java b/src/com/mysql/fabric/jdbc/FabricMySQLConnectionProxy.java
index 868b4f8..f527d68 100644
--- a/src/com/mysql/fabric/jdbc/FabricMySQLConnectionProxy.java
+++ b/src/com/mysql/fabric/jdbc/FabricMySQLConnectionProxy.java
@@ -466,15 +466,15 @@ public class FabricMySQLConnectionProxy extends ConnectionPropertiesImpl impleme
      * instead the {@link LoadBalancedConnectionProxy} for either the
      * master or slaves.
      */
-    protected MySQLConnection getActiveMySQLConnection() throws SQLException {
+    protected MySQLConnection getActiveMySQLConnectionChecked() throws SQLException {
         ReplicationConnection c = (ReplicationConnection) getActiveConnection();
         MySQLConnection mc = (MySQLConnection) c.getCurrentConnection();
         return mc;
     }
 
-    protected MySQLConnection getActiveMySQLConnectionPassive() {
+    public MySQLConnection getActiveMySQLConnection() {
         try {
-            return getActiveMySQLConnection();
+            return getActiveMySQLConnectionChecked();
         } catch (SQLException ex) {
             throw new IllegalStateException("Unable to determine active connection", ex);
         }
@@ -724,7 +724,7 @@ public class FabricMySQLConnectionProxy extends ConnectionPropertiesImpl impleme
     }
 
     public MySQLConnection getMultiHostSafeProxy() {
-        return getActiveMySQLConnectionPassive();
+        return getActiveMySQLConnection();
     }
 
     ////////////////////////////////////////////////////////
@@ -891,30 +891,30 @@ public class FabricMySQLConnectionProxy extends ConnectionPropertiesImpl impleme
 
     public ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency,
             boolean streamResults, String catalog, Field[] cachedMetadata) throws SQLException {
-        return getActiveMySQLConnection().execSQL(callingStatement, sql, maxRows, packet, resultSetType, resultSetConcurrency, streamResults, catalog,
+        return getActiveMySQLConnectionChecked().execSQL(callingStatement, sql, maxRows, packet, resultSetType, resultSetConcurrency, streamResults, catalog,
                 cachedMetadata);
     }
 
     public ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency,
             boolean streamResults, String catalog, Field[] cachedMetadata, boolean isBatch) throws SQLException {
-        return getActiveMySQLConnection().execSQL(callingStatement, sql, maxRows, packet, resultSetType, resultSetConcurrency, streamResults, catalog,
+        return getActiveMySQLConnectionChecked().execSQL(callingStatement, sql, maxRows, packet, resultSetType, resultSetConcurrency, streamResults, catalog,
                 cachedMetadata, isBatch);
     }
 
     public String extractSqlFromPacket(String possibleSqlQuery, Buffer queryPacket, int endOfQueryPacketPosition) throws SQLException {
-        return getActiveMySQLConnection().extractSqlFromPacket(possibleSqlQuery, queryPacket, endOfQueryPacketPosition);
+        return getActiveMySQLConnectionChecked().extractSqlFromPacket(possibleSqlQuery, queryPacket, endOfQueryPacketPosition);
     }
 
     public StringBuilder generateConnectionCommentBlock(StringBuilder buf) {
-        return getActiveMySQLConnectionPassive().generateConnectionCommentBlock(buf);
+        return getActiveMySQLConnection().generateConnectionCommentBlock(buf);
     }
 
     public MysqlIO getIO() throws SQLException {
-        return getActiveMySQLConnection().getIO();
+        return getActiveMySQLConnectionChecked().getIO();
     }
 
     public Calendar getCalendarInstanceForSessionOrNew() {
-        return getActiveMySQLConnectionPassive().getCalendarInstanceForSessionOrNew();
+        return getActiveMySQLConnection().getCalendarInstanceForSessionOrNew();
     }
 
     /**
@@ -926,11 +926,11 @@ public class FabricMySQLConnectionProxy extends ConnectionPropertiesImpl impleme
     }
 
     public String getServerCharset() {
-        return getActiveMySQLConnectionPassive().getServerCharset();
+        return getActiveMySQLConnection().getServerCharset();
     }
 
     public TimeZone getServerTimezoneTZ() {
-        return getActiveMySQLConnectionPassive().getServerTimezoneTZ();
+        return getActiveMySQLConnection().getServerTimezoneTZ();
     }
 
     /**
@@ -960,11 +960,11 @@ public class FabricMySQLConnectionProxy extends ConnectionPropertiesImpl impleme
     }
 
     public String getCharacterSetMetadata() {
-        return getActiveMySQLConnectionPassive().getCharacterSetMetadata();
+        return getActiveMySQLConnection().getCharacterSetMetadata();
     }
 
     public java.sql.Statement getMetadataSafeStatement() throws SQLException {
-        return getActiveMySQLConnection().getMetadataSafeStatement();
+        return getActiveMySQLConnectionChecked().getMetadataSafeStatement();
     }
 
     /**
@@ -2856,7 +2856,7 @@ public class FabricMySQLConnectionProxy extends ConnectionPropertiesImpl impleme
     }
 
     public String getHostPortPair() {
-        return getActiveMySQLConnectionPassive().getHostPortPair();
+        return getActiveMySQLConnection().getHostPortPair();
     }
 
     public long getId() {
@@ -2955,7 +2955,7 @@ public class FabricMySQLConnectionProxy extends ConnectionPropertiesImpl impleme
     }
 
     public boolean lowerCaseTableNames() {
-        return getActiveMySQLConnectionPassive().lowerCaseTableNames();
+        return getActiveMySQLConnection().lowerCaseTableNames();
     }
 
     /**
@@ -2984,7 +2984,7 @@ public class FabricMySQLConnectionProxy extends ConnectionPropertiesImpl impleme
     }
 
     public boolean serverSupportsConvertFn() throws SQLException {
-        return getActiveMySQLConnection().serverSupportsConvertFn();
+        return getActiveMySQLConnectionChecked().serverSupportsConvertFn();
     }
 
     public void setReadInfoMsgEnabled(boolean flag) {
@@ -2994,7 +2994,7 @@ public class FabricMySQLConnectionProxy extends ConnectionPropertiesImpl impleme
     }
 
     public boolean storesLowerCaseTableName() {
-        return getActiveMySQLConnectionPassive().storesLowerCaseTableName();
+        return getActiveMySQLConnection().storesLowerCaseTableName();
     }
 
     public void throwConnectionClosedException() throws SQLException {
@@ -3049,11 +3049,11 @@ public class FabricMySQLConnectionProxy extends ConnectionPropertiesImpl impleme
     }
 
     public SQLWarning getWarnings() throws SQLException {
-        return getActiveMySQLConnection().getWarnings();
+        return getActiveMySQLConnectionChecked().getWarnings();
     }
 
     public String nativeSQL(String sql) throws SQLException {
-        return getActiveMySQLConnection().nativeSQL(sql);
+        return getActiveMySQLConnectionChecked().nativeSQL(sql);
     }
 
     public ProfilerEventHandler getProfilerEventHandlerInstance() {
diff --git a/src/com/mysql/jdbc/CallableStatement.java b/src/com/mysql/jdbc/CallableStatement.java
index eb0ba45..867efcd 100644
--- a/src/com/mysql/jdbc/CallableStatement.java
+++ b/src/com/mysql/jdbc/CallableStatement.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -219,7 +219,21 @@ public class CallableStatement extends PreparedStatement implements java.sql.Cal
 
             while (paramTypesRs.next()) {
                 String paramName = paramTypesRs.getString(4);
-                int inOutModifier = paramTypesRs.getInt(5);
+                int inOutModifier;
+                switch (paramTypesRs.getInt(5)) {
+                    case DatabaseMetaData.procedureColumnIn:
+                        inOutModifier = ParameterMetaData.parameterModeIn;
+                        break;
+                    case DatabaseMetaData.procedureColumnInOut:
+                        inOutModifier = ParameterMetaData.parameterModeInOut;
+                        break;
+                    case DatabaseMetaData.procedureColumnOut:
+                    case DatabaseMetaData.procedureColumnReturn:
+                        inOutModifier = ParameterMetaData.parameterModeOut;
+                        break;
+                    default:
+                        inOutModifier = ParameterMetaData.parameterModeUnknown;
+                }
 
                 boolean isOutParameter = false;
                 boolean isInParameter = false;
@@ -1365,13 +1379,12 @@ public class CallableStatement extends PreparedStatement implements java.sql.Cal
                 throw SQLError.createSQLException(Messages.getString("CallableStatement.2"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
             }
 
-            if (this.paramInfo == null) {
+            CallableStatementParam namedParamInfo;
+            if (this.paramInfo == null || (namedParamInfo = this.paramInfo.getParameter(paramName)) == null) {
                 throw SQLError.createSQLException(Messages.getString("CallableStatement.3") + paramName + Messages.getString("CallableStatement.4"),
                         SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
             }
 
-            CallableStatementParam namedParamInfo = this.paramInfo.getParameter(paramName);
-
             if (forOut && !namedParamInfo.isOut) {
                 throw SQLError.createSQLException(Messages.getString("CallableStatement.5") + paramName + Messages.getString("CallableStatement.6"),
                         SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
diff --git a/src/com/mysql/jdbc/CharsetMapping.java b/src/com/mysql/jdbc/CharsetMapping.java
index a720daf..2f21cef 100644
--- a/src/com/mysql/jdbc/CharsetMapping.java
+++ b/src/com/mysql/jdbc/CharsetMapping.java
@@ -534,6 +534,8 @@ public class CharsetMapping {
 
         collation[300] = new Collation(300, "utf8mb4_vi_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4);
 
+        collation[303] = new Collation(303, "utf8mb4_ja_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4);
+
         collation[326] = new Collation(326, "utf8mb4_test_ci", 0, MYSQL_CHARSET_NAME_utf8mb4);
         collation[327] = new Collation(327, "utf16_test_ci", 0, MYSQL_CHARSET_NAME_utf16);
         collation[328] = new Collation(328, "utf8mb4_test_400_ci", 0, MYSQL_CHARSET_NAME_utf8mb4);
diff --git a/src/com/mysql/jdbc/ConnectionImpl.java b/src/com/mysql/jdbc/ConnectionImpl.java
index 4cfb89d..c9f72e3 100644
--- a/src/com/mysql/jdbc/ConnectionImpl.java
+++ b/src/com/mysql/jdbc/ConnectionImpl.java
@@ -125,6 +125,10 @@ public class ConnectionImpl extends ConnectionPropertiesImpl implements MySQLCon
         return this.getProxy();
     }
 
+    public MySQLConnection getActiveMySQLConnection() {
+        return this;
+    }
+
     public Object getConnectionMutex() {
         return (this.realProxy != null) ? this.realProxy : getProxy();
     }
@@ -363,23 +367,8 @@ public class ConnectionImpl extends ConnectionPropertiesImpl implements MySQLCon
     public Timer getCancelTimer() {
         synchronized (getConnectionMutex()) {
             if (this.cancelTimer == null) {
-                boolean createdNamedTimer = false;
-
-                // Use reflection magic to try this on JDK's 1.5 and newer, fallback to non-named timer on older VMs.
-                try {
-                    Constructor<Timer> ctr = Timer.class.getConstructor(new Class[] { String.class, Boolean.TYPE });
-
-                    this.cancelTimer = ctr.newInstance(new Object[] { "MySQL Statement Cancellation Timer", Boolean.TRUE });
-                    createdNamedTimer = true;
-                } catch (Throwable t) {
-                    createdNamedTimer = false;
+                this.cancelTimer = new Timer("MySQL Statement Cancellation Timer", true);
             }
-
-                if (!createdNamedTimer) {
-                    this.cancelTimer = new Timer(true);
-                }
-            }
-
             return this.cancelTimer;
         }
     }
@@ -3544,7 +3533,7 @@ public class ConnectionImpl extends ConnectionPropertiesImpl implements MySQLCon
                     }
                 } catch (SQLException ex1) {
                     if (ex1.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) {
-                        throw SQLError.createSQLException("Could not retrieve transation read-only status server", SQLError.SQL_STATE_GENERAL_ERROR, ex1,
+                        throw SQLError.createSQLException("Could not retrieve transaction read-only status from server", SQLError.SQL_STATE_GENERAL_ERROR, ex1,
                                 getExceptionInterceptor());
                     }
                 }
@@ -4061,7 +4050,8 @@ public class ConnectionImpl extends ConnectionPropertiesImpl implements MySQLCon
             if (this.useServerPreparedStmts && canServerPrepare) {
                 if (this.getCachePreparedStatements()) {
                     synchronized (this.serverSideStatementCache) {
-                        pStmt = (com.mysql.jdbc.ServerPreparedStatement) this.serverSideStatementCache.remove(sql);
+                        pStmt = (com.mysql.jdbc.ServerPreparedStatement) this.serverSideStatementCache
+                                .remove(makePreparedStatementCacheKey(this.database, sql));
 
                         if (pStmt != null) {
                             ((com.mysql.jdbc.ServerPreparedStatement) pStmt).setClosed(false);
@@ -4251,11 +4241,18 @@ public class ConnectionImpl extends ConnectionPropertiesImpl implements MySQLCon
 
     }
 
+    private String makePreparedStatementCacheKey(String catalog, String query) {
+        StringBuilder key = new StringBuilder();
+        key.append("/*").append(catalog).append("*/");
+        key.append(query);
+        return key.toString();
+    }
+
     public void recachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException {
         synchronized (getConnectionMutex()) {
             if (getCachePreparedStatements() && pstmt.isPoolable()) {
                 synchronized (this.serverSideStatementCache) {
-                    this.serverSideStatementCache.put(pstmt.originalSql, pstmt);
+                    this.serverSideStatementCache.put(makePreparedStatementCacheKey(pstmt.currentCatalog, pstmt.originalSql), pstmt);
                 }
             }
         }
@@ -4265,7 +4262,7 @@ public class ConnectionImpl extends ConnectionPropertiesImpl implements MySQLCon
         synchronized (getConnectionMutex()) {
             if (getCachePreparedStatements() && pstmt.isPoolable()) {
                 synchronized (this.serverSideStatementCache) {
-                    this.serverSideStatementCache.remove(pstmt.originalSql);
+                    this.serverSideStatementCache.remove(makePreparedStatementCacheKey(pstmt.currentCatalog, pstmt.originalSql));
                 }
             }
         }
@@ -4646,7 +4643,6 @@ public class ConnectionImpl extends ConnectionPropertiesImpl implements MySQLCon
      * @see java.sql.Connection#prepareStatement(String)
      */
     public java.sql.PreparedStatement serverPrepareStatement(String sql) throws SQLException {
-
         String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql) : sql;
 
         return ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.getCatalog(), DEFAULT_RESULT_SET_TYPE,
@@ -4981,31 +4977,35 @@ public class ConnectionImpl extends ConnectionPropertiesImpl implements MySQLCon
 
     private void setSessionVariables() throws SQLException {
         if (this.versionMeetsMinimum(4, 0, 0) && getSessionVariables() != null) {
-            List<String> variablesToSet = StringUtils.split(getSessionVariables(), ",", "\"'", "\"'", false);
-
-            int numVariablesToSet = variablesToSet.size();
+            List<String> variablesToSet = new ArrayList<String>();
+            for (String part : StringUtils.split(getSessionVariables(), ",", "\"'(", "\"')", "\"'", true)) {
+                variablesToSet.addAll(StringUtils.split(part, ";", "\"'(", "\"')", "\"'", true));
+            }
 
+            if (!variablesToSet.isEmpty()) {
                 java.sql.Statement stmt = null;
-
                 try {
                     stmt = getMetadataSafeStatement();
-
-                for (int i = 0; i < numVariablesToSet; i++) {
-                    String variableValuePair = variablesToSet.get(i);
-
-                    if (variableValuePair.startsWith("@")) {
-                        stmt.executeUpdate("SET " + variableValuePair);
-                    } else {
-                        stmt.executeUpdate("SET SESSION " + variableValuePair);
+                    StringBuilder query = new StringBuilder("SET ");
+                    String separator = "";
+                    for (String variableToSet : variablesToSet) {
+                        if (variableToSet.length() > 0) {
+                            query.append(separator);
+                            if (!variableToSet.startsWith("@")) {
+                                query.append("SESSION ");
+                            }
+                            query.append(variableToSet);
+                            separator = ",";
                         }
                     }
+                    stmt.executeUpdate(query.toString());
                 } finally {
                     if (stmt != null) {
                         stmt.close();
                     }
                 }
             }
-
+        }
     }
 
     /**
diff --git a/src/com/mysql/jdbc/ConnectionPropertiesImpl.java b/src/com/mysql/jdbc/ConnectionPropertiesImpl.java
index 50c67d7..191fee0 100644
--- a/src/com/mysql/jdbc/ConnectionPropertiesImpl.java
+++ b/src/com/mysql/jdbc/ConnectionPropertiesImpl.java
@@ -686,6 +686,9 @@ public class ConnectionPropertiesImpl implements Serializable, ConnectionPropert
     private BooleanConnectionProperty autoClosePStmtStreams = new BooleanConnectionProperty("autoClosePStmtStreams", false,
             Messages.getString("ConnectionProperties.autoClosePstmtStreams"), "3.1.12", MISC_CATEGORY, Integer.MIN_VALUE);
 
+    private StringConnectionProperty replicationConnectionGroup = new StringConnectionProperty("replicationConnectionGroup", null,
+            Messages.getString("ConnectionProperties.replicationConnectionGroup"), "5.1.27", HA_CATEGORY, Integer.MIN_VALUE);
+
     private BooleanConnectionProperty allowMasterDownConnections = new BooleanConnectionProperty("allowMasterDownConnections", false,
             Messages.getString("ConnectionProperties.allowMasterDownConnections"), "5.1.27", HA_CATEGORY, Integer.MAX_VALUE);
 
@@ -4813,6 +4816,14 @@ public class ConnectionPropertiesImpl implements Serializable, ConnectionPropert
         return this.disconnectOnExpiredPasswords.getValueAsBoolean();
     }
 
+    public String getReplicationConnectionGroup() {
+        return this.replicationConnectionGroup.getValueAsString();
+    }
+
+    public void setReplicationConnectionGroup(String replicationConnectionGroup) {
+        this.replicationConnectionGroup.setValue(replicationConnectionGroup);
+    }
+
     public boolean getAllowMasterDownConnections() {
         return this.allowMasterDownConnections.getValueAsBoolean();
     }
diff --git a/src/com/mysql/jdbc/DatabaseMetaData.java b/src/com/mysql/jdbc/DatabaseMetaData.java
index c041f78..924cf82 100644
--- a/src/com/mysql/jdbc/DatabaseMetaData.java
+++ b/src/com/mysql/jdbc/DatabaseMetaData.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -1839,9 +1839,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData {
                         paramName = paramName.substring(1, paramName.length() - 1);
                     }
 
-                    int wildCompareRes = StringUtils.wildCompare(paramName, parameterNamePattern);
-
-                    if (wildCompareRes != StringUtils.WILD_COMPARE_NO_MATCH) {
+                    if (StringUtils.wildCompareIgnoreCase(paramName, parameterNamePattern)) {
                         ResultSetRow row = convertTypeDescriptorToProcedureRow(procNameAsBytes, procCatAsBytes, paramName, isOutParam, isInParam, false,
                                 typeDesc, forGetFunctionColumns, ordinal++);
 
diff --git a/src/com/mysql/jdbc/ExportControlled.java b/src/com/mysql/jdbc/ExportControlled.java
index 2468dc1..db35c8f 100644
--- a/src/com/mysql/jdbc/ExportControlled.java
+++ b/src/com/mysql/jdbc/ExportControlled.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -31,13 +31,22 @@ import java.net.MalformedURLException;
 import java.net.Socket;
 import java.net.SocketException;
 import java.net.URL;
+import java.security.InvalidAlgorithmParameterException;
 import java.security.KeyFactory;
 import java.security.KeyManagementException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.security.UnrecoverableKeyException;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertPathValidatorResult;
 import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXCertPathValidatorResult;
+import java.security.cert.PKIXParameters;
+import java.security.cert.X509CertSelector;
 import java.security.cert.X509Certificate;
 import java.security.interfaces.RSAPublicKey;
 import java.security.spec.X509EncodedKeySpec;
@@ -48,10 +57,12 @@ import java.util.List;
 import java.util.Properties;
 
 import javax.crypto.Cipher;
+import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509TrustManager;
 
@@ -204,23 +215,123 @@ public class ExportControlled {
     private ExportControlled() { /* prevent instantiation */
     }
 
+    /**
+     * Implementation of X509TrustManager wrapping JVM X509TrustManagers to add expiration check
+     */
+    public static class X509TrustManagerWrapper implements X509TrustManager {
+
+        private X509TrustManager origTm = null;
+        private boolean verifyServerCert = false;
+        private CertificateFactory certFactory = null;
+        private PKIXParameters validatorParams = null;
+        private CertPathValidator validator = null;
+
+        public X509TrustManagerWrapper(X509TrustManager tm, boolean verifyServerCertificate, KeyStore trustKeyStore) throws CertificateException {
+            this.origTm = tm;
+            this.verifyServerCert = verifyServerCertificate;
+
+            if (verifyServerCertificate) {
+                try {
+                    this.validatorParams = new PKIXParameters(trustKeyStore);
+                    this.validatorParams.setRevocationEnabled(false);
+                    this.validator = CertPathValidator.getInstance("PKIX");
+                    this.certFactory = CertificateFactory.getInstance("X.509");
+                } catch (Exception e) {
+                    throw new CertificateException(e);
+                }
+            }
+
+        }
+
+        public X509TrustManagerWrapper() {
+        }
+
+        public X509Certificate[] getAcceptedIssuers() {
+            return this.origTm != null ? this.origTm.getAcceptedIssuers() : new X509Certificate[0];
+        }
+
+        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+            for (int i = 0; i < chain.length; i++) {
+                chain[i].checkValidity();
+            }
+
+            if (this.validatorParams != null) {
+
+                X509CertSelector certSelect = new X509CertSelector();
+                certSelect.setSerialNumber(chain[0].getSerialNumber());
+
+                try {
+                    CertPath certPath = this.certFactory.generateCertPath(Arrays.asList(chain));
+                    // Validate against truststore
+                    CertPathValidatorResult result = this.validator.validate(certPath, this.validatorParams);
+                    // Check expiration for the CA used to validate this path
+                    ((PKIXCertPathValidatorResult) result).getTrustAnchor().getTrustedCert().checkValidity();
+
+                } catch (InvalidAlgorithmParameterException e) {
+                    throw new CertificateException(e);
+                } catch (CertPathValidatorException e) {
+                    throw new CertificateException(e);
+                }
+            }
+
+            if (this.verifyServerCert) {
+                this.origTm.checkServerTrusted(chain, authType);
+            }
+        }
+
+        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+            this.origTm.checkClientTrusted(chain, authType);
+        }
+    };
+
     private static SSLSocketFactory getSSLSocketFactoryDefaultOrConfigured(MysqlIO mysqlIO) throws SQLException {
         String clientCertificateKeyStoreUrl = mysqlIO.connection.getClientCertificateKeyStoreUrl();
-        String trustCertificateKeyStoreUrl = mysqlIO.connection.getTrustCertificateKeyStoreUrl();
-        String clientCertificateKeyStoreType = mysqlIO.connection.getClientCertificateKeyStoreType();
         String clientCertificateKeyStorePassword = mysqlIO.connection.getClientCertificateKeyStorePassword();
-        String trustCertificateKeyStoreType = mysqlIO.connection.getTrustCertificateKeyStoreType();
+        String clientCertificateKeyStoreType = mysqlIO.connection.getClientCertificateKeyStoreType();
+        String trustCertificateKeyStoreUrl = mysqlIO.connection.getTrustCertificateKeyStoreUrl();
         String trustCertificateKeyStorePassword = mysqlIO.connection.getTrustCertificateKeyStorePassword();
+        String trustCertificateKeyStoreType = mysqlIO.connection.getTrustCertificateKeyStoreType();
 
-        if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl) && StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) {
-            if (mysqlIO.connection.getVerifyServerCertificate()) {
-                return (SSLSocketFactory) SSLSocketFactory.getDefault();
+        if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl)) {
+            clientCertificateKeyStoreUrl = System.getProperty("javax.net.ssl.keyStore");
+            clientCertificateKeyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword");
+            clientCertificateKeyStoreType = System.getProperty("javax.net.ssl.keyStoreType");
+            if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreType)) {
+                clientCertificateKeyStoreType = "JKS";
+            }
+            // check URL
+            if (!StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl)) {
+                try {
+                    new URL(clientCertificateKeyStoreUrl);
+                } catch (MalformedURLException e) {
+                    clientCertificateKeyStoreUrl = "file:" + clientCertificateKeyStoreUrl;
+                }
+            }
+        }
+
+        if (StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) {
+            trustCertificateKeyStoreUrl = System.getProperty("javax.net.ssl.trustStore");
+            trustCertificateKeyStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
+            trustCertificateKeyStoreType = System.getProperty("javax.net.ssl.trustStoreType");
+            if (StringUtils.isNullOrEmpty(trustCertificateKeyStoreType)) {
+                trustCertificateKeyStoreType = "JKS";
+            }
+            // check URL
+            if (!StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) {
+                try {
+                    new URL(trustCertificateKeyStoreUrl);
+                } catch (MalformedURLException e) {
+                    trustCertificateKeyStoreUrl = "file:" + trustCertificateKeyStoreUrl;
+                }
             }
         }
 
         TrustManagerFactory tmf = null;
         KeyManagerFactory kmf = null;
 
+        KeyManager[] kms = null;
+        List<TrustManager> tms = new ArrayList<TrustManager>();
+
         try {
             tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
             kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
@@ -240,6 +351,7 @@ public class ExportControlled {
                     ksIS = ksURL.openStream();
                     clientKeyStore.load(ksIS, password);
                     kmf.init(clientKeyStore, password);
+                    kms = kmf.getKeyManagers();
                 }
             } catch (UnrecoverableKeyException uke) {
                 throw SQLError.createSQLException("Could not recover keys from client keystore.  Check password?", SQL_STATE_BAD_SSL_PARAMS, 0, false,
@@ -285,6 +397,17 @@ public class ExportControlled {
                     ksIS = ksURL.openStream();
                     trustKeyStore.load(ksIS, password);
                     tmf.init(trustKeyStore);
+
+                    // building the customized list of TrustManagers from original one if it's available
+                    TrustManager[] origTms = tmf.getTrustManagers();
+                    final boolean verifyServerCertificate = mysqlIO.connection.getVerifyServerCertificate();
+
+                    for (int j = 0; j < origTms.length; j++) {
+                        tms.add(origTms[j] instanceof X509TrustManager
+                                ? new X509TrustManagerWrapper((X509TrustManager) origTms[j], verifyServerCertificate, trustKeyStore) // wrapping X509TrustManagers
+                                : origTms[j] // for non-X509 TrustManagers putting original ones
+                        );
+                    }
                 }
             } catch (NoSuchAlgorithmException nsae) {
                 throw SQLError.createSQLException("Unsupported keystore algorithm [" + nsae.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false,
@@ -316,26 +439,16 @@ public class ExportControlled {
             }
         }
 
-        SSLContext sslContext = null;
-
-        try {
-            sslContext = SSLContext.getInstance("TLS");
-            sslContext.init(StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl) ? null : kmf.getKeyManagers(),
-                    mysqlIO.connection.getVerifyServerCertificate() ? tmf.getTrustManagers() : new X509TrustManager[] { new X509TrustManager() {
-                        public void checkClientTrusted(X509Certificate[] chain, String authType) {
-                            // return without complaint
+        // if original TrustManagers are not available then putting one X509TrustManagerWrapper which take care only about expiration check 
+        if (tms.size() == 0) {
+            tms.add(new X509TrustManagerWrapper());
         }
 
-                        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
-                            // return without complaint
-                        }
-
-                        public X509Certificate[] getAcceptedIssuers() {
-                            return null;
-                        }
-                    } }, null);
-
+        try {
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+            sslContext.init(kms, tms.toArray(new TrustManager[tms.size()]), null);
             return sslContext.getSocketFactory();
+
         } catch (NoSuchAlgorithmException nsae) {
             throw SQLError.createSQLException("TLS is not a valid SSL protocol.", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor());
         } catch (KeyManagementException kme) {
diff --git a/src/com/mysql/jdbc/JDBC42ResultSet.java b/src/com/mysql/jdbc/JDBC42ResultSet.java
index 88d6d3c..558bb4e 100644
--- a/src/com/mysql/jdbc/JDBC42ResultSet.java
+++ b/src/com/mysql/jdbc/JDBC42ResultSet.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -27,6 +27,8 @@ import java.sql.Date;
 import java.sql.SQLException;
 import java.sql.SQLType;
 import java.sql.Struct;
+import java.sql.Time;
+import java.sql.Timestamp;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
@@ -62,20 +64,25 @@ public class JDBC42ResultSet extends JDBC4ResultSet {
         }
 
         if (type.equals(LocalDate.class)) {
-            return type.cast(getDate(columnIndex).toLocalDate());
+            final Date date = getDate(columnIndex);
+            return date == null ? null : type.cast(date.toLocalDate());
         } else if (type.equals(LocalDateTime.class)) {
-            return type.cast(getTimestamp(columnIndex).toLocalDateTime());
+            final Timestamp timestamp = getTimestamp(columnIndex);
+            return timestamp == null ? null : type.cast(timestamp.toLocalDateTime());
         } else if (type.equals(LocalTime.class)) {
-            return type.cast(getTime(columnIndex).toLocalTime());
+            final Time time = getTime(columnIndex);
+            return time == null ? null : type.cast(time.toLocalTime());
         } else if (type.equals(OffsetDateTime.class)) {
             try {
-                return type.cast(OffsetDateTime.parse(getString(columnIndex)));
+                final String string = getString(columnIndex);
+                return string == null ? null : type.cast(OffsetDateTime.parse(string));
             } catch (DateTimeParseException e) {
                 // Let it continue and try by object deserialization.
             }
         } else if (type.equals(OffsetTime.class)) {
             try {
-                return type.cast(OffsetTime.parse(getString(columnIndex)));
+                final String string = getString(columnIndex);
+                return string == null? null : type.cast(OffsetTime.parse(string));
             } catch (DateTimeParseException e) {
                 // Let it continue and try by object deserialization.
             }
diff --git a/src/com/mysql/jdbc/JDBC42UpdatableResultSet.java b/src/com/mysql/jdbc/JDBC42UpdatableResultSet.java
index 348360a..fd16063 100644
--- a/src/com/mysql/jdbc/JDBC42UpdatableResultSet.java
+++ b/src/com/mysql/jdbc/JDBC42UpdatableResultSet.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -61,6 +61,7 @@ public class JDBC42UpdatableResultSet extends JDBC4UpdatableResultSet {
      * @throws SQLException
      */
     public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (type == null) {
                 throw SQLError.createSQLException("Type parameter can not be null", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
             }
@@ -87,6 +88,7 @@ public class JDBC42UpdatableResultSet extends JDBC4UpdatableResultSet {
 
             return super.getObject(columnIndex, type);
         }
+    }
 
     /**
      * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime.
@@ -96,7 +98,7 @@ public class JDBC42UpdatableResultSet extends JDBC4UpdatableResultSet {
      * @throws SQLException
      */
     @Override
-    public synchronized void updateObject(int columnIndex, Object x) throws SQLException {
+    public void updateObject(int columnIndex, Object x) throws SQLException {
         super.updateObject(columnIndex, JDBC42Helper.convertJavaTimeToJavaSql(x));
     }
 
@@ -109,7 +111,7 @@ public class JDBC42UpdatableResultSet extends JDBC4UpdatableResultSet {
      * @throws SQLException
      */
     @Override
-    public synchronized void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException {
+    public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException {
         super.updateObject(columnIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), scaleOrLength);
     }
 
@@ -121,7 +123,7 @@ public class JDBC42UpdatableResultSet extends JDBC4UpdatableResultSet {
      * @throws SQLException
      */
     @Override
-    public synchronized void updateObject(String columnLabel, Object x) throws SQLException {
+    public void updateObject(String columnLabel, Object x) throws SQLException {
         super.updateObject(columnLabel, JDBC42Helper.convertJavaTimeToJavaSql(x));
     }
 
@@ -134,7 +136,7 @@ public class JDBC42UpdatableResultSet extends JDBC4UpdatableResultSet {
      * @throws SQLException
      */
     @Override
-    public synchronized void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException {
+    public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException {
         super.updateObject(columnLabel, JDBC42Helper.convertJavaTimeToJavaSql(x), scaleOrLength);
     }
 
@@ -147,7 +149,7 @@ public class JDBC42UpdatableResultSet extends JDBC4UpdatableResultSet {
      * @param targetSqlType
      * @throws SQLException
      */
-    public synchronized void updateObject(int columnIndex, Object x, SQLType targetSqlType) throws SQLException {
+    public void updateObject(int columnIndex, Object x, SQLType targetSqlType) throws SQLException {
         super.updateObjectInternal(columnIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType), 0);
     }
 
@@ -161,7 +163,7 @@ public class JDBC42UpdatableResultSet extends JDBC4UpdatableResultSet {
      * @param scaleOrLength
      * @throws SQLException
      */
-    public synchronized void updateObject(int columnIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException {
+    public void updateObject(int columnIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException {
         super.updateObjectInternal(columnIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType), scaleOrLength);
     }
 
@@ -174,7 +176,7 @@ public class JDBC42UpdatableResultSet extends JDBC4UpdatableResultSet {
      * @param targetSqlType
      * @throws SQLException
      */
-    public synchronized void updateObject(String columnLabel, Object x, SQLType targetSqlType) throws SQLException {
+    public void updateObject(String columnLabel, Object x, SQLType targetSqlType) throws SQLException {
         super.updateObjectInternal(findColumn(columnLabel), JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType), 0);
     }
 
@@ -188,7 +190,7 @@ public class JDBC42UpdatableResultSet extends JDBC4UpdatableResultSet {
      * @param scaleOrLength
      * @throws SQLException
      */
-    public synchronized void updateObject(String columnLabel, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException {
+    public void updateObject(String columnLabel, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException {
         super.updateObjectInternal(findColumn(columnLabel), JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType), scaleOrLength);
     }
 }
diff --git a/src/com/mysql/jdbc/JDBC4UpdatableResultSet.java b/src/com/mysql/jdbc/JDBC4UpdatableResultSet.java
index 1510779..dacee5f 100644
--- a/src/com/mysql/jdbc/JDBC4UpdatableResultSet.java
+++ b/src/com/mysql/jdbc/JDBC4UpdatableResultSet.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -201,7 +201,8 @@ public class JDBC4UpdatableResultSet extends UpdatableResultSet {
      * @exception SQLException
      *                if a database-access error occurs
      */
-    public synchronized void updateNCharacterStream(int columnIndex, java.io.Reader x, int length) throws SQLException {
+    public void updateNCharacterStream(int columnIndex, java.io.Reader x, int length) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             String fieldEncoding = this.fields[columnIndex - 1].getEncoding();
             if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
                 throw new SQLException("Can not call updateNCharacterStream() when field's character set isn't UTF-8");
@@ -224,6 +225,7 @@ public class JDBC4UpdatableResultSet extends UpdatableResultSet {
                 }
             }
         }
+    }
 
     /**
      * JDBC 4.0 Update a column with a character stream value. The updateXXX()
@@ -242,7 +244,7 @@ public class JDBC4UpdatableResultSet extends UpdatableResultSet {
      * @exception SQLException
      *                if a database-access error occurs
      */
-    public synchronized void updateNCharacterStream(String columnName, java.io.Reader reader, int length) throws SQLException {
+    public void updateNCharacterStream(String columnName, java.io.Reader reader, int length) throws SQLException {
         updateNCharacterStream(findColumn(columnName), reader, length);
     }
 
@@ -250,6 +252,7 @@ public class JDBC4UpdatableResultSet extends UpdatableResultSet {
      * @see ResultSet#updateNClob(int, NClob)
      */
     public void updateNClob(int columnIndex, java.sql.NClob nClob) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             String fieldEncoding = this.fields[columnIndex - 1].getEncoding();
             if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
                 throw new SQLException("Can not call updateNClob() when field's character set isn't UTF-8");
@@ -261,6 +264,7 @@ public class JDBC4UpdatableResultSet extends UpdatableResultSet {
                 updateNCharacterStream(columnIndex, nClob.getCharacterStream(), (int) nClob.length());
             }
         }
+    }
 
     /**
      * @see ResultSet#updateClob(int, Clob)
@@ -283,7 +287,8 @@ public class JDBC4UpdatableResultSet extends UpdatableResultSet {
      * @exception SQLException
      *                if a database-access error occurs
      */
-    public synchronized void updateNString(int columnIndex, String x) throws SQLException {
+    public void updateNString(int columnIndex, String x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             String fieldEncoding = this.fields[columnIndex - 1].getEncoding();
             if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
                 throw new SQLException("Can not call updateNString() when field's character set isn't UTF-8");
@@ -307,6 +312,7 @@ public class JDBC4UpdatableResultSet extends UpdatableResultSet {
                 }
             }
         }
+    }
 
     /**
      * JDBC 4.0 Update a column with NATIONAL CHARACTER. The updateXXX() methods
@@ -322,7 +328,7 @@ public class JDBC4UpdatableResultSet extends UpdatableResultSet {
      * @exception SQLException
      *                if a database-access error occurs
      */
-    public synchronized void updateNString(String columnName, String x) throws SQLException {
+    public void updateNString(String columnName, String x) throws SQLException {
         updateNString(findColumn(columnName), x);
     }
 
@@ -526,7 +532,7 @@ public class JDBC4UpdatableResultSet extends UpdatableResultSet {
         return asString;
     }
 
-    public synchronized boolean isClosed() throws SQLException {
+    public boolean isClosed() throws SQLException {
         return this.isClosed;
     }
 
diff --git a/src/com/mysql/jdbc/LocalizedErrorMessages.properties b/src/com/mysql/jdbc/LocalizedErrorMessages.properties
index 020828e..def833f 100644
--- a/src/com/mysql/jdbc/LocalizedErrorMessages.properties
+++ b/src/com/mysql/jdbc/LocalizedErrorMessages.properties
@@ -234,6 +234,7 @@ StringUtils.6='.
 StringUtils.10=Unsupported character encoding '
 StringUtils.11='.
 StringUtils.15=Illegal argument value {0} for openingMarkers and/or {1} for closingMarkers. These cannot be null and must have the same length.
+StringUtils.16=Illegal argument value {0} for overridingMarkers. These cannot be null and must be a sub-set of openingMarkers {1}.
 RowDataDynamic.2=WARN: Possible incomplete traversal of result set. Streaming result set had 
 RowDataDynamic.3=\ rows left to read when it was closed.
 RowDataDynamic.4=\n\nYou should consider re-formulating your query to 
@@ -458,6 +459,8 @@ ConnectionProperties.categorySecurity=Security
 #
 
 ConnectionProperties.loadDataLocal=Should the driver allow use of 'LOAD DATA LOCAL INFILE...' (defaults to 'true').
+ConnectionProperties.replicationEnableJMX=Enables JMX-based management of replication connection groups, including live slave promotion, addition of new slaves and removal of master or slave hosts from load-balanced master and slave connection pools.
+ConnectionProperties.replicationConnectionGroup=Logical group of replication connections within a classloader, used to manage different groups independently. If not specified, live management of replication connections is disabled.
 ConnectionProperties.allowMasterDownConnections=By default, a replication-aware connection will fail to connect when configured master hosts are all unavailable at initial connection. Setting this property to 'true' allows to establish the initial connection, by failing over to the slave servers, in read-only state. It won't prevent subsequent failures when switching back to the master hosts i.e. by setting the replication connection to read/write state.
 ConnectionProperties.allowSlaveDownConnections=By default, a replication-aware connection will fail to connect when configured slave hosts are all unavailable at initial connection. Setting this property to 'true' allows to establish the initial connection. It won't prevent failures when switching to slaves i.e. by setting the replication connection to read-only state. The property 'readFromMasterWhenNoSlaves' should be used for this purpose. 
 ConnectionProperties.readFromMasterWhenNoSlaves=Replication-aware connections distribute load by using the master hosts when in read/write state and by using the slave hosts when in read-only state. If, when setting the connection to read-only state, none of the slave hosts are available, an SQLExeception is thrown back. Setting this property to 'true' allows to fail over to the master hosts, while setting the connection state to read-only, when no slave hosts are available at switch instant.
@@ -524,7 +527,6 @@ ConnectionProperties.loadBalanceExceptionChecker=Fully-qualified class name of c
 ConnectionProperties.loadBalanceSQLStateFailover=Comma-delimited list of SQLState codes used by default load-balanced exception checker to determine whether a given SQLException should trigger failover.  The SQLState of a given SQLException is evaluated to determine whether it begins with any value in the comma-delimited list.
 ConnectionProperties.loadBalanceSQLExceptionSubclassFailover=Comma-delimited list of classes/interfaces used by default load-balanced exception checker to determine whether a given SQLException should trigger failover.  The comparison is done using Class.isInstance(SQLException) using the thrown SQLException.
 ConnectionProperties.loadBalanceEnableJMX=Enables JMX-based management of load-balanced connection groups, including live addition/removal of hosts from load-balancing pool.
-ConnectionProperties.replicationEnableJMX=Enables JMX-based management of replication connection groups, including live slave promotion, addition of new slaves and removal of master or slave hosts from load-balanced master and slave connection pools.
 ConnectionProperties.loadBalanceHostRemovalGracePeriod=Sets the grace period to wait for a host being removed from a load-balanced connection, to be released when it is currently the active host.
 ConnectionProperties.loadBalanceAutoCommitStatementThreshold=When auto-commit is enabled, the number of statements which should be executed before triggering load-balancing to rebalance.  Default value of 0 causes load-balanced connections to only rebalance when exceptions are encountered, or auto-commit is disabled and transactions are explicitly committed or rolled back.
 ConnectionProperties.loadBalanceAutoCommitStatementRegex=When load-balancing is enabled for auto-commit statements (via loadBalanceAutoCommitStatementThreshold), the statement counter will only increment when the SQL matches the regular expression.  By default, every statement issued matches.
@@ -577,7 +579,7 @@ ConnectionProperties.secondsBeforeRetryMaster=How long should the driver wait, w
 ConnectionProperties.selfDestructOnPingSecondsLifetime=If set to a non-zero value, the driver will report close the connection and report failure when Connection.ping() or Connection.isValid(int) is called if the connection's lifetime exceeds this value.
 ConnectionProperties.selfDestructOnPingMaxOperations==If set to a non-zero value, the driver will report close the connection and report failure when Connection.ping() or Connection.isValid(int) is called if the connection's count of commands sent to the server exceeds this value.
 ConnectionProperties.serverTimezone=Override detection/mapping of time zone. Used when time zone from server doesn't map to Java time zone
-ConnectionProperties.sessionVariables=A comma-separated list of name/value pairs to be sent as SET SESSION ... to the server when the driver connects.
+ConnectionProperties.sessionVariables=A comma or semicolon separated list of name=value pairs to be sent as SET [SESSION] ... to the server when the driver connects.
 ConnectionProperties.slowQueryThresholdMillis=If 'logSlowQueries' is enabled, how long should a query (in ms) before it is logged as 'slow'?
 ConnectionProperties.slowQueryThresholdNanos=If 'useNanosForElapsedTime' is set to true, and this property is set to a non-zero value, the driver will use this threshold (in nanosecond units) to determine if a query was slow.
 ConnectionProperties.socketFactory=The name of the class that the driver should use for creating socket connections to the server. This class must implement the interface 'com.mysql.jdbc.SocketFactory' and have public no-args constructor.
diff --git a/src/com/mysql/jdbc/MultiHostMySQLConnection.java b/src/com/mysql/jdbc/MultiHostMySQLConnection.java
index 4e20ca6..9f1b2bb 100644
--- a/src/com/mysql/jdbc/MultiHostMySQLConnection.java
+++ b/src/com/mysql/jdbc/MultiHostMySQLConnection.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -70,7 +70,7 @@ public class MultiHostMySQLConnection implements MySQLConnection {
         return this.thisAsProxy;
     }
 
-    protected MySQLConnection getActiveMySQLConnection() {
+    public MySQLConnection getActiveMySQLConnection() {
         synchronized (this.thisAsProxy) {
             return this.thisAsProxy.currentConnection;
         }
diff --git a/src/com/mysql/jdbc/MySQLConnection.java b/src/com/mysql/jdbc/MySQLConnection.java
index ae5dbcd..0ce5e6b 100644
--- a/src/com/mysql/jdbc/MySQLConnection.java
+++ b/src/com/mysql/jdbc/MySQLConnection.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -207,6 +207,8 @@ public interface MySQLConnection extends Connection, ConnectionProperties {
 
     MySQLConnection getMultiHostSafeProxy();
 
+    MySQLConnection getActiveMySQLConnection();
+
     ProfilerEventHandler getProfilerEventHandlerInstance();
 
     void setProfilerEventHandlerInstance(ProfilerEventHandler h);
diff --git a/src/com/mysql/jdbc/ReplicationMySQLConnection.java b/src/com/mysql/jdbc/ReplicationMySQLConnection.java
index 72a95d9..3f195f8 100644
--- a/src/com/mysql/jdbc/ReplicationMySQLConnection.java
+++ b/src/com/mysql/jdbc/ReplicationMySQLConnection.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -38,7 +38,7 @@ public class ReplicationMySQLConnection extends MultiHostMySQLConnection impleme
     }
 
     @Override
-    protected MySQLConnection getActiveMySQLConnection() {
+    public MySQLConnection getActiveMySQLConnection() {
         return (MySQLConnection) getCurrentConnection();
     }
 
diff --git a/src/com/mysql/jdbc/ResultSetImpl.java b/src/com/mysql/jdbc/ResultSetImpl.java
index 44a6e2c..6ac2659 100644
--- a/src/com/mysql/jdbc/ResultSetImpl.java
+++ b/src/com/mysql/jdbc/ResultSetImpl.java
@@ -2475,6 +2475,12 @@ public class ResultSetImpl implements ResultSetInternalMethods {
         if (!this.isBinaryEncoded) {
             int columnIndexMinusOne = columnIndex - 1;
 
+            if (this.thisRow.isNull(columnIndexMinusOne)) {
+                this.wasNullFlag = true;
+                return 0;
+            }
+            this.wasNullFlag = false;
+
             if (this.fields[columnIndexMinusOne].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
                 long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);
 
@@ -2486,16 +2492,6 @@ public class ResultSetImpl implements ResultSetInternalMethods {
             }
 
             if (this.useFastIntParsing) {
-                if (this.thisRow.isNull(columnIndexMinusOne)) {
-                    this.wasNullFlag = true;
-                } else {
-                    this.wasNullFlag = false;
-                }
-
-                if (this.wasNullFlag) {
-                    return 0;
-                }
-
                 if (this.thisRow.length(columnIndexMinusOne) == 0) {
                     return convertToZeroWithEmptyCheck();
                 }
@@ -2522,11 +2518,12 @@ public class ResultSetImpl implements ResultSetInternalMethods {
             }
 
             String val = null;
-
             try {
                 val = getString(columnIndex);
+                if ((val == null)) {
+                    return 0;
+                }
 
-                if ((val != null)) {
                 if (val.length() == 0) {
                     return convertToZeroWithEmptyCheck();
                 }
@@ -2545,9 +2542,7 @@ public class ResultSetImpl implements ResultSetInternalMethods {
                 checkForIntegerTruncation(columnIndex, null, intVal);
 
                 return intVal;
-                }
 
-                return 0;
             } catch (NumberFormatException nfe) {
                 try {
                     return parseIntAsDouble(columnIndex, val);
@@ -2662,21 +2657,17 @@ public class ResultSetImpl implements ResultSetInternalMethods {
         if (!this.isBinaryEncoded) {
             int columnIndexMinusOne = columnIndex - 1;
 
-            if (this.fields[columnIndexMinusOne].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
-                return getNumericRepresentationOfSQLBitType(columnIndex);
-            }
-
-            if (this.useFastIntParsing) {
             if (this.thisRow.isNull(columnIndexMinusOne)) {
                 this.wasNullFlag = true;
-                } else {
-                    this.wasNullFlag = false;
+                return 0;
             }
+            this.wasNullFlag = false;
 
-                if (this.wasNullFlag) {
-                    return 0;
+            if (this.fields[columnIndexMinusOne].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+                return getNumericRepresentationOfSQLBitType(columnIndex);
             }
 
+            if (this.useFastIntParsing) {
                 if (this.thisRow.length(columnIndexMinusOne) == 0) {
                     return convertToZeroWithEmptyCheck();
                 }
@@ -2703,11 +2694,12 @@ public class ResultSetImpl implements ResultSetInternalMethods {
             }
 
             String val = null;
-
             try {
                 val = getString(columnIndex);
+                if (val == null) {
+                    return 0;
+                }
 
-                if ((val != null)) {
                 if (val.length() == 0) {
                     return convertToZeroWithEmptyCheck();
                 }
@@ -2718,9 +2710,7 @@ public class ResultSetImpl implements ResultSetInternalMethods {
 
                 // Convert floating point
                 return parseLongAsDouble(columnIndexMinusOne, val);
-                }
 
-                return 0; // for NULL
             } catch (NumberFormatException nfe) {
                 try {
                     return parseLongAsDouble(columnIndexMinusOne, val);
@@ -4974,6 +4964,12 @@ public class ResultSetImpl implements ResultSetInternalMethods {
         checkColumnBounds(columnIndex);
 
         if (!this.isBinaryEncoded) {
+            if (this.thisRow.isNull(columnIndex - 1)) {
+                this.wasNullFlag = true;
+                return 0;
+            }
+            this.wasNullFlag = false;
+
             if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
                 long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);
 
@@ -4985,19 +4981,7 @@ public class ResultSetImpl implements ResultSetInternalMethods {
             }
 
             if (this.useFastIntParsing) {
-                Object value = this.thisRow.getColumnValue(columnIndex - 1);
-
-                if (value == null) {
-                    this.wasNullFlag = true;
-                } else {
-                    this.wasNullFlag = false;
-                }
-
-                if (this.wasNullFlag) {
-                    return 0;
-                }
-
-                byte[] shortAsBytes = (byte[]) value;
+                byte[] shortAsBytes = this.thisRow.getColumnValue(columnIndex - 1);
 
                 if (shortAsBytes.length == 0) {
                     return (short) convertToZeroWithEmptyCheck();
@@ -5031,11 +5015,11 @@ public class ResultSetImpl implements ResultSetInternalMethods {
             }
 
             String val = null;
-
             try {
                 val = getString(columnIndex);
-
-                if ((val != null)) {
+                if (val == null) {
+                    return 0;
+                }
 
                 if (val.length() == 0) {
                     return (short) convertToZeroWithEmptyCheck();
@@ -5047,9 +5031,7 @@ public class ResultSetImpl implements ResultSetInternalMethods {
 
                 // Convert floating point
                 return parseShortAsDouble(columnIndex, val);
-                }
 
-                return 0; // for NULL
             } catch (NumberFormatException nfe) {
                 try {
                     return parseShortAsDouble(columnIndex, val);
diff --git a/src/com/mysql/jdbc/StatementImpl.java b/src/com/mysql/jdbc/StatementImpl.java
index 411b82a..e750fbc 100644
--- a/src/com/mysql/jdbc/StatementImpl.java
+++ b/src/com/mysql/jdbc/StatementImpl.java
@@ -24,6 +24,8 @@
 package com.mysql.jdbc;
 
 import java.io.InputStream;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
 import java.math.BigInteger;
 import java.sql.BatchUpdateException;
 import java.sql.DriverManager;
@@ -67,15 +69,13 @@ public class StatementImpl implements Statement {
      * and simple way to implement a feature that isn't used all that often.
      */
     class CancelTask extends TimerTask {
-
-        long connectionId = 0;
         SQLException caughtWhileCancelling = null;
         StatementImpl toCancel;
         Properties origConnProps = null;
         String origConnURL = "";
+        long origConnId = 0;
 
         CancelTask(StatementImpl cancellee) throws SQLException {
-            this.connectionId = cancellee.connectionId;
             this.toCancel = cancellee;
             this.origConnProps = new Properties();
 
@@ -89,6 +89,7 @@ public class StatementImpl implements Statement {
             }
 
             this.origConnURL = StatementImpl.this.connection.getURL();
+            this.origConnId = StatementImpl.this.connection.getId();
         }
 
         @Override
@@ -103,23 +104,25 @@ public class StatementImpl implements Statement {
                     java.sql.Statement cancelStmt = null;
 
                     try {
-                        if (StatementImpl.this.connection.getQueryTimeoutKillsConnection()) {
+                        MySQLConnection physicalConn = StatementImpl.this.physicalConnection.get();
+                        if (physicalConn != null) {
+                            if (physicalConn.getQueryTimeoutKillsConnection()) {
                                 CancelTask.this.toCancel.wasCancelled = true;
                                 CancelTask.this.toCancel.wasCancelledByTimeout = true;
-                            StatementImpl.this.connection.realClose(false, false, true,
+                                physicalConn.realClose(false, false, true,
                                         new MySQLStatementCancelledException(Messages.getString("Statement.ConnectionKilledDueToTimeout")));
                             } else {
                                 synchronized (StatementImpl.this.cancelTimeoutMutex) {
-                                if (CancelTask.this.origConnURL.equals(StatementImpl.this.connection.getURL())) {
+                                    if (CancelTask.this.origConnURL.equals(physicalConn.getURL())) {
                                         // All's fine
-                                    cancelConn = StatementImpl.this.connection.duplicate();
+                                        cancelConn = physicalConn.duplicate();
                                         cancelStmt = cancelConn.createStatement();
-                                    cancelStmt.execute("KILL QUERY " + CancelTask.this.connectionId);
+                                        cancelStmt.execute("KILL QUERY " + physicalConn.getId());
                                     } else {
                                         try {
                                             cancelConn = (Connection) DriverManager.getConnection(CancelTask.this.origConnURL, CancelTask.this.origConnProps);
                                             cancelStmt = cancelConn.createStatement();
-                                        cancelStmt.execute("KILL QUERY " + CancelTask.this.connectionId);
+                                            cancelStmt.execute("KILL QUERY " + CancelTask.this.origConnId);
                                         } catch (NullPointerException npe) {
                                             // Log this? "Failed to connect to " + origConnURL + " and KILL query"
                                         }
@@ -128,13 +131,13 @@ public class StatementImpl implements Statement {
                                     CancelTask.this.toCancel.wasCancelledByTimeout = true;
                                 }
                             }
+                        }
                     } catch (SQLException sqlEx) {
                         CancelTask.this.caughtWhileCancelling = sqlEx;
                     } catch (NullPointerException npe) {
-                        // Case when connection closed while starting to cancel
-                        // We can't easily synchronize this, because then one thread can't cancel() a running query
-
-                        // ignore, we shouldn't re-throw this, because the connection's already closed, so the statement has been timed out.
+                        // Case when connection closed while starting to cancel.
+                        // We can't easily synchronize this, because then one thread can't cancel() a running query.
+                        // Ignore, we shouldn't re-throw this, because the connection's already closed, so the statement has been timed out.
                     } finally {
                         if (cancelStmt != null) {
                             try {
@@ -194,6 +197,9 @@ public class StatementImpl implements Statement {
     /** The connection that created us */
     protected volatile MySQLConnection connection = null;
 
+    /** The physical connection used to effectively execute the statement */
+    protected Reference<MySQLConnection> physicalConnection = null;
+
     protected long connectionId = 0;
 
     /** The catalog in use */
@@ -300,7 +306,7 @@ public class StatementImpl implements Statement {
      * Constructor for a Statement.
      * 
      * @param c
-     *            the Connection instantation that creates us
+     *            the Connection instance that creates us
      * @param catalog
      *            the database name in use when we were created
      * 
@@ -905,6 +911,12 @@ public class StatementImpl implements Statement {
     protected void statementBegins() {
         this.clearWarningsCalled = false;
         this.statementExecuting.set(true);
+
+        MySQLConnection physicalConn = this.connection.getMultiHostSafeProxy().getActiveMySQLConnection();
+        while (!(physicalConn instanceof ConnectionImpl)) {
+            physicalConn = physicalConn.getMultiHostSafeProxy().getActiveMySQLConnection();
+        }
+        this.physicalConnection = new WeakReference<MySQLConnection>(physicalConn);
     }
 
     protected void resetCancelledState() throws SQLException {
diff --git a/src/com/mysql/jdbc/StringUtils.java b/src/com/mysql/jdbc/StringUtils.java
index d7b7c87..f66bb12 100644
--- a/src/com/mysql/jdbc/StringUtils.java
+++ b/src/com/mysql/jdbc/StringUtils.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -37,6 +37,7 @@ import java.nio.charset.IllegalCharsetNameException;
 import java.nio.charset.UnsupportedCharsetException;
 import java.sql.SQLException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
@@ -107,11 +108,13 @@ public class StringUtils {
 
     private static Method toPlainStringMethod;
 
-    static final int WILD_COMPARE_MATCH_NO_WILD = 0;
+    private static final int WILD_COMPARE_MATCH = 0;
+    private static final int WILD_COMPARE_CONTINUE_WITH_WILD = 1;
+    private static final int WILD_COMPARE_NO_MATCH = -1;
 
-    static final int WILD_COMPARE_MATCH_WITH_WILD = 1;
-
-    static final int WILD_COMPARE_NO_MATCH = -1;
+    static final char WILDCARD_MANY = '%';
+    static final char WILDCARD_ONE = '_';
+    static final char WILDCARD_ESCAPE = '\\';
 
     private static final ConcurrentHashMap<String, Charset> charsetsByAlias = new ConcurrentHashMap<String, Charset>();
 
@@ -1048,7 +1051,7 @@ public class StringUtils {
             int wc = 0;
             boolean match = true;
             while (++wc < searchForWordsCount && match) {
-                int positionOfNextWord = indexOfNextChar(startingPositionForNextWord, searchInLength - 1, searchIn, null, null, searchMode2);
+                int positionOfNextWord = indexOfNextChar(startingPositionForNextWord, searchInLength - 1, searchIn, null, null, null, searchMode2);
                 if (startingPositionForNextWord == positionOfNextWord || !startsWithIgnoreCase(searchIn, positionOfNextWord, searchForSequence[wc])) {
                     // either no gap between words or match failed
                     match = false;
@@ -1085,6 +1088,33 @@ public class StringUtils {
      */
     public static int indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers,
             Set<SearchMode> searchMode) {
+        return indexOfIgnoreCase(startingPosition, searchIn, searchFor, openingMarkers, closingMarkers, "", searchMode);
+    }
+
+    /**
+     * Finds the position of a substring within a string, ignoring case, with the option to skip text delimited by given markers or within comments.
+     * 
+     * @param startingPosition
+     *            the position to start the search from
+     * @param searchIn
+     *            the string to search in
+     * @param searchFor
+     *            the string to search for
+     * @param openingMarkers
+     *            characters which delimit the beginning of a text block to skip
+     * @param closingMarkers
+     *            characters which delimit the end of a text block to skip
+     * @param overridingMarkers
+     *            the subset of <code>openingMarkers</code> that override the remaining markers, e.g., if <code>openingMarkers = "'("</code> and
+     *            <code>overridingMarkers = "'"</code> then the block between the outer parenthesis in <code>"start ('max('); end"</code> is strictly consumed,
+     *            otherwise the suffix <code>" end"</code> would end up being consumed too in the process of handling the nested parenthesis.
+     * @param searchMode
+     *            a <code>Set</code>, ideally an <code>EnumSet</code>, containing the flags from the enum <code>StringUtils.SearchMode</code> that determine the
+     *            behavior of the search
+     * @return the position where <code>searchFor</code> is found within <code>searchIn</code> starting from <code>startingPosition</code>.
+     */
+    public static int indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers,
+            String overridingMarkers, Set<SearchMode> searchMode) {
         if (searchIn == null || searchFor == null) {
             return -1;
         }
@@ -1097,10 +1127,19 @@ public class StringUtils {
             return -1;
         }
 
-        if (searchMode.contains(SearchMode.SKIP_BETWEEN_MARKERS)
-                && (openingMarkers == null || closingMarkers == null || openingMarkers.length() != closingMarkers.length())) {
+        if (searchMode.contains(SearchMode.SKIP_BETWEEN_MARKERS)) {
+            if (openingMarkers == null || closingMarkers == null || openingMarkers.length() != closingMarkers.length()) {
                 throw new IllegalArgumentException(Messages.getString("StringUtils.15", new String[] { openingMarkers, closingMarkers }));
             }
+            if (overridingMarkers == null) {
+                throw new IllegalArgumentException(Messages.getString("StringUtils.16", new String[] { overridingMarkers, openingMarkers }));
+            }
+            for (char c : overridingMarkers.toCharArray()) {
+                if (openingMarkers.indexOf(c) == -1) {
+                    throw new IllegalArgumentException(Messages.getString("StringUtils.16", new String[] { overridingMarkers, openingMarkers }));
+                }
+            }
+        }
 
         // Some locales don't follow upper-case rule, so need to check both
         char firstCharOfSearchForUc = Character.toUpperCase(searchFor.charAt(0));
@@ -1113,7 +1152,7 @@ public class StringUtils {
         }
 
         for (int i = startingPosition; i <= stopSearchingAt; i++) {
-            i = indexOfNextChar(i, stopSearchingAt, searchIn, openingMarkers, closingMarkers, searchMode);
+            i = indexOfNextChar(i, stopSearchingAt, searchIn, openingMarkers, closingMarkers, overridingMarkers, searchMode);
 
             if (i == -1) {
                 return -1;
@@ -1148,7 +1187,7 @@ public class StringUtils {
      * @return the position where <code>searchFor</code> is found within <code>searchIn</code> starting from <code>startingPosition</code>.
      */
     private static int indexOfNextChar(int startingPosition, int stopPosition, String searchIn, String openingMarkers, String closingMarkers,
-            Set<SearchMode> searchMode) {
+            String overridingMarkers, Set<SearchMode> searchMode) {
         if (searchIn == null) {
             return -1;
         }
@@ -1182,8 +1221,25 @@ public class StringUtils {
                 int nestedMarkersCount = 0;
                 char openingMarker = c0;
                 char closingMarker = closingMarkers.charAt(markerIndex);
+                boolean outerIsAnOverridingMarker = overridingMarkers.indexOf(openingMarker) != -1;
                 while (++i <= stopPosition && ((c0 = searchIn.charAt(i)) != closingMarker || nestedMarkersCount != 0)) {
-                    if (c0 == openingMarker) {
+                    if (!outerIsAnOverridingMarker && overridingMarkers.indexOf(c0) != -1) {
+                        // there is an overriding marker that needs to be consumed before returning to the previous marker
+                        int overridingMarkerIndex = openingMarkers.indexOf(c0); // overridingMarkers must be a sub-list of openingMarkers
+                        int overridingNestedMarkersCount = 0;
+                        char overridingOpeningMarker = c0;
+                        char overridingClosingMarker = closingMarkers.charAt(overridingMarkerIndex);
+                        while (++i <= stopPosition && ((c0 = searchIn.charAt(i)) != overridingClosingMarker || overridingNestedMarkersCount != 0)) {
+                            // do as before, but this marker can't be overridden
+                            if (c0 == overridingOpeningMarker) {
+                                overridingNestedMarkersCount++;
+                            } else if (c0 == overridingClosingMarker) {
+                                overridingNestedMarkersCount--;
+                            } else if (searchMode.contains(SearchMode.ALLOW_BACKSLASH_ESCAPE) && c0 == '\\') {
+                                i++; // next char is escaped, skip it
+                            }
+                        }
+                    } else if (c0 == openingMarker) {
                         nestedMarkersCount++;
                     } else if (c0 == closingMarker) {
                         nestedMarkersCount--;
@@ -1314,12 +1370,16 @@ public class StringUtils {
     }
 
     /**
-     * Splits stringToSplit into a list, using the given delimiter
+     * Splits stringToSplit into a list, using the given delimiter and skipping all between the given markers.
      * 
      * @param stringToSplit
      *            the string to split
      * @param delimiter
      *            the string to split on
+     * @param openingMarkers
+     *            characters which delimit the beginning of a text block to skip
+     * @param closingMarkers
+     *            characters which delimit the end of a text block to skip
      * @param trim
      *            should the split strings be whitespace trimmed?
      * 
@@ -1327,7 +1387,34 @@ public class StringUtils {
      * 
      * @throws IllegalArgumentException
      */
-    public static List<String> split(String stringToSplit, String delimiter, String markers, String markerCloses, boolean trim) {
+    public static List<String> split(String stringToSplit, String delimiter, String openingMarkers, String closingMarkers, boolean trim) {
+        return split(stringToSplit, delimiter, openingMarkers, closingMarkers, "", trim);
+    }
+
+    /**
+     * Splits stringToSplit into a list, using the given delimiter and skipping all between the given markers.
+     * 
+     * @param stringToSplit
+     *            the string to split
+     * @param delimiter
+     *            the string to split on
+     * @param openingMarkers
+     *            characters which delimit the beginning of a text block to skip
+     * @param closingMarkers
+     *            characters which delimit the end of a text block to skip
+     * @param overridingMarkers
+     *            the subset of <code>openingMarkers</code> that override the remaining markers, e.g., if <code>openingMarkers = "'("</code> and
+     *            <code>overridingMarkers = "'"</code> then the block between the outer parenthesis in <code>"start ('max('); end"</code> is strictly consumed,
+     *            otherwise the suffix <code>" end"</code> would end up being consumed too in the process of handling the nested parenthesis.
+     * @param trim
+     *            should the split strings be whitespace trimmed?
+     * 
+     * @return the list of strings, split by delimiter
+     * 
+     * @throws IllegalArgumentException
+     */
+    public static List<String> split(String stringToSplit, String delimiter, String openingMarkers, String closingMarkers, String overridingMarkers,
+            boolean trim) {
         if (stringToSplit == null) {
             return new ArrayList<String>();
         }
@@ -1341,7 +1428,8 @@ public class StringUtils {
 
         List<String> splitTokens = new ArrayList<String>();
 
-        while ((delimPos = indexOfIgnoreCase(currentPos, stringToSplit, delimiter, markers, markerCloses, SEARCH_MODE__MRK_COM_WS)) != -1) {
+        while ((delimPos = indexOfIgnoreCase(currentPos, stringToSplit, delimiter, openingMarkers, closingMarkers, overridingMarkers,
+                SEARCH_MODE__MRK_COM_WS)) != -1) {
             String token = stringToSplit.substring(currentPos, delimPos);
 
             if (trim) {
@@ -1565,95 +1653,93 @@ public class StringUtils {
     }
 
     /**
-     * Compares searchIn against searchForWildcard with wildcards (heavily
-     * borrowed from strings/ctype-simple.c in the server sources)
+     * Compares searchIn against searchForWildcard with wildcards, in a case insensitive manner.
+     * 
+     * @param searchIn
+     *            the string to search in
+     * @param searchFor
+     *            the string to search for, using the 'standard' SQL wildcard chars of '%' and '_'
+     */
+    public static boolean wildCompareIgnoreCase(String searchIn, String searchFor) {
+        return wildCompareInternal(searchIn, searchFor) == WILD_COMPARE_MATCH;
+    }
+
+    /**
+     * Compares searchIn against searchForWildcard with wildcards (heavily borrowed from strings/ctype-simple.c in the server sources)
+     * 
+     * This method does a single passage matching for normal characters and WILDCARD_ONE (_), and recursive matching for WILDCARD_MANY (%) which may be repeated
+     * for as many anchor chars are found.
      * 
      * @param searchIn
      *            the string to search in
-     * @param searchForWildcard
-     *            the string to search for, using the 'standard' SQL wildcard
-     *            chars of '%' and '_'
+     * @param searchFor
+     *            the string to search for, using the 'standard' SQL wildcard chars of '%' and '_'
      * 
-     * @return WILD_COMPARE_MATCH_NO_WILD if matched, WILD_COMPARE_NO_MATCH if
-     *         not matched with wildcard, WILD_COMPARE_MATCH_WITH_WILD if
-     *         matched with wildcard
+     * @return WILD_COMPARE_MATCH if matched, WILD_COMPARE_NO_MATCH if not matched, WILD_COMPARE_CONTINUE_WITH_WILD if not matched yet, but it may in one of
+     *         following recursion rounds
      */
-    public static int wildCompare(String searchIn, String searchForWildcard) {
-        if ((searchIn == null) || (searchForWildcard == null)) {
+    private static int wildCompareInternal(String searchIn, String searchFor) {
+        if ((searchIn == null) || (searchFor == null)) {
             return WILD_COMPARE_NO_MATCH;
         }
 
-        if (searchForWildcard.equals("%")) {
-
-            return WILD_COMPARE_MATCH_WITH_WILD;
+        if (searchFor.equals("%")) {
+            return WILD_COMPARE_MATCH;
         }
 
-        int result = WILD_COMPARE_NO_MATCH; /* Not found, using wildcards */
-
-        char wildcardMany = '%';
-        char wildcardOne = '_';
-        char wildcardEscape = '\\';
-
         int searchForPos = 0;
-        int searchForEnd = searchForWildcard.length();
+        int searchForEnd = searchFor.length();
 
         int searchInPos = 0;
         int searchInEnd = searchIn.length();
 
-        while (searchForPos != searchForEnd) {
-            char wildstrChar = searchForWildcard.charAt(searchForPos);
+        int result = WILD_COMPARE_NO_MATCH; /* Not found, using wildcards */
 
-            while ((searchForWildcard.charAt(searchForPos) != wildcardMany) && (wildstrChar != wildcardOne)) {
-                if ((searchForWildcard.charAt(searchForPos) == wildcardEscape) && ((searchForPos + 1) != searchForEnd)) {
+        while (searchForPos != searchForEnd) {
+            while ((searchFor.charAt(searchForPos) != WILDCARD_MANY) && (searchFor.charAt(searchForPos) != WILDCARD_ONE)) {
+                if ((searchFor.charAt(searchForPos) == WILDCARD_ESCAPE) && ((searchForPos + 1) != searchForEnd)) {
                     searchForPos++;
                 }
 
                 if ((searchInPos == searchInEnd)
-                        || (Character.toUpperCase(searchForWildcard.charAt(searchForPos++)) != Character.toUpperCase(searchIn.charAt(searchInPos++)))) {
-                    return WILD_COMPARE_MATCH_WITH_WILD; /* No match */
+                        || (Character.toUpperCase(searchFor.charAt(searchForPos++)) != Character.toUpperCase(searchIn.charAt(searchInPos++)))) {
+                    return WILD_COMPARE_CONTINUE_WITH_WILD; /* No match */
                 }
 
                 if (searchForPos == searchForEnd) {
-                    return ((searchInPos != searchInEnd) ? WILD_COMPARE_MATCH_WITH_WILD : WILD_COMPARE_MATCH_NO_WILD); /* Match if both are at end */
+                    return ((searchInPos != searchInEnd) ? WILD_COMPARE_CONTINUE_WITH_WILD : WILD_COMPARE_MATCH); /* Match if both are at end */
                 }
 
-                result = WILD_COMPARE_MATCH_WITH_WILD; /* Found an anchor char */
+                result = WILD_COMPARE_CONTINUE_WITH_WILD; /* Found an anchor char */
             }
 
-            if (searchForWildcard.charAt(searchForPos) == wildcardOne) {
+            if (searchFor.charAt(searchForPos) == WILDCARD_ONE) {
                 do {
                     if (searchInPos == searchInEnd) { /* Skip one char if possible */
-
-                        return (result);
+                        return result;
                     }
-
                     searchInPos++;
-                } while ((++searchForPos < searchForEnd) && (searchForWildcard.charAt(searchForPos) == wildcardOne));
+                } while ((++searchForPos < searchForEnd) && (searchFor.charAt(searchForPos) == WILDCARD_ONE));
 
                 if (searchForPos == searchForEnd) {
                     break;
                 }
             }
 
-            if (searchForWildcard.charAt(searchForPos) == wildcardMany) { /* Found w_many */
-
-                char cmp;
-
+            if (searchFor.charAt(searchForPos) == WILDCARD_MANY) { /* Found w_many */
                 searchForPos++;
 
                 /* Remove any '%' and '_' from the wild search string */
                 for (; searchForPos != searchForEnd; searchForPos++) {
-                    if (searchForWildcard.charAt(searchForPos) == wildcardMany) {
+                    if (searchFor.charAt(searchForPos) == WILDCARD_MANY) {
                         continue;
                     }
 
-                    if (searchForWildcard.charAt(searchForPos) == wildcardOne) {
-                        if (searchInPos == searchInEnd) {
-                            return (WILD_COMPARE_NO_MATCH);
+                    if (searchFor.charAt(searchForPos) == WILDCARD_ONE) {
+                        if (searchInPos == searchInEnd) { /* Skip one char if possible */
+                            return WILD_COMPARE_NO_MATCH;
                         }
-
                         searchInPos++;
-
                         continue;
                     }
 
@@ -1661,15 +1747,16 @@ public class StringUtils {
                 }
 
                 if (searchForPos == searchForEnd) {
-                    return WILD_COMPARE_MATCH_NO_WILD; /* Ok if w_many is last */
+                    return WILD_COMPARE_MATCH; /* Ok if w_many is last */
                 }
 
                 if (searchInPos == searchInEnd) {
                     return WILD_COMPARE_NO_MATCH;
                 }
 
-                if (((cmp = searchForWildcard.charAt(searchForPos)) == wildcardEscape) && ((searchForPos + 1) != searchForEnd)) {
-                    cmp = searchForWildcard.charAt(++searchForPos);
+                char cmp;
+                if (((cmp = searchFor.charAt(searchForPos)) == WILDCARD_ESCAPE) && ((searchForPos + 1) != searchForEnd)) {
+                    cmp = searchFor.charAt(++searchForPos);
                 }
 
                 searchForPos++;
@@ -1677,26 +1764,24 @@ public class StringUtils {
                 do {
                     while ((searchInPos != searchInEnd) && (Character.toUpperCase(searchIn.charAt(searchInPos)) != Character.toUpperCase(cmp))) {
                         searchInPos++;
-                    }
+                    } /* Searches for an anchor char */
 
                     if (searchInPos++ == searchInEnd) {
                         return WILD_COMPARE_NO_MATCH;
                     }
 
-                    {
-                        int tmp = wildCompare(searchIn, searchForWildcard);
-
+                    int tmp = wildCompareInternal(searchIn.substring(searchInPos), searchFor.substring(searchForPos));
                     if (tmp <= 0) {
-                            return (tmp);
+                        return tmp;
                     }
-                    }
-                } while ((searchInPos != searchInEnd) && (searchForWildcard.charAt(0) != wildcardMany));
+
+                } while (searchInPos != searchInEnd);
 
                 return WILD_COMPARE_NO_MATCH;
             }
         }
 
-        return ((searchInPos != searchInEnd) ? WILD_COMPARE_MATCH_WITH_WILD : WILD_COMPARE_MATCH_NO_WILD);
+        return ((searchInPos != searchInEnd) ? WILD_COMPARE_CONTINUE_WITH_WILD : WILD_COMPARE_MATCH);
     }
 
     static byte[] s2b(String s, MySQLConnection conn) throws SQLException {
@@ -1851,7 +1936,7 @@ public class StringUtils {
                         strBuilder.append('-');
 
                         if (currentChar != -1) {
-                            strBuilder.append(currentChar);
+                            strBuilder.append((char) currentChar);
                         }
 
                         continue;
@@ -1895,69 +1980,42 @@ public class StringUtils {
     }
 
     /**
-     * Next we check if there is anything to split. If so
-     * we return result in form of "database";"name"
-     * If string is NULL or wildcard (%), returns null and exits.
+     * Splits an entity identifier into its parts (database and entity name) and returns a list containing the two elements. If the identifier doesn't contain
+     * the database part then the argument <code>catalog</code> is used in its place and <code>source</code> corresponds to the full entity name.
+     * If argument <code>source</code> is NULL or wildcard (%), returns an empty list.
      * 
-     * @param src
+     * @param source
      *            the source string
-     * @param cat
+     * @param catalog
      *            Catalog, if available
-     * @param quotId
-     *            quoteId as defined on server
+     * @param quoteId
+     *            quote character as defined on server
      * @param isNoBslashEscSet
-     *            Is our connection in BackSlashEscape mode
+     *            is our connection in no BackSlashEscape mode
      * @return the input string with all comment-delimited data removed
      */
-    public static List<String> splitDBdotName(String src, String cat, String quotId, boolean isNoBslashEscSet) {
-        if ((src == null) || (src.equals("%"))) {
-            return new ArrayList<String>();
+    public static List<String> splitDBdotName(String source, String catalog, String quoteId, boolean isNoBslashEscSet) {
+        if ((source == null) || (source.equals("%"))) {
+            return Collections.emptyList();
         }
 
-        boolean isQuoted = StringUtils.indexOfIgnoreCase(0, src, quotId) > -1;
-
-        String retval = src;
-        String tmpCat = cat;
-        //I.e., what if database is named `MyDatabase 1.0.0`... thus trueDotIndex
-        int trueDotIndex = -1;
-        if (!" ".equals(quotId)) {
-            //Presumably, if there is a database name attached and it contains dots, then it should be quoted so we first check for that
-            if (isQuoted) {
-                trueDotIndex = StringUtils.indexOfIgnoreCase(0, retval, quotId + "." + quotId);
-            } else {
-                // NOT quoted, fetch first DOT
-                // ex: cStmt = this.conn.prepareCall("{call bug57022.procbug57022(?, ?)}");
-                trueDotIndex = StringUtils.indexOfIgnoreCase(0, retval, ".");
-            }
+        int dotIndex = -1;
+        if (" ".equals(quoteId)) {
+            dotIndex = source.indexOf(".");
         } else {
-            trueDotIndex = retval.indexOf(".");
-        }
-
-        List<String> retTokens = new ArrayList<String>(2);
-
-        if (trueDotIndex != -1) {
-            //There is a catalog attached
-            if (isQuoted) {
-                tmpCat = StringUtils.toString(StringUtils.stripEnclosure(retval.substring(0, trueDotIndex + 1).getBytes(), quotId, quotId));
-                if (StringUtils.startsWithIgnoreCaseAndWs(tmpCat, quotId)) {
-                    tmpCat = tmpCat.substring(1, tmpCat.length() - 1);
+            dotIndex = indexOfIgnoreCase(0, source, ".", quoteId, quoteId, isNoBslashEscSet ? SEARCH_MODE__MRK_WS : SEARCH_MODE__BSESC_MRK_WS);
         }
 
-                retval = retval.substring(trueDotIndex + 2);
-                retval = StringUtils.toString(StringUtils.stripEnclosure(retval.getBytes(), quotId, quotId));
-            } else {
-                //NOT quoted, adjust indexOf
-                tmpCat = retval.substring(0, trueDotIndex);
-                retval = retval.substring(trueDotIndex + 1);
-            }
+        String database = catalog;
+        String entityName;
+        if (dotIndex != -1) {
+            database = unQuoteIdentifier(source.substring(0, dotIndex), quoteId);
+            entityName = unQuoteIdentifier(source.substring(dotIndex + 1), quoteId);
         } else {
-            //No catalog attached, strip retval and return
-            retval = StringUtils.toString(StringUtils.stripEnclosure(retval.getBytes(), quotId, quotId));
+            entityName = unQuoteIdentifier(source, quoteId);
         }
 
-        retTokens.add(tmpCat);
-        retTokens.add(retval);
-        return retTokens;
+        return Arrays.asList(database, entityName);
     }
 
     public static boolean isEmptyOrWhitespaceOnly(String str) {
diff --git a/src/com/mysql/jdbc/UpdatableResultSet.java b/src/com/mysql/jdbc/UpdatableResultSet.java
index 0564866..c423255 100644
--- a/src/com/mysql/jdbc/UpdatableResultSet.java
+++ b/src/com/mysql/jdbc/UpdatableResultSet.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -145,7 +145,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                set type is TYPE_FORWARD_ONLY.
      */
     @Override
-    public synchronized boolean absolute(int row) throws SQLException {
+    public boolean absolute(int row) throws SQLException {
         return super.absolute(row);
     }
 
@@ -161,7 +161,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                TYPE_FORWARD_ONLY.
      */
     @Override
-    public synchronized void afterLast() throws SQLException {
+    public void afterLast() throws SQLException {
         super.afterLast();
     }
 
@@ -177,7 +177,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                TYPE_FORWARD_ONLY
      */
     @Override
-    public synchronized void beforeFirst() throws SQLException {
+    public void beforeFirst() throws SQLException {
         super.beforeFirst();
     }
 
@@ -192,14 +192,14 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                the insert row.
      */
     @Override
-    public synchronized void cancelRowUpdates() throws SQLException {
-        checkClosed();
-
+    public void cancelRowUpdates() throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (this.doingUpdates) {
                 this.doingUpdates = false;
                 this.updater.clearParameters();
             }
         }
+    }
 
     /*
      * (non-Javadoc)
@@ -207,13 +207,13 @@ public class UpdatableResultSet extends ResultSetImpl {
      * @see com.mysql.jdbc.ResultSet#checkRowPos()
      */
     @Override
-    protected synchronized void checkRowPos() throws SQLException {
-        checkClosed();
-
+    protected void checkRowPos() throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 super.checkRowPos();
             }
         }
+    }
 
     /**
      * Is this ResultSet updateable?
@@ -420,9 +420,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *             if the ResultSet is not updatable or some other error occurs
      */
     @Override
-    public synchronized void deleteRow() throws SQLException {
-        checkClosed();
-
+    public void deleteRow() throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.isUpdatable) {
                 throw new NotUpdatable(this.notUpdatableReason);
             }
@@ -465,11 +464,10 @@ public class UpdatableResultSet extends ResultSetImpl {
 
             // position on previous row - Bug#27431
             previous();
-
+        }
     }
 
-    private synchronized void setParamValue(PreparedStatement ps, int psIdx, ResultSetRow row, int rsIdx, int sqlType) throws SQLException {
-
+    private void setParamValue(PreparedStatement ps, int psIdx, ResultSetRow row, int rsIdx, int sqlType) throws SQLException {
         byte[] val = row.getColumnValue(rsIdx);
         if (val == null) {
             ps.setNull(psIdx, Types.NULL);
@@ -521,7 +519,7 @@ public class UpdatableResultSet extends ResultSetImpl {
 
     }
 
-    private synchronized void extractDefaultValues() throws SQLException {
+    private void extractDefaultValues() throws SQLException {
         java.sql.DatabaseMetaData dbmd = this.connection.getMetaData();
         this.defaultColumnValue = new byte[this.fields.length][];
 
@@ -571,7 +569,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                TYPE_FORWARD_ONLY.
      */
     @Override
-    public synchronized boolean first() throws SQLException {
+    public boolean first() throws SQLException {
         return super.first();
     }
 
@@ -582,7 +580,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      * @throws SQLException
      * @throws NotUpdatable
      */
-    protected synchronized void generateStatements() throws SQLException {
+    protected void generateStatements() throws SQLException {
         if (!this.isUpdatable) {
             this.doingUpdates = false;
             this.onInsertRow = false;
@@ -782,7 +780,7 @@ public class UpdatableResultSet extends ResultSetImpl {
         return nameToIndex;
     }
 
-    private synchronized SingleByteCharsetConverter getCharConverter() throws SQLException {
+    private SingleByteCharsetConverter getCharConverter() throws SQLException {
         if (!this.initializedCharConverter) {
             this.initializedCharConverter = true;
 
@@ -806,10 +804,12 @@ public class UpdatableResultSet extends ResultSetImpl {
      */
     @Override
     public int getConcurrency() throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             return (this.isUpdatable ? CONCUR_UPDATABLE : CONCUR_READ_ONLY);
         }
+    }
 
-    private synchronized String getQuotedIdChar() throws SQLException {
+    private String getQuotedIdChar() throws SQLException {
         if (this.quotedIdChar == null) {
             boolean useQuotedIdentifiers = this.connection.supportsQuotedIdentifiers();
 
@@ -834,9 +834,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                insert row have not been given a value
      */
     @Override
-    public synchronized void insertRow() throws SQLException {
-        checkClosed();
-
+    public void insertRow() throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.7"), getExceptionInterceptor());
             }
@@ -870,6 +869,7 @@ public class UpdatableResultSet extends ResultSetImpl {
             this.rowData.addRow(resultSetRow);
             resetInserter();
         }
+    }
 
     /**
      * JDBC 2.0
@@ -885,7 +885,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs.
      */
     @Override
-    public synchronized boolean isAfterLast() throws SQLException {
+    public boolean isAfterLast() throws SQLException {
         return super.isAfterLast();
     }
 
@@ -903,7 +903,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs.
      */
     @Override
-    public synchronized boolean isBeforeFirst() throws SQLException {
+    public boolean isBeforeFirst() throws SQLException {
         return super.isBeforeFirst();
     }
 
@@ -920,7 +920,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs.
      */
     @Override
-    public synchronized boolean isFirst() throws SQLException {
+    public boolean isFirst() throws SQLException {
         return super.isFirst();
     }
 
@@ -938,7 +938,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs.
      */
     @Override
-    public synchronized boolean isLast() throws SQLException {
+    public boolean isLast() throws SQLException {
         return super.isLast();
     }
 
@@ -960,7 +960,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                TYPE_FORWARD_ONLY.
      */
     @Override
-    public synchronized boolean last() throws SQLException {
+    public boolean last() throws SQLException {
         return super.last();
     }
 
@@ -975,9 +975,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *             if the ResultSet is not updatable or some other error occurs
      */
     @Override
-    public synchronized void moveToCurrentRow() throws SQLException {
-        checkClosed();
-
+    public void moveToCurrentRow() throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.isUpdatable) {
                 throw new NotUpdatable(this.notUpdatableReason);
             }
@@ -987,6 +986,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow = this.savedCurrentRow;
             }
         }
+    }
 
     /**
      * JDBC 2.0 Move to the insert row. The current cursor position is
@@ -1005,9 +1005,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      * @throws NotUpdatable
      */
     @Override
-    public synchronized void moveToInsertRow() throws SQLException {
-        checkClosed();
-
+    public void moveToInsertRow() throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.isUpdatable) {
                 throw new NotUpdatable(this.notUpdatableReason);
             }
@@ -1078,6 +1077,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 }
             }
         }
+    }
 
     // ---------------------------------------------------------------------
     // Updates
@@ -1098,7 +1098,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database access error occurs
      */
     @Override
-    public synchronized boolean next() throws SQLException {
+    public boolean next() throws SQLException {
         return super.next();
     }
 
@@ -1117,7 +1117,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database access error occurs
      */
     @Override
-    public synchronized boolean prev() throws SQLException {
+    public boolean prev() throws SQLException {
         return super.prev();
     }
 
@@ -1139,7 +1139,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                TYPE_FORWAR_DONLY.
      */
     @Override
-    public synchronized boolean previous() throws SQLException {
+    public boolean previous() throws SQLException {
         return super.previous();
     }
 
@@ -1154,11 +1154,14 @@ public class UpdatableResultSet extends ResultSetImpl {
      *             if an error occurs.
      */
     @Override
-    public synchronized void realClose(boolean calledExplicitly) throws SQLException {
-        if (this.isClosed) {
-            return;
+    public void realClose(boolean calledExplicitly) throws SQLException {
+        MySQLConnection locallyScopedConn = this.connection;
+
+        if (locallyScopedConn == null) {
+            return; // already closed
         }
 
+        synchronized (checkClosed().getConnectionMutex()) {
             SQLException sqlEx = null;
 
             if (this.useUsageAdvisor) {
@@ -1212,6 +1215,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 throw sqlEx;
             }
         }
+    }
 
     /**
      * JDBC 2.0 Refresh the value of the current row with its current value in
@@ -1233,9 +1237,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      * @throws NotUpdatable
      */
     @Override
-    public synchronized void refreshRow() throws SQLException {
-        checkClosed();
-
+    public void refreshRow() throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.isUpdatable) {
                 throw new NotUpdatable();
             }
@@ -1252,8 +1255,9 @@ public class UpdatableResultSet extends ResultSetImpl {
 
             refreshRow(this.updater, this.thisRow);
         }
+    }
 
-    private synchronized void refreshRow(PreparedStatement updateInsertStmt, ResultSetRow rowToRefresh) throws SQLException {
+    private void refreshRow(PreparedStatement updateInsertStmt, ResultSetRow rowToRefresh) throws SQLException {
         if (this.refresher == null) {
             if (this.refreshSQL == null) {
                 generateStatements();
@@ -1364,7 +1368,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                row, or result set type is TYPE_FORWARD_ONLY.
      */
     @Override
-    public synchronized boolean relative(int rows) throws SQLException {
+    public boolean relative(int rows) throws SQLException {
         return super.relative(rows);
     }
 
@@ -1391,7 +1395,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      * @see DatabaseMetaData#deletesAreDetected
      */
     @Override
-    public synchronized boolean rowDeleted() throws SQLException {
+    public boolean rowDeleted() throws SQLException {
         throw SQLError.createSQLFeatureNotSupportedException();
     }
 
@@ -1409,7 +1413,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      * @see DatabaseMetaData#insertsAreDetected
      */
     @Override
-    public synchronized boolean rowInserted() throws SQLException {
+    public boolean rowInserted() throws SQLException {
         throw SQLError.createSQLFeatureNotSupportedException();
     }
 
@@ -1427,7 +1431,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      * @see DatabaseMetaData#updatesAreDetected
      */
     @Override
-    public synchronized boolean rowUpdated() throws SQLException {
+    public boolean rowUpdated() throws SQLException {
         throw SQLError.createSQLFeatureNotSupportedException();
     }
 
@@ -1462,7 +1466,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      * 
      * @throws SQLException
      */
-    protected synchronized void syncUpdate() throws SQLException {
+    protected void syncUpdate() throws SQLException {
         if (this.updater == null) {
             if (this.updateSQL == null) {
                 generateStatements();
@@ -1518,7 +1522,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateAsciiStream(int columnIndex, java.io.InputStream x, int length) throws SQLException {
+    public void updateAsciiStream(int columnIndex, java.io.InputStream x, int length) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -1531,6 +1536,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER);
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with an ascii stream value. The updateXXX()
@@ -1550,7 +1556,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateAsciiStream(String columnName, java.io.InputStream x, int length) throws SQLException {
+    public void updateAsciiStream(String columnName, java.io.InputStream x, int length) throws SQLException {
         updateAsciiStream(findColumn(columnName), x, length);
     }
 
@@ -1569,7 +1575,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException {
+    public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -1587,6 +1594,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 }
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a BigDecimal value. The updateXXX() methods
@@ -1603,7 +1611,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateBigDecimal(String columnName, BigDecimal x) throws SQLException {
+    public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException {
         updateBigDecimal(findColumn(columnName), x);
     }
 
@@ -1625,7 +1633,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateBinaryStream(int columnIndex, java.io.InputStream x, int length) throws SQLException {
+    public void updateBinaryStream(int columnIndex, java.io.InputStream x, int length) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -1643,6 +1652,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 }
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a binary stream value. The updateXXX()
@@ -1662,7 +1672,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateBinaryStream(String columnName, java.io.InputStream x, int length) throws SQLException {
+    public void updateBinaryStream(String columnName, java.io.InputStream x, int length) throws SQLException {
         updateBinaryStream(findColumn(columnName), x, length);
     }
 
@@ -1670,7 +1680,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      * @see ResultSetInternalMethods#updateBlob(int, Blob)
      */
     @Override
-    public synchronized void updateBlob(int columnIndex, java.sql.Blob blob) throws SQLException {
+    public void updateBlob(int columnIndex, java.sql.Blob blob) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -1688,12 +1699,13 @@ public class UpdatableResultSet extends ResultSetImpl {
                 }
             }
         }
+    }
 
     /**
      * @see ResultSetInternalMethods#updateBlob(String, Blob)
      */
     @Override
-    public synchronized void updateBlob(String columnName, java.sql.Blob blob) throws SQLException {
+    public void updateBlob(String columnName, java.sql.Blob blob) throws SQLException {
         updateBlob(findColumn(columnName), blob);
     }
 
@@ -1712,7 +1724,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateBoolean(int columnIndex, boolean x) throws SQLException {
+    public void updateBoolean(int columnIndex, boolean x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -1726,6 +1739,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1));
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a boolean value. The updateXXX() methods
@@ -1742,7 +1756,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateBoolean(String columnName, boolean x) throws SQLException {
+    public void updateBoolean(String columnName, boolean x) throws SQLException {
         updateBoolean(findColumn(columnName), x);
     }
 
@@ -1761,7 +1775,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateByte(int columnIndex, byte x) throws SQLException {
+    public void updateByte(int columnIndex, byte x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -1775,6 +1790,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1));
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a byte value. The updateXXX() methods are
@@ -1791,7 +1807,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateByte(String columnName, byte x) throws SQLException {
+    public void updateByte(String columnName, byte x) throws SQLException {
         updateByte(findColumn(columnName), x);
     }
 
@@ -1810,7 +1826,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateBytes(int columnIndex, byte[] x) throws SQLException {
+    public void updateBytes(int columnIndex, byte[] x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -1824,6 +1841,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, x);
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a byte array value. The updateXXX() methods
@@ -1840,7 +1858,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateBytes(String columnName, byte[] x) throws SQLException {
+    public void updateBytes(String columnName, byte[] x) throws SQLException {
         updateBytes(findColumn(columnName), x);
     }
 
@@ -1862,7 +1880,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateCharacterStream(int columnIndex, java.io.Reader x, int length) throws SQLException {
+    public void updateCharacterStream(int columnIndex, java.io.Reader x, int length) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -1880,6 +1899,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 }
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a character stream value. The updateXXX()
@@ -1899,7 +1919,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateCharacterStream(String columnName, java.io.Reader reader, int length) throws SQLException {
+    public void updateCharacterStream(String columnName, java.io.Reader reader, int length) throws SQLException {
         updateCharacterStream(findColumn(columnName), reader, length);
     }
 
@@ -1908,12 +1928,14 @@ public class UpdatableResultSet extends ResultSetImpl {
      */
     @Override
     public void updateClob(int columnIndex, java.sql.Clob clob) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (clob == null) {
                 updateNull(columnIndex);
             } else {
                 updateCharacterStream(columnIndex, clob.getCharacterStream(), (int) clob.length());
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a Date value. The updateXXX() methods are
@@ -1930,7 +1952,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateDate(int columnIndex, java.sql.Date x) throws SQLException {
+    public void updateDate(int columnIndex, java.sql.Date x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -1944,6 +1967,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1));
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a Date value. The updateXXX() methods are
@@ -1960,7 +1984,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateDate(String columnName, java.sql.Date x) throws SQLException {
+    public void updateDate(String columnName, java.sql.Date x) throws SQLException {
         updateDate(findColumn(columnName), x);
     }
 
@@ -1979,7 +2003,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateDouble(int columnIndex, double x) throws SQLException {
+    public void updateDouble(int columnIndex, double x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -1993,6 +2018,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1));
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a double value. The updateXXX() methods are
@@ -2009,7 +2035,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateDouble(String columnName, double x) throws SQLException {
+    public void updateDouble(String columnName, double x) throws SQLException {
         updateDouble(findColumn(columnName), x);
     }
 
@@ -2028,7 +2054,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateFloat(int columnIndex, float x) throws SQLException {
+    public void updateFloat(int columnIndex, float x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -2042,6 +2069,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1));
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a float value. The updateXXX() methods are
@@ -2058,7 +2086,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateFloat(String columnName, float x) throws SQLException {
+    public void updateFloat(String columnName, float x) throws SQLException {
         updateFloat(findColumn(columnName), x);
     }
 
@@ -2077,7 +2105,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateInt(int columnIndex, int x) throws SQLException {
+    public void updateInt(int columnIndex, int x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -2091,6 +2120,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1));
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with an integer value. The updateXXX() methods
@@ -2107,7 +2137,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateInt(String columnName, int x) throws SQLException {
+    public void updateInt(String columnName, int x) throws SQLException {
         updateInt(findColumn(columnName), x);
     }
 
@@ -2126,7 +2156,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateLong(int columnIndex, long x) throws SQLException {
+    public void updateLong(int columnIndex, long x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -2140,6 +2171,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1));
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a long value. The updateXXX() methods are
@@ -2156,7 +2188,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateLong(String columnName, long x) throws SQLException {
+    public void updateLong(String columnName, long x) throws SQLException {
         updateLong(findColumn(columnName), x);
     }
 
@@ -2173,7 +2205,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateNull(int columnIndex) throws SQLException {
+    public void updateNull(int columnIndex) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -2187,6 +2220,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, null);
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a null value. The updateXXX() methods are
@@ -2201,7 +2235,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateNull(String columnName) throws SQLException {
+    public void updateNull(String columnName) throws SQLException {
         updateNull(findColumn(columnName));
     }
 
@@ -2220,7 +2254,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateObject(int columnIndex, Object x) throws SQLException {
+    public void updateObject(int columnIndex, Object x) throws SQLException {
         updateObjectInternal(columnIndex, x, null, 0);
     }
 
@@ -2243,7 +2277,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateObject(int columnIndex, Object x, int scale) throws SQLException {
+    public void updateObject(int columnIndex, Object x, int scale) throws SQLException {
         updateObjectInternal(columnIndex, x, null, scale);
     }
 
@@ -2257,7 +2291,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      * @param scaleOrLength
      * @throws SQLException
      */
-    protected synchronized void updateObjectInternal(int columnIndex, Object x, Integer targetType, int scaleOrLength) throws SQLException {
+    protected void updateObjectInternal(int columnIndex, Object x, Integer targetType, int scaleOrLength) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -2279,6 +2314,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1));
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with an Object value. The updateXXX() methods
@@ -2295,7 +2331,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateObject(String columnName, Object x) throws SQLException {
+    public void updateObject(String columnName, Object x) throws SQLException {
         updateObject(findColumn(columnName), x);
     }
 
@@ -2318,7 +2354,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateObject(String columnName, Object x, int scale) throws SQLException {
+    public void updateObject(String columnName, Object x, int scale) throws SQLException {
         updateObject(findColumn(columnName), x);
     }
 
@@ -2332,7 +2368,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      * @throws NotUpdatable
      */
     @Override
-    public synchronized void updateRow() throws SQLException {
+    public void updateRow() throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.isUpdatable) {
                 throw new NotUpdatable(this.notUpdatableReason);
             }
@@ -2350,6 +2387,7 @@ public class UpdatableResultSet extends ResultSetImpl {
             // updates on same row...
             syncUpdate();
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a short value. The updateXXX() methods are
@@ -2366,7 +2404,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateShort(int columnIndex, short x) throws SQLException {
+    public void updateShort(int columnIndex, short x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -2380,6 +2419,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1));
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a short value. The updateXXX() methods are
@@ -2396,7 +2436,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateShort(String columnName, short x) throws SQLException {
+    public void updateShort(String columnName, short x) throws SQLException {
         updateShort(findColumn(columnName), x);
     }
 
@@ -2415,9 +2455,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateString(int columnIndex, String x) throws SQLException {
-        checkClosed();
-
+    public void updateString(int columnIndex, String x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -2440,6 +2479,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 }
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a String value. The updateXXX() methods are
@@ -2456,7 +2496,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateString(String columnName, String x) throws SQLException {
+    public void updateString(String columnName, String x) throws SQLException {
         updateString(findColumn(columnName), x);
     }
 
@@ -2475,7 +2515,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateTime(int columnIndex, java.sql.Time x) throws SQLException {
+    public void updateTime(int columnIndex, java.sql.Time x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -2489,6 +2530,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1));
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a Time value. The updateXXX() methods are
@@ -2505,7 +2547,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateTime(String columnName, java.sql.Time x) throws SQLException {
+    public void updateTime(String columnName, java.sql.Time x) throws SQLException {
         updateTime(findColumn(columnName), x);
     }
 
@@ -2524,7 +2566,8 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateTimestamp(int columnIndex, java.sql.Timestamp x) throws SQLException {
+    public void updateTimestamp(int columnIndex, java.sql.Timestamp x) throws SQLException {
+        synchronized (checkClosed().getConnectionMutex()) {
             if (!this.onInsertRow) {
                 if (!this.doingUpdates) {
                     this.doingUpdates = true;
@@ -2538,6 +2581,7 @@ public class UpdatableResultSet extends ResultSetImpl {
                 this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1));
             }
         }
+    }
 
     /**
      * JDBC 2.0 Update a column with a Timestamp value. The updateXXX() methods
@@ -2554,7 +2598,7 @@ public class UpdatableResultSet extends ResultSetImpl {
      *                if a database-access error occurs
      */
     @Override
-    public synchronized void updateTimestamp(String columnName, java.sql.Timestamp x) throws SQLException {
+    public void updateTimestamp(String columnName, java.sql.Timestamp x) throws SQLException {
         updateTimestamp(findColumn(columnName), x);
     }
 }
diff --git a/src/testsuite/regression/CallableStatementRegressionTest.java b/src/testsuite/regression/CallableStatementRegressionTest.java
index 951e4ce..5af12fd 100644
--- a/src/testsuite/regression/CallableStatementRegressionTest.java
+++ b/src/testsuite/regression/CallableStatementRegressionTest.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -30,12 +30,14 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.sql.CallableStatement;
 import java.sql.Connection;
+import java.sql.ParameterMetaData;
 import java.sql.PreparedStatement;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.sql.Types;
 import java.util.Properties;
+import java.util.concurrent.Callable;
 
 import com.mysql.jdbc.NonRegisteringDriver;
 import com.mysql.jdbc.SQLError;
@@ -1590,7 +1592,142 @@ public class CallableStatementRegressionTest extends BaseTestCase {
         call.registerOutParameter(1, Types.INTEGER);
         call.execute();
         assertEquals(10, call.getInt(1));
+    }
+
+    /**
+     * Tests fix for Bug#79561 - NullPointerException when calling a fully qualified stored procedure
+     */
+    public void testBug79561() throws Exception {
+        createProcedure("testBug79561", "(OUT o VARCHAR(100)) BEGIN SELECT 'testBug79561 data' INTO o; END");
+
+        String dbName = this.conn.getCatalog();
+        String[] sql = new String[] { String.format("{CALL %s.testBug79561(?)}", dbName), String.format("{CALL `%s`.testBug79561(?)}", dbName),
+                String.format("{CALL %s.`testBug79561`(?)}", dbName), String.format("{CALL `%s`.`testBug79561`(?)}", dbName) };
+
+        for (int i = 0; i < sql.length; i++) {
+            for (int m = 0; m < 4; m++) { // Method call type: 0) by index; 1) by name; 2) by invalid index; 3) by invalid name;
+                final String testCase = String.format("Case: [sql: %d, method: %d ]", i, m);
+                final CallableStatement cstmt = this.conn.prepareCall(sql[i]);
+                boolean dataExpected = true;
+
+                // Register the output parameter using one of the different methods.
+                if (m == 0) {
+                    cstmt.registerOutParameter(1, Types.VARCHAR);
+                } else if (m == 1) {
+                    cstmt.registerOutParameter("o", Types.VARCHAR);
+                } else if (m == 2) {
+                    assertThrows(testCase, SQLException.class, "Parameter index of 2 is out of range \\(1, 1\\)", new Callable<Void>() {
+                        public Void call() throws Exception {
+                            cstmt.registerOutParameter(2, Types.VARCHAR);
+                            return null;
+                        }
+                    });
+                    dataExpected = false;
+                } else {
+                    assertThrows(testCase, SQLException.class, "No parameter named 'oparam'", new Callable<Void>() {
+                        public Void call() throws Exception {
+                            cstmt.registerOutParameter("oparam", Types.VARCHAR);
+                            return null;
+                        }
+                    });
+                    dataExpected = false;
+                }
+
+                // Check the returned data, if any expected, using different methods.
+                if (dataExpected) {
+                    cstmt.execute();
+                    assertEquals(testCase, "testBug79561 data", cstmt.getString(1));
+                    assertEquals(testCase, "testBug79561 data", cstmt.getString("o"));
+                    assertThrows(testCase, SQLException.class, "Parameter index of 2 is out of range \\(1, 1\\)", new Callable<Void>() {
+                        public Void call() throws Exception {
+                            cstmt.getString(2);
+                            return null;
+                        }
+                    });
+                    assertThrows(testCase, SQLException.class, "Column '@com_mysql_jdbc_outparam_oparam' not found\\.", new Callable<Void>() {
+                        public Void call() throws Exception {
+                            cstmt.getString("oparam");
+                            return null;
+                        }
+                    });
+                }
 
+                cstmt.close();
+            }
+        }
     }
 
+    /**
+     * Tests fix for Bug#84324 - CallableStatement.extractProcedureName() not work when catalog name with dash.
+     */
+    public void testBug84324() throws Exception {
+        createDatabase("`testBug84324-db`");
+
+        /*
+         * Test procedure.
+         */
+        createProcedure("`testBug84324-db`.`testBug84324-proc`", "(IN a INT, INOUT b VARCHAR(100)) BEGIN SELECT a, b; END");
+
+        final CallableStatement cstmtP = this.conn.prepareCall("CALL testBug84324-db.testBug84324-proc(?, ?)");
+        ParameterMetaData pmd = cstmtP.getParameterMetaData();
+
+        assertEquals(2, pmd.getParameterCount());
+        // 1st parameter
+        assertEquals("INT", pmd.getParameterTypeName(1));
+        assertEquals(Types.INTEGER, pmd.getParameterType(1));
+        assertEquals(Integer.class.getName(), pmd.getParameterClassName(1));
+        assertEquals(ParameterMetaData.parameterModeIn, pmd.getParameterMode(1));
+        // 2nd parameter
+        assertEquals("VARCHAR", pmd.getParameterTypeName(2));
+        assertEquals(Types.VARCHAR, pmd.getParameterType(2));
+        assertEquals(String.class.getName(), pmd.getParameterClassName(2));
+        assertEquals(ParameterMetaData.parameterModeInOut, pmd.getParameterMode(2));
+
+        cstmtP.setInt(1, 1);
+        cstmtP.setString(2, "foo");
+        assertThrows(SQLException.class, new Callable<Void>() {
+            public Void call() throws Exception {
+                cstmtP.execute();
+                return null;
+            }
+        }); // Although the procedure metadata could be obtained, the end query actually fails due to syntax errors.
+        cstmtP.close();
+
+        /*
+         * Test function.
+         */
+        createFunction("`testBug84324-db`.`testBug84324-func`", "(a INT, b VARCHAR(123)) RETURNS INT BEGIN RETURN a + LENGTH(b); END");
+
+        final CallableStatement cstmtF = this.conn.prepareCall("{? = CALL testBug84324-db.testBug84324-func(?, ?)}");
+        pmd = cstmtF.getParameterMetaData();
+
+        assertEquals(3, pmd.getParameterCount());
+        // 1st parameter
+        assertEquals("INT", pmd.getParameterTypeName(1));
+        assertEquals(Types.INTEGER, pmd.getParameterType(1));
+        assertEquals(Integer.class.getName(), pmd.getParameterClassName(1));
+        assertEquals(ParameterMetaData.parameterModeOut, pmd.getParameterMode(1));
+        // 2nd parameter
+        assertEquals("INT", pmd.getParameterTypeName(2));
+        assertEquals(Types.INTEGER, pmd.getParameterType(2));
+        assertEquals(Integer.class.getName(), pmd.getParameterClassName(2));
+        assertEquals(ParameterMetaData.parameterModeIn, pmd.getParameterMode(2));
+        // 3rd parameter
+        assertEquals("VARCHAR", pmd.getParameterTypeName(3));
+        assertEquals(Types.VARCHAR, pmd.getParameterType(3));
+        assertEquals(String.class.getName(), pmd.getParameterClassName(3));
+        assertEquals(ParameterMetaData.parameterModeIn, pmd.getParameterMode(3));
+
+        cstmtF.registerOutParameter(1, Types.INTEGER);
+        cstmtF.setInt(2, 1);
+        cstmtF.setString(3, "foo");
+        assertThrows(SQLException.class, new Callable<Void>() {
+            public Void call() throws Exception {
+                cstmtF.execute();
+                return null;
+            }
+        }); // Although the function metadata could be obtained, the end query actually fails due to syntax errors.
+        cstmtP.close();
+        cstmtF.close();
+    }
 }
\ No newline at end of file
diff --git a/src/testsuite/regression/ConnectionRegressionTest.java b/src/testsuite/regression/ConnectionRegressionTest.java
index 15dae45..64c241e 100644
--- a/src/testsuite/regression/ConnectionRegressionTest.java
+++ b/src/testsuite/regression/ConnectionRegressionTest.java
@@ -1718,7 +1718,7 @@ public class ConnectionRegressionTest extends BaseTestCase {
 
         createProcedure("testBug25545", "() BEGIN SELECT 1; END");
 
-        String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store";
+        String trustStorePath = "src/testsuite/ssl-test-certs/ca-truststore";
 
         System.setProperty("javax.net.ssl.keyStore", trustStorePath);
         System.setProperty("javax.net.ssl.keyStorePassword", "password");
@@ -1775,7 +1775,7 @@ public class ConnectionRegressionTest extends BaseTestCase {
             props.remove("trustCertificateKeyStorePassword");
 
             final String url = "jdbc:mysql://" + hostSpec + "/" + db + "?useSSL=true&requireSSL=true&verifyServerCertificate=true"
-                    + "&trustCertificateKeyStoreUrl=file:src/testsuite/ssl-test-certs/test-cert-store&trustCertificateKeyStoreType=JKS"
+                    + "&trustCertificateKeyStoreUrl=file:src/testsuite/ssl-test-certs/ca-truststore&trustCertificateKeyStoreType=JKS"
                     + "&trustCertificateKeyStorePassword=password";
 
             _conn = DriverManager.getConnection(url, props);
@@ -3991,7 +3991,7 @@ public class ConnectionRegressionTest extends BaseTestCase {
                 }
 
                 try {
-                    String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store";
+                    String trustStorePath = "src/testsuite/ssl-test-certs/ca-truststore";
                     System.setProperty("javax.net.ssl.keyStore", trustStorePath);
                     System.setProperty("javax.net.ssl.keyStorePassword", "password");
                     System.setProperty("javax.net.ssl.trustStore", trustStorePath);
@@ -4042,7 +4042,7 @@ public class ConnectionRegressionTest extends BaseTestCase {
      * @throws Exception
      */
     public void testSha256PasswordPlugin() throws Exception {
-        String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store";
+        String trustStorePath = "src/testsuite/ssl-test-certs/ca-truststore";
         System.setProperty("javax.net.ssl.keyStore", trustStorePath);
         System.setProperty("javax.net.ssl.keyStorePassword", "password");
         System.setProperty("javax.net.ssl.trustStore", trustStorePath);
@@ -4656,23 +4656,56 @@ public class ConnectionRegressionTest extends BaseTestCase {
             } finally {
                 testConn.close();
             }
+            props.remove("cacheServerConfiguration");
 
             // Error messages may also be received after the handshake but before connection initialization is complete. This tests the interpretation of
-            // errors thrown during this time window using an invalid session variable
+            // errors thrown during this time window using a SatementInterceptor that throws an Exception while setting the session variables.
+            // Start by getting the Latin1 version of the error to compare later.
+            String latin1ErrorMsg = "";
+            int latin1ErrorLen = 0;
+            try {
+                props.setProperty("characterEncoding", "Latin1");
+                props.setProperty("characterSetResults", "Latin1");
+                props.setProperty("sessionVariables", "lc_messages=ru_RU");
+                props.setProperty("statementInterceptors", TestBug64205StatementInterceptor.class.getName());
+                testConn = getConnectionWithProps(props);
+                fail("Exception should be trown for syntax error, caused by the exception interceptor");
+            } catch (Exception e) {
+                latin1ErrorMsg = e.getMessage();
+                latin1ErrorLen = latin1ErrorMsg.length();
+            }
+            // Now compare with results when using a proper encoding.
             try {
+                props.setProperty("characterEncoding", "EUC_JP");
                 props.setProperty("characterSetResults", "EUC_JP");
-                props.setProperty("sessionVariables", "lc_messages=ru_RU,invalidVar=1");
+                props.setProperty("sessionVariables", "lc_messages=ru_RU");
+                props.setProperty("statementInterceptors", TestBug64205StatementInterceptor.class.getName());
                 testConn = getConnectionWithProps(props);
-                fail("Exception should be thrown for attempting to set an unknown system variable");
-            } catch (SQLException e1) {
-                // The Russian version of this error message is 45 characters long. A mis-interpretation, e.g. decoding as latin1, would return a length of 75
-                assertEquals(45, e1.getMessage().length());
+                fail("Exception should be trown for syntax error, caused by the exception interceptor");
+            } catch (SQLException e) {
+                // There should be the Russian version of this error message, correctly encoded. A mis-interpretation, e.g. decoding as latin1, would return a
+                // wrong message with the wrong size.
+                assertEquals(29 + dbname.length(), e.getMessage().length());
+                assertFalse(latin1ErrorMsg.equals(e.getMessage()));
+                assertFalse(latin1ErrorLen == e.getMessage().length());
             } finally {
                 testConn.close();
             }
         }
     }
 
+    public static class TestBug64205StatementInterceptor extends BaseStatementInterceptor {
+        @Override
+        public ResultSetInternalMethods postProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, ResultSetInternalMethods originalResultSet,
+                com.mysql.jdbc.Connection connection, int warningCount, boolean noIndexUsed, boolean noGoodIndexUsed, SQLException statementException)
+                throws SQLException {
+            if (sql.contains("lc_messages=ru_RU") && statementException == null) {
+                connection.createStatement().executeQuery("SELECT * FROM `" + connection.getCatalog() + "`.`\u307b\u3052\u307b\u3052`");
+            }
+            return super.postProcess(sql, interceptedStatement, originalResultSet, connection, warningCount, noIndexUsed, noGoodIndexUsed, statementException);
+        }
+    }
+
     public void testIsLocal() throws Exception {
         boolean normalState = ((ConnectionImpl) this.conn).isServerLocal();
 
@@ -5876,7 +5909,7 @@ public class ConnectionRegressionTest extends BaseTestCase {
                 }
 
                 try {
-                    String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store";
+                    String trustStorePath = "src/testsuite/ssl-test-certs/ca-truststore";
                     System.setProperty("javax.net.ssl.keyStore", trustStorePath);
                     System.setProperty("javax.net.ssl.keyStorePassword", "password");
                     System.setProperty("javax.net.ssl.trustStore", trustStorePath);
@@ -6475,7 +6508,7 @@ public class ConnectionRegressionTest extends BaseTestCase {
                 props.setProperty("useCompression", "true");
                 testBug18869381WithProperties(props);
 
-                String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store";
+                String trustStorePath = "src/testsuite/ssl-test-certs/ca-truststore";
                 System.setProperty("javax.net.ssl.keyStore", trustStorePath);
                 System.setProperty("javax.net.ssl.keyStorePassword", "password");
                 System.setProperty("javax.net.ssl.trustStore", trustStorePath);
@@ -7444,7 +7477,7 @@ public class ConnectionRegressionTest extends BaseTestCase {
         props.setProperty("useSSL", "true");
         props.setProperty("requireSSL", "true");
         props.setProperty("verifyServerCertificate", "true");
-        props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/test-cert-store");
+        props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/ca-truststore");
         props.setProperty("trustCertificateKeyStoreType", "JKS");
         props.setProperty("trustCertificateKeyStorePassword", "password");
 
@@ -7458,7 +7491,7 @@ public class ConnectionRegressionTest extends BaseTestCase {
         props.setProperty("requireSSL", "true");
         props.setProperty("verifyServerCertificate", "true");
 
-        String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store";
+        String trustStorePath = "src/testsuite/ssl-test-certs/ca-truststore";
         System.setProperty("javax.net.ssl.keyStore", trustStorePath);
         System.setProperty("javax.net.ssl.keyStorePassword", "password");
         System.setProperty("javax.net.ssl.trustStore", trustStorePath);
@@ -8271,7 +8304,7 @@ public class ConnectionRegressionTest extends BaseTestCase {
 
             // 3. Explicit useSSL=true
             props.setProperty("useSSL", "true");
-            props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/test-cert-store");
+            props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/ca-truststore");
             props.setProperty("trustCertificateKeyStoreType", "JKS");
             props.setProperty("trustCertificateKeyStorePassword", "password");
             sslConn = getConnectionWithProps(props);
@@ -8404,7 +8437,7 @@ public class ConnectionRegressionTest extends BaseTestCase {
         props.setProperty("allowPublicKeyRetrieval", "true");
         props.setProperty("useSSL", "true");
         props.setProperty("requireSSL", "true");
-        props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/test-cert-store");
+        props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/ca-truststore");
         props.setProperty("trustCertificateKeyStoreType", "JKS");
         props.setProperty("trustCertificateKeyStorePassword", "password");
 
diff --git a/src/testsuite/regression/MetaDataRegressionTest.java b/src/testsuite/regression/MetaDataRegressionTest.java
index 9c8bfdd..a4f17c2 100644
--- a/src/testsuite/regression/MetaDataRegressionTest.java
+++ b/src/testsuite/regression/MetaDataRegressionTest.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -4226,4 +4226,82 @@ public class MetaDataRegressionTest extends BaseTestCase {
             assertEquals(testCase, "id", rsmd.getColumnName(1));
         } while (useSPS = !useSPS);
     }
+
+    /**
+     * Tests fix for Bug#73775 - DBMD.getProcedureColumns()/.getFunctionColumns() fail to filter by columnPattern
+     * 
+     * Test duplicated in testsuite.regression.jdbc4.MetaDataRegressionTest.
+     */
+    public void testBug73775() throws Exception {
+        createFunction("testBug73775f", "(param1 CHAR(20), param2 CHAR(20)) RETURNS CHAR(40) DETERMINISTIC RETURN CONCAT(param1, param2)");
+        createProcedure("testBug73775p", "(INOUT param1 CHAR(20), IN param2 CHAR(20)) BEGIN  SELECT CONCAT(param1, param2) INTO param1; END");
+
+        boolean useIS = false;
+        do {
+            final String testCase = String.format("Case: [useIS: %s]", useIS ? "Y" : "N");
+
+            final Properties props = new Properties();
+            final Connection testConn = getConnectionWithProps(props);
+            props.setProperty("useInformationSchema", Boolean.toString(useIS));
+            final DatabaseMetaData dbmd = testConn.getMetaData();
+
+            this.rs = dbmd.getProcedureColumns(null, "", "testBug73775%", "%");
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+            assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
+            assertEquals(testCase, DatabaseMetaData.procedureColumnReturn, this.rs.getInt(5));
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+            assertEquals(testCase, "param1", this.rs.getString(4));
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+            assertEquals(testCase, "param2", this.rs.getString(4));
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, "testBug73775p", this.rs.getString(3));
+            assertEquals(testCase, "param1", this.rs.getString(4));
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, "testBug73775p", this.rs.getString(3));
+            assertEquals(testCase, "param2", this.rs.getString(4));
+            assertFalse(testCase, this.rs.next());
+
+            for (String ptn : new String[] { "param1", "_____1", "%1", "p_r_m%1" }) {
+                this.rs = dbmd.getProcedureColumns(null, "", "testBug73775%", ptn);
+                assertTrue(this.rs.next());
+                assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
+                assertEquals(testCase, DatabaseMetaData.procedureColumnReturn, this.rs.getInt(5));
+                assertTrue(testCase, this.rs.next());
+                assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                assertEquals(testCase, "param1", this.rs.getString(4));
+                assertTrue(testCase, this.rs.next());
+                assertEquals(testCase, "testBug73775p", this.rs.getString(3));
+                assertEquals(testCase, "param1", this.rs.getString(4));
+                assertFalse(testCase, this.rs.next());
+            }
+
+            for (String ptn : new String[] { "param2", "_____2", "%2", "p_r_m%2" }) {
+                this.rs = dbmd.getProcedureColumns(null, "", "testBug73775%", "param2");
+                assertTrue(this.rs.next());
+                assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
+                assertEquals(testCase, DatabaseMetaData.procedureColumnReturn, this.rs.getInt(5));
+                assertTrue(testCase, this.rs.next());
+                assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                assertEquals(testCase, "param2", this.rs.getString(4));
+                assertTrue(testCase, this.rs.next());
+                assertEquals(testCase, "testBug73775p", this.rs.getString(3));
+                assertEquals(testCase, "param2", this.rs.getString(4));
+                assertFalse(testCase, this.rs.next());
+            }
+
+            this.rs = dbmd.getProcedureColumns(null, "", "testBug73775%", "");
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+            assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
+            assertEquals(testCase, DatabaseMetaData.procedureColumnReturn, this.rs.getInt(5));
+            assertFalse(testCase, this.rs.next());
+
+            testConn.close();
+        } while (useIS = !useIS);
+    }
 }
diff --git a/src/testsuite/regression/ResultSetRegressionTest.java b/src/testsuite/regression/ResultSetRegressionTest.java
index c894885..55bfdc4 100644
--- a/src/testsuite/regression/ResultSetRegressionTest.java
+++ b/src/testsuite/regression/ResultSetRegressionTest.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -24,6 +24,9 @@
 package testsuite.regression;
 
 import java.io.Reader;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
@@ -49,6 +52,7 @@ import java.util.Locale;
 import java.util.Properties;
 import java.util.TimeZone;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
@@ -5272,4 +5276,111 @@ public class ResultSetRegressionTest extends BaseTestCase {
         }
         assertFalse(this.rs.next());
     }
+
+    /**
+     * Tests fix for Bug#83368 - 5.1.40 regression: wasNull not updated when calling getInt for a bit column.
+     */
+    public void testBug83368() throws Exception {
+        createTable("testBug83368", "(c1 VARCHAR(1), c2 BIT)");
+        this.stmt.execute("INSERT INTO testBug83368 VALUES (NULL, 1)");
+        this.rs = this.stmt.executeQuery("SELECT * FROM testBug83368");
+
+        assertTrue(this.rs.next());
+
+        assertNull(this.rs.getString(1));
+        assertTrue(this.rs.wasNull());
+        assertEquals((byte) 1, this.rs.getByte(2));
+        assertFalse(this.rs.wasNull());
+
+        assertNull(this.rs.getString(1));
+        assertTrue(this.rs.wasNull());
+        assertEquals((short) 1, this.rs.getShort(2));
+        assertFalse(this.rs.wasNull());
+
+        assertNull(this.rs.getString(1));
+        assertTrue(this.rs.wasNull());
+        assertEquals(1, this.rs.getInt(2));
+        assertFalse(this.rs.wasNull());
+
+        assertNull(this.rs.getString(1));
+        assertTrue(this.rs.wasNull());
+        assertEquals(1L, this.rs.getLong(2));
+        assertFalse(this.rs.wasNull());
+
+        assertNull(this.rs.getString(1));
+        assertTrue(this.rs.wasNull());
+        assertEquals(BigDecimal.valueOf(1), this.rs.getBigDecimal(2));
+        assertFalse(this.rs.wasNull());
+    }
+
+    /**
+     * Tests fix for Bug#83662 - NullPointerException while reading NULL boolean value from DB.
+     * 
+     * This fix was actually done in the patch for Bug#83368, as both are fixed in the same way.
+     */
+    public void testBug83662() throws Exception {
+        createTable("testBug83662", "(b BIT(1) NULL)");
+        this.stmt.executeUpdate("INSERT INTO testBug83662 VALUES (null)");
+
+        this.rs = this.stmt.executeQuery("SELECT * FROM testBug83662");
+        assertTrue(this.rs.next());
+        assertEquals((byte) 0, this.rs.getByte(1));
+        assertEquals((short) 0, this.rs.getShort(1));
+        assertEquals(0, this.rs.getInt(1));
+        assertEquals(0L, this.rs.getLong(1));
+        assertEquals(0, this.rs.getInt(1));
+        assertNull(this.rs.getBigDecimal(1));
+    }
+
+    /**
+     * Tests fix for Bug#70704 - Deadlock using UpdatableResultSet.
+     * 
+     * Doesn't actually test the buggy behavior since it is not verifiable since the fix for Bug#59462 (revision 385a151). However, the patch for this fix is
+     * needed because the synchronization in UpdatableResultSet was dated.
+     * This test makes sure there is no regression.
+     * 
+     * WARNING! If this test fails there is no guarantee that the JVM will remain stable and won't affect any other tests. It is imperative that this test
+     * passes to ensure other tests results.
+     */
+    public void testBug70704() throws Exception {
+        for (int i = 0; i < 100; i++) {
+            final Statement testStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
+            final ResultSet testRs = testStmt.executeQuery("SELECT 1");
+
+            ExecutorService executorService = Executors.newFixedThreadPool(2);
+
+            executorService.submit(new Callable<Void>() {
+                public Void call() throws Exception {
+                    testStmt.close();
+                    return null;
+                }
+            });
+
+            executorService.submit(new Callable<Void>() {
+                public Void call() throws Exception {
+                    testRs.close();
+                    return null;
+                }
+            });
+
+            executorService.shutdown();
+            if (!executorService.awaitTermination(2, TimeUnit.SECONDS)) {
+                ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
+                long[] threadIds = threadMXBean.findMonitorDeadlockedThreads();
+                if (threadIds != null) {
+                    System.err.println("Deadlock detected!");
+                    ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadIds, Integer.MAX_VALUE);
+                    for (ThreadInfo ti : threadInfos) {
+                        System.err.println();
+                        System.err.println(ti);
+                        System.err.println("Stack trace:");
+                        for (StackTraceElement ste : ti.getStackTrace()) {
+                            System.err.println("   " + ste);
+                        }
+                    }
+                    fail("Unexpected deadlock detected. Consult system output for more details. WARNING: this failure may lead to JVM instability.");
+                }
+            }
+        }
+    }
 }
diff --git a/src/testsuite/regression/StatementRegressionTest.java b/src/testsuite/regression/StatementRegressionTest.java
index 31b4f55..3aeecca 100644
--- a/src/testsuite/regression/StatementRegressionTest.java
+++ b/src/testsuite/regression/StatementRegressionTest.java
@@ -3873,7 +3873,7 @@ public class StatementRegressionTest extends BaseTestCase {
             assertEquals(earlier, timestampSeconds1);
 
             SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm z");
-            sdf.setTimeZone(TimeZone.getTimeZone("America/New York"));
+            sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
             System.out.println(sdf.format(ts2));
             System.out.println(sdf.format(ts1));
         } finally {
@@ -8066,7 +8066,7 @@ public class StatementRegressionTest extends BaseTestCase {
     }
 
     /**
-     * Test fix for Bug#81706 - NullPointerException in driver.
+     * Tests fix for Bug#81706 - NullPointerException in driver.
      */
     public void testBug81706() throws Exception {
         boolean useSPS = false;
@@ -8140,4 +8140,154 @@ public class StatementRegressionTest extends BaseTestCase {
             return super.preProcess(sql, interceptedStatement, connection);
         }
     }
+
+    /**
+     * Tests fix for Bug#66430 - setCatalog on connection leaves ServerPreparedStatement cache for old catalog.
+     */
+    public void testBug66430() throws Exception {
+        createDatabase("testBug66430DB1");
+        createTable("testBug66430DB1.testBug66430", "(id INT)");
+        this.stmt.executeUpdate("INSERT INTO testBug66430DB1.testBug66430 VALUES (1)");
+
+        createDatabase("testBug66430DB2");
+        createTable("testBug66430DB2.testBug66430", "(id INT)");
+        this.stmt.executeUpdate("INSERT INTO testBug66430DB2.testBug66430 VALUES (2)");
+
+        boolean useSPS = false;
+        boolean cachePS = false;
+        do {
+            final String testCase = String.format("Case: [useSPS: %s, cachePS: %s ]", useSPS ? "Y" : "N", cachePS ? "Y" : "N");
+
+            Properties props = new Properties();
+            props.setProperty("cachePrepStmts", Boolean.toString(cachePS));
+            props.setProperty("useServerPrepStmts", Boolean.toString(useSPS));
+
+            Connection testConn = getConnectionWithProps(props);
+
+            testConn.setCatalog("testBug66430DB1");
+            PreparedStatement testPStmt = testConn.prepareStatement("SELECT * FROM testBug66430 WHERE id > ?");
+            testPStmt.setInt(1, 0);
+            this.rs = testPStmt.executeQuery();
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, 1, this.rs.getInt(1));
+            assertFalse(testCase, this.rs.next());
+            testPStmt.close();
+
+            testConn.setCatalog("testBug66430DB2");
+            testPStmt = testConn.prepareStatement("SELECT * FROM testBug66430 WHERE id > ?");
+            testPStmt.setInt(1, 0);
+            this.rs = testPStmt.executeQuery();
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, 2, this.rs.getInt(1));
+            assertFalse(testCase, this.rs.next());
+            testPStmt.close();
+
+            // Do it again to make sure cached prepared statements behave correctly.
+            testConn.setCatalog("testBug66430DB1");
+            testPStmt = testConn.prepareStatement("SELECT * FROM testBug66430 WHERE id > ?");
+            testPStmt.setInt(1, 0);
+            this.rs = testPStmt.executeQuery();
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, 1, this.rs.getInt(1));
+            assertFalse(testCase, this.rs.next());
+            testPStmt.close();
+
+            testConn.setCatalog("testBug66430DB2");
+            testPStmt = testConn.prepareStatement("SELECT * FROM testBug66430 WHERE id > ?");
+            testPStmt.setInt(1, 0);
+            this.rs = testPStmt.executeQuery();
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, 2, this.rs.getInt(1));
+            assertFalse(testCase, this.rs.next());
+            testPStmt.close();
+
+            testConn.close();
+        } while ((useSPS = !useSPS) || (cachePS = !cachePS));
+    }
+
+    /**
+     * Tests fix for Bug#84783 - query timeout is not working(thread hang).
+     */
+    public void testBug84783() throws Exception {
+        // Test using a standard connection.
+        final Statement testStmt = this.conn.createStatement();
+        testStmt.setQueryTimeout(1);
+        assertThrows(SQLException.class, "Statement cancelled due to timeout or client request", new Callable<Void>() {
+            public Void call() throws Exception {
+                testStmt.executeQuery("SELECT SLEEP(3)");
+                return null;
+            }
+        });
+        testStmt.close();
+
+        boolean useSPS = false;
+        do {
+            final Properties props = new Properties();
+            props.setProperty("useServerPrepStmts", Boolean.toString(useSPS));
+
+            final String testCase = String.format("Case [SPS: %s]", useSPS ? "Y" : "N");
+
+            Connection testConn;
+
+            // Test using a failover connection.
+            testConn = getUnreliableFailoverConnection(new String[] { "host1", "host2" }, null);
+            final Statement testStmtFO = testConn.createStatement();
+            testStmtFO.setQueryTimeout(1);
+            assertThrows(testCase, SQLException.class, "Statement cancelled due to timeout or client request", new Callable<Void>() {
+                public Void call() throws Exception {
+                    testStmtFO.executeQuery("SELECT SLEEP(3)");
+                    return null;
+                }
+            });
+            final PreparedStatement testPstmtFO = testConn.prepareStatement("SELECT SLEEP(3)");
+            testPstmtFO.setQueryTimeout(1);
+            assertThrows(testCase, SQLException.class, "Statement cancelled due to timeout or client request", new Callable<Void>() {
+                public Void call() throws Exception {
+                    testPstmtFO.executeQuery();
+                    return null;
+                }
+            });
+            testConn.close();
+
+            // Test using a load-balanced connection.
+            testConn = getUnreliableLoadBalancedConnection(new String[] { "host1", "host2" }, null);
+            final Statement testStmtLB = testConn.createStatement();
+            testStmtLB.setQueryTimeout(1);
+            assertThrows(testCase, SQLException.class, "Statement cancelled due to timeout or client request", new Callable<Void>() {
+                public Void call() throws Exception {
+                    testStmtLB.executeQuery("SELECT SLEEP(3)");
+                    return null;
+                }
+            });
+            final PreparedStatement testPstmtLB = testConn.prepareStatement("SELECT SLEEP(3)");
+            testPstmtLB.setQueryTimeout(1);
+            assertThrows(testCase, SQLException.class, "Statement cancelled due to timeout or client request", new Callable<Void>() {
+                public Void call() throws Exception {
+                    testPstmtLB.executeQuery();
+                    return null;
+                }
+            });
+            testConn.close();
+
+            // Test using a replication connection.
+            testConn = getUnreliableReplicationConnection(new String[] { "host1", "host2" }, null);
+            final Statement testStmtR = testConn.createStatement();
+            testStmtR.setQueryTimeout(1);
+            assertThrows(testCase, SQLException.class, "Statement cancelled due to timeout or client request", new Callable<Void>() {
+                public Void call() throws Exception {
+                    testStmtR.executeQuery("SELECT SLEEP(3)");
+                    return null;
+                }
+            });
+            final PreparedStatement testPstmtR = testConn.prepareStatement("SELECT SLEEP(3)");
+            testPstmtR.setQueryTimeout(1);
+            assertThrows(testCase, SQLException.class, "Statement cancelled due to timeout or client request", new Callable<Void>() {
+                public Void call() throws Exception {
+                    testPstmtR.executeQuery();
+                    return null;
+                }
+            });
+            testConn.close();
+        } while (useSPS = !useSPS);
+    }
 }
diff --git a/src/testsuite/regression/jdbc4/ConnectionRegressionTest.java b/src/testsuite/regression/jdbc4/ConnectionRegressionTest.java
index db4e844..0658859 100644
--- a/src/testsuite/regression/jdbc4/ConnectionRegressionTest.java
+++ b/src/testsuite/regression/jdbc4/ConnectionRegressionTest.java
@@ -157,7 +157,7 @@ public class ConnectionRegressionTest extends BaseTestCase {
         props.setProperty("useSSL", "true");
         props.setProperty("requireSSL", "true");
         props.setProperty("verifyServerCertificate", "true");
-        props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/test-cert-store");
+        props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/ca-truststore");
         props.setProperty("trustCertificateKeyStoreType", "JKS");
         props.setProperty("trustCertificateKeyStorePassword", "password");
 
@@ -171,7 +171,7 @@ public class ConnectionRegressionTest extends BaseTestCase {
         props.setProperty("requireSSL", "true");
         props.setProperty("verifyServerCertificate", "true");
 
-        String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store";
+        String trustStorePath = "src/testsuite/ssl-test-certs/ca-truststore";
         System.setProperty("javax.net.ssl.keyStore", trustStorePath);
         System.setProperty("javax.net.ssl.keyStorePassword", "password");
         System.setProperty("javax.net.ssl.trustStore", trustStorePath);
diff --git a/src/testsuite/regression/jdbc4/MetaDataRegressionTest.java b/src/testsuite/regression/jdbc4/MetaDataRegressionTest.java
index 54c7628..c20d703 100644
--- a/src/testsuite/regression/jdbc4/MetaDataRegressionTest.java
+++ b/src/testsuite/regression/jdbc4/MetaDataRegressionTest.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -31,6 +31,7 @@ import java.sql.SQLException;
 import java.sql.Types;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Properties;
 
 import com.mysql.jdbc.ConnectionProperties;
 import com.mysql.jdbc.Util;
@@ -859,4 +860,142 @@ public class MetaDataRegressionTest extends BaseTestCase {
             }
         }
     }
+
+    /**
+     * Tests fix for Bug#73775 - DBMD.getProcedureColumns()/.getFunctionColumns() fail to filter by columnPattern
+     * 
+     * Test duplicated in testsuite.regression.MetaDataRegressionTest.
+     */
+    public void testBug73775() throws Exception {
+        createFunction("testBug73775f", "(param1 CHAR(20), param2 CHAR(20)) RETURNS CHAR(40) DETERMINISTIC RETURN CONCAT(param1, param2)");
+        createProcedure("testBug73775p", "(INOUT param1 CHAR(20), IN param2 CHAR(20)) BEGIN  SELECT CONCAT(param1, param2) INTO param1; END");
+
+        boolean useIS = false;
+        boolean inclFuncs = false;
+        do {
+            final String testCase = String.format("Case: [useIS: %s, inclFuncs: %s]", useIS ? "Y" : "N", inclFuncs ? "Y" : "N");
+
+            final Properties props = new Properties();
+            props.setProperty("useInformationSchema", Boolean.toString(useIS));
+            props.setProperty("getProceduresReturnsFunctions", Boolean.toString(inclFuncs));
+            final Connection testConn = getConnectionWithProps(props);
+            final DatabaseMetaData dbmd = testConn.getMetaData();
+
+            /*
+             * Test getProcedureColumns()
+             */
+            this.rs = dbmd.getProcedureColumns(null, "", "testBug73775%", "%");
+            if (inclFuncs) {
+                assertTrue(testCase, this.rs.next());
+                assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
+                assertEquals(DatabaseMetaData.procedureColumnReturn, this.rs.getInt(5));
+                assertTrue(testCase, this.rs.next());
+                assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                assertEquals(testCase, "param1", this.rs.getString(4));
+                assertTrue(testCase, this.rs.next());
+                assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                assertEquals(testCase, "param2", this.rs.getString(4));
+            }
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, "testBug73775p", this.rs.getString(3));
+            assertEquals(testCase, "param1", this.rs.getString(4));
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, "testBug73775p", this.rs.getString(3));
+            assertEquals(testCase, "param2", this.rs.getString(4));
+            assertFalse(testCase, this.rs.next());
+
+            for (String ptn : new String[] { "param1", "_____1", "%1", "p_r_m%1" }) {
+                this.rs = dbmd.getProcedureColumns(null, "", "testBug73775%", ptn);
+                if (inclFuncs) {
+                    assertTrue(this.rs.next());
+                    assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                    assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
+                    assertEquals(DatabaseMetaData.procedureColumnReturn, this.rs.getInt(5));
+                    assertTrue(testCase, this.rs.next());
+                    assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                    assertEquals(testCase, "param1", this.rs.getString(4));
+                }
+                assertTrue(testCase, this.rs.next());
+                assertEquals(testCase, "testBug73775p", this.rs.getString(3));
+                assertEquals(testCase, "param1", this.rs.getString(4));
+                assertFalse(testCase, this.rs.next());
+            }
+
+            for (String ptn : new String[] { "param2", "_____2", "%2", "p_r_m%2" }) {
+                this.rs = dbmd.getProcedureColumns(null, "", "testBug73775%", "param2");
+                if (inclFuncs) {
+                    assertTrue(this.rs.next());
+                    assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                    assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
+                    assertEquals(DatabaseMetaData.procedureColumnReturn, this.rs.getInt(5));
+                    assertTrue(testCase, this.rs.next());
+                    assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                    assertEquals(testCase, "param2", this.rs.getString(4));
+                }
+                assertTrue(testCase, this.rs.next());
+                assertEquals(testCase, "testBug73775p", this.rs.getString(3));
+                assertEquals(testCase, "param2", this.rs.getString(4));
+                assertFalse(testCase, this.rs.next());
+            }
+
+            this.rs = dbmd.getProcedureColumns(null, "", "testBug73775%", "");
+            if (inclFuncs) {
+                assertTrue(testCase, this.rs.next());
+                assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
+                assertEquals(testCase, DatabaseMetaData.procedureColumnReturn, this.rs.getInt(5));
+            }
+            assertFalse(testCase, this.rs.next());
+
+            /*
+             * Test getFunctionColumns()
+             */
+            this.rs = dbmd.getFunctionColumns(null, "", "testBug73775%", "%");
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+            assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
+            assertEquals(testCase, DatabaseMetaData.functionReturn, this.rs.getInt(5));
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+            assertEquals(testCase, "param1", this.rs.getString(4));
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+            assertEquals(testCase, "param2", this.rs.getString(4));
+            assertFalse(testCase, this.rs.next());
+
+            for (String ptn : new String[] { "param1", "_____1", "%1", "p_r_m%1" }) {
+                this.rs = dbmd.getFunctionColumns(null, "", "testBug73775%", ptn);
+                assertTrue(this.rs.next());
+                assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
+                assertEquals(testCase, DatabaseMetaData.functionReturn, this.rs.getInt(5));
+                assertTrue(testCase, this.rs.next());
+                assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                assertEquals(testCase, "param1", this.rs.getString(4));
+                assertFalse(testCase, this.rs.next());
+            }
+
+            for (String ptn : new String[] { "param2", "_____2", "%2", "p_r_m%2" }) {
+                this.rs = dbmd.getFunctionColumns(null, "", "testBug73775%", "param2");
+                assertTrue(this.rs.next());
+                assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
+                assertEquals(testCase, DatabaseMetaData.functionReturn, this.rs.getInt(5));
+                assertTrue(testCase, this.rs.next());
+                assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+                assertEquals(testCase, "param2", this.rs.getString(4));
+                assertFalse(testCase, this.rs.next());
+            }
+
+            this.rs = dbmd.getFunctionColumns(null, "", "testBug73775%", "");
+            assertTrue(testCase, this.rs.next());
+            assertEquals(testCase, "testBug73775f", this.rs.getString(3));
+            assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
+            assertEquals(testCase, DatabaseMetaData.functionReturn, this.rs.getInt(5));
+            assertFalse(testCase, this.rs.next());
+
+            testConn.close();
+        } while ((useIS = !useIS) || (inclFuncs = !inclFuncs));
+    }
 }
diff --git a/src/testsuite/regression/jdbc42/ResultSetRegressionTest.java b/src/testsuite/regression/jdbc42/ResultSetRegressionTest.java
new file mode 100644
index 0000000..705c97e
--- /dev/null
+++ b/src/testsuite/regression/jdbc42/ResultSetRegressionTest.java
@@ -0,0 +1,75 @@
+/*
+  Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+
+  The MySQL Connector/J is licensed under the terms of the GPLv2
+  <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, 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
+  <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
+
+  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 testsuite.regression.jdbc42;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.ZoneOffset;
+
+import testsuite.BaseTestCase;
+
+public class ResultSetRegressionTest extends BaseTestCase {
+    public ResultSetRegressionTest(String name) {
+        super(name);
+    }
+    
+    /**
+     * Tests fix for Bug#84189 - Allow null when extracting java.time.* classes from ResultSet.
+     */
+    public void testBug84189() throws Exception {
+        createTable("testBug84189", "(d DATE NULL, t TIME NULL, dt DATETIME NULL, ts TIMESTAMP NULL, ot VARCHAR(100), odt VARCHAR(100))");
+        this.stmt.execute(
+                "INSERT INTO testBug84189 VALUES ('2017-01-01', '10:20:30', '2017-01-01 10:20:30', '2017-01-01 10:20:30', '10:20:30+04:00', '2017-01-01T10:20:30+04:00')");
+        this.stmt.execute("INSERT INTO testBug84189 VALUES (NULL, NULL, NULL, NULL, NULL, NULL)");
+
+        this.rs = this.stmt.executeQuery("SELECT * FROM testBug84189");
+        assertTrue(this.rs.next());
+        assertEquals(LocalDate.of(2017, 1, 1), this.rs.getObject(1, LocalDate.class));
+        assertEquals(LocalTime.of(10, 20, 30), this.rs.getObject(2, LocalTime.class));
+        assertEquals(LocalDateTime.of(2017, 1, 1, 10, 20, 30), this.rs.getObject(3, LocalDateTime.class));
+        assertEquals(LocalDateTime.of(2017, 1, 1, 10, 20, 30), this.rs.getObject(4, LocalDateTime.class));
+        assertEquals(OffsetTime.of(10, 20, 30, 0, ZoneOffset.ofHours(4)), this.rs.getObject(5, OffsetTime.class));
+        assertEquals(OffsetDateTime.of(2017, 01, 01, 10, 20, 30, 0, ZoneOffset.ofHours(4)), this.rs.getObject(6, OffsetDateTime.class));
+
+        assertEquals(LocalDate.class, this.rs.getObject(1, LocalDate.class).getClass());
+        assertEquals(LocalTime.class, this.rs.getObject(2, LocalTime.class).getClass());
+        assertEquals(LocalDateTime.class, this.rs.getObject(3, LocalDateTime.class).getClass());
+        assertEquals(LocalDateTime.class, this.rs.getObject(4, LocalDateTime.class).getClass());
+        assertEquals(OffsetTime.class, this.rs.getObject(5, OffsetTime.class).getClass());
+        assertEquals(OffsetDateTime.class, this.rs.getObject(6, OffsetDateTime.class).getClass());
+
+        assertTrue(this.rs.next());
+        assertNull(this.rs.getObject(1, LocalDate.class));
+        assertNull(this.rs.getObject(2, LocalTime.class));
+        assertNull(this.rs.getObject(3, LocalDateTime.class));
+        assertNull(this.rs.getObject(4, LocalDateTime.class));
+        assertNull(this.rs.getObject(5, OffsetTime.class));
+        assertNull(this.rs.getObject(6, OffsetDateTime.class));
+
+        assertFalse(this.rs.next());
+    }
+}
diff --git a/src/testsuite/simple/ConnectionTest.java b/src/testsuite/simple/ConnectionTest.java
index 40bc337..07bcd42 100644
--- a/src/testsuite/simple/ConnectionTest.java
+++ b/src/testsuite/simple/ConnectionTest.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -53,6 +53,7 @@ import java.util.Enumeration;
 import java.util.List;
 import java.util.Properties;
 import java.util.TimeZone;
+import java.util.concurrent.Callable;
 
 import com.mysql.jdbc.CharsetMapping;
 import com.mysql.jdbc.MySQLConnection;
@@ -1960,4 +1961,182 @@ public class ConnectionTest extends BaseTestCase {
             return super.preProcess(sql, interceptedStatement, connection);
         }
     }
+
+    /**
+     * Test authentication with a user that requires an SSL connection.
+     * 
+     * This test requires the CA truststore and the client keystore available in testsuite/ssl-test-certs.
+     * The server needs to be configured with the CA and server certificates from testsuite/ssl-test-certs.
+     */
+    public void testUserRequireSSL() throws Exception {
+        if (!versionMeetsMinimum(5, 7, 6)) {
+            return;
+        }
+
+        Connection testConn;
+        Statement testStmt;
+
+        final String user = "testUserReqSSL";
+        final String password = "testUserReqSSL";
+
+        final Properties props = new Properties();
+        props.setProperty(NonRegisteringDriver.USER_PROPERTY_KEY, user);
+        props.setProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY, password);
+
+        createUser("'" + user + "'@'%'", "IDENTIFIED BY '" + password + "' REQUIRE SSL");
+        this.stmt.execute("GRANT SELECT ON *.* TO '" + user + "'@'%'");
+
+        /*
+         * No SSL.
+         */
+        props.setProperty("useSSL", "false");
+        assertThrows(SQLException.class, "Access denied for user '" + user + "'@.*", new Callable<Void>() {
+            public Void call() throws Exception {
+                getConnectionWithProps(props);
+                return null;
+            }
+        });
+
+        /*
+         * SSL: no server certificate validation & no client certificate.
+         */
+        props.setProperty("useSSL", "true");
+        props.setProperty("verifyServerCertificate", "false");
+        testConn = getConnectionWithProps(props);
+        testStmt = testConn.createStatement();
+        this.rs = testStmt.executeQuery("SELECT CURRENT_USER()");
+        assertTrue(this.rs.next());
+        assertEquals(user, this.rs.getString(1).split("@")[0]);
+        testConn.close();
+
+        /*
+         * SSL: server certificate validation & no client certificate.
+         */
+        props.setProperty("verifyServerCertificate", "true");
+        props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/ca-truststore");
+        props.setProperty("trustCertificateKeyStoreType", "JKS");
+        props.setProperty("trustCertificateKeyStorePassword", "password");
+        testConn = getConnectionWithProps(props);
+        testStmt = testConn.createStatement();
+        this.rs = testStmt.executeQuery("SELECT CURRENT_USER()");
+        assertTrue(this.rs.next());
+        assertEquals(user, this.rs.getString(1).split("@")[0]);
+        testConn.close();
+
+        /*
+         * SSL: server certificate validation & client certificate.
+         */
+        props.setProperty("clientCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/client-keystore");
+        props.setProperty("clientCertificateKeyStoreType", "JKS");
+        props.setProperty("clientCertificateKeyStorePassword", "password");
+        testConn = getConnectionWithProps(props);
+        testStmt = testConn.createStatement();
+        this.rs = testStmt.executeQuery("SELECT CURRENT_USER()");
+        assertTrue(this.rs.next());
+        assertEquals(user, this.rs.getString(1).split("@")[0]);
+        testConn.close();
+
+        /*
+         * SSL: no server certificate validation & client certificate.
+         */
+        props.setProperty("verifyServerCertificate", "false");
+        props.remove("trustCertificateKeyStoreUrl");
+        props.remove("trustCertificateKeyStoreType");
+        props.remove("trustCertificateKeyStorePassword");
+        testConn = getConnectionWithProps(props);
+        testStmt = testConn.createStatement();
+        this.rs = testStmt.executeQuery("SELECT CURRENT_USER()");
+        assertTrue(this.rs.next());
+        assertEquals(user, this.rs.getString(1).split("@")[0]);
+        testConn.close();
+    }
+
+    /**
+     * Test authentication with a user that requires an SSL connection and an authorized client certificate.
+     * 
+     * This test requires the CA truststore and the client keystore available in testsuite/ssl-test-certs.
+     * The server needs to be configured with the CA and server certificates from testsuite/ssl-test-certs.
+     */
+    public void testUserRequireX509() throws Exception {
+        if (!versionMeetsMinimum(5, 7, 6)) {
+            return;
+        }
+
+        Connection testConn;
+        Statement testStmt;
+
+        final String user = "testUserReqX509";
+        final String password = "testUserReqX509";
+
+        final Properties props = new Properties();
+        props.setProperty(NonRegisteringDriver.USER_PROPERTY_KEY, user);
+        props.setProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY, password);
+
+        createUser("'" + user + "'@'%'", "IDENTIFIED BY '" + password + "' REQUIRE X509");
+        this.stmt.execute("GRANT SELECT ON *.* TO '" + user + "'@'%'");
+
+        /*
+         * No SSL.
+         */
+        props.setProperty("useSSL", "false");
+        assertThrows(SQLException.class, "Access denied for user '" + user + "'@.*", new Callable<Void>() {
+            public Void call() throws Exception {
+                getConnectionWithProps(props);
+                return null;
+            }
+        });
+
+        /*
+         * SSL: no server certificate validation & no client certificate.
+         */
+        props.setProperty("useSSL", "true");
+        props.setProperty("verifyServerCertificate", "false");
+        assertThrows(SQLException.class, "Access denied for user '" + user + "'@.*", new Callable<Void>() {
+            public Void call() throws Exception {
+                getConnectionWithProps(props);
+                return null;
+            }
+        });
+
+        /*
+         * SSL: server certificate validation & no client certificate.
+         */
+        props.setProperty("verifyServerCertificate", "true");
+        props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/ca-truststore");
+        props.setProperty("trustCertificateKeyStoreType", "JKS");
+        props.setProperty("trustCertificateKeyStorePassword", "password");
+        assertThrows(SQLException.class, "Access denied for user '" + user + "'@.*", new Callable<Void>() {
+            public Void call() throws Exception {
+                getConnectionWithProps(props);
+                return null;
+            }
+        });
+
+        /*
+         * SSL: server certificate validation & client certificate.
+         */
+        props.setProperty("clientCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/client-keystore");
+        props.setProperty("clientCertificateKeyStoreType", "JKS");
+        props.setProperty("clientCertificateKeyStorePassword", "password");
+        testConn = getConnectionWithProps(props);
+        testStmt = testConn.createStatement();
+        this.rs = testStmt.executeQuery("SELECT CURRENT_USER()");
+        assertTrue(this.rs.next());
+        assertEquals(user, this.rs.getString(1).split("@")[0]);
+        testConn.close();
+
+        /*
+         * SSL: no server certificate validation & client certificate.
+         */
+        props.setProperty("verifyServerCertificate", "false");
+        props.remove("trustCertificateKeyStoreUrl");
+        props.remove("trustCertificateKeyStoreType");
+        props.remove("trustCertificateKeyStorePassword");
+        testConn = getConnectionWithProps(props);
+        testStmt = testConn.createStatement();
+        this.rs = testStmt.executeQuery("SELECT CURRENT_USER()");
+        assertTrue(this.rs.next());
+        assertEquals(user, this.rs.getString(1).split("@")[0]);
+        testConn.close();
+    }
 }
diff --git a/src/testsuite/simple/ResultSetTest.java b/src/testsuite/simple/ResultSetTest.java
index 6146cdb..ea36d58 100644
--- a/src/testsuite/simple/ResultSetTest.java
+++ b/src/testsuite/simple/ResultSetTest.java
@@ -63,7 +63,10 @@ public class ResultSetTest extends BaseTestCase {
         // build map of charsets supported by server
         Connection c = getConnectionWithProps("detectCustomCollations=true");
         Map<String, Integer> charsetsMap = new HashMap<String, Integer>();
-        for (int index = 1; index < CharsetMapping.MAP_SIZE; index++) {
+        this.rs = this.stmt.executeQuery("SHOW COLLATION");
+        while (this.rs.next()) {
+            int index = ((Number) this.rs.getObject(3)).intValue();
+
             String charsetName = null;
             if (((ConnectionImpl) c).indexToCustomMysqlCharset != null) {
                 charsetName = ((ConnectionImpl) c).indexToCustomMysqlCharset.get(index);
@@ -88,6 +91,7 @@ public class ResultSetTest extends BaseTestCase {
 
         while (charsetNames.hasNext()) {
             String charsetName = charsetNames.next();
+            System.out.println(charsetName);
 
             if (charsetName.equalsIgnoreCase("LATIN7") || charsetName.equalsIgnoreCase("BINARY")) {
                 continue; // no mapping in Java
diff --git a/src/testsuite/simple/StringUtilsTest.java b/src/testsuite/simple/StringUtilsTest.java
index 6929769..762abe6 100644
--- a/src/testsuite/simple/StringUtilsTest.java
+++ b/src/testsuite/simple/StringUtilsTest.java
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
 
   The MySQL Connector/J is licensed under the terms of the GPLv2
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
@@ -25,6 +25,7 @@ package testsuite.simple;
 
 import java.nio.charset.Charset;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Callable;
@@ -299,8 +300,8 @@ public class StringUtilsTest extends BaseTestCase {
         assertEquals(-1, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos));
 
         /*
-         * E. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers, Set<SearchMode>
-         * searchMode) illegal markers arguments
+         * E. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers[, String
+         * overridingMarkers], Set<SearchMode> searchMode) illegal markers arguments
          */
         assertThrows(IllegalArgumentException.class,
                 "Illegal argument value null for openingMarkers and/or - for closingMarkers. These cannot be null and must have the same length.",
@@ -334,6 +335,20 @@ public class StringUtilsTest extends BaseTestCase {
                         return null;
                     }
                 });
+        assertThrows(IllegalArgumentException.class,
+                "Illegal argument value null for overridingMarkers. These cannot be null and must be a sub-set of openingMarkers -!.", new Callable<Void>() {
+                    public Void call() throws Exception {
+                        StringUtils.indexOfIgnoreCase(0, "abc", "abc", "-!", "-!", null, EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS));
+                        return null;
+                    }
+                });
+        assertThrows(IllegalArgumentException.class,
+                "Illegal argument value ' for overridingMarkers. These cannot be null and must be a sub-set of openingMarkers -!.", new Callable<Void>() {
+                    public Void call() throws Exception {
+                        StringUtils.indexOfIgnoreCase(0, "abc", "abc", "-!", "-!", "'", EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS));
+                        return null;
+                    }
+                });
 
         /*
          * F. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers, Set<SearchMode>
@@ -817,4 +832,334 @@ public class StringUtilsTest extends BaseTestCase {
                     StringUtils.unQuoteIdentifier(identifiersQuotedPedantic[i], "\""));
         }
     }
+
+    /**
+     * Tests StringUtils.wildCompare() method.
+     */
+    public void testWildCompare() throws Exception {
+        // Null values.
+        assertFalse(StringUtils.wildCompareIgnoreCase(null, null));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", null));
+        assertFalse(StringUtils.wildCompareIgnoreCase(null, "abcxyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase(null, "_"));
+        assertFalse(StringUtils.wildCompareIgnoreCase(null, "%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase(null, "_%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase(null, "%_"));
+
+        // Empty values.
+        assertTrue(StringUtils.wildCompareIgnoreCase("", ""));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", ""));
+        assertFalse(StringUtils.wildCompareIgnoreCase("", "abcxyz"));
+
+        // Different letter case.
+        assertTrue(StringUtils.wildCompareIgnoreCase("ABCxyz", "abcxyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcXYZ", "abcxyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("ABCXYZ", "abcxyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "ABCxyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abcXYZ"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "ABCXYZ"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("ABCxyz", "ab%YZ"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcXYZ", "AB%yz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("ABCxyz", "ab__YZ"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcXYZ", "AB__yz"));
+
+        // Patterns without wildcards.
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "zyxcba"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxy"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "z"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "bcxyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "aabcxyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyzz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abclmnxyz"));
+
+        // Patterns with wildcard %.
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyz%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyz%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%z"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%z"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%abcxyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%abcxyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a%z"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a%%%z"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%%%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%cx%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%cx%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%abcxyz%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%abcxyz%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%b%y%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%b%%%y%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%a%b%c%x%y%z%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%a%%%b%%%c%%%x%%%y%%%z%%%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyz%z"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyz%%%z"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a%abcxyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a%%%abcxyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "ab%cx%cx%yz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "ab%%%cx%%%cx%%%yz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%x"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%%%x"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "c%xyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "c%%%xyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%a%m%z%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%a%%%m%%%z%%%"));
+
+        // Patterns with wildcard _.
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxy_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc___"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a_____"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_bcxyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "___xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_____z"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "ab__yz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a____z"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_bcxy_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "__cx__"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a_c_y_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_b_x_z"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "______"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyz_"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abc____"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a______"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "_abcxyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "____xyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "______z"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abc_xyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "ab___yz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a_____z"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "_bc_xy_"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "__c_x__"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a_c_y__"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "__b_x_z"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "_______"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abcx_"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abc__"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a____"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "_cxyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "__xyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "____z"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "ab_yz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a___z"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "_cxy_"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a_c__"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "__x_z"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "_____"));
+
+        // Patterns with both wildcards.
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc_%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc_%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc___%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc___%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%%%_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%___"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%%%___"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%_xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%_xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%___xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%___xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_%%%_xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "___%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "___%%%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%_cx_%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%_cx_%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%__cx__%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%__cx__%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_%cx%_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_%%%cx%%%_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "__%cx%__"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "__%%%cx%%%__"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_b%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_b%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_____z%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_____z%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%y_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%y_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%a_____"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%a_____"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc%_%_%_%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%abc%%%_%%%_%%%_%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%_%_%_%xyz%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%_%%%_%%%_%%%xyz%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%a%_%c%x%_%z%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%a%%%_%%%c%%%x%%%_%%%z%%%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%_c%m%x_%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%%_c%%%m%%%x_%%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a%a_c%_yz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a%%%a_c%%%_yz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "ab_%x_z%z"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "ab_%%%x_z%%%Z"));
+
+        // Patterns with wildcards only.
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%_%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%_%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%___%___%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%___%%%___%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%_%_%_%_%_%_%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%_%%%_%%%_%%%_%%%_%%%_%%%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%___%_%___%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%___%%%_%%%___%%%"));
+
+        // Escaped wildcards
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc%", "abc%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc%%%", "abc%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc%", "abc_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc%%%", "abc___"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc%", "abc\\%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc%%%", "abc\\%\\%\\%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc%", "abc%\\%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc%%%", "abc%\\%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc%", "abc\\%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc%%%", "abc\\%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc_", "abc%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc___", "abc%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc_", "abc_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc___", "abc___"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc_", "abc\\_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc___", "abc\\_\\_\\_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc___", "abc\\_\\__"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc___", "abc\\__\\_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abc___", "abc_\\_\\_"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("%xyz", "%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("%%%xyz", "%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("%xyz", "_xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("%%%xyz", "___xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("%xyz", "\\%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("%%%xyz", "\\%\\%\\%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("%xyz", "%\\%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("%%%xyz", "%\\%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("%xyz", "\\%%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("%%%xyz", "\\%%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("_xyz", "%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("___xyz", "%xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("_xyz", "_xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("___xyz", "___xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("_xyz", "\\_xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("___xyz", "\\_\\_\\_xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("___xyz", "\\_\\__xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("___xyz", "\\__\\_xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("___xyz", "_\\_\\_xyz"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("%_%", "\\%\\_\\%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("_%_", "\\_\\%\\_"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc\\%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc%\\%%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "\\%xyz%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%\\%%xyz%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc\\%xyz%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc%\\%%xyz%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc\\_%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc%\\_%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%\\_xyz%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%\\_%xyz%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc\\_xyz%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc%\\_%xyz%"));
+
+        // Values with repeated patterns.
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "%abc"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "%%%abc"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "abc%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "abc%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "%abc%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "%%%abc%%%"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "abc%abc"));
+        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "abc%%%abc"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("xyzxyzxyzxyzxyzabc", "%xyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("xyzxyzxyzxyzxyzabc", "%%%xyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzxyz", "xyz%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzxyz", "xyz%%%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "%xyz%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "%%%xyz%%%"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzabc", "abc%xyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzabc", "abc%%%xyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzabc", "xyz%abc"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzabc", "xyz%%%abc"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzabc", "xyz%xyz"));
+        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzabc", "xyz%%%xyz"));
+    }
+
+    /**
+     * Tests StringUtils.split() methods.
+     */
+    public void testSplit() throws Exception {
+        String testString = "  abstract, (contents, table of \"['figure''s'],(tables\"),  introduction  , \"methods(), ()results\", ['discussion'']', conclusion]   ";
+        List<String> stringParts;
+
+        // non existing split char, trim
+        stringParts = StringUtils.split(testString, ";", true);
+        assertEquals(1, stringParts.size());
+        assertEquals(testString.trim(), stringParts.get(0));
+
+        // non existing split char, don't trim
+        stringParts = StringUtils.split(testString, ";", false);
+        assertEquals(1, stringParts.size());
+        assertEquals(testString, stringParts.get(0));
+
+        // full split, trim
+        stringParts = StringUtils.split(testString, ",", true);
+        assertEquals(9, stringParts.size());
+        assertEquals("abstract", stringParts.get(0));
+        assertEquals("(contents", stringParts.get(1));
+        assertEquals("table of \"['figure''s']", stringParts.get(2));
+        assertEquals("(tables\")", stringParts.get(3));
+        assertEquals("introduction", stringParts.get(4));
+        assertEquals("\"methods()", stringParts.get(5));
+        assertEquals("()results\"", stringParts.get(6));
+        assertEquals("['discussion'']'", stringParts.get(7));
+        assertEquals("conclusion]", stringParts.get(8));
+
+        // full split, don't trim
+        stringParts = StringUtils.split(testString, ",", false);
+        assertEquals(9, stringParts.size());
+        assertEquals("  abstract", stringParts.get(0));
+        assertEquals(" (contents", stringParts.get(1));
+        assertEquals(" table of \"['figure''s']", stringParts.get(2));
+        assertEquals("(tables\")", stringParts.get(3));
+        assertEquals("  introduction  ", stringParts.get(4));
+        assertEquals(" \"methods()", stringParts.get(5));
+        assertEquals(" ()results\"", stringParts.get(6));
+        assertEquals(" ['discussion'']'", stringParts.get(7));
+        assertEquals(" conclusion]   ", stringParts.get(8));
+
+        // most common markers, trim
+        stringParts = StringUtils.split(testString, ",", "'\"", "'\"", true);
+        assertEquals(7, stringParts.size());
+        assertEquals("abstract", stringParts.get(0));
+        assertEquals("(contents", stringParts.get(1));
+        assertEquals("table of \"['figure''s'],(tables\")", stringParts.get(2));
+        assertEquals("introduction", stringParts.get(3));
+        assertEquals("\"methods(), ()results\"", stringParts.get(4));
+        assertEquals("['discussion'']'", stringParts.get(5));
+        assertEquals("conclusion]", stringParts.get(6));
+
+        // extended markers, trim
+        stringParts = StringUtils.split(testString, ",", "'\"([{", "'\")]}", true);
+        assertEquals(2, stringParts.size());
+        assertEquals("abstract", stringParts.get(0));
+        assertEquals("(contents, table of \"['figure''s'],(tables\"),  introduction  , \"methods(), ()results\", ['discussion'']', conclusion]",
+                stringParts.get(1));
+
+        // extended markers with overridable markers, trim
+        stringParts = StringUtils.split(testString, ",", "'\"([{", "'\")]}", "'\"", true);
+        assertEquals(5, stringParts.size());
+        assertEquals("abstract", stringParts.get(0));
+        assertEquals("(contents, table of \"['figure''s'],(tables\")", stringParts.get(1));
+        assertEquals("introduction", stringParts.get(2));
+        assertEquals("\"methods(), ()results\"", stringParts.get(3));
+        assertEquals("['discussion'']', conclusion]", stringParts.get(4));
+    }
 }
diff --git a/src/testsuite/ssl-test-certs/ca-cert.pem b/src/testsuite/ssl-test-certs/ca-cert.pem
index 7e39d72..fd14c6a 100644
--- a/src/testsuite/ssl-test-certs/ca-cert.pem
+++ b/src/testsuite/ssl-test-certs/ca-cert.pem
@@ -1,24 +1,24 @@
 -----BEGIN CERTIFICATE-----
-MIIEAzCCAuugAwIBAgIJAJ8S2uoC12H6MA0GCSqGSIb3DQEBCwUAMIGXMQswCQYD
-VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEQMA4GA1UEBwwHUmVkd29vZDEP
-MA0GA1UECgwGT3JhY2xlMQ4wDAYDVQQLDAVNeVNRTDESMBAGA1UEAwwJbG9jYWxo
-b3N0MSwwKgYJKoZIhvcNAQkBFh1hbGV4YW5kZXIuc29rbGFrb3ZAb3JhY2xlLmNv
-bTAeFw0xNjA2MjIxMDAwNThaFw0yNjA2MjAxMDAwNThaMIGXMQswCQYDVQQGEwJV
-UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEQMA4GA1UEBwwHUmVkd29vZDEPMA0GA1UE
-CgwGT3JhY2xlMQ4wDAYDVQQLDAVNeVNRTDESMBAGA1UEAwwJbG9jYWxob3N0MSww
-KgYJKoZIhvcNAQkBFh1hbGV4YW5kZXIuc29rbGFrb3ZAb3JhY2xlLmNvbTCCASIw
-DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANPL0Q3YF6ouvKJdqhP1JjzuN0ay
-62kZegjiV6WYH0+j+7otxukmuIymY/uzTjEtv1IaNW5/Z/PPZnux2a1N+Hrrd4Cu
-UYcj4U0RYDId2JJZAEhdYqr94YuOAb1r8fPYD+9eKXRzwV6QJYCtcpbdwbtRHU4O
-nFnW1kUX9Ic7NlMTgcCsHZNE5g6OzfelT0iS1NxBzzz6h3RSoFxe1pRfw4+2SHG1
-Hm0LLUb/LqDtfpqrN71OUqUTfnefioQFVwl0shcJeUqlNNTh89dwAFeZ0kgVUWBE
-YCCpAweCi4+DHosBG07JWaih76p4XfcR0TMa9G49pb9hN4K488R9T3oCMacCAwEA
-AaNQME4wHQYDVR0OBBYEFLjtdDzKgsO9QkFrAgc0oiDFcZfgMB8GA1UdIwQYMBaA
-FLjtdDzKgsO9QkFrAgc0oiDFcZfgMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL
-BQADggEBAKdVDtwoIijJ50ugR7hD+GLlTvLPvKDAUHNZrA04muqHEC1E99ZHf260
-b7ZFd96xAwCGXBzbOL8eara3DOQeXre0gWaID/24QKbo2r+Fv4FbrppsxNngxVlZ
-f6yRJyBaK1PEJLTyHMwScmiDd3w9VrnpqKpsfi5HYnUps7j6+BEA1Y3FV+ZWlnkr
-D6dLjVh/fjCP2dkxEe9iYbOyf6u8yOt1u40yoQNW69Ldf3W+C8ZoXnYPphAwxLGm
-I6yensCMLApPy7z2QTheuTMi4LdUFtP8PxcrqwlP8Z2hkPnxY83+rhG3WiMsc8N1
-DrEq+HYZznCbWVLeFq2B1Fy/aPcJ8tk=
+MIIEDTCCAvWgAwIBAgIJAIiYHFajCcAZMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD
+VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEXMBUGA1UEBwwOUmVkd29vZCBT
+aG9yZXMxDzANBgNVBAoMBk9yYWNsZTEOMAwGA1UECwwFTXlTUUwxHTAbBgNVBAMM
+FE15U1FMIENvbm5lY3Rvci9KIENBMR8wHQYJKoZIhvcNAQkBFhBteXNxbEBvcmFj
+bGUuY29tMB4XDTE3MDMwNDAwMDM0M1oXDTI3MDMwMjAwMDM0M1owgZwxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRcwFQYDVQQHDA5SZWR3b29kIFNo
+b3JlczEPMA0GA1UECgwGT3JhY2xlMQ4wDAYDVQQLDAVNeVNRTDEdMBsGA1UEAwwU
+TXlTUUwgQ29ubmVjdG9yL0ogQ0ExHzAdBgkqhkiG9w0BCQEWEG15c3FsQG9yYWNs
+ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLkHXpKMO4oUI0
+S64R4D2MIFtS9xXX4BK7jDW/VefKh87k9kLBPqKPb7NKqdCFcJk/JBrfvgQwb63U
+FH2TX3NYSQ/4gXG/UZoUMG9EK8KZ7gpKvrbgBSYAyOhE+dLrE7HYCesmS3Y4Owib
+0QJx8IozDQm/JencKk7swfT6sSBvPb/rvAegiOoxb7S2EW0zWlIw4P9kPmu8X4IW
+6u79gW1HyOAIi0wKFbqgXwTHaz+VtqjRglYi0U48H2xqw5IpVhZg9Di1VzeZESkD
+RVPNoFhhlX0xYfl3/+ZoNRVJDQHE6qXSfkDLePzw81Bl+OwO2CYZ0EXwW0bzgk9u
+HXuvuKrpAgMBAAGjUDBOMB0GA1UdDgQWBBSaoJ8RI8gbLGSrUwrrCbqvZR3i6DAf
+BgNVHSMEGDAWgBSaoJ8RI8gbLGSrUwrrCbqvZR3i6DAMBgNVHRMEBTADAQH/MA0G
+CSqGSIb3DQEBCwUAA4IBAQCHl2ezisRplspyEyqEH4uvBbBr1zGDXKn6BtPufXws
+4S+itxrw16sGI3RmYheyYuZPM+9PlwFMsWRJtfxZc4svZRr/s9IoHHQiV/KBILfu
+5JDxY1UwZcGHZ0b8XlgeBOxnq15z7YaU7+pPAVmpxmnehJCK8s+y5TlM2S2OEvYP
+I2swAXDE5Gzwq1Y/VExQZhRQL5LXGiNjZV+VHeLO3Vf5HrKUsKSTnq5kvdIwWELz
+o3NqS88gr36lBzh3aqX3/rxS0QdR4iecoG+ztO5ZS5mFpgqP+2aNAano/8cQ7jIQ
+8cm4akxWarBQRLik2lB+kwsxiyGF+k8NjVUAIUh70U7H
 -----END CERTIFICATE-----
diff --git a/src/testsuite/ssl-test-certs/ca-key.pem b/src/testsuite/ssl-test-certs/ca-key.pem
index 0ca150b..6ee64c0 100644
--- a/src/testsuite/ssl-test-certs/ca-key.pem
+++ b/src/testsuite/ssl-test-certs/ca-key.pem
@@ -1,27 +1,27 @@
 -----BEGIN RSA PRIVATE KEY-----
-MIIEpAIBAAKCAQEA08vRDdgXqi68ol2qE/UmPO43RrLraRl6COJXpZgfT6P7ui3G
-6Sa4jKZj+7NOMS2/Uho1bn9n889me7HZrU34eut3gK5RhyPhTRFgMh3YklkASF1i
-qv3hi44BvWvx89gP714pdHPBXpAlgK1ylt3Bu1EdTg6cWdbWRRf0hzs2UxOBwKwd
-k0TmDo7N96VPSJLU3EHPPPqHdFKgXF7WlF/Dj7ZIcbUebQstRv8uoO1+mqs3vU5S
-pRN+d5+KhAVXCXSyFwl5SqU01OHz13AAV5nSSBVRYERgIKkDB4KLj4MeiwEbTslZ
-qKHvqnhd9xHRMxr0bj2lv2E3grjzxH1PegIxpwIDAQABAoIBACHQNWUPbd/GrGkD
-qSacielKYtrPr9PMtHX8pN+UJNOTK5oyusH4Y5hY7DeADei+FABonMUyZAdBwmvX
-HhiUDSqasy6BJhT8PnkOYBxANZZPzULAXgNMmxirqE10kWFLymd90gJ8hI+zVblw
-d43sg4SDU7lljcKuEPFg/LImWuryNOP/hRE0fIT2lYjjQHkPMpsYPfhvqPP+Rn1G
-0rlzh2XhUR8vJz9JfNVZCdzHGQIsnhrKn30s6zqrA9MGg8yot0sEpkwHY6tOOLXZ
-6B7uG9L4ps/pLT7Yplg3xFgELEidHZYHQGLLzc43vKHEo3lczGhbIpNXQw8zJDQP
-TMcNvgECgYEA88/EiJipttD4iL976Y26Y8EUAxNQlRMcrLSauRdLBDmMyRsX/r4I
-kAcmLL65V02iHShbx0Y/B77Wct3MxNHUeGqPS2lLPA7oB011IiYKw2QQYAwaLy1r
-bd8ryA92KO3UeMiNi3z1YRP4TmB96EmCvaywzfSYazxfkGTcFXWCWwECgYEA3mJT
-CkgBu7ISmbFm2Z+MVShSTQoJbsE9IiEj2u5h3yJGMoYMgyHEicDK9UNW4K6YZrax
-v5zeYzUGGfkCdD2h118D/r1qtvbQvs7dSa6OVIW2ENpqnASXILi3isQhTwvzseLe
-y6Y5t8P+84lVnEgBuNHbQwtn+LhpA0k64mqc1KcCgYEAibFI+SvejRSvh+s8e8ZD
-AZtFRgdedxJ4G0FUMDvrbdNioyDeCEwiYdTnxlVgLuH1MCpeysftSN6KFg0VXAJ0
-0By+GIS6rIxuldE02bhKU9brOJMdJo+sIDztaOryfGL0n9IDvkcv/Udd8EWmVc1O
-PPiOUYJVohpSjiWn9aaLnAECgYEAwoWjOYUO7w2us324B3hGGl8wzm6PHbAuN2Jh
-qKmINtQWLy+OVk39Srm3Tp5eqh5O9Nmt1Xv0EzdYZPOpr+ARZwCX0AYECJMQuTbd
-3guwOELVpRxI0Lw7LcYl1l4E/M3plppqZBy8s//fTUu99PwPdYkrkBO0GJ3GTbeK
-qG61TFUCgYBBmRhW6M+A5x67DUE7+MsdWH/v/iGslFZlAjj9mrxtwxfiBStjRp3O
-aTkclm4LTCOGW9G1Gc+O1G19JMoGD7U7X6/A8YmwDJ1izJ+UI4bCMI3evVq3oT+e
-Nq2REQlmJ0JFg3Y3RRjpVUQfSfKKSLofwtGxYQ4YUyfClYMKlKMHdw==
+MIIEpAIBAAKCAQEAy5B16SjDuKFCNEuuEeA9jCBbUvcV1+ASu4w1v1XnyofO5PZC
+wT6ij2+zSqnQhXCZPyQa374EMG+t1BR9k19zWEkP+IFxv1GaFDBvRCvCme4KSr62
+4AUmAMjoRPnS6xOx2AnrJkt2ODsIm9ECcfCKMw0JvyXp3CpO7MH0+rEgbz2/67wH
+oIjqMW+0thFtM1pSMOD/ZD5rvF+CFuru/YFtR8jgCItMChW6oF8Ex2s/lbao0YJW
+ItFOPB9sasOSKVYWYPQ4tVc3mREpA0VTzaBYYZV9MWH5d//maDUVSQ0BxOql0n5A
+y3j88PNQZfjsDtgmGdBF8FtG84JPbh17r7iq6QIDAQABAoIBAQCAQDGphl9ZUW/u
+lsfCx26/fBtNeBKxAaGLu9iZBbyVo168blK8gYPVruBc6ARSLhC+8TZlRMhK4/G2
+rno3gWmgc9e2D4fkq7rRgXp4jsF6Tbj6QpHhLh01XV7JJkS2ip//prAaXmzLHJZd
+5R8PPicz5sD/RxnB9n3nOIPp9fKjYjlikzESEn+BaimTFGTX2X2w/exrzDpT6jd5
+hTBg/v4UAs/+5v/M6646VPctz+Mc9Db1b2EH35Nv3kXHX2ts3ChRzjgKEDbEBT/q
+d6yb+3zd8sdZJqxWQtWMAN6BGwzSJZ3//zaqyY8K7YNEHwHJWAjjlRG1PcgQRgZc
+zcKVfKOpAoGBAPlu+strrwUBJD9AA1FgUQBMmCtvS1L5e4VQSqyR1g62ok3VBnJ/
+olUREbMVoSJK0Kjuz+Hzrs4t6md8i1AC4zwOly/JKqWsuPXb6sCNNEi6SB6zrg4p
+9CmUIp99QdpZV5Gqkk2kK/nyMHuwIbuZRLBoEpzAFmPghU+ajalKcemzAoGBANDs
+WiSZ6iRa9AHY69kC2V8eoRueZJTl1Kb7MsRo3vJ4GNHA5g/qbkSzDndPwpgfalvV
+nOZLYOCBw/HpNxAjjLMgELaVi6LsG9yA7BiXjpcUFykFsc1KdHhTOFoIS+E9mspq
+Otj26gwuPNJkzePJjPLAbD8KszCvoDFr8iI1EdLzAoGBAPcS5GAdcYdegx4X9XFl
+G7dVJ6JWouE0+RKr3twjtUprxEeejL1cCluDBepDsZH7U8cSj16899mJh9gseP6v
+bZEuzrlp5dGoTqpSxa0n8bNculWXHAExs4aAEu+yrklwGBrVQAiiZ2E1cqsCY9ek
+0XKAFHeqmkMiwkqAyHP5pu6bAoGAYe85PkkMEei00xzdScflH4qbGPhBy+6EpZXB
+MYK7Z8vWTpTvA2zwLbIFyPYRkP/A+rWCrA2Ppw4NTRv2nNEdRmZ0jzJA16l4Uf/5
+LHn29GkNhkROGZ6aREE2fDXBZB9drR7o4QW1puoRxspMHUhAkxaSZ2bboqnhHOmu
+Ak0AQvMCgYAEBbYfB6PmIvPL458OL3DSc+CGyp+SMep9d5WJrSjjersO55nn3/LL
+lxV+zdN6gR7jQn0xtn7rHp/IpPFBc7FUqcTPFnc9YrIrBaS5yvhmv94yOf+4MCq0
+7FYG2XRl9QRl+FSsxHFl8m/sl38GdIl7CQvafc2BXozdwTrzSjTSuA==
 -----END RSA PRIVATE KEY-----
diff --git a/src/testsuite/ssl-test-certs/ca-truststore b/src/testsuite/ssl-test-certs/ca-truststore
new file mode 100644
index 0000000..c35eab4
Binary files /dev/null and b/src/testsuite/ssl-test-certs/ca-truststore differ
diff --git a/src/testsuite/ssl-test-certs/certs_howto.txt b/src/testsuite/ssl-test-certs/certs_howto.txt
index f9c614f..6dc7bfd 100644
--- a/src/testsuite/ssl-test-certs/certs_howto.txt
+++ b/src/testsuite/ssl-test-certs/certs_howto.txt
@@ -3,46 +3,113 @@
 STEPS TO GENERATE TEST CERTIFICATES
 ===================================
 
-1. Create CA cert.
-==================
 
-$ openssl genrsa 2048 > ca-key.pem
+1. CA key and certficate
+========================
 
-$ openssl req -new -x509 -nodes -days 3650 -key ca-key.pem -out ca-cert.pem
-Country Name (2 letter code) [XX]:US
+(Generate the CA key)
+$ openssl genrsa -out ca-key.pem 2048
+
+(Generate a self-signed certificate for the CA)
+$ openssl req -new -x509 -nodes -sha256 -days 3650 -key ca-key.pem -out ca-cert.pem
+(...)
+Country Name (2 letter code) []:US
+State or Province Name (full name) []:California
+Locality Name (eg, city) []:Redwood Shores
+Organization Name (eg, company) []:Oracle
+Organizational Unit Name (eg, section) []:MySQL
+Common Name (e.g. server FQDN or YOUR name) []:MySQL Connector/J CA
+Email Address []:mysql@oracle.com
+
+
+2. Server key and certificate
+=============================
+
+(Generate the server key)
+$ openssl genrsa -out server-key.pem 2048
+
+(Generate a certificate signing request for the server)
+$ openssl req -new -key server-key.pem -out server-csr.pem
+(...)
+Country Name (2 letter code) []:US
 State or Province Name (full name) []:California
-Locality Name (eg, city) [Default City]:Redwood
-Organization Name (eg, company) [Default Company Ltd]:Oracle
+Locality Name (eg, city) []:Redwood Shores
+Organization Name (eg, company) []:Oracle
 Organizational Unit Name (eg, section) []:MySQL
-Common Name (eg, your name or your server's hostname) []:localhost
-Email Address []:alexander.soklakov@oracle.com
+Common Name (e.g. server FQDN or YOUR name) []:MySQL Connector/J Server
+Email Address []:mysql@oracle.com
+(...)
+A challenge password []:
+An optional company name []:
+
+(Sign the server certificate signing request)
+$ openssl x509 -req -in server-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -days 3650 -sha256 -out server-cert.pem
+
+(OPTIONAL: Delete the certificate signing request file)
+$ rm server-csr.pem
 
+(OPTIONAL: Verify the server certificate)
+$ openssl verify -CAfile ca-cert.pem server-cert.pem
 
-2. Create server cert.
-======================
 
-$ openssl req -newkey rsa:2048 -days 3650 -nodes -keyout server-key.pem -out server-req.pem
-Country Name (2 letter code) [XX]:US
+3. Client key and certificate
+=============================
+
+(Generate the client key)
+$ openssl genrsa -out client-key.pem 2048
+
+(Generate a certificate signing request for the client)
+$ openssl req -new -key client-key.pem -out client-csr.pem
+(...)
+Country Name (2 letter code) []:US
 State or Province Name (full name) []:California
-Locality Name (eg, city) [Default City]:Redwood
-Organization Name (eg, company) [Default Company Ltd]:Oracle
+Locality Name (eg, city) []:Redwood Shores
+Organization Name (eg, company) []:Oracle
 Organizational Unit Name (eg, section) []:MySQL
-Common Name (eg, your name or your server's hostname) []:localhost
-Email Address []:alexander.soklakov@oracle.com
-A challenge password []:password
-An optional company name []:password
+Common Name (e.g. server FQDN or YOUR name) []:MySQL Connector/J Client
+Email Address []:mysql@oracle.com
+(...)
+A challenge password []:
+An optional company name []:
+
+(Sign the client certificate signing request)
+$ openssl x509 -req -in client-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -days 3650 -sha256 -out client-cert.pem
 
-$ openssl rsa -in server-key.pem -out server-key.pem
-$ openssl x509 -req -in server-req.pem -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
+(OPTIONAL: Delete the certificate signing request file)
+$ rm client-csr.pem
 
-3. Create truststore
-====================
+(OPTIONAL: Verify the client certificate)
+$ openssl verify -CAfile ca-cert.pem client-cert.pem
 
-$ keytool -importcert -alias mysqlservercacert -file ca-cert.pem -keystore test-cert-store
-Enter keystore password: password
-Re-enter new password: password
+
+4. CA truststore
+================
+
+(Create a truststore containing the CA certificate)
+$ keytool -importcert -alias mysqlcacert -file ca-cert.pem -keystore ca-truststore -storepass password
 Trust this certificate? [no]:  yes
 
+(OPTIONAL: List the contents of the truststore)
+$ keytool -list -keystore ca-truststore -storepass password
+
+
+5. Client key and certificate keystore
+======================================
+
+(Convert client key to pkcs12 format)
+$ openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem -name "mysqlclient" -passout pass:password -out client-keystore.p12
+
+(Create a keystore containing the client key)
+$ keytool -importkeystore -srckeystore client-keystore.p12 -srcstoretype pkcs12 -srcstorepass password -destkeystore client-keystore -deststoretype JKS -deststorepass password
+
+(OPTIONAL: Delete the client key in pkcs12 format)
+$ rm client-keystore.p12
+
+(OPTIONAL: List the contents of the client keystore)
+$ keytool -list -keystore client-keystore -storepass password
+
+
+
 ==========================
 RUN SERVER WITH TEST CERTS
 ==========================
diff --git a/src/testsuite/ssl-test-certs/client-cert.pem b/src/testsuite/ssl-test-certs/client-cert.pem
new file mode 100644
index 0000000..6ee95da
--- /dev/null
+++ b/src/testsuite/ssl-test-certs/client-cert.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDsjCCApoCAQEwDQYJKoZIhvcNAQELBQAwgZwxCzAJBgNVBAYTAlVTMRMwEQYD
+VQQIDApDYWxpZm9ybmlhMRcwFQYDVQQHDA5SZWR3b29kIFNob3JlczEPMA0GA1UE
+CgwGT3JhY2xlMQ4wDAYDVQQLDAVNeVNRTDEdMBsGA1UEAwwUTXlTUUwgQ29ubmVj
+dG9yL0ogQ0ExHzAdBgkqhkiG9w0BCQEWEG15c3FsQG9yYWNsZS5jb20wHhcNMTcw
+MzA0MDAwNzA3WhcNMjcwMzAyMDAwNzA3WjCBoDELMAkGA1UEBhMCVVMxEzARBgNV
+BAgMCkNhbGlmb3JuaWExFzAVBgNVBAcMDlJlZHdvb2QgU2hvcmVzMQ8wDQYDVQQK
+DAZPcmFjbGUxDjAMBgNVBAsMBU15U1FMMSEwHwYDVQQDDBhNeVNRTCBDb25uZWN0
+b3IvSiBDbGllbnQxHzAdBgkqhkiG9w0BCQEWEG15c3FsQG9yYWNsZS5jb20wggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCluIoIzfZDPJYrHet8kbyeG+rp
+8GLVDcZ0n0EOHqkviSSpi2iEkxolpWXVBN6DddP0jE9Pitpf8dyfEnRUeEYQrU21
+da788gaA17mZZsfP36QRW3Jz2tekodlYtKSeo4Gl+IPIyBjeQG9sqSpQe9oyruJV
+LUmz9euPtJj1UqhTBVDAW9NgCGgfqAEw0Hos+H8+/LRia/9O8VThVTzlPaiJylli
+xxin4/JkFiQj+R7CDyhNfnY/9kY0XoeVemjkOWCMAstSqjQzevRtVdXeduzsL6fq
+g4tKgqOXaWUT9g2XC1807qExmo4o13X9v9fXCNv4R+vwzofYp0DSmbHUrBPhAgMB
+AAEwDQYJKoZIhvcNAQELBQADggEBAJg0npMHVfOXDDGbqQzRaqNY51id6/0tagRa
+FQfVNjaLDWI5WDDw+LRGxgiBEthbWP/dPWjiZrEr0LQFIvDcAqEtZNNcoAQEMr28
+tuOmVad0Es/lMf1xxghBkn8XjVwUUmLpW+WEfI1NvSdrsn70e04A0LXj88zxa2eJ
+smUU2ML7dAfvb+EYhLB5jnlO/ARQ8eMwHiN/lsEntX8iEDwhID+7gFubgmIkxyjM
+LsqgqayvTvQ/WvWSCk73oVqSRhlN30N/ses7TUDWyUvZTJorrekIiMCwNFJ6gPCU
+OlWO3mCFgt47XlCzLMfqfOKxDxGkcxW+S4nU0m7FbRPf7rMmBxk=
+-----END CERTIFICATE-----
diff --git a/src/testsuite/ssl-test-certs/client-key.pem b/src/testsuite/ssl-test-certs/client-key.pem
new file mode 100644
index 0000000..e9b2e26
--- /dev/null
+++ b/src/testsuite/ssl-test-certs/client-key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEApbiKCM32QzyWKx3rfJG8nhvq6fBi1Q3GdJ9BDh6pL4kkqYto
+hJMaJaVl1QTeg3XT9IxPT4raX/HcnxJ0VHhGEK1NtXWu/PIGgNe5mWbHz9+kEVty
+c9rXpKHZWLSknqOBpfiDyMgY3kBvbKkqUHvaMq7iVS1Js/Xrj7SY9VKoUwVQwFvT
+YAhoH6gBMNB6LPh/Pvy0Ymv/TvFU4VU85T2oicpZYscYp+PyZBYkI/kewg8oTX52
+P/ZGNF6HlXpo5DlgjALLUqo0M3r0bVXV3nbs7C+n6oOLSoKjl2llE/YNlwtfNO6h
+MZqOKNd1/b/X1wjb+Efr8M6H2KdA0pmx1KwT4QIDAQABAoIBADV0tXHpngpKPNZj
+rFZeXE1UDA6yL+8xplZwGHgJg+GFh4H5Khm5qbGDDLoN/AjDtT8pE1xBGffg+J2H
+QmcM9OzkOjDSaUPGfjqJW6ET+lgfd7jxmpWsferpGkb23jMd4vA6nafig8MNXePb
+HX8gtF2s8+0t0pxuMzIDu2NG6mz8wQ3/V4vrbOM/AV5oXysfWei1NyU8z0DDHBd6
+GrriUvKUPmSXWR1uO6LKMO0+TxCZlAFHN0K7Ej1LU5nw3nzGaHkggvmch7od1qXr
+7C6Hlf1I2iX2x7l14P99rQrj0JLZtm0aB3HHAymfNk0yrzkEraaUgkTccSfknfaq
+iD4ZBwECgYEA0SMQXt4ZyOVGYFPxegQF8BW/7S/FRdoQS94t5NJ04uZgWo0VMYII
+Pl05r3QLn4+86ZB5Pzg6lsC0d/GpsHxPoHXl9+LTlXkGWoorLGbMA1L+P7ZrUwP0
+tx/tDioc6mwautExV/VcIJm5j1c6PbpsEBCRBDCu9meVcKl18UAl2qkCgYEAytrz
+6Dy5qxyXui3jcBvKPtk6ETs5uxWekQvMNB6jmN6qKlKle1yaJwIwWwV671iUjPst
+UN+AJGELdgZJOzg9AHeoIpvgGQ2bMp5NwTimvY6M6kbH4za+bgZAXZdYoqB5ReZG
+5QR1+eEL+r9oVvqcCaAYc3r30uDxMPNRZR6oKnkCgYEAkvyO6+4ztyOVU+yLolMj
+jTrsGprhjpeVrozoOpPD/RknjVztEU+v1Y0WFv97j9ipUIru/ITbgMrNzCM4PUcR
+e9iBGbj9NmfKFGZZs6pIJun0sfjW85CrNO2mYbctfYEfRD/06zoSVwNUDK+kym+0
+tZG/Km/A+IOS34zqzPVl5YkCgYEAiDLOBKWQ+6Qs8yIQTJs8BUqQRDWBo2z2k+hZ
+6LyRlvgZRGVNGjCoh3xevx5E1iKhSq7yVBRb0xEdQtchoBM4UfIE/4esxOVvyGKl
+ThAdU7Q8RKfVWWbOIM0ttikBp98azaW4/9co5ucExgxxn8SPs568C/0KG5pQFk+n
+3L7ipfECgYBAoVUMbq8WbT9GQr2lIs4P3/tDX7PZ3YgoN2+txoVi16Xzxcy1YUBE
+gd9prvNpOULwlXd7/Ip3riXkypU7tKXmsu8WloucOO+cHSpGXKzt0I8bbHYYx46N
+2GZb7oVya/0nv9vWuPF94eKAd//qA/FyCgQexDG/F7fT44uf5Vw0Vw==
+-----END RSA PRIVATE KEY-----
diff --git a/src/testsuite/ssl-test-certs/client-keystore b/src/testsuite/ssl-test-certs/client-keystore
new file mode 100644
index 0000000..8d91b82
Binary files /dev/null and b/src/testsuite/ssl-test-certs/client-keystore differ
diff --git a/src/testsuite/ssl-test-certs/server-cert.pem b/src/testsuite/ssl-test-certs/server-cert.pem
index 5e5c591..0082c29 100644
--- a/src/testsuite/ssl-test-certs/server-cert.pem
+++ b/src/testsuite/ssl-test-certs/server-cert.pem
@@ -1,22 +1,22 @@
 -----BEGIN CERTIFICATE-----
-MIIDpDCCAowCAQEwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYD
-VQQIDApDYWxpZm9ybmlhMRAwDgYDVQQHDAdSZWR3b29kMQ8wDQYDVQQKDAZPcmFj
-bGUxDjAMBgNVBAsMBU15U1FMMRIwEAYDVQQDDAlsb2NhbGhvc3QxLDAqBgkqhkiG
-9w0BCQEWHWFsZXhhbmRlci5zb2tsYWtvdkBvcmFjbGUuY29tMB4XDTE2MDYyMjEw
-MDU1MVoXDTI2MDYyMDEwMDU1MVowgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD
-YWxpZm9ybmlhMRAwDgYDVQQHDAdSZWR3b29kMQ8wDQYDVQQKDAZPcmFjbGUxDjAM
-BgNVBAsMBU15U1FMMRIwEAYDVQQDDAlsb2NhbGhvc3QxLDAqBgkqhkiG9w0BCQEW
-HWFsZXhhbmRlci5zb2tsYWtvdkBvcmFjbGUuY29tMIIBIjANBgkqhkiG9w0BAQEF
-AAOCAQ8AMIIBCgKCAQEAxHTdUYU/gt6nefynr3KQtWl0UsjsGfbeM0TQObZPDJWf
-UyFZbRO/yHYLaZN1HrX4XCgRmHEAXSyoKLGyLlHlGGzzKdXZxYX/IC3hRJG2sHkW
-kvjtL/LCu4PHULxH/YAuXJ3jNrhJDNNRnZH/Bv3SPjLpieEF8bhvwy6hJBN3/Hc2
-LYPPbl0HjvpqsZIf5R2LzIuBbajGU2R/KK0yPGryYOC/EubCGyq1wNEaBitq0bh6
-H9VruJdCxjympVBHp6kJ6e4SjJyjdJEI/iF9Q4/hKmvwttNN6wMuN/2NBlntzNOO
-BkMfHnZcQQEKH/XMxAKAVwyOoSCw1qMeZIo92vz1lQIDAQABMA0GCSqGSIb3DQEB
-CwUAA4IBAQBsdzkgdw95IBaynNIIRwqw8/tnzWW2UUlYiIqYCIKxdc+eWtbymNJT
-4DXzckRSq9R2XUSHmdmFidLeYNdayzRPiecacG7wMo436et1ZLWXQ8DLe//lMJY1
-muj3As2W6/zY6jfGlMWQJ5zVujS5eqFdc4RQxxQfEEQG0NYmz7pXAnrpooE4yguf
-nI3uzhQ3eoeTk13DngavdsxFVPSiYObHmcA7KahWYTNmzy70IR81KqMqn0/iOasH
-d5Cdsq1A0slICSZ0VOndXIXAhByT5tuFlx/QT5fktobpp41CHDmV6+votEU4gpFC
-qiSns89VrtSqY5YRiRs3Va5Oph7rk7r3
+MIIDsjCCApoCAQEwDQYJKoZIhvcNAQELBQAwgZwxCzAJBgNVBAYTAlVTMRMwEQYD
+VQQIDApDYWxpZm9ybmlhMRcwFQYDVQQHDA5SZWR3b29kIFNob3JlczEPMA0GA1UE
+CgwGT3JhY2xlMQ4wDAYDVQQLDAVNeVNRTDEdMBsGA1UEAwwUTXlTUUwgQ29ubmVj
+dG9yL0ogQ0ExHzAdBgkqhkiG9w0BCQEWEG15c3FsQG9yYWNsZS5jb20wHhcNMTcw
+MzA0MDAwNDM5WhcNMjcwMzAyMDAwNDM5WjCBoDELMAkGA1UEBhMCVVMxEzARBgNV
+BAgMCkNhbGlmb3JuaWExFzAVBgNVBAcMDlJlZHdvb2QgU2hvcmVzMQ8wDQYDVQQK
+DAZPcmFjbGUxDjAMBgNVBAsMBU15U1FMMSEwHwYDVQQDDBhNeVNRTCBDb25uZWN0
+b3IvSiBTZXJ2ZXIxHzAdBgkqhkiG9w0BCQEWEG15c3FsQG9yYWNsZS5jb20wggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcPGRTppAa1JgMD7ZlkrXX1keh
+k8NZn81hAysM98B4sqpGdrQS8VUrPm6Lu3fYoPr/re3ds7HNiOCAKyua77G4Xmf6
+oY8axXpBgIHyR7mNifbHYT9tPC79w0n/vRmMgjG3HEjWxRFjZBOE78B7P3HpK9Pl
+Ix9XieO+li44rg02cFJUeV/7lTylg69pRszLfggiDk8oDKo+qYtdQq9D4/iFCvej
+cAYPrTpJw/Ge25fe/RZfFjT5ekhoYUOhS7X2Qs+1ORShMm2K0WIFX3eQxpHusu+l
+TmjfWBaUu1b0geW/jpGdVrTYKOyGqOT6k+zZLOU/YK9ozWVSY1s9foSL6RDPAgMB
+AAEwDQYJKoZIhvcNAQELBQADggEBAJN2cCywMjvSkmT1i84gp1Ct9hpnVj0BJWul
+Y/Y6myIeFkkmxrjhtYC8i3eSWQreOFKMwjpFqK7ap5xQt8ZM5g5NKtqmieZkKAg8
+9POO87Z0XtTk3yhRha+XD+WzvfeYMxobjVznY6rWeDI80GHKIkNAtCDha3secdqQ
+xtWCiwf7RVbjsvB0n36ryow2AHhYqKMsZg/9a7Qio+EZ/+92pJ0RkPbR6OoT8IPP
+vy7MJhLtx9T4+WZghtWo28i5op5iLmCLSTAEhscDpd40vmr4gTLeQNOs4orkIWm0
+xYDUhJMMmjnmJysOp6fvEwTuOYBpl5VBQ5q5bhBSh7dpcHfUUIc=
 -----END CERTIFICATE-----
diff --git a/src/testsuite/ssl-test-certs/server-key.pem b/src/testsuite/ssl-test-certs/server-key.pem
index 2fa963f..aea291f 100644
--- a/src/testsuite/ssl-test-certs/server-key.pem
+++ b/src/testsuite/ssl-test-certs/server-key.pem
@@ -1,27 +1,27 @@
 -----BEGIN RSA PRIVATE KEY-----
-MIIEpQIBAAKCAQEAxHTdUYU/gt6nefynr3KQtWl0UsjsGfbeM0TQObZPDJWfUyFZ
-bRO/yHYLaZN1HrX4XCgRmHEAXSyoKLGyLlHlGGzzKdXZxYX/IC3hRJG2sHkWkvjt
-L/LCu4PHULxH/YAuXJ3jNrhJDNNRnZH/Bv3SPjLpieEF8bhvwy6hJBN3/Hc2LYPP
-bl0HjvpqsZIf5R2LzIuBbajGU2R/KK0yPGryYOC/EubCGyq1wNEaBitq0bh6H9Vr
-uJdCxjympVBHp6kJ6e4SjJyjdJEI/iF9Q4/hKmvwttNN6wMuN/2NBlntzNOOBkMf
-HnZcQQEKH/XMxAKAVwyOoSCw1qMeZIo92vz1lQIDAQABAoIBAQCpZM8UpJ1PvaaQ
-057O9Cz8j5JCc4bJGIkdkd8tdBoTEsxPRLk0CUxbkBVlEkVxlpww8kfBtuYGcSQJ
-+rZVpD2y4vaEgyWEYHbMi2Lq2e25mp8xWTxDFMJ+JzFsVvyIA8d2CpnJK/uo+Gku
-QZ2xrJhlE0c1oPacJ1kO5h0B21uhLrB5SYq34/eK4kw7yd9n6ZjpvuRq3DBkR0Xp
-I3B8k7BgMYrvQPGeiVddNhTZcVU16TYwPtALS0VaUXaHKtdIpxs7szsJPi9BqKRo
-wKxT60zWJkB1FXg5WDaXRuofgV6RV/yTiZtBOt5OJubZbGTbs6Dq6R9UnvtvxhQm
-UVNfuUcdAoGBAPlT0AHxb2bHDn6I1JC77a8P/Gnd28bY90Tb8zB1LFytkz+ASu/l
-SAp9RAJi685uk10xI3ROpQPi/YSmt+gJSAKvbuPiD+iZomHhBZP32e8uedPtH2fU
-W0Qdet3foYaU3rbn2NRU7c2JiZF0dKhKrLsNMd3aowIIfPV9uDI0Y6bzAoGBAMm2
-0ulyXqDx2GILgsPFjHBRKBiVB8yrWbJsCJhM5SmPLcpfYrtCEBudu5vS0wkxmtFM
-OzWJZfxy6Smzv86ryEJ1OGlxr1yFwUE8UUuhmM/zbIfb37fwjfTXsi/EidmZhHRF
-V8OsWC5YMvcDPaGipOp3QHGEHedrwjtY1xpIhCNXAoGBAI6qAo/aEqCAri6BJQBc
-SDivsQLyy9sQMwzXvmOv6F5vg54GTtrOG1bFLrEe4UcRxojAoUTf61Q6Ak5xBzJi
-fS3sLEuBAxFZN61CEOsGG1HdCiDVwe6reD2VkMR8PfTAImAOceetYRUG6gys0kOp
-1wSBHFOR00xKhxYBhfBUQeE5AoGAOduU150Ug3mbJVRD57+MLtM4ewSUPiKQEdtn
-A7haqvcEnV2KxkeGs0UlAOcR3Ts7OvlwqkSE2kpjkrCaPb+MK3PQAH65XAKJkhqo
-/taVLGCYKsfofawkK6yK0aTAHYgpM+iH6fpPOMvXon5teSVbxFz8QRMyOKTyC2/K
-EyHltPUCgYEAkB10UHWcF8r+QwlZvCfg0eFVcOyeU87ZMAfD4AQC0KC5p1Jb1cuK
-iCilqEmZ2YAm24GyxPhX6QZy84S61QBXJdaW2gQy9yL8m3wHZEAlbgEf5UvNtX1X
-WG51C7gvTuMvsONOe413734xOSSQ7NCQwXfi8b9ljnxEzL9KxIXBJhU=
+MIIEowIBAAKCAQEAnDxkU6aQGtSYDA+2ZZK119ZHoZPDWZ/NYQMrDPfAeLKqRna0
+EvFVKz5ui7t32KD6/63t3bOxzYjggCsrmu+xuF5n+qGPGsV6QYCB8ke5jYn2x2E/
+bTwu/cNJ/70ZjIIxtxxI1sURY2QThO/Aez9x6SvT5SMfV4njvpYuOK4NNnBSVHlf
++5U8pYOvaUbMy34IIg5PKAyqPqmLXUKvQ+P4hQr3o3AGD606ScPxntuX3v0WXxY0
++XpIaGFDoUu19kLPtTkUoTJtitFiBV93kMaR7rLvpU5o31gWlLtW9IHlv46RnVa0
+2Cjshqjk+pPs2SzlP2CvaM1lUmNbPX6Ei+kQzwIDAQABAoIBAGGepn1SBJaPIVXf
+93Kt4asz+1vtDNGSNOyZ4yteHkgMMrKGduujGTlE8COlBjQ4Gbzp+0dcgQtQrB3J
+W5yaiiwN6OBEVDtBCquk1Q0CXeEfRC++BriFM0Nh43nEuRL/QnsMVELFraCxpjSY
+7WbU9KLhvKteb7XttXyfL9BAiIbrW5TrHQ9FPfADg6pmckHoQnNkE4S1CQq1z/4w
+erlyeTNFa/ExAsrPsmjMp/6bur4zdW4t+5GpB2DZf+4P4pEIHJGRwNzU/Eq0TlNs
+TTmhlba6X/gSMNm0vYEXnNItb+ItiOsGqoLCzUzaAN7jvdsqAiVaiRvObtBLj76/
+17INnOECgYEA0E2ZHzVg9MaMUhC9dwHawGbbqeZfnAC89J9Hapt0WDtMuulsA+V8
+C2Ek4Rxpe62DuXdcvYSSuL3VJ4oWffkKngll5m+SiCSuuaDT9KAJ01sklIsGDAWE
+E0gYWg1sqsj5UE2r7O5TzfRnitglDwyNY87KYvxX7jI6mpHCPSHbblECgYEAwAKy
+DaIS6gtOfXLhXfzV+Qslsulcf4FRdI06XFVhmXziW6spdl2qVuKTWAiKK69zTRQD
+2cXVxMf+VEvF7btyllNBYEdU1NcX3gCqCmb1LRqngIQkL0oPlXVaZnN5q17tXAPO
+Oxd3waefIqBjWJ03qc37oQzuCAXBiYKlUFLVJR8CgYAmgNF29VTwqeqyGKoM32jh
++6FK7Gxr168VwINg7084J+3s9syig77EE7bPbYMJ+27Hz4py9ZhxUZWe82Vo76nI
+wI/3YlRBmhY59cJ4C0UFrmk6k+AQRMy/C6qEqsqVXwTGlTTcXMlc3nptLbrb50QT
+WNwMI/zU6K/wdlTjG+kFoQKBgG79X+UHib1mOcHENUe6Dgm+itUnlcNSgj5IILMc
+26T4jnQEtZvpOrysjhV2IkBsU877QdqRRCdSn84kWs43612/1GuN8Bh0GSTH54LJ
+zvF6ldZxC+WV/voa8t2D5JHN9pCY1o5L23rCpPdEFQk1H+b03X5T1ggnI5+UOHD8
+4J5rAoGBAJ2x4bRSj/vBgiNUvyvTv6JQoH1hCvDNEYT4TUr8XzMjVcS5voqokAQc
+mGseepXn6OU+JDW7N/DvgNStkgN6Gj1J/aTwPerC7exkMr4c6YOudjZUZJnOjUj4
+qHfAGQ5Vwo7fc9S6ymaxzAw5bFNHhZd4P3D1qzpU4804r8CMExUM
 -----END RSA PRIVATE KEY-----
diff --git a/src/testsuite/ssl-test-certs/server-req.pem b/src/testsuite/ssl-test-certs/server-req.pem
deleted file mode 100644
index 8724c3b..0000000
--- a/src/testsuite/ssl-test-certs/server-req.pem
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE REQUEST-----
-MIIDDzCCAfcCAQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh
-MRAwDgYDVQQHDAdSZWR3b29kMQ8wDQYDVQQKDAZPcmFjbGUxDjAMBgNVBAsMBU15
-U1FMMRIwEAYDVQQDDAlsb2NhbGhvc3QxLDAqBgkqhkiG9w0BCQEWHWFsZXhhbmRl
-ci5zb2tsYWtvdkBvcmFjbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAxHTdUYU/gt6nefynr3KQtWl0UsjsGfbeM0TQObZPDJWfUyFZbRO/yHYL
-aZN1HrX4XCgRmHEAXSyoKLGyLlHlGGzzKdXZxYX/IC3hRJG2sHkWkvjtL/LCu4PH
-ULxH/YAuXJ3jNrhJDNNRnZH/Bv3SPjLpieEF8bhvwy6hJBN3/Hc2LYPPbl0Hjvpq
-sZIf5R2LzIuBbajGU2R/KK0yPGryYOC/EubCGyq1wNEaBitq0bh6H9VruJdCxjym
-pVBHp6kJ6e4SjJyjdJEI/iF9Q4/hKmvwttNN6wMuN/2NBlntzNOOBkMfHnZcQQEK
-H/XMxAKAVwyOoSCw1qMeZIo92vz1lQIDAQABoDIwFwYJKoZIhvcNAQkCMQoMCHBh
-c3N3b3JkMBcGCSqGSIb3DQEJBzEKDAhwYXNzd29yZDANBgkqhkiG9w0BAQsFAAOC
-AQEAkDQz/89WlMiJGEvRmauLXay8dLx+N9aTv8RlN0yHicmV/FkJXhJ6G97fdGZs
-GrHN0vRC4mKf9svEl5hp2jmDicOf5AEfaLyJHWiRf99B/wNvKyeWPYsjHlYhPpRd
-RjHB7h2uXsE6JaLL4XFjso2axbuCDJ9GsHiuAjgiMLfBZhaDxMe/OTflnYDJdVWo
-EfMVbuZ3M0nq5ro/wE8KNQVw5aL6ZKadyxPSuoGT38dQXLj5d0mIvMtwmXdXHsmz
-pylIIO7a4+9HhwQf7z80aF8sgNgO9/LMD9uEwp81GedQYHyPw+8z4aezVBkx9GFl
-qyJgIiR6oeORktXuRfXtCm87kA==
------END CERTIFICATE REQUEST-----
diff --git a/src/testsuite/ssl-test-certs/test-cert-store b/src/testsuite/ssl-test-certs/test-cert-store
deleted file mode 100644
index b86e928..0000000
Binary files a/src/testsuite/ssl-test-certs/test-cert-store and /dev/null differ

--- End Message ---
--- Begin Message ---
Unblocked mysql-connector-java.

--- End Message ---

Reply to: