From a3765138d0f5ca1b3353a8751a5cfd8f3a3982f4 Mon Sep 17 00:00:00 2001 From: Bart De Neuter Date: Tue, 20 Dec 2022 08:38:47 +0100 Subject: [PATCH] Replace connectionMutex and synchronized blocks with a ReentrantLock. The synchronized blocks were removed and replaced by `lock.lock()` and `lock.unlock()`. This avoids that the carrier thread (OS thread) is pinned when running on virtual threads which were introduced as a preview feature in JDK 19. https://openjdk.org/jeps/425 `There are two scenarios in which a virtual thread cannot be unmounted during blocking operations because it is pinned to its carrier: When it executes code inside a synchronized block or method, or When it executes a native method or a foreign function.` --- .../java/com/mysql/cj/MysqlConnection.java | 3 +- .../mysql/cj/protocol/ResultsetRowsOwner.java | 4 +- .../java/com/mysql/cj/NativeSession.java | 10 +- .../a/result/ResultsetRowsCursor.java | 7 +- .../a/result/ResultsetRowsStreaming.java | 10 +- .../com/mysql/cj/jdbc/CallableStatement.java | 433 +++++++++++++++--- .../cj/jdbc/ClientPreparedStatement.java | 429 ++++++++++++++--- .../com/mysql/cj/jdbc/ConnectionImpl.java | 284 ++++++++++-- .../com/mysql/cj/jdbc/ConnectionWrapper.java | 3 +- .../cj/jdbc/ServerPreparedStatement.java | 91 +++- .../java/com/mysql/cj/jdbc/StatementImpl.java | 313 ++++++++++--- .../cj/jdbc/ha/MultiHostMySQLConnection.java | 3 +- .../mysql/cj/jdbc/result/ResultSetImpl.java | 203 ++++++-- .../cj/jdbc/result/UpdatableResultSet.java | 187 ++++++-- .../regression/DataSourceRegressionTest.java | 2 +- .../PooledConnectionRegressionTest.java | 7 +- .../regression/StatementRegressionTest.java | 3 +- 17 files changed, 1645 insertions(+), 347 deletions(-) diff --git a/src/main/core-api/java/com/mysql/cj/MysqlConnection.java b/src/main/core-api/java/com/mysql/cj/MysqlConnection.java index 5b1dfe190..a680821ab 100644 --- a/src/main/core-api/java/com/mysql/cj/MysqlConnection.java +++ b/src/main/core-api/java/com/mysql/cj/MysqlConnection.java @@ -30,6 +30,7 @@ package com.mysql.cj; import java.util.Properties; +import java.util.concurrent.locks.ReentrantLock; import com.mysql.cj.conf.PropertySet; import com.mysql.cj.exceptions.ExceptionInterceptor; @@ -56,7 +57,7 @@ public interface MysqlConnection { */ Properties getProperties(); - Object getConnectionMutex(); + ReentrantLock getConnectionMutex(); Session getSession(); diff --git a/src/main/core-api/java/com/mysql/cj/protocol/ResultsetRowsOwner.java b/src/main/core-api/java/com/mysql/cj/protocol/ResultsetRowsOwner.java index 3cd462839..c715dd5e7 100644 --- a/src/main/core-api/java/com/mysql/cj/protocol/ResultsetRowsOwner.java +++ b/src/main/core-api/java/com/mysql/cj/protocol/ResultsetRowsOwner.java @@ -33,6 +33,8 @@ import com.mysql.cj.Query; import com.mysql.cj.Session; +import java.util.concurrent.locks.ReentrantLock; + public interface ResultsetRowsOwner { void closeOwner(boolean calledExplicitly); @@ -41,7 +43,7 @@ public interface ResultsetRowsOwner { Session getSession(); - Object getSyncMutex(); + ReentrantLock getSyncMutex(); /** * StackTrace generated where ResultSet was created... used when profiling diff --git a/src/main/core-impl/java/com/mysql/cj/NativeSession.java b/src/main/core-impl/java/com/mysql/cj/NativeSession.java index bf2c7b4d6..7e218364e 100644 --- a/src/main/core-impl/java/com/mysql/cj/NativeSession.java +++ b/src/main/core-impl/java/com/mysql/cj/NativeSession.java @@ -43,6 +43,7 @@ import java.util.Properties; import java.util.Timer; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import com.mysql.cj.conf.HostInfo; @@ -317,8 +318,9 @@ public void setLocalInfileInputStream(InputStream stream) { this.protocol.setLocalInfileInputStream(stream); } - private void createConfigCacheIfNeeded(Object syncMutex) { - synchronized (syncMutex) { + private void createConfigCacheIfNeeded(ReentrantLock syncMutex) { + syncMutex.lock(); + try { if (this.serverConfigCache != null) { return; } @@ -364,6 +366,8 @@ public Exception interceptException(Exception sqlEx) { new Object[] { getPropertySet().getStringProperty(PropertyKey.queryInfoCacheFactory).getValue(), PropertyKey.queryInfoCacheFactory }), e, getExceptionInterceptor()); } + } finally { + syncMutex.unlock(); } } @@ -379,7 +383,7 @@ public Exception interceptException(Exception sqlEx) { * @param version * driver version string */ - public void loadServerVariables(Object syncMutex, String version) { + public void loadServerVariables(ReentrantLock syncMutex, String version) { if (this.cacheServerConfiguration.getValue()) { createConfigCacheIfNeeded(syncMutex); diff --git a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/result/ResultsetRowsCursor.java b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/result/ResultsetRowsCursor.java index a0fd6035e..60a6f849c 100644 --- a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/result/ResultsetRowsCursor.java +++ b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/result/ResultsetRowsCursor.java @@ -31,6 +31,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.locks.ReentrantLock; import com.mysql.cj.Messages; import com.mysql.cj.exceptions.ExceptionFactory; @@ -204,7 +205,9 @@ private void fetchMoreRows() { return; } - synchronized (this.owner.getSyncMutex()) { + ReentrantLock lock = this.owner.getSyncMutex(); + lock.lock(); + try { try { boolean oldFirstFetchCompleted = this.firstFetchCompleted; @@ -253,6 +256,8 @@ private void fetchMoreRows() { } catch (Exception ex) { throw ExceptionFactory.createException(ex.getMessage(), ex); } + } finally { + lock.unlock(); } } diff --git a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/result/ResultsetRowsStreaming.java b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/result/ResultsetRowsStreaming.java index 9cba95947..46dafcb1f 100644 --- a/src/main/protocol-impl/java/com/mysql/cj/protocol/a/result/ResultsetRowsStreaming.java +++ b/src/main/protocol-impl/java/com/mysql/cj/protocol/a/result/ResultsetRowsStreaming.java @@ -50,6 +50,8 @@ import com.mysql.cj.result.Row; import com.mysql.cj.util.Util; +import java.util.concurrent.locks.ReentrantLock; + /** * Provides streaming of Resultset rows. Each next row is consumed from the * input stream only on {@link #next()} call. Consumed rows are not cached thus @@ -78,6 +80,7 @@ private ProtocolEntityFactory resultSetFactory; private NativeMessageBuilder commandBuilder = null; + private final ReentrantLock lock = new ReentrantLock(); /** * Creates a new RowDataDynamic object. @@ -106,12 +109,13 @@ public ResultsetRowsStreaming(NativeProtocol io, ColumnDefinition columnDefiniti @Override public void close() { - Object mutex = this.owner != null && this.owner.getSyncMutex() != null ? this.owner.getSyncMutex() : this; + ReentrantLock mutex = this.owner != null && this.owner.getSyncMutex() != null ? this.owner.getSyncMutex() : lock; boolean hadMore = false; int howMuchMore = 0; - synchronized (mutex) { + mutex.lock(); + try { // drain the rest of the records. while (next() != null) { hadMore = true; @@ -143,6 +147,8 @@ public void close() { Messages.getString("RowDataDynamic.1", new String[] { String.valueOf(howMuchMore), this.owner.getPointOfOrigin() })); } } + } finally { + mutex.unlock(); } this.metadata = null; diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/CallableStatement.java b/src/main/user-impl/java/com/mysql/cj/jdbc/CallableStatement.java index 6e7bfa37d..01f819a77 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/CallableStatement.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/CallableStatement.java @@ -55,6 +55,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; import com.mysql.cj.Messages; import com.mysql.cj.MysqlType; @@ -506,7 +507,9 @@ protected static CallableStatement getInstance(JdbcConnection conn, CallableStat private int[] placeholderToParameterIndexMap; private void generateParameterMap() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.paramInfo == null) { return; } @@ -557,6 +560,8 @@ private void generateParameterMap() throws SQLException { } } } + } finally { + lock.unlock(); } } @@ -609,7 +614,9 @@ public void addBatch() throws SQLException { private CallableStatementParam checkIsOutputParam(int paramIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.callingStoredFunction) { if (paramIndex == 1) { @@ -649,6 +656,8 @@ private CallableStatementParam checkIsOutputParam(int paramIndex) throws SQLExce this.hasOutputParams = true; return paramDescriptor; + } finally { + lock.unlock(); } } @@ -660,8 +669,12 @@ private CallableStatementParam checkIsOutputParam(int paramIndex) throws SQLExce * if a database access error occurs or this method is called on a closed PreparedStatement */ private void checkParameterIndexBounds(int paramIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.paramInfo.checkBounds(paramIndex); + } finally { + lock.unlock(); } } @@ -682,7 +695,9 @@ private void checkStreamability() throws SQLException { @Override public void clearParameters() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { super.clearParameters(); try { @@ -692,6 +707,8 @@ public void clearParameters() throws SQLException { } finally { this.outputParameterResults = null; } + } finally { + lock.unlock(); } } @@ -706,7 +723,9 @@ public void clearParameters() throws SQLException { * if we can't build the metadata. */ private void fakeParameterTypes(boolean isReallyProcedure) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { String encoding = this.connection.getSession().getServerSession().getCharsetSettings().getMetadataEncoding(); int collationIndex = this.connection.getSession().getServerSession().getCharsetSettings().getMetadataCollationIndex(); Field[] fields = new Field[13]; @@ -760,11 +779,15 @@ private void fakeParameterTypes(boolean isReallyProcedure) throws SQLException { new ResultsetRowsStatic(resultRows, new DefaultColumnDefinition(fields))); convertGetProcedureColumnsToInternalDescriptors(paramTypesRs); + } finally { + lock.unlock(); } } private void determineParameterTypes() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { java.sql.ResultSet paramTypesRs = null; try { @@ -825,18 +848,26 @@ private void determineParameterTypes() throws SQLException { throw sqlExRethrow; } } + } finally { + lock.unlock(); } } private void convertGetProcedureColumnsToInternalDescriptors(java.sql.ResultSet paramTypesRs) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.paramInfo = new CallableStatementParamInfo(paramTypesRs); + } finally { + lock.unlock(); } } @Override public boolean execute() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { boolean returnVal = false; checkStreamability(); @@ -874,12 +905,16 @@ public boolean execute() throws SQLException { // Functions can't return results return false; + } finally { + lock.unlock(); } } @Override public java.sql.ResultSet executeQuery() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { checkStreamability(); @@ -893,6 +928,8 @@ public java.sql.ResultSet executeQuery() throws SQLException { retrieveOutParams(); return execResults; + } finally { + lock.unlock(); } } @@ -949,7 +986,9 @@ private String extractProcedureName() throws SQLException { * if the parameter name is null or empty. */ protected String fixParameterName(String paramNameIn) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (paramNameIn == null) { paramNameIn = "nullpn"; } @@ -960,12 +999,16 @@ protected String fixParameterName(String paramNameIn) throws SQLException { } return mangleParameterName(paramNameIn); + } finally { + lock.unlock(); } } @Override public Array getArray(int i) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(i); Array retValue = rs.getArray(mapOutputParameterIndexToRsIndex(i)); @@ -973,12 +1016,16 @@ public Array getArray(int i) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Array getArray(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= Array retValue = rs.getArray(fixParameterName(parameterName)); @@ -986,12 +1033,16 @@ public Array getArray(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); BigDecimal retValue = rs.getBigDecimal(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -999,13 +1050,17 @@ public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override @Deprecated public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); BigDecimal retValue = rs.getBigDecimal(mapOutputParameterIndexToRsIndex(parameterIndex), scale); @@ -1013,12 +1068,16 @@ public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLExcepti this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public BigDecimal getBigDecimal(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= BigDecimal retValue = rs.getBigDecimal(fixParameterName(parameterName)); @@ -1026,12 +1085,16 @@ public BigDecimal getBigDecimal(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Blob getBlob(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Blob retValue = rs.getBlob(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1039,12 +1102,16 @@ public Blob getBlob(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Blob getBlob(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= Blob retValue = rs.getBlob(fixParameterName(parameterName)); @@ -1052,12 +1119,16 @@ public Blob getBlob(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public boolean getBoolean(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); boolean retValue = rs.getBoolean(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1065,12 +1136,16 @@ public boolean getBoolean(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public boolean getBoolean(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= boolean retValue = rs.getBoolean(fixParameterName(parameterName)); @@ -1078,12 +1153,16 @@ public boolean getBoolean(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public byte getByte(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); byte retValue = rs.getByte(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1091,12 +1170,16 @@ public byte getByte(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public byte getByte(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= byte retValue = rs.getByte(fixParameterName(parameterName)); @@ -1104,12 +1187,16 @@ public byte getByte(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public byte[] getBytes(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); byte[] retValue = rs.getBytes(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1117,12 +1204,16 @@ public byte[] getBytes(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public byte[] getBytes(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= byte[] retValue = rs.getBytes(fixParameterName(parameterName)); @@ -1130,12 +1221,16 @@ public byte[] getBytes(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Clob getClob(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Clob retValue = rs.getClob(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1143,12 +1238,16 @@ public Clob getClob(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Clob getClob(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= Clob retValue = rs.getClob(fixParameterName(parameterName)); @@ -1156,12 +1255,16 @@ public Clob getClob(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Date getDate(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Date retValue = rs.getDate(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1169,12 +1272,16 @@ public Date getDate(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Date getDate(int parameterIndex, Calendar cal) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Date retValue = rs.getDate(mapOutputParameterIndexToRsIndex(parameterIndex), cal); @@ -1182,12 +1289,16 @@ public Date getDate(int parameterIndex, Calendar cal) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Date getDate(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= Date retValue = rs.getDate(fixParameterName(parameterName)); @@ -1195,12 +1306,16 @@ public Date getDate(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Date getDate(String parameterName, Calendar cal) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= Date retValue = rs.getDate(fixParameterName(parameterName), cal); @@ -1208,12 +1323,16 @@ public Date getDate(String parameterName, Calendar cal) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public double getDouble(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); double retValue = rs.getDouble(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1221,12 +1340,16 @@ public double getDouble(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public double getDouble(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= double retValue = rs.getDouble(fixParameterName(parameterName)); @@ -1234,12 +1357,16 @@ public double getDouble(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public float getFloat(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); float retValue = rs.getFloat(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1247,12 +1374,16 @@ public float getFloat(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public float getFloat(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= float retValue = rs.getFloat(fixParameterName(parameterName)); @@ -1260,12 +1391,16 @@ public float getFloat(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public int getInt(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); int retValue = rs.getInt(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1273,12 +1408,16 @@ public int getInt(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public int getInt(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= int retValue = rs.getInt(fixParameterName(parameterName)); @@ -1286,12 +1425,16 @@ public int getInt(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public long getLong(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); long retValue = rs.getLong(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1299,12 +1442,16 @@ public long getLong(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public long getLong(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= long retValue = rs.getLong(fixParameterName(parameterName)); @@ -1312,11 +1459,15 @@ public long getLong(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } protected int getNamedParamIndex(String paramName, boolean forOut) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.noAccessToProcedureBodies) { throw SQLError.createSQLException("No access to parameters by name when connection has been configured not to access procedure bodies", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); @@ -1350,12 +1501,16 @@ protected int getNamedParamIndex(String paramName, boolean forOut) throws SQLExc throw SQLError.createSQLException(Messages.getString("CallableStatement.6", new Object[] { paramName }), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } finally { + lock.unlock(); } } @Override public Object getObject(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { CallableStatementParam paramDescriptor = checkIsOutputParam(parameterIndex); ResultSetInternalMethods rs = getOutputParameters(parameterIndex); @@ -1365,12 +1520,16 @@ public Object getObject(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retVal; + } finally { + lock.unlock(); } } @Override public Object getObject(int parameterIndex, Map> map) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Object retVal = rs.getObject(mapOutputParameterIndexToRsIndex(parameterIndex), map); @@ -1378,12 +1537,16 @@ public Object getObject(int parameterIndex, Map> map) throws SQ this.outputParamWasNull = rs.wasNull(); return retVal; + } finally { + lock.unlock(); } } @Override public Object getObject(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= Object retValue = rs.getObject(fixParameterName(parameterName)); @@ -1391,12 +1554,16 @@ public Object getObject(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Object getObject(String parameterName, Map> map) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= Object retValue = rs.getObject(fixParameterName(parameterName), map); @@ -1404,12 +1571,16 @@ public Object getObject(String parameterName, Map> map) throws this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public T getObject(int parameterIndex, Class type) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); // remove cast once 1.5, 1.6 EOL'd @@ -1418,12 +1589,16 @@ public T getObject(int parameterIndex, Class type) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retVal; + } finally { + lock.unlock(); } } @Override public T getObject(String parameterName, Class type) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= T retValue = ((ResultSetImpl) rs).getObject(fixParameterName(parameterName), type); @@ -1431,6 +1606,8 @@ public T getObject(String parameterName, Class type) throws SQLException this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @@ -1448,7 +1625,9 @@ public T getObject(String parameterName, Class type) throws SQLException * parameters were returned. */ protected ResultSetInternalMethods getOutputParameters(int paramIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.outputParamWasNull = false; if (paramIndex == 1 && this.callingStoredFunction && this.returnValueParam != null) { @@ -1465,23 +1644,31 @@ protected ResultSetInternalMethods getOutputParameters(int paramIndex) throws SQ } return this.outputParameterResults; + } finally { + lock.unlock(); } } @Override public ParameterMetaData getParameterMetaData() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.placeholderToParameterIndexMap == null) { return this.paramInfo; } return new CallableStatementParamInfo(this.paramInfo); + } finally { + lock.unlock(); } } @Override public Ref getRef(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Ref retValue = rs.getRef(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1489,12 +1676,16 @@ public Ref getRef(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Ref getRef(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= Ref retValue = rs.getRef(fixParameterName(parameterName)); @@ -1502,12 +1693,16 @@ public Ref getRef(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public short getShort(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); short retValue = rs.getShort(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1515,12 +1710,16 @@ public short getShort(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public short getShort(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= short retValue = rs.getShort(fixParameterName(parameterName)); @@ -1528,12 +1727,16 @@ public short getShort(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public String getString(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); String retValue = rs.getString(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1541,12 +1744,16 @@ public String getString(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public String getString(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= String retValue = rs.getString(fixParameterName(parameterName)); @@ -1554,12 +1761,16 @@ public String getString(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Time getTime(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Time retValue = rs.getTime(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1567,12 +1778,16 @@ public Time getTime(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Time getTime(int parameterIndex, Calendar cal) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Time retValue = rs.getTime(mapOutputParameterIndexToRsIndex(parameterIndex), cal); @@ -1580,12 +1795,16 @@ public Time getTime(int parameterIndex, Calendar cal) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Time getTime(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= Time retValue = rs.getTime(fixParameterName(parameterName)); @@ -1593,12 +1812,16 @@ public Time getTime(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Time getTime(String parameterName, Calendar cal) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= Time retValue = rs.getTime(fixParameterName(parameterName), cal); @@ -1606,12 +1829,16 @@ public Time getTime(String parameterName, Calendar cal) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Timestamp getTimestamp(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Timestamp retValue = rs.getTimestamp(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1619,12 +1846,16 @@ public Timestamp getTimestamp(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); Timestamp retValue = rs.getTimestamp(mapOutputParameterIndexToRsIndex(parameterIndex), cal); @@ -1632,12 +1863,16 @@ public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLExcept this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Timestamp getTimestamp(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= Timestamp retValue = rs.getTimestamp(fixParameterName(parameterName)); @@ -1645,12 +1880,16 @@ public Timestamp getTimestamp(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= Timestamp retValue = rs.getTimestamp(fixParameterName(parameterName), cal); @@ -1658,12 +1897,16 @@ public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLExce this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public URL getURL(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(parameterIndex); URL retValue = rs.getURL(mapOutputParameterIndexToRsIndex(parameterIndex)); @@ -1671,12 +1914,16 @@ public URL getURL(int parameterIndex) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } @Override public URL getURL(String parameterName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= URL retValue = rs.getURL(fixParameterName(parameterName)); @@ -1684,12 +1931,16 @@ public URL getURL(String parameterName) throws SQLException { this.outputParamWasNull = rs.wasNull(); return retValue; + } finally { + lock.unlock(); } } protected int mapOutputParameterIndexToRsIndex(int paramIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.returnValueParam != null && paramIndex == 1) { return 1; } @@ -1710,6 +1961,8 @@ protected int mapOutputParameterIndexToRsIndex(int paramIndex) throws SQLExcepti } return rsIndex + 1; + } finally { + lock.unlock(); } } @@ -1782,8 +2035,12 @@ public void registerOutParameter(int parameterIndex, SQLType sqlType, String typ @Override public void registerOutParameter(String parameterName, int sqlType) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { registerOutParameter(getNamedParamIndex(parameterName, true), sqlType); + } finally { + lock.unlock(); } } @@ -1831,7 +2088,9 @@ public void registerOutParameter(String parameterName, SQLType sqlType, String t * if an error occurs. */ private void retrieveOutParams() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { int numParameters = this.paramInfo.numberOfParameters(); this.parameterIndexToRsIndex = new int[numParameters]; @@ -1902,6 +2161,8 @@ private void retrieveOutParams() throws SQLException { } else { this.outputParameterResults = null; } + } finally { + lock.unlock(); } } @@ -1961,7 +2222,9 @@ public void setFloat(String parameterName, float x) throws SQLException { } private void setInOutParamsOnServer() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.paramInfo.numParameters > 0) { for (Iterator paramIter = this.paramInfo.iterator(); paramIter.hasNext();) { @@ -1992,6 +2255,8 @@ private void setInOutParamsOnServer() throws SQLException { } } } + } finally { + lock.unlock(); } } @@ -2027,8 +2292,12 @@ public void setObject(String parameterName, Object x, int targetSqlType) throws @Override public void setObject(String parameterName, Object x, SQLType targetSqlType) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { setObject(getNamedParamIndex(parameterName, false), x, targetSqlType); + } finally { + lock.unlock(); } } @@ -2039,13 +2308,19 @@ public void setObject(String parameterName, Object x, int targetSqlType, int sca @Override public void setObject(String parameterName, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { setObject(getNamedParamIndex(parameterName, false), x, targetSqlType, scaleOrLength); + } finally { + lock.unlock(); } } private void setOutParams() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.paramInfo.numParameters > 0) { for (Iterator paramIter = this.paramInfo.iterator(); paramIter.hasNext();) { CallableStatementParam outParamInfo = paramIter.next(); @@ -2084,6 +2359,8 @@ private void setOutParams() throws SQLException { } } } + } finally { + lock.unlock(); } } @@ -2124,8 +2401,12 @@ public void setURL(String parameterName, URL val) throws SQLException { @Override public boolean wasNull() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.outputParamWasNull; + } finally { + lock.unlock(); } } @@ -2236,7 +2517,9 @@ public void setNCharacterStream(String parameterName, Reader value, long length) * if a database access error occurs or this method is called on a closed PreparedStatement */ private boolean checkReadOnlyProcedure() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.noAccessToProcedureBodies) { return false; } @@ -2293,6 +2576,8 @@ private boolean checkReadOnlyProcedure() throws SQLException { } this.paramInfo.isReadOnlySafeChecked = false; this.paramInfo.isReadOnlySafeProcedure = false; + } finally { + lock.unlock(); } return false; @@ -2495,7 +2780,9 @@ protected byte[] s2b(String s) { @Override public long executeLargeUpdate() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { long returnVal = -1; checkStreamability(); @@ -2514,6 +2801,8 @@ public long executeLargeUpdate() throws SQLException { retrieveOutParams(); return returnVal; + } finally { + lock.unlock(); } } diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ClientPreparedStatement.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ClientPreparedStatement.java index 5c40b54ad..b328a1d59 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ClientPreparedStatement.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ClientPreparedStatement.java @@ -50,6 +50,7 @@ import java.sql.Wrapper; import java.util.ArrayList; import java.util.Calendar; +import java.util.concurrent.locks.ReentrantLock; import com.mysql.cj.BindValue; import com.mysql.cj.CancelQueryTask; @@ -244,37 +245,53 @@ public String toString() { @Override public void addBatch() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { QueryBindings queryBindings = ((PreparedQuery) this.query).getQueryBindings(); queryBindings.checkAllParametersSet(); this.query.addBatch(queryBindings.clone()); + } finally { + lock.unlock(); } } @Override public void addBatch(String sql) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.batchHasPlainStatements = true; super.addBatch(sql); + } finally { + lock.unlock(); } } @Override public void clearBatch() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.batchHasPlainStatements = false; super.clearBatch(); + } finally { + lock.unlock(); } } @Override public void clearParameters() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { for (BindValue bv : ((PreparedQuery) this.query).getQueryBindings().getBindValues()) { bv.reset(); } + } finally { + lock.unlock(); } } @@ -286,15 +303,21 @@ public void clearParameters() throws SQLException { * if a database access error occurs or this method is called on a closed PreparedStatement */ protected boolean checkReadOnlySafeStatement() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return QueryInfo.isReadOnlySafeQuery(((PreparedQuery) this.query).getOriginalSql(), this.session.getServerSession().isNoBackslashEscapesSet()) || !this.connection.isReadOnly(); + } finally { + lock.unlock(); } } @Override public boolean execute() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { JdbcConnection locallyScopedConn = this.connection; @@ -376,12 +399,16 @@ public boolean execute() throws SQLException { } return ((rs != null) && rs.hasRows()); + } finally { + lock.unlock(); } } @Override protected long[] executeBatchInternal() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.connection.isReadOnly()) { throw new SQLException(Messages.getString("PreparedStatement.25") + Messages.getString("PreparedStatement.26"), @@ -421,6 +448,8 @@ protected long[] executeBatchInternal() throws SQLException { clearBatch(); } + } finally { + lock.unlock(); } } @@ -435,7 +464,9 @@ protected long[] executeBatchInternal() throws SQLException { * if a database access error occurs or this method is called on a closed PreparedStatement */ protected long[] executePreparedBatchAsMultiStatement(int batchTimeout) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { // This is kind of an abuse, but it gets the job done if (this.batchedValuesClause == null) { this.batchedValuesClause = ((PreparedQuery) this.query).getOriginalSql() + ";"; @@ -581,6 +612,8 @@ protected long[] executePreparedBatchAsMultiStatement(int batchTimeout) throws S clearBatch(); } + } finally { + lock.unlock(); } } @@ -595,7 +628,9 @@ protected int setOneBatchedParameterSet(java.sql.PreparedStatement batchedStatem } private String generateMultiStatementForBatch(int numBatches) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { String origSql = ((PreparedQuery) this.query).getOriginalSql(); StringBuilder newStatementSql = new StringBuilder((origSql.length() + 1) * numBatches); @@ -607,6 +642,8 @@ private String generateMultiStatementForBatch(int numBatches) throws SQLExceptio } return newStatementSql.toString(); + } finally { + lock.unlock(); } } @@ -621,7 +658,9 @@ private String generateMultiStatementForBatch(int numBatches) throws SQLExceptio * if a database access error occurs or this method is called on a closed PreparedStatement */ protected long[] executeBatchWithMultiValuesClause(int batchTimeout) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { JdbcConnection locallyScopedConn = this.connection; int numBatchedArgs = this.query.getBatchedArgs().size(); @@ -736,6 +775,8 @@ protected long[] executeBatchWithMultiValuesClause(int batchTimeout) throws SQLE stopQueryTimer(timeoutTask, false, false); resetCancelledState(); } + } finally { + lock.unlock(); } } @@ -750,7 +791,9 @@ protected long[] executeBatchWithMultiValuesClause(int batchTimeout) throws SQLE */ protected long[] executeBatchSerially(int batchTimeout) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.connection == null) { checkClosed(); } @@ -840,6 +883,8 @@ protected long[] executeBatchSerially(int batchTimeout) throws SQLException { } return (updateCounts != null) ? updateCounts : new long[0]; + } finally { + lock.unlock(); } } @@ -870,8 +915,10 @@ protected long[] executeBatchSerially(int batchTimeout) throws SQLException { * if an error occurs. */ protected ResultSetInternalMethods executeInternal(int maxRowsToRetrieve, M sendPacket, boolean createStreamingResultSet, - boolean queryIsSelectOnly, ColumnDefinition metadata, boolean isBatch) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + boolean queryIsSelectOnly, ColumnDefinition metadata, boolean isBatch) throws SQLException { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { try { JdbcConnection locallyScopedConnection = this.connection; @@ -913,12 +960,16 @@ protected ResultSetInternalMethods executeInternal(int maxRo throw npe; } + } finally { + lock.unlock(); } } @Override public java.sql.ResultSet executeQuery() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { JdbcConnection locallyScopedConn = this.connection; @@ -986,6 +1037,8 @@ public java.sql.ResultSet executeQuery() throws SQLException { this.lastInsertId = this.results.getUpdateID(); return this.results; + } finally { + lock.unlock(); } } @@ -1000,13 +1053,17 @@ public int executeUpdate() throws SQLException { * keys we need to gather for the batch. */ protected long executeUpdateInternal(boolean clearBatchedGeneratedKeysAndWarnings, boolean isBatch) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (clearBatchedGeneratedKeysAndWarnings) { clearWarnings(); this.batchedGeneratedKeys = null; } return executeUpdateInternal(((PreparedQuery) this.query).getQueryBindings(), isBatch); + } finally { + lock.unlock(); } } @@ -1025,7 +1082,9 @@ protected long executeUpdateInternal(boolean clearBatchedGeneratedKeysAndWarning */ protected long executeUpdateInternal(QueryBindings bindings, boolean isReallyBatch) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { JdbcConnection locallyScopedConn = this.connection; @@ -1081,6 +1140,8 @@ protected long executeUpdateInternal(QueryBindings bindings, boolean isReallyBat this.lastInsertId = rs.getUpdateID(); return this.updateCount; + } finally { + lock.unlock(); } } @@ -1100,7 +1161,9 @@ protected boolean containsOnDuplicateKeyUpdate() { * if a database access error occurs or this method is called on a closed PreparedStatement */ protected ClientPreparedStatement prepareBatchedInsertSQL(JdbcConnection localConn, int numBatches) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ClientPreparedStatement pstmt = new ClientPreparedStatement(localConn, "Rewritten batch of: " + ((PreparedQuery) this.query).getOriginalSql(), this.getCurrentDatabase(), getQueryInfo().getQueryInfoForBatch(numBatches)); pstmt.setRetrieveGeneratedKeys(this.retrieveGeneratedKeys); @@ -1109,26 +1172,38 @@ protected ClientPreparedStatement prepareBatchedInsertSQL(JdbcConnection localCo getQueryAttributesBindings().runThroughAll(a -> ((JdbcStatement) pstmt).setAttribute(a.getName(), a.getValue())); return pstmt; + } finally { + lock.unlock(); } } protected void setRetrieveGeneratedKeys(boolean flag) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.retrieveGeneratedKeys = flag; + } finally { + lock.unlock(); } } @Override public byte[] getBytesRepresentation(int parameterIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return ((PreparedQuery) this.query).getQueryBindings().getBytesRepresentation(getCoreParameterIndex(parameterIndex)); + } finally { + lock.unlock(); } } @Override public java.sql.ResultSetMetaData getMetaData() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { // // We could just tack on a LIMIT 0 here no matter what the statement, and check if a result set was returned or not, but I'm not comfortable with // that, myself, so we take the "safer" road, and only allow metadata for _actual_ SELECTS (but not SHOWs). @@ -1197,6 +1272,8 @@ public java.sql.ResultSetMetaData getMetaData() throws SQLException { } return this.pstmtResultMetaData; + } finally { + lock.unlock(); } } @@ -1224,7 +1301,9 @@ private boolean isNonResultSetProducingQuery() { @Override public ParameterMetaData getParameterMetaData() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.parameterMetaData == null) { if (this.session.getPropertySet().getBooleanProperty(PropertyKey.generateSimpleParameterMetadata).getValue()) { this.parameterMetaData = new MysqlParameterMetadata(((PreparedQuery) this.query).getParameterCount()); @@ -1235,6 +1314,8 @@ public ParameterMetaData getParameterMetaData() throws SQLException { } return this.parameterMetaData; + } finally { + lock.unlock(); } } @@ -1244,19 +1325,27 @@ public QueryInfo getQueryInfo() { } private void initializeFromQueryInfo() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { int parameterCount = getQueryInfo().getStaticSqlParts().length - 1; ((PreparedQuery) this.query).setParameterCount(parameterCount); ((PreparedQuery) this.query).setQueryBindings(new NativeQueryBindings(parameterCount, this.session, NativeQueryBindValue::new)); clearParameters(); + } finally { + lock.unlock(); } } @Override public boolean isNull(int paramIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return ((PreparedQuery) this.query).getQueryBindings().getBindValues()[getCoreParameterIndex(paramIndex)].isNull(); + } finally { + lock.unlock(); } } @@ -1268,7 +1357,9 @@ public void realClose(boolean calledExplicitly, boolean closeOpenResults) throws return; // already closed } - synchronized (locallyScopedConn.getConnectionMutex()) { + ReentrantLock lock = locallyScopedConn.getConnectionMutex(); + lock.lock(); + try { // additional check in case Statement was closed // while current thread was waiting for lock @@ -1288,17 +1379,23 @@ public void realClose(boolean calledExplicitly, boolean closeOpenResults) throws ((PreparedQuery) this.query).setOriginalSql(null); ((PreparedQuery) this.query).setQueryBindings(null); + } finally { + lock.unlock(); } } @Override public String getPreparedSql() { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.rewrittenBatchSize == 0) { return ((PreparedQuery) this.query).getOriginalSql(); } return getQueryInfo().getSqlForBatch(); + } finally { + lock.unlock(); } } @@ -1321,8 +1418,12 @@ public long executeLargeUpdate() throws SQLException { } public ParameterBindings getParameterBindings() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return new ParameterBindingsImpl((PreparedQuery) this.query, this.session, this.resultSetFactory); + } finally { + lock.unlock(); } } @@ -1337,7 +1438,9 @@ protected int getParameterIndexOffset() { } protected void checkBounds(int paramIndex, int parameterIndexOffset) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if ((paramIndex < 1)) { throw SQLError.createSQLException(Messages.getString("PreparedStatement.49") + paramIndex + Messages.getString("PreparedStatement.50"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); @@ -1350,6 +1453,8 @@ protected void checkBounds(int paramIndex, int parameterIndexOffset) throws SQLE throw SQLError.createSQLException(Messages.getString("PreparedStatement.63"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); } + } finally { + lock.unlock(); } } @@ -1366,162 +1471,254 @@ public void setArray(int i, Array x) throws SQLException { @Override public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setAsciiStream(getCoreParameterIndex(parameterIndex), x, -1); + } finally { + lock.unlock(); } } @Override public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setAsciiStream(getCoreParameterIndex(parameterIndex), x, length); + } finally { + lock.unlock(); } } @Override public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setAsciiStream(getCoreParameterIndex(parameterIndex), x, (int) length); + } finally { + lock.unlock(); } } @Override public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setBigDecimal(getCoreParameterIndex(parameterIndex), x); + } finally { + lock.unlock(); } } @Override public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setBinaryStream(getCoreParameterIndex(parameterIndex), x, -1); + } finally { + lock.unlock(); } } @Override public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setBinaryStream(getCoreParameterIndex(parameterIndex), x, length); + } finally { + lock.unlock(); } } @Override public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setBinaryStream(getCoreParameterIndex(parameterIndex), x, (int) length); + } finally { + lock.unlock(); } } @Override public void setBlob(int i, java.sql.Blob x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setBlob(getCoreParameterIndex(i), x); + } finally { + lock.unlock(); } } @Override public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setBinaryStream(getCoreParameterIndex(parameterIndex), inputStream, -1); + } finally { + lock.unlock(); } } @Override public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setBinaryStream(getCoreParameterIndex(parameterIndex), inputStream, (int) length); + } finally { + lock.unlock(); } } @Override public void setBoolean(int parameterIndex, boolean x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setBoolean(getCoreParameterIndex(parameterIndex), x); + } finally { + lock.unlock(); } } @Override public void setByte(int parameterIndex, byte x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setByte(getCoreParameterIndex(parameterIndex), x); + } finally { + lock.unlock(); } } @Override public void setBytes(int parameterIndex, byte[] x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setBytes(getCoreParameterIndex(parameterIndex), x, true); + } finally { + lock.unlock(); } } @Override public void setBytes(int parameterIndex, byte[] x, boolean escapeIfNeeded) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setBytes(getCoreParameterIndex(parameterIndex), x, escapeIfNeeded); + } finally { + lock.unlock(); } } @Override public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setCharacterStream(getCoreParameterIndex(parameterIndex), reader, -1); + } finally { + lock.unlock(); } } @Override public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setCharacterStream(getCoreParameterIndex(parameterIndex), reader, length); + } finally { + lock.unlock(); } } @Override public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setCharacterStream(getCoreParameterIndex(parameterIndex), reader, (int) length); + } finally { + lock.unlock(); } } @Override public void setClob(int parameterIndex, Reader reader) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setCharacterStream(getCoreParameterIndex(parameterIndex), reader, -1); + } finally { + lock.unlock(); } } @Override public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setCharacterStream(getCoreParameterIndex(parameterIndex), reader, (int) length); + } finally { + lock.unlock(); } } @Override public void setClob(int i, Clob x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setClob(getCoreParameterIndex(i), x); + } finally { + lock.unlock(); } } @Override public void setDate(int parameterIndex, Date x) throws java.sql.SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setDate(getCoreParameterIndex(parameterIndex), x, null); + } finally { + lock.unlock(); } } @Override public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setDate(getCoreParameterIndex(parameterIndex), x, cal); + } finally { + lock.unlock(); } } @Override public void setDouble(int parameterIndex, double x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setDouble(getCoreParameterIndex(parameterIndex), x); + } finally { + lock.unlock(); } } @@ -1532,57 +1729,89 @@ public void setFloat(int parameterIndex, float x) throws SQLException { @Override public void setInt(int parameterIndex, int x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setInt(getCoreParameterIndex(parameterIndex), x); + } finally { + lock.unlock(); } } @Override public void setLong(int parameterIndex, long x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setLong(getCoreParameterIndex(parameterIndex), x); + } finally { + lock.unlock(); } } @Override public void setBigInteger(int parameterIndex, BigInteger x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setBigInteger(getCoreParameterIndex(parameterIndex), x); + } finally { + lock.unlock(); } } @Override public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setNCharacterStream(getCoreParameterIndex(parameterIndex), value, -1); + } finally { + lock.unlock(); } } @Override public void setNCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setNCharacterStream(getCoreParameterIndex(parameterIndex), reader, length); + } finally { + lock.unlock(); } } @Override public void setNClob(int parameterIndex, Reader reader) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setNCharacterStream(getCoreParameterIndex(parameterIndex), reader, -1); + } finally { + lock.unlock(); } } @Override public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setNCharacterStream(getCoreParameterIndex(parameterIndex), reader, length); + } finally { + lock.unlock(); } } @Override public void setNClob(int parameterIndex, NClob value) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setNClob(getCoreParameterIndex(parameterIndex), value); + } finally { + lock.unlock(); } } @@ -1602,22 +1831,34 @@ public void setNClob(int parameterIndex, NClob value) throws SQLException { */ @Override public void setNString(int parameterIndex, String x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setNString(getCoreParameterIndex(parameterIndex), x); + } finally { + lock.unlock(); } } @Override public void setNull(int parameterIndex, int sqlType) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setNull(getCoreParameterIndex(parameterIndex)); // MySQL ignores sqlType + } finally { + lock.unlock(); } } @Override public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setNull(getCoreParameterIndex(parameterIndex)); + } finally { + lock.unlock(); } } @@ -1628,14 +1869,20 @@ public void setNull(int parameterIndex, MysqlType mysqlType) throws SQLException @Override public void setObject(int parameterIndex, Object parameterObj) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setObject(getCoreParameterIndex(parameterIndex), parameterObj); + } finally { + lock.unlock(); } } @Override public void setObject(int parameterIndex, Object parameterObj, int targetSqlType) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { try { ((PreparedQuery) this.query).getQueryBindings().setObject(getCoreParameterIndex(parameterIndex), parameterObj, MysqlType.getByJdbcType(targetSqlType), -1); @@ -1643,23 +1890,31 @@ public void setObject(int parameterIndex, Object parameterObj, int targetSqlType throw SQLError.createSQLFeatureNotSupportedException(Messages.getString("Statement.UnsupportedSQLType") + JDBCType.valueOf(targetSqlType), MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, this.exceptionInterceptor); } + } finally { + lock.unlock(); } } @Override public void setObject(int parameterIndex, Object parameterObj, SQLType targetSqlType) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (targetSqlType instanceof MysqlType) { ((PreparedQuery) this.query).getQueryBindings().setObject(getCoreParameterIndex(parameterIndex), parameterObj, (MysqlType) targetSqlType, -1); } else { setObject(parameterIndex, parameterObj, targetSqlType.getVendorTypeNumber()); } + } finally { + lock.unlock(); } } @Override public void setObject(int parameterIndex, Object parameterObj, int targetSqlType, int scale) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { try { ((PreparedQuery) this.query).getQueryBindings().setObject(getCoreParameterIndex(parameterIndex), parameterObj, MysqlType.getByJdbcType(targetSqlType), scale); @@ -1667,17 +1922,23 @@ public void setObject(int parameterIndex, Object parameterObj, int targetSqlType throw SQLError.createSQLFeatureNotSupportedException(Messages.getString("Statement.UnsupportedSQLType") + JDBCType.valueOf(targetSqlType), MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, this.exceptionInterceptor); } + } finally { + lock.unlock(); } } @Override public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (targetSqlType instanceof MysqlType) { ((PreparedQuery) this.query).getQueryBindings().setObject(getCoreParameterIndex(parameterIndex), x, (MysqlType) targetSqlType, scaleOrLength); } else { setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber(), scaleOrLength); } + } finally { + lock.unlock(); } } @@ -1693,8 +1954,12 @@ public void setRowId(int parameterIndex, RowId x) throws SQLException { @Override public void setShort(int parameterIndex, short x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setShort(getCoreParameterIndex(parameterIndex), x); + } finally { + lock.unlock(); } } @@ -1710,36 +1975,56 @@ public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException @Override public void setString(int parameterIndex, String x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setString(getCoreParameterIndex(parameterIndex), x); + } finally { + lock.unlock(); } } @Override public void setTime(int parameterIndex, Time x) throws java.sql.SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setTime(getCoreParameterIndex(parameterIndex), x, null); + } finally { + lock.unlock(); } } @Override public void setTime(int parameterIndex, java.sql.Time x, Calendar cal) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setTime(getCoreParameterIndex(parameterIndex), x, cal); + } finally { + lock.unlock(); } } @Override public void setTimestamp(int parameterIndex, Timestamp x) throws java.sql.SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setTimestamp(getCoreParameterIndex(parameterIndex), x, null, null, MysqlType.TIMESTAMP); + } finally { + lock.unlock(); } } @Override public void setTimestamp(int parameterIndex, java.sql.Timestamp x, Calendar cal) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setTimestamp(getCoreParameterIndex(parameterIndex), x, cal, null, MysqlType.TIMESTAMP); + } finally { + lock.unlock(); } } diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionImpl.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionImpl.java index 2ac7357fb..bf18fc3b5 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionImpl.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionImpl.java @@ -54,6 +54,7 @@ import java.util.Stack; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; +import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import com.mysql.cj.CacheAdapter; @@ -117,9 +118,12 @@ public String getHost() { return this.session.getHostInfo().getHost(); } + private final ReentrantLock connectionLock = new ReentrantLock(); private JdbcConnection parentProxy = null; private JdbcConnection topProxy = null; + private final ReentrantLock topProxyLock = new ReentrantLock(); private InvocationHandler realProxy = null; + private final ReentrantLock realProxyLock = new ReentrantLock(); @Override public boolean isProxySet() { @@ -141,6 +145,10 @@ private JdbcConnection getProxy() { return (this.topProxy != null) ? this.topProxy : (JdbcConnection) this; } + private ReentrantLock getProxyLock() { + return (this.topProxy != null) ? this.topProxyLock : connectionLock; + } + @Override public JdbcConnection getMultiHostSafeProxy() { return this.getProxy(); @@ -157,8 +165,8 @@ public JdbcConnection getActiveMySQLConnection() { } @Override - public Object getConnectionMutex() { - return (this.realProxy != null) ? this.realProxy : getProxy(); + public ReentrantLock getConnectionMutex() { + return (this.realProxy != null) ? this.realProxyLock : getProxyLock(); } /** @@ -296,6 +304,7 @@ private static boolean nullSafeCompare(String s1, String s2) { private final CopyOnWriteArrayList openStatements = new CopyOnWriteArrayList<>(); private LRUCache parsedCallableStatementCache; + private ReentrantLock parsedCallableStatementCacheLock = new ReentrantLock(); /** The password we used */ private String password = null; @@ -308,6 +317,7 @@ private static boolean nullSafeCompare(String s1, String s2) { /** Cache of ResultSet metadata */ protected LRUCache resultSetMetadataCache; + protected final ReentrantLock resultSetMetadataCacheLock = new ReentrantLock(); /** * The type map for UDTs (not implemented, but used by some third-party @@ -319,7 +329,9 @@ private static boolean nullSafeCompare(String s1, String s2) { private String user = null; private LRUCache serverSideStatementCheckCache; + private final ReentrantLock serverSideStatementCheckCacheLock = new ReentrantLock(); private LRUCache serverSideStatementCache; + private final ReentrantLock serverSideStatementCacheLock = new ReentrantLock(); private HostInfo origHostInfo; @@ -508,7 +520,8 @@ private boolean canHandleAsServerPreparedStatement(String sql) throws SQLExcepti boolean allowMultiQueries = this.propertySet.getBooleanProperty(PropertyKey.allowMultiQueries).getValue(); if (this.cachePrepStmts.getValue()) { - synchronized (this.serverSideStatementCheckCache) { + serverSideStatementCheckCacheLock.lock(); + try { Boolean flag = this.serverSideStatementCheckCache.get(sql); if (flag != null) { @@ -523,6 +536,8 @@ private boolean canHandleAsServerPreparedStatement(String sql) throws SQLExcepti } return canHandle; + } finally { + serverSideStatementCheckCacheLock.unlock(); } } @@ -532,7 +547,9 @@ private boolean canHandleAsServerPreparedStatement(String sql) throws SQLExcepti @Override public void changeUser(String userName, String newPassword) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { checkClosed(); if ((userName == null) || userName.equals("")) { @@ -560,6 +577,8 @@ public void changeUser(String userName, String newPassword) throws SQLException this.session.setSessionVariables(); setupServerForTruncationChecks(); + } finally { + lock.unlock(); } } @@ -703,7 +722,9 @@ public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultS @Override public void close() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { if (this.connectionLifecycleInterceptors != null) { for (ConnectionLifecycleInterceptor cli : this.connectionLifecycleInterceptors) { cli.close(); @@ -711,6 +732,8 @@ public void close() throws SQLException { } realClose(true, true, false, null); + } finally { + lock.unlock(); } } @@ -759,7 +782,9 @@ private void closeStatement(java.sql.Statement stmt) { @Override public void commit() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { checkClosed(); try { @@ -802,13 +827,17 @@ void forEach(ConnectionLifecycleInterceptor each) throws SQLException { } finally { this.session.setNeedsPing(this.reconnectAtTxEnd.getValue()); } + } finally { + lock.unlock(); } return; } @Override public void createNewIO(boolean isForReconnect) { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { // Synchronization Not needed for *new* connections, but definitely for connections going through fail-over, since we might get the new connection // up and running *enough* to start sending cached or still-open server-side prepared statements over to the backend before we get a chance to // re-prepare them... @@ -824,6 +853,8 @@ public void createNewIO(boolean isForReconnect) { } catch (SQLException ex) { throw ExceptionFactory.createException(UnableToConnectException.class, ex.getMessage(), ex); } + } finally { + lock.unlock(); } } @@ -847,7 +878,9 @@ private void connectWithRetries(boolean isForReconnect) throws SQLException { boolean oldReadOnly; String oldDb; - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { // save state from old connection oldAutoCommit = getAutoCommit(); oldIsolationLevel = this.isolationLevel; @@ -855,6 +888,8 @@ private void connectWithRetries(boolean isForReconnect) throws SQLException { oldDb = getDatabase(); this.session.setQueryInterceptors(this.queryInterceptors); + } finally { + lock.unlock(); } // Server properties might be different from previous connection, so initialize again... @@ -1015,7 +1050,9 @@ private int getLoginTimeout() { } private void createPreparedStatementCaches() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { int cacheSize = this.propertySet.getIntegerProperty(PropertyKey.prepStmtCacheSize).getValue(); String queryInfoCacheFactory = this.propertySet.getStringProperty(PropertyKey.queryInfoCacheFactory).getValue(); @@ -1075,6 +1112,8 @@ protected boolean removeEldestEntry(java.util.Map.EntrygetEnumProperty(PropertyKey.databaseTerm).getValue() == DatabaseTerm.SCHEMA ? null : this.database; + } finally { + lock.unlock(); } } @Override public String getCharacterSetMetadata() { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { return this.session.getServerSession().getCharsetSettings().getMetadataEncoding(); + } finally { + lock.unlock(); } } @@ -1151,8 +1202,12 @@ public long getId() { */ @Override public long getIdleFor() { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { return this.session.getIdleFor(); + } finally { + lock.unlock(); } } @@ -1204,7 +1259,9 @@ public ServerVersion getServerVersion() { @Override public int getTransactionIsolation() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { if (!this.useLocalSessionState.getValue()) { String s = this.session.queryServerVariable( versionMeetsMinimum(8, 0, 3) || (versionMeetsMinimum(5, 7, 20) && !versionMeetsMinimum(8, 0, 0)) ? "@@session.transaction_isolation" @@ -1223,17 +1280,23 @@ public int getTransactionIsolation() throws SQLException { } return this.isolationLevel; + } finally { + lock.unlock(); } } @Override public java.util.Map> getTypeMap() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { if (this.typeMap == null) { this.typeMap = new HashMap<>(); } return this.typeMap; + } finally { + lock.unlock(); } } @@ -1404,7 +1467,9 @@ public boolean isReadOnly(boolean useSessionStatus) throws SQLException { @Override public boolean isSameResource(JdbcConnection otherConnection) { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { if (otherConnection == null) { return false; } @@ -1445,6 +1510,8 @@ public boolean isSameResource(JdbcConnection otherConnection) { } return false; + } finally { + lock.unlock(); } } @@ -1520,7 +1587,8 @@ public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int cStmt = parseCallableStatement(sql); } else { - synchronized (this.parsedCallableStatementCache) { + this.parsedCallableStatementCacheLock.lock(); + try { CompoundCacheKey key = new CompoundCacheKey(getDatabase(), sql); CallableStatement.CallableStatementParamInfo cachedParamInfo = this.parsedCallableStatementCache.get(key); @@ -1536,6 +1604,8 @@ public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int this.parsedCallableStatementCache.put(key, cachedParamInfo); } + } finally { + this.parsedCallableStatementCacheLock.unlock(); } } @@ -1574,7 +1644,9 @@ public java.sql.PreparedStatement prepareStatement(String sql, int autoGenKeyInd @Override public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { checkClosed(); // @@ -1592,7 +1664,8 @@ public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType if (this.useServerPrepStmts.getValue() && canServerPrepare) { if (this.cachePrepStmts.getValue()) { - synchronized (this.serverSideStatementCache) { + this.serverSideStatementCacheLock.lock(); + try { pStmt = this.serverSideStatementCache.remove(new CompoundCacheKey(this.database, sql)); if (pStmt != null) { @@ -1623,6 +1696,8 @@ public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType } } } + } finally { + this.serverSideStatementCacheLock.unlock(); } } else { try { @@ -1644,6 +1719,8 @@ public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType } return pStmt; + } finally { + lock.unlock(); } } @@ -1748,9 +1825,12 @@ public void realClose(boolean calledExplicitly, boolean issueRollback, boolean s @Override public void recachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { if (this.cachePrepStmts.getValue() && pstmt.isPoolable()) { - synchronized (this.serverSideStatementCache) { + this.serverSideStatementCacheLock.lock(); + try { Object oldServerPrepStmt = this.serverSideStatementCache.put( new CompoundCacheKey(pstmt.getCurrentDatabase(), ((PreparedQuery) pstmt.getQuery()).getOriginalSql()), (ServerPreparedStatement) pstmt); @@ -1759,19 +1839,30 @@ public void recachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLExce ((ServerPreparedStatement) oldServerPrepStmt).setClosed(false); ((ServerPreparedStatement) oldServerPrepStmt).realClose(true, true); } + } finally { + this.serverSideStatementCacheLock.unlock(); } } + } finally { + lock.unlock(); } } @Override public void decachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { if (this.cachePrepStmts.getValue()) { - synchronized (this.serverSideStatementCache) { + this.serverSideStatementCacheLock.lock(); + try { this.serverSideStatementCache.remove(new CompoundCacheKey(pstmt.getCurrentDatabase(), ((PreparedQuery) pstmt.getQuery()).getOriginalSql())); + } finally { + this.serverSideStatementCacheLock.unlock(); } } + } finally { + lock.unlock(); } } @@ -1794,7 +1885,9 @@ public void resetServerState() throws SQLException { @Override public void rollback() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { checkClosed(); try { @@ -1840,13 +1933,17 @@ void forEach(ConnectionLifecycleInterceptor each) throws SQLException { } finally { this.session.setNeedsPing(this.reconnectAtTxEnd.getValue()); } + } finally { + lock.unlock(); } } @Override public void rollback(final Savepoint savepoint) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { checkClosed(); try { @@ -1890,7 +1987,7 @@ void forEach(ConnectionLifecycleInterceptor each) throws SQLException { int indexOfError153 = msg.indexOf("153"); if (indexOfError153 != -1) { - throw SQLError.createSQLException(Messages.getString("Connection.22", new Object[] { savepoint.getSavepointName() }), + throw SQLError.createSQLException(Messages.getString("Connection.22", new Object[]{savepoint.getSavepointName()}), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, errno, getExceptionInterceptor()); } } @@ -1913,11 +2010,15 @@ void forEach(ConnectionLifecycleInterceptor each) throws SQLException { } finally { this.session.setNeedsPing(this.reconnectAtTxEnd.getValue()); } + } finally { + lock.unlock(); } } private void rollbackNoChecks() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { if (this.useLocalTransactionState.getValue()) { if (!this.session.getServerSession().inTransactionOnServer()) { return; // effectively a no-op @@ -1926,6 +2027,8 @@ private void rollbackNoChecks() throws SQLException { this.session.execSQL(null, "rollback", -1, null, false, this.nullStatementResultSetFactory, null, false); + } finally { + lock.unlock(); } } @@ -1989,7 +2092,9 @@ public java.sql.PreparedStatement serverPrepareStatement(String sql, String[] au @Override public void setAutoCommit(final boolean autoCommitFlag) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { checkClosed(); if (this.connectionLifecycleInterceptors != null) { @@ -2047,6 +2152,8 @@ void forEach(ConnectionLifecycleInterceptor each) throws SQLException { } return; + } finally { + lock.unlock(); } } @@ -2058,7 +2165,9 @@ public void setCatalog(final String catalog) throws SQLException { } public void setDatabase(final String db) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { checkClosed(); if (db == null) { @@ -2108,13 +2217,19 @@ void forEach(ConnectionLifecycleInterceptor each) throws SQLException { this.session.execSQL(null, query.toString(), -1, null, false, this.nullStatementResultSetFactory, null, false); this.database = db; + } finally { + lock.unlock(); } } @Override public String getDatabase() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { return this.database; + } finally { + lock.unlock(); } } @@ -2141,7 +2256,9 @@ public void setReadOnly(boolean readOnlyFlag) throws SQLException { @Override public void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { // note this this is safe even inside a transaction if (this.readOnlyPropagatesToServer.getValue() && versionMeetsMinimum(5, 6, 5)) { if (!this.useLocalSessionState.getValue() || (readOnlyFlag != this.readOnly)) { @@ -2151,6 +2268,8 @@ public void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException { } this.readOnly = readOnlyFlag; + } finally { + lock.unlock(); } } @@ -2165,7 +2284,9 @@ public java.sql.Savepoint setSavepoint() throws SQLException { private void setSavepoint(MysqlSavepoint savepoint) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { checkClosed(); StringBuilder savePointQuery = new StringBuilder("SAVEPOINT "); @@ -2182,23 +2303,31 @@ private void setSavepoint(MysqlSavepoint savepoint) throws SQLException { } finally { closeStatement(stmt); } + } finally { + lock.unlock(); } } @Override public java.sql.Savepoint setSavepoint(String name) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { MysqlSavepoint savepoint = new MysqlSavepoint(name, getExceptionInterceptor()); setSavepoint(savepoint); return savepoint; + } finally { + lock.unlock(); } } @Override public void setTransactionIsolation(int level) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { checkClosed(); String sql = null; @@ -2243,7 +2372,7 @@ public void setTransactionIsolation(int level) throws SQLException { break; default: - throw SQLError.createSQLException(Messages.getString("Connection.25", new Object[] { level }), + throw SQLError.createSQLException(Messages.getString("Connection.25", new Object[]{level}), MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor()); } @@ -2251,18 +2380,26 @@ public void setTransactionIsolation(int level) throws SQLException { this.isolationLevel = level; } + } finally { + lock.unlock(); } } @Override public void setTypeMap(java.util.Map> map) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { this.typeMap = map; + } finally { + lock.unlock(); } } private void setupServerForTruncationChecks() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { RuntimeProperty jdbcCompliantTruncation = this.propertySet.getProperty(PropertyKey.jdbcCompliantTruncation); if (jdbcCompliantTruncation.getValue()) { String currentSqlMode = this.session.getServerSession().getServerVariable("sql_mode"); @@ -2287,6 +2424,8 @@ private void setupServerForTruncationChecks() throws SQLException { jdbcCompliantTruncation.setValue(false); // server's handling this for us now } } + } finally { + lock.unlock(); } } @@ -2316,8 +2455,11 @@ public boolean versionMeetsMinimum(int major, int minor, int subminor) { @Override public CachedResultSetMetaData getCachedMetaData(String sql) { if (this.resultSetMetadataCache != null) { - synchronized (this.resultSetMetadataCache) { + this.resultSetMetadataCacheLock.lock(); + try { return this.resultSetMetadataCache.get(sql); + } finally { + this.resultSetMetadataCacheLock.unlock(); } } @@ -2365,19 +2507,27 @@ public void setStatementComment(String comment) { @Override public void transactionBegun() { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { if (this.connectionLifecycleInterceptors != null) { this.connectionLifecycleInterceptors.stream().forEach(ConnectionLifecycleInterceptor::transactionBegun); } + } finally { + lock.unlock(); } } @Override public void transactionCompleted() { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { if (this.connectionLifecycleInterceptors != null) { this.connectionLifecycleInterceptors.stream().forEach(ConnectionLifecycleInterceptor::transactionCompleted); } + } finally { + lock.unlock(); } } @@ -2395,32 +2545,44 @@ public ExceptionInterceptor getExceptionInterceptor() { @Override public boolean isServerLocal() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { try { return this.session.isServerLocal(this.getSession()); } catch (CJException ex) { SQLException sqlEx = SQLExceptionsMapping.translateException(ex, getExceptionInterceptor()); throw sqlEx; } + } finally { + lock.unlock(); } } @Override public int getSessionMaxRows() { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { return this.session.getSessionMaxRows(); + } finally { + lock.unlock(); } } @Override public void setSessionMaxRows(int max) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { checkClosed(); if (this.session.getSessionMaxRows() != max) { this.session.setSessionMaxRows(max); this.session.execSQL(null, "SET SQL_SELECT_LIMIT=" + (this.session.getSessionMaxRows() == -1 ? "DEFAULT" : this.session.getSessionMaxRows()), -1, null, false, this.nullStatementResultSetFactory, null, false); } + } finally { + lock.unlock(); } } @@ -2434,9 +2596,13 @@ public void setSchema(String schema) throws SQLException { @Override public String getSchema() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { checkClosed(); return this.propertySet.getEnumProperty(PropertyKey.databaseTerm).getValue() == DatabaseTerm.SCHEMA ? this.database : null; + } finally { + lock.unlock(); } } @@ -2467,7 +2633,9 @@ public void run() { @Override public void setNetworkTimeout(Executor executor, final int milliseconds) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { SecurityManager sec = System.getSecurityManager(); if (sec != null) { @@ -2481,6 +2649,8 @@ public void setNetworkTimeout(Executor executor, final int milliseconds) throws checkClosed(); executor.execute(new NetworkTimeoutSetter(this, milliseconds)); + } finally { + lock.unlock(); } } @@ -2497,8 +2667,12 @@ public NetworkTimeoutSetter(JdbcConnection conn, int milliseconds) { public void run() { JdbcConnection conn = this.connRef.get(); if (conn != null) { - synchronized (conn.getConnectionMutex()) { + ReentrantLock lock = conn.getConnectionMutex(); + lock.lock(); + try { ((NativeSession) conn.getSession()).setSocketTimeout(this.milliseconds); + } finally { + lock.unlock(); } } } @@ -2506,9 +2680,13 @@ public void run() { @Override public int getNetworkTimeout() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { checkClosed(); return this.session.getSocketTimeout(); + } finally { + lock.unlock(); } } @@ -2534,7 +2712,9 @@ public SQLXML createSQLXML() throws SQLException { @Override public boolean isValid(int timeout) throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { if (isClosed()) { return false; } @@ -2557,6 +2737,8 @@ public boolean isValid(int timeout) throws SQLException { } return true; + } finally { + lock.unlock(); } } @@ -2564,7 +2746,9 @@ public boolean isValid(int timeout) throws SQLException { @Override public ClientInfoProvider getClientInfoProviderImpl() throws SQLException { - synchronized (getConnectionMutex()) { + ReentrantLock lock = getConnectionMutex(); + lock.lock(); + try { if (this.infoProvider == null) { String clientInfoProvider = this.propertySet.getStringProperty(PropertyKey.clientInfoProvider).getStringValue(); try { @@ -2589,6 +2773,8 @@ public ClientInfoProvider getClientInfoProviderImpl() throws SQLException { } return this.infoProvider; + } finally { + lock.unlock(); } } diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionWrapper.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionWrapper.java index 4b0202058..bc72f6b28 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionWrapper.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionWrapper.java @@ -45,6 +45,7 @@ import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; +import java.util.concurrent.locks.ReentrantLock; import com.mysql.cj.Messages; import com.mysql.cj.MysqlConnection; @@ -886,7 +887,7 @@ public void abortInternal() throws SQLException { } @Override - public Object getConnectionMutex() { + public ReentrantLock getConnectionMutex() { return this.mc.getConnectionMutex(); } diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ServerPreparedStatement.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ServerPreparedStatement.java index 91847c2e2..9de78058a 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ServerPreparedStatement.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ServerPreparedStatement.java @@ -36,6 +36,7 @@ import java.sql.SQLException; import java.sql.Wrapper; import java.util.ArrayList; +import java.util.concurrent.locks.ReentrantLock; import com.mysql.cj.BindValue; import com.mysql.cj.CancelQueryTask; @@ -155,8 +156,12 @@ public String toString() { @Override public void addBatch() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.query.addBatch(((PreparedQuery) this.query).getQueryBindings().clone()); + } finally { + lock.unlock(); } } @@ -171,8 +176,12 @@ protected JdbcConnection checkClosed() { @Override public void clearParameters() { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((ServerPreparedQuery) this.query).clearParameters(true); + } finally { + lock.unlock(); } } @@ -188,7 +197,9 @@ public void close() throws SQLException { return; // already closed } - synchronized (locallyScopedConn.getConnectionMutex()) { + ReentrantLock lock = locallyScopedConn.getConnectionMutex(); + lock.lock(); + try { if (this.isClosed) { return; // already closed } @@ -206,12 +217,16 @@ public void close() throws SQLException { this.isClosed = false; realClose(true, true); + } finally { + lock.unlock(); } } @Override protected long[] executeBatchSerially(int batchTimeout) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { JdbcConnection locallyScopedConn = this.connection; if (locallyScopedConn.isReadOnly()) { @@ -315,6 +330,8 @@ protected long[] executeBatchSerially(int batchTimeout) throws SQLException { clearBatch(); } + } finally { + lock.unlock(); } } @@ -330,8 +347,10 @@ private static SQLException appendMessageToException(SQLException sqlEx, String @Override protected com.mysql.cj.jdbc.result.ResultSetInternalMethods executeInternal(int maxRowsToRetrieve, M sendPacket, - boolean createStreamingResultSet, boolean queryIsSelectOnly, ColumnDefinition metadata, boolean isBatch) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + boolean createStreamingResultSet, boolean queryIsSelectOnly, ColumnDefinition metadata, boolean isBatch) throws SQLException { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ((PreparedQuery) this.query).getQueryBindings().setNumberOfExecutions(((PreparedQuery) this.query).getQueryBindings().getNumberOfExecutions() + 1); // We defer to server-side execution @@ -373,6 +392,8 @@ protected com.mysql.cj.jdbc.result.ResultSetInternalMethods throw sqlEx; } + } finally { + lock.unlock(); } } @@ -389,33 +410,45 @@ protected com.mysql.cj.jdbc.result.ResultSetInternalMethods * if a database access error occurs or this method is called on a closed PreparedStatement */ protected BindValue getBinding(int parameterIndex, boolean forLongData) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { int i = getCoreParameterIndex(parameterIndex); return ((ServerPreparedQuery) this.query).getQueryBindings().getBinding(i, forLongData); + } finally { + lock.unlock(); } } @Override public java.sql.ResultSetMetaData getMetaData() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { ColumnDefinition resultFields = ((ServerPreparedQuery) this.query).getResultFields(); return resultFields == null || resultFields.getFields() == null ? null : new ResultSetMetaData(this.session, resultFields.getFields(), - this.session.getPropertySet().getBooleanProperty(PropertyKey.useOldAliasMetadataBehavior).getValue(), - this.session.getPropertySet().getBooleanProperty(PropertyKey.yearIsDateType).getValue(), this.exceptionInterceptor); + this.session.getPropertySet().getBooleanProperty(PropertyKey.useOldAliasMetadataBehavior).getValue(), + this.session.getPropertySet().getBooleanProperty(PropertyKey.yearIsDateType).getValue(), this.exceptionInterceptor); + } finally { + lock.unlock(); } } @Override public ParameterMetaData getParameterMetaData() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.parameterMetaData == null) { this.parameterMetaData = new MysqlParameterMetadata(this.session, ((ServerPreparedQuery) this.query).getParameterFields(), ((PreparedQuery) this.query).getParameterCount(), this.exceptionInterceptor); } return this.parameterMetaData; + } finally { + lock.unlock(); } } @@ -431,7 +464,9 @@ public void realClose(boolean calledExplicitly, boolean closeOpenResults) throws return; // already closed } - synchronized (locallyScopedConn.getConnectionMutex()) { + ReentrantLock lock = locallyScopedConn.getConnectionMutex(); + lock.lock(); + try { if (this.connection != null) { // // Don't communicate with the server if we're being called from the finalizer... @@ -453,13 +488,17 @@ public void realClose(boolean calledExplicitly, boolean closeOpenResults) throws // Finally deallocate the prepared statement. if (calledExplicitly && !locallyScopedConn.isClosed()) { - synchronized (locallyScopedConn.getConnectionMutex()) { + ReentrantLock locallyScopedConnLock = locallyScopedConn.getConnectionMutex(); + locallyScopedConnLock.lock(); + try { try { ((NativeSession) locallyScopedConn.getSession()).getProtocol().sendCommand( this.commandBuilder.buildComStmtClose(null, ((ServerPreparedQuery) this.query).getServerStatementId()), true, 0); } catch (CJException sqlEx) { exceptionDuringClose = sqlEx; } + } finally { + locallyScopedConnLock.unlock(); } } @@ -467,6 +506,8 @@ public void realClose(boolean calledExplicitly, boolean closeOpenResults) throws throw exceptionDuringClose; } } + } finally { + lock.unlock(); } } @@ -478,7 +519,9 @@ public void realClose(boolean calledExplicitly, boolean closeOpenResults) throws * if an error occurs. */ protected void rePrepare() { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.invalidationException = null; try { @@ -515,6 +558,8 @@ protected void rePrepare() { this.connection.unregisterStatement(this); } } + } finally { + lock.unlock(); } } @@ -549,14 +594,20 @@ protected void rePrepare() { * if a database access error occurs or this method is called on a closed PreparedStatement */ protected ResultSetInternalMethods serverExecute(int maxRowsToRetrieve, boolean createStreamingResultSet, ColumnDefinition metadata) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.results = ((ServerPreparedQuery) this.query).serverExecute(maxRowsToRetrieve, createStreamingResultSet, metadata, this.resultSetFactory); return this.results; + } finally { + lock.unlock(); } } protected void serverPrepare(String sql) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { SQLException t = null; try { @@ -591,6 +642,8 @@ protected void serverPrepare(String sql) throws SQLException { throw t; } } + } finally { + lock.unlock(); } } @@ -636,7 +689,9 @@ protected boolean containsOnDuplicateKeyUpdate() { @Override protected ClientPreparedStatement prepareBatchedInsertSQL(JdbcConnection localConn, int numBatches) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { try { ClientPreparedStatement pstmt = ((Wrapper) localConn.prepareStatement(((PreparedQuery) this.query).getQueryInfo().getSqlForBatch(numBatches), this.resultSetConcurrency, this.query.getResultType().getIntValue())).unwrap(ClientPreparedStatement.class); @@ -652,6 +707,8 @@ protected ClientPreparedStatement prepareBatchedInsertSQL(JdbcConnection localCo throw sqlEx; } + } finally { + lock.unlock(); } } diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/StatementImpl.java b/src/main/user-impl/java/com/mysql/cj/jdbc/StatementImpl.java index fd1e7dac6..fcdf9e5fc 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/StatementImpl.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/StatementImpl.java @@ -41,6 +41,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; import com.mysql.cj.CancelQueryTask; import com.mysql.cj.Messages; @@ -265,10 +266,14 @@ protected void initQuery() { @Override public void addBatch(String sql) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (sql != null) { this.query.addBatch(sql); } + } finally { + lock.unlock(); } } @@ -386,8 +391,12 @@ protected void checkNullOrEmptyQuery(String sql) throws SQLException { @Override public void clearBatch() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.query.clearBatchedArgs(); + } finally { + lock.unlock(); } } @@ -398,10 +407,14 @@ public void clearBatchedArgs() { @Override public void clearWarnings() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { setClearWarningsCalled(true); this.warningChain = null; // TODO souldn't we also clear warnings from _server_ ? + } finally { + lock.unlock(); } } @@ -437,7 +450,9 @@ protected void closeAllOpenResults() throws SQLException { return; // already closed } - synchronized (locallyScopedConn.getConnectionMutex()) { + ReentrantLock lock = locallyScopedConn.getConnectionMutex(); + lock.lock(); + try { if (this.openResults != null) { for (ResultSetInternalMethods element : this.openResults) { try { @@ -449,6 +464,8 @@ protected void closeAllOpenResults() throws SQLException { this.openResults.clear(); } + } finally { + lock.unlock(); } } @@ -478,7 +495,9 @@ protected void implicitlyCloseAllOpenResults() throws SQLException { @Override public void removeOpenResultSet(ResultSetInternalMethods rs) { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.openResults != null) { this.openResults.remove(rs); } @@ -499,6 +518,8 @@ public void removeOpenResultSet(ResultSetInternalMethods rs) { if (!this.isImplicitlyClosingResults && !hasMoreResults) { checkAndPerformCloseOnCompletionAction(); } + } finally { + lock.unlock(); } } catch (StatementIsClosedException e) { // we can't break the interface, having this be no-op in case of error is ok @@ -508,12 +529,16 @@ public void removeOpenResultSet(ResultSetInternalMethods rs) { @Override public int getOpenResultSetCount() { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.openResults != null) { return this.openResults.size(); } return 0; + } finally { + lock.unlock(); } } catch (StatementIsClosedException e) { // we can't break the interface, having this be no-op in case of error is ok @@ -528,12 +553,16 @@ public int getOpenResultSetCount() { */ private void checkAndPerformCloseOnCompletionAction() { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (isCloseOnCompletion() && !this.dontTrackOpenResources.getValue() && getOpenResultSetCount() == 0 && (this.results == null || !this.results.hasRows() || this.results.isClosed()) && (this.generatedKeysResults == null || !this.generatedKeysResults.hasRows() || this.generatedKeysResults.isClosed())) { realClose(false, false); } + } finally { + lock.unlock(); } } catch (SQLException e) { } @@ -547,7 +576,9 @@ private void checkAndPerformCloseOnCompletionAction() { * if a database access error occurs or this method is called on a closed Statement */ private ResultSetInternalMethods createResultSetUsingServerFetch(String sql) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { java.sql.PreparedStatement pStmt = this.connection.prepareStatement(sql, this.query.getResultType().getIntValue(), this.resultSetConcurrency); pStmt.setFetchSize(this.query.getResultFetchSize()); @@ -574,6 +605,8 @@ private ResultSetInternalMethods createResultSetUsingServerFetch(String sql) thr this.results = rs; return rs; + } finally { + lock.unlock(); } } @@ -594,22 +627,30 @@ protected boolean createStreamingResultSet() { @Override public void enableStreamingResults() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.originalResultSetType = this.query.getResultType(); this.originalFetchSize = this.query.getResultFetchSize(); setFetchSize(Integer.MIN_VALUE); setResultSetType(Type.FORWARD_ONLY); + } finally { + lock.unlock(); } } @Override public void disableStreamingResults() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.query.getResultFetchSize() == Integer.MIN_VALUE && this.query.getResultType() == Type.FORWARD_ONLY) { setFetchSize(this.originalFetchSize); setResultSetType(this.originalResultSetType); } + } finally { + lock.unlock(); } } @@ -651,7 +692,9 @@ public boolean execute(String sql) throws SQLException { private boolean executeInternal(String sql, boolean returnGeneratedKeys) throws SQLException { JdbcConnection locallyScopedConn = checkClosed(); - synchronized (locallyScopedConn.getConnectionMutex()) { + ReentrantLock lock = locallyScopedConn.getConnectionMutex(); + lock.lock(); + try { checkClosed(); checkNullOrEmptyQuery(sql); @@ -760,6 +803,8 @@ private boolean executeInternal(String sql, boolean returnGeneratedKeys) throws } finally { this.query.getStatementExecuting().set(false); } + } finally { + lock.unlock(); } } @@ -770,8 +815,12 @@ public void statementBegins() { @Override public void resetCancelledState() { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.query.resetCancelledState(); + } finally { + lock.unlock(); } } @@ -798,7 +847,9 @@ public int[] executeBatch() throws SQLException { protected long[] executeBatchInternal() throws SQLException { JdbcConnection locallyScopedConn = checkClosed(); - synchronized (locallyScopedConn.getConnectionMutex()) { + ReentrantLock lock = locallyScopedConn.getConnectionMutex(); + lock.lock(); + try { if (locallyScopedConn.isReadOnly()) { throw SQLError.createSQLException(Messages.getString("Statement.34") + Messages.getString("Statement.35"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); @@ -910,6 +961,8 @@ protected long[] executeBatchInternal() throws SQLException { clearBatch(); } + } finally { + lock.unlock(); } } @@ -947,7 +1000,9 @@ private long[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled, int nb JdbcConnection locallyScopedConn = checkClosed(); - synchronized (locallyScopedConn.getConnectionMutex()) { + ReentrantLock lock = locallyScopedConn.getConnectionMutex(); + lock.lock(); + try { if (!multiQueriesEnabled) { this.session.enableMultiQueries(); } @@ -1048,11 +1103,15 @@ private long[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled, int nb } } } + } finally { + lock.unlock(); } } protected int processMultiCountsAndKeys(StatementImpl batchedStatement, int updateCountCounter, long[] updateCounts) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { updateCounts[updateCountCounter++] = batchedStatement.getLargeUpdateCount(); boolean doGenKeys = this.batchedGeneratedKeys != null; @@ -1080,6 +1139,8 @@ protected int processMultiCountsAndKeys(StatementImpl batchedStatement, int upda } return updateCountCounter; + } finally { + lock.unlock(); } } @@ -1102,7 +1163,9 @@ protected SQLException handleExceptionForBatch(int endOfBatchIndex, int numValue @Override public java.sql.ResultSet executeQuery(String sql) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { JdbcConnection locallyScopedConn = this.connection; this.retrieveGeneratedKeys = false; @@ -1197,11 +1260,15 @@ public java.sql.ResultSet executeQuery(String sql) throws SQLException { } return this.results; + } finally { + lock.unlock(); } } protected void doPingInstead() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.pingTarget != null) { try { this.pingTarget.doPing(); @@ -1216,27 +1283,37 @@ protected void doPingInstead() throws SQLException { ResultSetInternalMethods fakeSelectOneResultSet = generatePingResultSet(); this.results = fakeSelectOneResultSet; + } finally { + lock.unlock(); } } protected ResultSetInternalMethods generatePingResultSet() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { String encoding = this.session.getServerSession().getCharsetSettings().getMetadataEncoding(); int collationIndex = this.session.getServerSession().getCharsetSettings().getMetadataCollationIndex(); - Field[] fields = { new Field(null, "1", collationIndex, encoding, MysqlType.BIGINT, 1) }; + Field[] fields = {new Field(null, "1", collationIndex, encoding, MysqlType.BIGINT, 1)}; ArrayList rows = new ArrayList<>(); - byte[] colVal = new byte[] { (byte) '1' }; + byte[] colVal = new byte[]{(byte) '1'}; - rows.add(new ByteArrayRow(new byte[][] { colVal }, getExceptionInterceptor())); + rows.add(new ByteArrayRow(new byte[][]{colVal}, getExceptionInterceptor())); return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, new ResultsetRowsStatic(rows, new DefaultColumnDefinition(fields))); + } finally { + lock.unlock(); } } public void executeSimpleNonQuery(JdbcConnection c, String nonQuery) throws SQLException { - synchronized (c.getConnectionMutex()) { + ReentrantLock lock = c.getConnectionMutex(); + lock.lock(); + try { ((NativeSession) c.getSession()).execSQL(this, nonQuery, -1, null, false, getResultSetFactory(), null, false).close(); + } finally { + lock.unlock(); } } @@ -1246,7 +1323,9 @@ public int executeUpdate(String sql) throws SQLException { } protected long executeUpdateInternal(String sql, boolean isBatch, boolean returnGeneratedKeys) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { JdbcConnection locallyScopedConn = this.connection; checkNullOrEmptyQuery(sql); @@ -1331,6 +1410,8 @@ protected long executeUpdateInternal(String sql, boolean isBatch, boolean return this.lastInsertId = rs.getUpdateID(); return this.updateCount; + } finally { + lock.unlock(); } } @@ -1351,8 +1432,12 @@ public int executeUpdate(String sql, String[] columnNames) throws SQLException { @Override public java.sql.Connection getConnection() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.connection; + } finally { + lock.unlock(); } } @@ -1363,14 +1448,20 @@ public int getFetchDirection() throws SQLException { @Override public int getFetchSize() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.query.getResultFetchSize(); + } finally { + lock.unlock(); } } @Override public java.sql.ResultSet getGeneratedKeys() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.retrieveGeneratedKeys) { throw SQLError.createSQLException(Messages.getString("Statement.GeneratedKeysNotRequested"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); @@ -1392,6 +1483,8 @@ public java.sql.ResultSet getGeneratedKeys() throws SQLException { new ResultsetRowsStatic(this.batchedGeneratedKeys, new DefaultColumnDefinition(fields))); return this.generatedKeysResults; + } finally { + lock.unlock(); } } @@ -1406,7 +1499,9 @@ protected ResultSetInternalMethods getGeneratedKeysInternal() throws SQLExceptio } protected ResultSetInternalMethods getGeneratedKeysInternal(long numKeys) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { String encoding = this.session.getServerSession().getCharsetSettings().getMetadataEncoding(); int collationIndex = this.session.getServerSession().getCharsetSettings().getMetadataCollationIndex(); Field[] fields = new Field[1]; @@ -1456,6 +1551,8 @@ protected ResultSetInternalMethods getGeneratedKeysInternal(long numKeys) throws new ResultsetRowsStatic(rowSet, new DefaultColumnDefinition(fields))); return gkRs; + } finally { + lock.unlock(); } } @@ -1471,8 +1568,12 @@ protected ResultSetInternalMethods getGeneratedKeysInternal(long numKeys) throws * @return the last update ID. */ public long getLastInsertID() { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.lastInsertId; + } finally { + lock.unlock(); } } @@ -1488,7 +1589,9 @@ public long getLastInsertID() { * @return the current update count. */ public long getLongUpdateCount() { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.results == null) { return -1; } @@ -1498,24 +1601,34 @@ public long getLongUpdateCount() { } return this.updateCount; + } finally { + lock.unlock(); } } @Override public int getMaxFieldSize() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.maxFieldSize; + } finally { + lock.unlock(); } } @Override public int getMaxRows() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.maxRows <= 0) { return 0; } return this.maxRows; + } finally { + lock.unlock(); } } @@ -1526,7 +1639,9 @@ public boolean getMoreResults() throws SQLException { @Override public boolean getMoreResults(int current) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.results == null) { return false; } @@ -1602,13 +1717,19 @@ public boolean getMoreResults(int current) throws SQLException { checkAndPerformCloseOnCompletionAction(); } return moreResults; + } finally { + lock.unlock(); } } @Override public int getQueryTimeout() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return getTimeoutInMillis() / 1000; + } finally { + lock.unlock(); } } @@ -1682,15 +1803,23 @@ private long getRecordCountFromInfo(String serverInfo) { @Override public java.sql.ResultSet getResultSet() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return ((this.results != null) && this.results.hasRows()) ? (java.sql.ResultSet) this.results : null; + } finally { + lock.unlock(); } } @Override public int getResultSetConcurrency() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.resultSetConcurrency; + } finally { + lock.unlock(); } } @@ -1702,8 +1831,12 @@ public int getResultSetHoldability() throws SQLException { @Override public ResultSetInternalMethods getResultSetInternal() { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.results; + } finally { + lock.unlock(); } } catch (StatementIsClosedException e) { return this.results; // you end up with the same thing as before, you'll get exception when actually trying to use it @@ -1712,8 +1845,12 @@ public ResultSetInternalMethods getResultSetInternal() { @Override public int getResultSetType() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.query.getResultType().getIntValue(); + } finally { + lock.unlock(); } } @@ -1724,7 +1861,9 @@ public int getUpdateCount() throws SQLException { @Override public java.sql.SQLWarning getWarnings() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (isClearWarningsCalled()) { return null; @@ -1739,6 +1878,8 @@ public java.sql.SQLWarning getWarnings() throws SQLException { } return this.warningChain; + } finally { + lock.unlock(); } } @@ -1820,8 +1961,12 @@ public void setCursorName(String name) throws SQLException { @Override public void setEscapeProcessing(boolean enable) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.doEscapeProcessing = enable; + } finally { + lock.unlock(); } } @@ -1840,20 +1985,28 @@ public void setFetchDirection(int direction) throws SQLException { @Override public void setFetchSize(int rows) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (((rows < 0) && (rows != Integer.MIN_VALUE)) || ((this.maxRows > 0) && (rows > this.getMaxRows()))) { throw SQLError.createSQLException(Messages.getString("Statement.7"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } this.query.setResultFetchSize(rows); + } finally { + lock.unlock(); } } @Override public void setHoldResultsOpenOverClose(boolean holdResultsOpenOverClose) { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.holdResultsOpenOverClose = holdResultsOpenOverClose; + } finally { + lock.unlock(); } } catch (StatementIsClosedException e) { // FIXME: can't break interface at this point @@ -1862,7 +2015,9 @@ public void setHoldResultsOpenOverClose(boolean holdResultsOpenOverClose) { @Override public void setMaxFieldSize(int max) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (max < 0) { throw SQLError.createSQLException(Messages.getString("Statement.11"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } @@ -1870,11 +2025,13 @@ public void setMaxFieldSize(int max) throws SQLException { int maxBuf = this.maxAllowedPacket.getValue(); if (max > maxBuf) { - throw SQLError.createSQLException(Messages.getString("Statement.13", new Object[] { Long.valueOf(maxBuf) }), + throw SQLError.createSQLException(Messages.getString("Statement.13", new Object[]{Long.valueOf(maxBuf)}), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } this.maxFieldSize = max; + } finally { + lock.unlock(); } } @@ -1885,12 +2042,16 @@ public void setMaxRows(int max) throws SQLException { @Override public void setQueryTimeout(int seconds) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (seconds < 0) { throw SQLError.createSQLException(Messages.getString("Statement.21"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } setTimeoutInMillis(seconds * 1000); + } finally { + lock.unlock(); } } @@ -1904,10 +2065,14 @@ public void setQueryTimeout(int seconds) throws SQLException { */ void setResultSetConcurrency(int concurrencyFlag) throws SQLException { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.resultSetConcurrency = concurrencyFlag; // updating resultset factory because concurrency is cached there this.resultSetFactory = new ResultSetFactory(this.connection, this); + } finally { + lock.unlock(); } } catch (StatementIsClosedException e) { // FIXME: Can't break interface atm, we'll get the exception later when you try and do something useful with a closed statement... @@ -1924,10 +2089,14 @@ void setResultSetConcurrency(int concurrencyFlag) throws SQLException { */ void setResultSetType(Resultset.Type typeFlag) throws SQLException { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.query.setResultType(typeFlag); // updating resultset factory because type is cached there this.resultSetFactory = new ResultSetFactory(this.connection, this); + } finally { + lock.unlock(); } } catch (StatementIsClosedException e) { // FIXME: Can't break interface atm, we'll get the exception later when you try and do something useful with a closed statement... @@ -1939,7 +2108,9 @@ void setResultSetType(int typeFlag) throws SQLException { } protected void getBatchedGeneratedKeys(java.sql.Statement batchedStatement) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.retrieveGeneratedKeys) { java.sql.ResultSet rs = null; @@ -1947,7 +2118,7 @@ protected void getBatchedGeneratedKeys(java.sql.Statement batchedStatement) thro rs = batchedStatement.getGeneratedKeys(); while (rs.next()) { - this.batchedGeneratedKeys.add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor())); + this.batchedGeneratedKeys.add(new ByteArrayRow(new byte[][]{rs.getBytes(1)}, getExceptionInterceptor())); } } finally { if (rs != null) { @@ -1955,18 +2126,22 @@ protected void getBatchedGeneratedKeys(java.sql.Statement batchedStatement) thro } } } + } finally { + lock.unlock(); } } protected void getBatchedGeneratedKeys(int maxKeys) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.retrieveGeneratedKeys) { java.sql.ResultSet rs = null; try { rs = maxKeys == 0 ? getGeneratedKeysInternal() : getGeneratedKeysInternal(maxKeys); while (rs.next()) { - this.batchedGeneratedKeys.add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor())); + this.batchedGeneratedKeys.add(new ByteArrayRow(new byte[][]{rs.getBytes(1)}, getExceptionInterceptor())); } } finally { this.isImplicitlyClosingResults = true; @@ -1979,13 +2154,19 @@ protected void getBatchedGeneratedKeys(int maxKeys) throws SQLException { } } } + } finally { + lock.unlock(); } } private boolean useServerFetch() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.session.getPropertySet().getBooleanProperty(PropertyKey.useCursorFetch).getValue() && this.query.getResultFetchSize() > 0 && this.query.getResultType() == Type.FORWARD_ONLY; + } finally { + lock.unlock(); } } @@ -1995,8 +2176,12 @@ public boolean isClosed() throws SQLException { if (locallyScopedConn == null) { return true; } - synchronized (locallyScopedConn.getConnectionMutex()) { + ReentrantLock lock = locallyScopedConn.getConnectionMutex(); + lock.lock(); + try { return this.isClosed; + } finally { + lock.unlock(); } } @@ -2062,15 +2247,23 @@ protected boolean containsOnDuplicateKeyInString(String sql) { @Override public void closeOnCompletion() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.closeOnCompletion = true; + } finally { + lock.unlock(); } } @Override public boolean isCloseOnCompletion() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.closeOnCompletion; + } finally { + lock.unlock(); } } @@ -2107,7 +2300,9 @@ public long getLargeMaxRows() throws SQLException { @Override public long getLargeUpdateCount() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.results == null) { return -1; } @@ -2117,12 +2312,16 @@ public long getLargeUpdateCount() throws SQLException { } return this.results.getUpdateCount(); + } finally { + lock.unlock(); } } @Override public void setLargeMaxRows(long max) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if ((max > MAX_ROWS) || (max < 0)) { throw SQLError.createSQLException(Messages.getString("Statement.15") + max + " > " + MAX_ROWS + ".", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); @@ -2133,6 +2332,8 @@ public void setLargeMaxRows(long max) throws SQLException { } this.maxRows = (int) max; + } finally { + lock.unlock(); } } diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/MultiHostMySQLConnection.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/MultiHostMySQLConnection.java index 7b1042b15..cc73ed545 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/MultiHostMySQLConnection.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/MultiHostMySQLConnection.java @@ -46,6 +46,7 @@ import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; +import java.util.concurrent.locks.ReentrantLock; import com.mysql.cj.Messages; import com.mysql.cj.ServerVersion; @@ -642,7 +643,7 @@ public int getNetworkTimeout() throws SQLException { } @Override - public Object getConnectionMutex() { + public ReentrantLock getConnectionMutex() { return getActiveMySQLConnection().getConnectionMutex(); } diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/result/ResultSetImpl.java b/src/main/user-impl/java/com/mysql/cj/jdbc/result/ResultSetImpl.java index 4a287878d..55ce5a363 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/result/ResultSetImpl.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/result/ResultSetImpl.java @@ -64,6 +64,7 @@ import java.util.Calendar; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; import com.mysql.cj.Messages; import com.mysql.cj.MysqlType; @@ -328,7 +329,9 @@ public ResultSetImpl(ResultsetRows tuples, JdbcConnection conn, StatementImpl cr @Override public void initializeWithMetadata() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { initRowsWithMetadata(); if (this.useUsageAdvisor) { @@ -365,12 +368,16 @@ public void initializeWithMetadata() throws SQLException { this.session.getProtocol().getMetricsHolder().reportNumberOfTablesAccessed(tableNamesSet.size()); } + } finally { + lock.unlock(); } } @Override public boolean absolute(int row) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); @@ -418,12 +425,16 @@ public boolean absolute(int row) throws SQLException { setRowPositionValidity(); return b; + } finally { + lock.unlock(); } } @Override public void afterLast() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); @@ -439,12 +450,16 @@ public void afterLast() throws SQLException { } setRowPositionValidity(); + } finally { + lock.unlock(); } } @Override public void beforeFirst() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); @@ -462,6 +477,8 @@ public void beforeFirst() throws SQLException { this.thisRow = null; setRowPositionValidity(); + } finally { + lock.unlock(); } } @@ -499,7 +516,9 @@ protected final JdbcConnection checkClosed() throws SQLException { * if the index is out of bounds */ protected final void checkColumnBounds(int columnIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if ((columnIndex < 1)) { throw SQLError.createSQLException( Messages.getString("ResultSet.Column_Index_out_of_range_low", @@ -515,6 +534,8 @@ protected final void checkColumnBounds(int columnIndex) throws SQLException { if (this.useUsageAdvisor) { this.columnUsed[columnIndex - 1] = true; } + } finally { + lock.unlock(); } } @@ -554,8 +575,12 @@ private void setRowPositionValidity() throws SQLException { @Override public void clearWarnings() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.warningChain = null; + } finally { + lock.unlock(); } } @@ -577,7 +602,9 @@ public void deleteRow() throws SQLException { @Override public int findColumn(String columnName) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { int index = this.columnDefinition.findColumn(columnName, this.useColumnNamesInFindColumn, 1); if (index == -1) { @@ -587,12 +614,16 @@ public int findColumn(String columnName) throws SQLException { } return index; + } finally { + lock.unlock(); } } @Override public boolean first() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); @@ -614,6 +645,8 @@ public boolean first() throws SQLException { setRowPositionValidity(); return b; + } finally { + lock.unlock(); } } @@ -1080,23 +1113,35 @@ public String getCursorName() throws SQLException { @Override public int getFetchDirection() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.fetchDirection; + } finally { + lock.unlock(); } } @Override public int getFetchSize() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.fetchSize; + } finally { + lock.unlock(); } } @Override public char getFirstCharOfQuery() { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.firstCharOfQuery; + } finally { + lock.unlock(); } } catch (SQLException e) { throw new RuntimeException(e); // FIXME: Need to evolve interface @@ -1293,7 +1338,9 @@ public T getObject(int columnIndex, Class type) throws SQLException { throw SQLError.createSQLException("Type parameter can not be null", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (type.equals(String.class)) { return (T) getString(columnIndex); @@ -1428,6 +1475,8 @@ public T getObject(int columnIndex, Class type) throws SQLException { throw SQLError.createSQLException("Conversion not supported for type " + type.getName(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } finally { + lock.unlock(); } } @@ -1624,12 +1673,16 @@ public int getRow() throws SQLException { @Override public java.sql.Statement getStatement() throws SQLException { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (this.wrapperStatement != null) { return this.wrapperStatement; } return this.owningStatement; + } finally { + lock.unlock(); } } catch (SQLException sqlEx) { @@ -1692,8 +1745,12 @@ public URL getURL(String colName) throws SQLException { @Override public java.sql.SQLWarning getWarnings() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return this.warningChain; + } finally { + lock.unlock(); } } @@ -1704,48 +1761,64 @@ public void insertRow() throws SQLException { @Override public boolean isAfterLast() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); } return this.rowData.isAfterLast(); + } finally { + lock.unlock(); } } @Override public boolean isBeforeFirst() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); } return this.rowData.isBeforeFirst(); + } finally { + lock.unlock(); } } @Override public boolean isFirst() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); } return this.rowData.isFirst(); + } finally { + lock.unlock(); } } @Override public boolean isLast() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); } return this.rowData.isLast(); + } finally { + lock.unlock(); } } @@ -1762,7 +1835,9 @@ protected boolean isStrictlyForwardOnly() { @Override public boolean last() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); @@ -1784,6 +1859,8 @@ public boolean last() throws SQLException { setRowPositionValidity(); return b; + } finally { + lock.unlock(); } } @@ -1799,7 +1876,9 @@ public void moveToInsertRow() throws SQLException { @Override public boolean next() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); @@ -1825,6 +1904,8 @@ public boolean next() throws SQLException { setRowPositionValidity(); return b; + } finally { + lock.unlock(); } } @@ -1842,7 +1923,9 @@ public boolean next() throws SQLException { * if a database access error occurs */ public boolean prev() throws java.sql.SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { int rowIndex = this.rowData.getPosition(); @@ -1867,12 +1950,16 @@ public boolean prev() throws java.sql.SQLException { setRowPositionValidity(); return b; + } finally { + lock.unlock(); } } @Override public boolean previous() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); @@ -1883,6 +1970,8 @@ public boolean previous() throws SQLException { } return prev(); + } finally { + lock.unlock(); } } @@ -1894,7 +1983,9 @@ public void realClose(boolean calledExplicitly) throws SQLException { return; // already closed } - synchronized (locallyScopedConn.getConnectionMutex()) { + ReentrantLock lock = locallyScopedConn.getConnectionMutex(); + lock.lock(); + try { // additional check in case ResultSet was closed while current thread was waiting for lock if (this.isClosed) { return; @@ -1984,6 +2075,8 @@ public void realClose(boolean calledExplicitly) throws SQLException { throw exceptionDuringClose; } } + } finally { + lock.unlock(); } } @@ -1999,7 +2092,9 @@ public void refreshRow() throws SQLException { @Override public boolean relative(int rows) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); @@ -2021,6 +2116,8 @@ public boolean relative(int rows) throws SQLException { setRowPositionValidity(); return (!this.rowData.isAfterLast() && !this.rowData.isBeforeFirst()); + } finally { + lock.unlock(); } } @@ -2041,7 +2138,9 @@ public boolean rowUpdated() throws SQLException { @Override public void setFetchDirection(int direction) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if ((direction != FETCH_FORWARD) && (direction != FETCH_REVERSE) && (direction != FETCH_UNKNOWN)) { throw SQLError.createSQLException(Messages.getString("ResultSet.Illegal_value_for_fetch_direction_64"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); @@ -2049,30 +2148,40 @@ public void setFetchDirection(int direction) throws SQLException { if (isStrictlyForwardOnly() && direction != FETCH_FORWARD) { String constName = direction == ResultSet.FETCH_REVERSE ? "ResultSet.FETCH_REVERSE" : "ResultSet.FETCH_UNKNOWN"; - throw ExceptionFactory.createException(Messages.getString("ResultSet.Unacceptable_value_for_fetch_direction", new Object[] { constName })); + throw ExceptionFactory.createException(Messages.getString("ResultSet.Unacceptable_value_for_fetch_direction", new Object[]{constName})); } this.fetchDirection = direction; + } finally { + lock.unlock(); } } @Override public void setFetchSize(int rows) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (rows < 0) { /* || rows > getMaxRows() */ throw SQLError.createSQLException(Messages.getString("ResultSet.Value_must_be_between_0_and_getMaxRows()_66"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } this.fetchSize = rows; + } finally { + lock.unlock(); } } @Override public void setFirstCharOfQuery(char c) { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.firstCharOfQuery = c; + } finally { + lock.unlock(); } } catch (SQLException e) { throw new RuntimeException(e); // FIXME: Need to evolve public interface @@ -2082,8 +2191,12 @@ public void setFirstCharOfQuery(char c) { @Override public void setOwningStatement(JdbcStatement owningStatement) { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.owningStatement = (StatementImpl) owningStatement; + } finally { + lock.unlock(); } } catch (SQLException e) { throw new RuntimeException(e); // FIXME: Need to evolve public interface @@ -2098,8 +2211,12 @@ public void setOwningStatement(JdbcStatement owningStatement) { */ public synchronized void setResultSetConcurrency(int concurrencyFlag) { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.resultSetConcurrency = concurrencyFlag; + } finally { + lock.unlock(); } } catch (SQLException e) { throw new RuntimeException(e); // TODO: FIXME: Need to evolve public interface @@ -2115,8 +2232,12 @@ public synchronized void setResultSetConcurrency(int concurrencyFlag) { */ public synchronized void setResultSetType(int typeFlag) { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.resultSetType = typeFlag; + } finally { + lock.unlock(); } } catch (SQLException e) { throw new RuntimeException(e); // TODO: FIXME: Need to evolve public interface @@ -2131,8 +2252,12 @@ public synchronized void setResultSetType(int typeFlag) { */ public void setServerInfo(String info) { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.serverInfo = info; + } finally { + lock.unlock(); } } catch (SQLException e) { throw new RuntimeException(e); // TODO: FIXME: Need to evolve public interface @@ -2142,8 +2267,12 @@ public void setServerInfo(String info) { @Override public synchronized void setStatementUsedForFetchingRows(JdbcPreparedStatement stmt) { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.statementUsedForFetchingRows = stmt; + } finally { + lock.unlock(); } } catch (SQLException e) { throw new RuntimeException(e); // TODO: FIXME: Need to evolve public interface @@ -2153,8 +2282,12 @@ public synchronized void setStatementUsedForFetchingRows(JdbcPreparedStatement s @Override public synchronized void setWrapperStatement(java.sql.Statement wrapperStatement) { try { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { this.wrapperStatement = wrapperStatement; + } finally { + lock.unlock(); } } catch (SQLException e) { throw new RuntimeException(e); // TODO: FIXME: Need to evolve public interface @@ -2736,7 +2869,7 @@ public long getOwningStatementServerId() { } @Override - public Object getSyncMutex() { + public ReentrantLock getSyncMutex() { return this.connection != null ? this.connection.getConnectionMutex() : null; } diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/result/UpdatableResultSet.java b/src/main/user-impl/java/com/mysql/cj/jdbc/result/UpdatableResultSet.java index 8edd64d73..c471b6718 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/result/UpdatableResultSet.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/result/UpdatableResultSet.java @@ -46,6 +46,7 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.concurrent.locks.ReentrantLock; import com.mysql.cj.Messages; import com.mysql.cj.MysqlType; @@ -393,7 +394,9 @@ public void checkUpdatability() throws SQLException { @Override public void deleteRow() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.isUpdatable) { throw new NotUpdatable(this.notUpdatableReason); } @@ -428,6 +431,8 @@ public void deleteRow() throws SQLException { this.rowData.remove(); prev(); // position on previous row - Bug#27431 + } finally { + lock.unlock(); } } @@ -713,8 +718,12 @@ private Map getColumnsToIndexMapForTableAndDB(String databaseNa @Override public int getConcurrency() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { return (this.isUpdatable ? CONCUR_UPDATABLE : CONCUR_READ_ONLY); + } finally { + lock.unlock(); } } @@ -728,7 +737,9 @@ private String getQuotedIdChar() throws SQLException { @Override public void insertRow() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.7"), getExceptionInterceptor()); } @@ -758,6 +769,8 @@ public void insertRow() throws SQLException { this.rowData.addRow(resultSetRow); resetInserter(); + } finally { + lock.unlock(); } } @@ -799,7 +812,9 @@ public boolean last() throws SQLException { @Override public void moveToCurrentRow() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.isUpdatable) { throw new NotUpdatable(this.notUpdatableReason); } @@ -808,12 +823,16 @@ public void moveToCurrentRow() throws SQLException { this.onInsertRow = false; this.thisRow = this.savedCurrentRow; } + } finally { + lock.unlock(); } } @Override public void moveToInsertRow() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.isUpdatable) { throw new NotUpdatable(this.notUpdatableReason); } @@ -881,6 +900,8 @@ public void moveToInsertRow() throws SQLException { } } } + } finally { + lock.unlock(); } } @@ -926,7 +947,9 @@ public void realClose(boolean calledExplicitly) throws SQLException { return; } - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { SQLException sqlEx = null; if (this.useUsageAdvisor) { @@ -973,12 +996,16 @@ public void realClose(boolean calledExplicitly) throws SQLException { if (sqlEx != null) { throw sqlEx; } + } finally { + lock.unlock(); } } @Override public void refreshRow() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (isStrictlyForwardOnly()) { throw ExceptionFactory.createException(Messages.getString("ResultSet.ForwardOnly")); } @@ -998,6 +1025,8 @@ public void refreshRow() throws SQLException { } refreshRow(this.updater, this.thisRow); + } finally { + lock.unlock(); } } @@ -1167,7 +1196,9 @@ protected void syncUpdate() throws SQLException { @Override public void updateRow() throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.isUpdatable) { throw new NotUpdatable(this.notUpdatableReason); } @@ -1182,6 +1213,8 @@ public void updateRow() throws SQLException { // fixes calling updateRow() and then doing more updates on same row... syncUpdate(); + } finally { + lock.unlock(); } } @@ -1197,7 +1230,9 @@ public void updateAsciiStream(String columnLabel, java.io.InputStream x, int len @Override public void updateAsciiStream(int columnIndex, java.io.InputStream x, int length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1209,6 +1244,8 @@ public void updateAsciiStream(int columnIndex, java.io.InputStream x, int length this.inserter.setAsciiStream(columnIndex, x, length); this.thisRow.setBytes(columnIndex - 1, STREAM_DATA_MARKER); } + } finally { + lock.unlock(); } } @@ -1219,7 +1256,9 @@ public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLExcepti @Override public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1231,6 +1270,8 @@ public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException this.inserter.setBigDecimal(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, x == null ? null : StringUtils.getBytes(x.toString())); } + } finally { + lock.unlock(); } } @@ -1241,7 +1282,9 @@ public void updateBinaryStream(String columnLabel, java.io.InputStream x, int le @Override public void updateBinaryStream(int columnIndex, java.io.InputStream x, int length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1253,6 +1296,8 @@ public void updateBinaryStream(int columnIndex, java.io.InputStream x, int lengt this.inserter.setBinaryStream(columnIndex, x, length); this.thisRow.setBytes(columnIndex - 1, x == null ? null : STREAM_DATA_MARKER); } + } finally { + lock.unlock(); } } @@ -1263,7 +1308,9 @@ public void updateBlob(String columnLabel, java.sql.Blob blob) throws SQLExcepti @Override public void updateBlob(int columnIndex, java.sql.Blob blob) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1275,6 +1322,8 @@ public void updateBlob(int columnIndex, java.sql.Blob blob) throws SQLException this.inserter.setBlob(columnIndex, blob); this.thisRow.setBytes(columnIndex - 1, blob == null ? null : STREAM_DATA_MARKER); } + } finally { + lock.unlock(); } } @@ -1285,7 +1334,9 @@ public void updateBoolean(String columnLabel, boolean x) throws SQLException { @Override public void updateBoolean(int columnIndex, boolean x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1297,6 +1348,8 @@ public void updateBoolean(int columnIndex, boolean x) throws SQLException { this.inserter.setBoolean(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex)); } + } finally { + lock.unlock(); } } @@ -1307,7 +1360,9 @@ public void updateByte(String columnLabel, byte x) throws SQLException { @Override public void updateByte(int columnIndex, byte x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1319,6 +1374,8 @@ public void updateByte(int columnIndex, byte x) throws SQLException { this.inserter.setByte(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex)); } + } finally { + lock.unlock(); } } @@ -1329,7 +1386,9 @@ public void updateBytes(String columnLabel, byte[] x) throws SQLException { @Override public void updateBytes(int columnIndex, byte[] x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1341,6 +1400,8 @@ public void updateBytes(int columnIndex, byte[] x) throws SQLException { this.inserter.setBytes(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, x); } + } finally { + lock.unlock(); } } @@ -1351,7 +1412,9 @@ public void updateCharacterStream(String columnLabel, java.io.Reader reader, int @Override public void updateCharacterStream(int columnIndex, java.io.Reader x, int length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1363,6 +1426,8 @@ public void updateCharacterStream(int columnIndex, java.io.Reader x, int length) this.inserter.setCharacterStream(columnIndex, x, length); this.thisRow.setBytes(columnIndex - 1, x == null ? null : STREAM_DATA_MARKER); } + } finally { + lock.unlock(); } } @@ -1373,12 +1438,16 @@ public void updateClob(String columnLabel, Clob clob) throws SQLException { @Override public void updateClob(int columnIndex, java.sql.Clob clob) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (clob == null) { updateNull(columnIndex); } else { updateCharacterStream(columnIndex, clob.getCharacterStream(), (int) clob.length()); } + } finally { + lock.unlock(); } } @@ -1389,7 +1458,9 @@ public void updateDate(String columnLabel, java.sql.Date x) throws SQLException @Override public void updateDate(int columnIndex, java.sql.Date x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1401,6 +1472,8 @@ public void updateDate(int columnIndex, java.sql.Date x) throws SQLException { this.inserter.setDate(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex)); } + } finally { + lock.unlock(); } } @@ -1411,7 +1484,9 @@ public void updateDouble(String columnLabel, double x) throws SQLException { @Override public void updateDouble(int columnIndex, double x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1423,6 +1498,8 @@ public void updateDouble(int columnIndex, double x) throws SQLException { this.inserter.setDouble(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex)); } + } finally { + lock.unlock(); } } @@ -1433,7 +1510,9 @@ public void updateFloat(String columnLabel, float x) throws SQLException { @Override public void updateFloat(int columnIndex, float x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1445,6 +1524,8 @@ public void updateFloat(int columnIndex, float x) throws SQLException { this.inserter.setFloat(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex)); } + } finally { + lock.unlock(); } } @@ -1455,7 +1536,9 @@ public void updateInt(String columnLabel, int x) throws SQLException { @Override public void updateInt(int columnIndex, int x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1467,6 +1550,8 @@ public void updateInt(int columnIndex, int x) throws SQLException { this.inserter.setInt(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex)); } + } finally { + lock.unlock(); } } @@ -1477,7 +1562,9 @@ public void updateLong(String columnLabel, long x) throws SQLException { @Override public void updateLong(int columnIndex, long x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1489,6 +1576,8 @@ public void updateLong(int columnIndex, long x) throws SQLException { this.inserter.setLong(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex)); } + } finally { + lock.unlock(); } } @@ -1499,7 +1588,9 @@ public void updateNull(String columnLabel) throws SQLException { @Override public void updateNull(int columnIndex) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1511,6 +1602,8 @@ public void updateNull(int columnIndex) throws SQLException { this.inserter.setNull(columnIndex, 0); this.thisRow.setBytes(columnIndex - 1, null); } + } finally { + lock.unlock(); } } @@ -1575,7 +1668,9 @@ protected void updateObjectInternal(int columnIndex, Object x, Integer targetTyp * if an error occurs */ protected void updateObjectInternal(int columnIndex, Object x, SQLType targetType, int scaleOrLength) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1596,6 +1691,8 @@ protected void updateObjectInternal(int columnIndex, Object x, SQLType targetTyp this.thisRow.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex)); } + } finally { + lock.unlock(); } } @@ -1626,7 +1723,9 @@ public void updateShort(String columnLabel, short x) throws SQLException { @Override public void updateShort(int columnIndex, short x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1638,6 +1737,8 @@ public void updateShort(int columnIndex, short x) throws SQLException { this.inserter.setShort(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex)); } + } finally { + lock.unlock(); } } @@ -1648,7 +1749,9 @@ public void updateString(String columnLabel, String x) throws SQLException { @Override public void updateString(int columnIndex, String x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1660,6 +1763,8 @@ public void updateString(int columnIndex, String x) throws SQLException { this.inserter.setString(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, x == null ? null : StringUtils.getBytes(x, this.charEncoding)); } + } finally { + lock.unlock(); } } @@ -1670,7 +1775,9 @@ public void updateTime(String columnLabel, java.sql.Time x) throws SQLException @Override public void updateTime(int columnIndex, java.sql.Time x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1682,6 +1789,8 @@ public void updateTime(int columnIndex, java.sql.Time x) throws SQLException { this.inserter.setTime(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex)); } + } finally { + lock.unlock(); } } @@ -1692,7 +1801,9 @@ public void updateTimestamp(String columnLabel, java.sql.Timestamp x) throws SQL @Override public void updateTimestamp(int columnIndex, java.sql.Timestamp x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; @@ -1704,6 +1815,8 @@ public void updateTimestamp(int columnIndex, java.sql.Timestamp x) throws SQLExc this.inserter.setTimestamp(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex)); } + } finally { + lock.unlock(); } } @@ -1919,7 +2032,9 @@ public void updateNCharacterStream(String columnLabel, Reader reader, long lengt @Override public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { String fieldEncoding = this.getMetadata().getFields()[columnIndex - 1].getEncoding(); if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) { throw new SQLException(Messages.getString("ResultSet.16")); @@ -1936,6 +2051,8 @@ public void updateNCharacterStream(int columnIndex, Reader x, long length) throw this.inserter.setNCharacterStream(columnIndex, x, length); this.thisRow.setBytes(columnIndex - 1, x == null ? null : STREAM_DATA_MARKER); } + } finally { + lock.unlock(); } } @@ -1974,7 +2091,9 @@ public void updateNClob(String columnLabel, java.sql.NClob nClob) throws SQLExce @Override public void updateNClob(int columnIndex, java.sql.NClob nClob) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { String fieldEncoding = this.getMetadata().getFields()[columnIndex - 1].getEncoding(); if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) { throw new SQLException(Messages.getString("ResultSet.17")); @@ -1985,6 +2104,8 @@ public void updateNClob(int columnIndex, java.sql.NClob nClob) throws SQLExcepti } else { updateNCharacterStream(columnIndex, nClob.getCharacterStream(), (int) nClob.length()); } + } finally { + lock.unlock(); } } @@ -2005,7 +2126,9 @@ public void updateNString(String columnLabel, String x) throws SQLException { @Override public void updateNString(int columnIndex, String x) throws SQLException { - synchronized (checkClosed().getConnectionMutex()) { + ReentrantLock lock = checkClosed().getConnectionMutex(); + lock.lock(); + try { String fieldEncoding = this.getMetadata().getFields()[columnIndex - 1].getEncoding(); if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) { throw new SQLException(Messages.getString("ResultSet.18")); @@ -2022,6 +2145,8 @@ public void updateNString(int columnIndex, String x) throws SQLException { this.inserter.setNString(columnIndex, x); this.thisRow.setBytes(columnIndex - 1, x == null ? null : StringUtils.getBytes(x, fieldEncoding)); } + } finally { + lock.unlock(); } } diff --git a/src/test/java/testsuite/regression/DataSourceRegressionTest.java b/src/test/java/testsuite/regression/DataSourceRegressionTest.java index 4f6de68b6..5ef3e0f95 100644 --- a/src/test/java/testsuite/regression/DataSourceRegressionTest.java +++ b/src/test/java/testsuite/regression/DataSourceRegressionTest.java @@ -497,7 +497,7 @@ public void testBug72890() throws Exception { final XAConnection xaConn = myDs.getXAConnection(); final XAResource xaRes = xaConn.getXAResource(); final Connection dbConn = xaConn.getConnection(); - final long connId = ((MysqlConnection) ((MysqlConnection) dbConn).getConnectionMutex()).getSession().getThreadId(); + final long connId = ((MysqlConnection) dbConn).getSession().getThreadId(); xaRes.start(xid, XAResource.TMNOFLAGS); xaRes.end(xid, XAResource.TMSUCCESS); diff --git a/src/test/java/testsuite/regression/PooledConnectionRegressionTest.java b/src/test/java/testsuite/regression/PooledConnectionRegressionTest.java index 2581cba74..8164709dc 100644 --- a/src/test/java/testsuite/regression/PooledConnectionRegressionTest.java +++ b/src/test/java/testsuite/regression/PooledConnectionRegressionTest.java @@ -46,6 +46,7 @@ import java.sql.Statement; import java.util.Properties; import java.util.concurrent.Callable; +import java.util.concurrent.locks.ReentrantLock; import javax.sql.ConnectionEvent; import javax.sql.ConnectionEventListener; @@ -500,7 +501,7 @@ public Void call() throws Exception { cw.setStatementComment("Test comment"); assertNotEquals(((JdbcConnection) this.conn).getStatementComment(), cw.getStatementComment()); - assertEquals(ConnectionImpl.class, cw.getConnectionMutex().getClass()); + assertEquals(ReentrantLock.class, cw.getConnectionMutex().getClass()); assertNull(cw.getExceptionInterceptor()); assertEquals(((JdbcConnection) this.conn).getNetworkTimeout(), cw.getNetworkTimeout()); assertEquals(((JdbcConnection) this.conn).getTypeMap(), cw.getTypeMap()); @@ -880,7 +881,7 @@ public Void call() throws Exception { } }); - assertEquals(ConnectionImpl.class, cw.getConnectionMutex().getClass()); + assertEquals(ReentrantLock.class, cw.getConnectionMutex().getClass()); assertNull(cw.getExceptionInterceptor()); assertEquals(((JdbcConnection) this.conn).getNetworkTimeout(), cw.getNetworkTimeout()); assertThrows(SQLNonTransientConnectionException.class, "Logical handle no longer valid", new Callable() { @@ -1336,7 +1337,7 @@ public Void call() throws Exception { return null; } }); - assertEquals(ConnectionImpl.class, cw.getConnectionMutex().getClass()); + assertEquals(ReentrantLock.class, cw.getConnectionMutex().getClass()); assertNull(cw.getExceptionInterceptor()); String comment = cw.getStatementComment(); diff --git a/src/test/java/testsuite/regression/StatementRegressionTest.java b/src/test/java/testsuite/regression/StatementRegressionTest.java index 4ca8cfc13..1e548464f 100644 --- a/src/test/java/testsuite/regression/StatementRegressionTest.java +++ b/src/test/java/testsuite/regression/StatementRegressionTest.java @@ -102,6 +102,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.function.ToIntFunction; @@ -4569,7 +4570,7 @@ public ResultsetRows getRows() { } @Override - public Object getSyncMutex() { + public ReentrantLock getSyncMutex() { return null; } };