From 5a27bc4b82765dd9ab28f3cf75fa04b814369719 Mon Sep 17 00:00:00 2001 From: Axyoan Marcelo Date: Sun, 1 Sep 2024 21:22:27 -0600 Subject: [PATCH] Fix for Bug#115265 (Bug#36843227), Second stored procedure call with cacheCallableStmts might fail. Change-Id: I919d31e88db41e8893cadfb42c4dcd7d4f8a88a4 --- CHANGES | 2 + .../com/mysql/cj/jdbc/CallableStatement.java | 45 ++++++++++--------- .../CallableStatementRegressionTest.java | 42 +++++++++++++++++ 3 files changed, 68 insertions(+), 21 deletions(-) diff --git a/CHANGES b/CHANGES index fc8b7a67f..d197be6e8 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,8 @@ Version 9.1.0 + - Fix for Bug#115265 (Bug#36843227), Second stored procedure call with cacheCallableStmts might fail. + - Fix for Bug#36936407, PrepareCall method doesn't work as expected when DB name is involved. - WL#16490, OpenID Connect authentication support. 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 c9148d5e5..577e80e63 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 @@ -141,6 +141,8 @@ public class CallableStatementParamInfo implements ParameterMetaData { Map parameterMap; + int[] placeholderToParameterIndexMap; + boolean isReadOnlySafeProcedure = false; boolean isReadOnlySafeChecked = false; @@ -158,17 +160,15 @@ public class CallableStatementParamInfo implements ParameterMetaData { this.dbInUse = getCurrentDatabase(); this.isFunctionCall = fullParamInfo.isFunctionCall; this.fakeParameters = fullParamInfo.fakeParameters; - @SuppressWarnings("synthetic-access") - int[] localParameterMap = CallableStatement.this.placeholderToParameterIndexMap; - int parameterMapLength = localParameterMap.length; this.isReadOnlySafeProcedure = fullParamInfo.isReadOnlySafeProcedure; this.isReadOnlySafeChecked = fullParamInfo.isReadOnlySafeChecked; this.parameterList = new ArrayList<>(fullParamInfo.numParameters); this.parameterMap = new HashMap<>(fullParamInfo.numParameters); + setPlaceholderToParameterIndexMap(fullParamInfo.placeholderToParameterIndexMap); - for (int i = 0; i < parameterMapLength; i++) { - CallableStatementParam param = fullParamInfo.parameterList.get(localParameterMap[i]); + for (int i : this.placeholderToParameterIndexMap) { + CallableStatementParam param = fullParamInfo.parameterList.get(i); this.parameterList.add(param); this.parameterMap.put(param.paramName, param); @@ -385,6 +385,10 @@ public T unwrap(Class iface) throws java.sql.SQLException { } } + void setPlaceholderToParameterIndexMap(int[] localPlaceholderToParameterIndexMap) { + this.placeholderToParameterIndexMap = localPlaceholderToParameterIndexMap.clone(); + } + } private final static int NOT_OUTPUT_PARAMETER_INDICATOR = Integer.MIN_VALUE; @@ -489,8 +493,6 @@ protected static CallableStatement getInstance(JdbcConnection conn, CallableStat return new CallableStatement(conn, paramInfo); } - private int[] placeholderToParameterIndexMap; - private void generateParameterMap() throws SQLException { Lock connectionLock = checkClosed().getConnectionLock(); connectionLock.lock(); @@ -506,12 +508,12 @@ private void generateParameterMap() throws SQLException { PreparedQuery q = (PreparedQuery) this.query; if (this.paramInfo != null && !this.paramInfo.fakeParameters && q.getParameterCount() >= 0) { - this.placeholderToParameterIndexMap = new int[q.getParameterCount()]; + int[] localPlaceholderToParameterIndexMap = new int[q.getParameterCount()]; int startIndex = 0; if (this.callingStoredFunction) { - this.placeholderToParameterIndexMap[0] = 0; + localPlaceholderToParameterIndexMap[0] = 0; startIndex = 1; } @@ -550,12 +552,13 @@ private void generateParameterMap() throws SQLException { } } for (int j = 0; j < questionMarkCount; j++) { - this.placeholderToParameterIndexMap[placeholderCount++] = startIndex + i; + localPlaceholderToParameterIndexMap[placeholderCount++] = startIndex + i; } } } } } + this.paramInfo.setPlaceholderToParameterIndexMap(localPlaceholderToParameterIndexMap); } } finally { connectionLock.unlock(); @@ -629,8 +632,8 @@ private CallableStatementParam checkIsOutputParam(int paramIndex) throws SQLExce int localParamIndex = paramIndex - 1; - if (this.placeholderToParameterIndexMap != null) { - localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; + if (this.paramInfo.placeholderToParameterIndexMap != null) { + localParamIndex = this.paramInfo.placeholderToParameterIndexMap[localParamIndex]; } CallableStatementParam paramDescriptor = this.paramInfo.getParameter(localParamIndex); @@ -1529,12 +1532,12 @@ protected int getNamedParamIndex(String paramName, boolean forOut) throws SQLExc MysqlErrorNumbers.SQLSTATE_CONNJ_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } - if (this.placeholderToParameterIndexMap == null) { + if (this.paramInfo.placeholderToParameterIndexMap == null) { return namedParamInfo.index + 1; // JDBC indices are 1-based } - for (int i = 0; i < this.placeholderToParameterIndexMap.length; i++) { - if (this.placeholderToParameterIndexMap[i] == namedParamInfo.index) { + for (int i = 0; i < this.paramInfo.placeholderToParameterIndexMap.length; i++) { + if (this.paramInfo.placeholderToParameterIndexMap[i] == namedParamInfo.index) { return i + 1; } } @@ -1694,7 +1697,7 @@ public ParameterMetaData getParameterMetaData() throws SQLException { Lock connectionLock = checkClosed().getConnectionLock(); connectionLock.lock(); try { - if (this.placeholderToParameterIndexMap == null) { + if (this.paramInfo.placeholderToParameterIndexMap == null) { return this.paramInfo; } @@ -1988,8 +1991,8 @@ protected int mapOutputParameterIndexToRsIndex(int paramIndex) throws SQLExcepti int localParamIndex = paramIndex - 1; - if (this.placeholderToParameterIndexMap != null) { - localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; + if (this.paramInfo.placeholderToParameterIndexMap != null) { + localParamIndex = this.paramInfo.placeholderToParameterIndexMap[localParamIndex]; } int rsIndex = this.parameterIndexToRsIndex[localParamIndex]; @@ -2374,14 +2377,14 @@ private void setOutParams() throws SQLException { int outParamIndex = 0; - if (this.placeholderToParameterIndexMap == null) { + if (this.paramInfo.placeholderToParameterIndexMap == null) { outParamIndex = outParamInfo.index + 1; } else { // Find it, todo: remove this linear search boolean found = false; - for (int i = 0; i < this.placeholderToParameterIndexMap.length; i++) { - if (this.placeholderToParameterIndexMap[i] == outParamInfo.index) { + for (int i = 0; i < this.paramInfo.placeholderToParameterIndexMap.length; i++) { + if (this.paramInfo.placeholderToParameterIndexMap[i] == outParamInfo.index) { outParamIndex = i + 1; /* JDBC is 1-based */ found = true; break; diff --git a/src/test/java/testsuite/regression/CallableStatementRegressionTest.java b/src/test/java/testsuite/regression/CallableStatementRegressionTest.java index 16b4abc5f..744274725 100644 --- a/src/test/java/testsuite/regression/CallableStatementRegressionTest.java +++ b/src/test/java/testsuite/regression/CallableStatementRegressionTest.java @@ -1906,4 +1906,46 @@ public void testBug36936407() throws Exception { } } + /** + * Tests fix for Bug#115265 (Bug#36843227), Second stored procedure call with cacheCallableStmts might fail. + * + * @throws Exception + */ + @Test + public void testBug115265() throws Exception { + createProcedure("testBug115265Procedure", "(IN a INT, IN b INT, OUT c INT) BEGIN SELECT a + b INTO c; END"); + createFunction("testBug115265Function", "(a INT, b INT) RETURNS INT DETERMINISTIC RETURN a + b"); + Properties props = new Properties(); + props.setProperty(PropertyKey.cacheCallableStmts.getKeyName(), "true"); + try (Connection testConn = getConnectionWithProps(props)) { + final String testProcedureQuery1 = "{CALL testBug115265Procedure(?,?,?)}"; + final String testProcedureQuery2 = "{CALL testBug115265Procedure(1,?,?)}"; + final String testFunctionQuery1 = "{? = CALL testBug115265Function(?,?)}"; + final String testFunctionQuery2 = "{? = CALL testBug115265Function(1,?)}"; + + testBug115265RunTest(testConn, testProcedureQuery1, 3, true); + testBug115265RunTest(testConn, testProcedureQuery1, 3, true); + + testBug115265RunTest(testConn, testProcedureQuery2, 2, true); + testBug115265RunTest(testConn, testProcedureQuery2, 2, true); + + testBug115265RunTest(testConn, testFunctionQuery1, 3, false); + testBug115265RunTest(testConn, testFunctionQuery1, 3, false); + + testBug115265RunTest(testConn, testFunctionQuery2, 2, false); + testBug115265RunTest(testConn, testFunctionQuery2, 2, false); + } + } + + private void testBug115265RunTest(Connection testConn, String query, int placeholderCount, boolean isProcedure) throws SQLException { + CallableStatement testCstmt = testConn.prepareCall(query); + testCstmt.setInt("b", 10); + if (placeholderCount == 3) { + testCstmt.setInt("a", 1); + } + testCstmt.registerOutParameter(isProcedure ? placeholderCount : 1, Types.INTEGER); + testCstmt.execute(); + assertEquals(11, isProcedure ? testCstmt.getInt("c") : testCstmt.getInt(1)); + } + }