From: Date: March 9 2006 10:00pm Subject: Connector/J commit: r5035 - in branches: branch_3_1/connector-j/src/com/mysql/jdbc branch_3_1/connector-j/src/testsuite/regression branch_5_0/connector-j/src/com/mysql/jdbc branch_5_0/connector-j/src/testsuite/regression branch_5_1/connector-j/src/com/mysql/jdbc branch_5_1/connector-j/src/testsuite/regression List-Archive: http://lists.mysql.com/commits/3667 X-Bug: 17898 Message-Id: <200603092100.k29L0JaW004989@bk-internal.mysql.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modified: branches/branch_3_1/connector-j/src/com/mysql/jdbc/CallableStatement.java branches/branch_3_1/connector-j/src/testsuite/regression/CallableStatementRegressionTest.java branches/branch_5_0/connector-j/src/com/mysql/jdbc/CallableStatement.java branches/branch_5_0/connector-j/src/testsuite/regression/CallableStatementRegressionTest.java branches/branch_5_1/connector-j/src/com/mysql/jdbc/CallableStatement.java branches/branch_5_1/connector-j/src/testsuite/regression/CallableStatementRegressionTest.java Log: Fixed BUG#17898 - registerOutParameter not working when some parameters pre-populated. Still waiting for feedback from JDBC experts group to determine what correct parameter count from getMetaData() should be, however. Modified: branches/branch_3_1/connector-j/src/com/mysql/jdbc/CallableStatement.java =================================================================== --- branches/branch_3_1/connector-j/src/com/mysql/jdbc/CallableStatement.java 2006-03-09 20:56:13 UTC (rev 5034) +++ branches/branch_3_1/connector-j/src/com/mysql/jdbc/CallableStatement.java 2006-03-09 21:00:13 UTC (rev 5035) @@ -377,8 +377,52 @@ super(conn, catalog, null); determineParameterTypes(); + generateParameterMap(); } + private int[] placeholderToParameterIndexMap; + + private void generateParameterMap() throws SQLException { + // if the user specified some parameters as literals, we need to + // provide a map from the specified placeholders to the actual + // parameter numbers + + if (this.parameterCount != this.paramInfo.getParameterCount()) { + this.placeholderToParameterIndexMap = new int[this.parameterCount]; + + int startPos = StringUtils.indexOfIgnoreCase(this.originalSql, + "CALL"); + if (startPos != -1) { + int parenOpenPos = this.originalSql.indexOf('(', startPos + 4); + + if (parenOpenPos != -1) { + int parenClosePos = StringUtils.indexOfIgnoreCaseRespectQuotes(parenOpenPos, + this.originalSql, ")", '\'', true); + + if (parenClosePos != -1) { + List parsedParameters = StringUtils.split(this.originalSql.substring(parenOpenPos + 1, parenClosePos), ",", "'\"", "'\"", true); + + int numParsedParameters = parsedParameters.size(); + + // sanity check + + if (numParsedParameters != this.parameterCount) { + // bail? + } + + int placeholderCount = 0; + + for (int i = 0; i < numParsedParameters; i++) { + if (((String)parsedParameters.get(i)).equals("?")) { + this.placeholderToParameterIndexMap[placeholderCount++] = i; + } + } + } + } + } + } + } + /** * Creates a new CallableStatement * @@ -399,6 +443,7 @@ this.callingStoredFunction = isFunctionCall; determineParameterTypes(); + generateParameterMap(); } /* @@ -436,6 +481,10 @@ int localParamIndex = paramIndex - 1; + if (this.placeholderToParameterIndexMap != null) { + localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; + } + CallableStatementParam paramDescriptor = this.paramInfo .getParameter(localParamIndex); @@ -1114,7 +1163,19 @@ SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } - return namedParamInfo.index + 1; // JDBC indices are 1-based + + if (this.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) { + return i + 1; + } + } + + throw new SQLException("Can't find local placeholder mapping for parameter named \"" + + paramName + "\".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } /** @@ -1180,6 +1241,11 @@ return retValue; } + + public synchronized ParameterMetaData getParameterMetaData() + throws SQLException { + return (CallableStatementParamInfoJDBC3) this.paramInfo; + } /** * Returns the ResultSet that holds the output parameters, or throws an @@ -1459,6 +1525,10 @@ checkParameterIndexBounds(paramIndex); int localParamIndex = paramIndex - 1; + + if (this.placeholderToParameterIndexMap != null) { + localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; + } int rsIndex = this.parameterIndexToRsIndex[localParamIndex]; @@ -1831,7 +1901,15 @@ if (outParamInfo.isOut) { String outParameterName = mangleParameterName(outParamInfo.paramName); - this.setBytesNoEscapeNoQuotes(outParamInfo.index + 1, + int outParamIndex; + + if (this.placeholderToParameterIndexMap == null) { + outParamIndex = outParamInfo.index + 1; + } else { + outParamIndex = this.placeholderToParameterIndexMap[outParamInfo.index - 1 /* JDBC is 1-based */]; + } + + this.setBytesNoEscapeNoQuotes(outParamIndex, StringUtils.getBytes(outParameterName, this.charConverter, this.charEncoding, this.connection Modified: branches/branch_3_1/connector-j/src/testsuite/regression/CallableStatementRegressionTest.java =================================================================== --- branches/branch_3_1/connector-j/src/testsuite/regression/CallableStatementRegressionTest.java 2006-03-09 20:56:13 UTC (rev 5034) +++ branches/branch_3_1/connector-j/src/testsuite/regression/CallableStatementRegressionTest.java 2006-03-09 21:00:13 UTC (rev 5035) @@ -623,4 +623,29 @@ } } } + + /** + * Tests fix for BUG#17898 - registerOutParameter not working when some + * parameters pre-populated. Still waiting for feedback from JDBC experts + * group to determine what correct parameter count from getMetaData() + * should be, however. + * + * @throws Exception if the test fails + */ + public void testBug17898() throws Exception { + if (versionMeetsMinimum(5, 0)) { + this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug17898"); + this.stmt.executeUpdate("CREATE PROCEDURE testBug17898(param1 VARCHAR(50), OUT param2 INT)\nBEGIN\nDECLARE rtn INT;\nSELECT 1 INTO rtn;\nSET param2=rtn;\nEND"); + + CallableStatement cstmt = this.conn.prepareCall("{CALL testBug17898('foo', ?)}"); + cstmt.registerOutParameter(1, Types.INTEGER); + cstmt.execute(); + assertEquals(1, cstmt.getInt(1)); + + cstmt.clearParameters(); + cstmt.registerOutParameter("param2", Types.INTEGER); + cstmt.execute(); + assertEquals(1, cstmt.getInt(1)); + } + } } Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/CallableStatement.java =================================================================== --- branches/branch_5_0/connector-j/src/com/mysql/jdbc/CallableStatement.java 2006-03-09 20:56:13 UTC (rev 5034) +++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/CallableStatement.java 2006-03-09 21:00:13 UTC (rev 5035) @@ -379,8 +379,52 @@ super(conn, catalog, null); determineParameterTypes(); + generateParameterMap(); } + private int[] placeholderToParameterIndexMap; + + private void generateParameterMap() throws SQLException { + // if the user specified some parameters as literals, we need to + // provide a map from the specified placeholders to the actual + // parameter numbers + + if (this.parameterCount != this.paramInfo.getParameterCount()) { + this.placeholderToParameterIndexMap = new int[this.parameterCount]; + + int startPos = StringUtils.indexOfIgnoreCase(this.originalSql, + "CALL"); + if (startPos != -1) { + int parenOpenPos = this.originalSql.indexOf('(', startPos + 4); + + if (parenOpenPos != -1) { + int parenClosePos = StringUtils.indexOfIgnoreCaseRespectQuotes(parenOpenPos, + this.originalSql, ")", '\'', true); + + if (parenClosePos != -1) { + List parsedParameters = StringUtils.split(this.originalSql.substring(parenOpenPos + 1, parenClosePos), ",", "'\"", "'\"", true); + + int numParsedParameters = parsedParameters.size(); + + // sanity check + + if (numParsedParameters != this.parameterCount) { + // bail? + } + + int placeholderCount = 0; + + for (int i = 0; i < numParsedParameters; i++) { + if (((String)parsedParameters.get(i)).equals("?")) { + this.placeholderToParameterIndexMap[placeholderCount++] = i; + } + } + } + } + } + } + } + /** * Creates a new CallableStatement * @@ -401,6 +445,7 @@ this.callingStoredFunction = isFunctionCall; determineParameterTypes(); + generateParameterMap(); } /* @@ -438,6 +483,10 @@ int localParamIndex = paramIndex - 1; + if (this.placeholderToParameterIndexMap != null) { + localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; + } + CallableStatementParam paramDescriptor = this.paramInfo .getParameter(localParamIndex); @@ -1094,14 +1143,14 @@ } private int getNamedParamIndex(String paramName, boolean forOut) - throws SQLException { + throws SQLException { if ((paramName == null) || (paramName.length() == 0)) { throw SQLError.createSQLException(Messages.getString("CallableStatement.2"), //$NON-NLS-1$ SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } CallableStatementParam namedParamInfo = this.paramInfo - .getParameter(paramName); + .getParameter(paramName); if (this.paramInfo == null) { throw SQLError.createSQLException( @@ -1112,11 +1161,23 @@ if (forOut && !namedParamInfo.isOut) { throw SQLError.createSQLException( Messages.getString("CallableStatement.5") + paramName //$NON-NLS-1$ - + Messages.getString("CallableStatement.6"), //$NON-NLS-1$ + + Messages.getString("CallableStatement.6"), //$NON-NLS-1$ SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } - return namedParamInfo.index + 1; // JDBC indices are 1-based + + if (this.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) { + return i + 1; + } + } + + throw SQLError.createSQLException("Can't find local placeholder mapping for parameter named \"" + + paramName + "\".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } /** @@ -1467,6 +1528,10 @@ int localParamIndex = paramIndex - 1; + if (this.placeholderToParameterIndexMap != null) { + localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; + } + int rsIndex = this.parameterIndexToRsIndex[localParamIndex]; if (rsIndex == NOT_OUTPUT_PARAMETER_INDICATOR) { @@ -1827,7 +1892,15 @@ if (outParamInfo.isOut) { String outParameterName = mangleParameterName(outParamInfo.paramName); - this.setBytesNoEscapeNoQuotes(outParamInfo.index + 1, + int outParamIndex; + + if (this.placeholderToParameterIndexMap == null) { + outParamIndex = outParamInfo.index + 1; + } else { + outParamIndex = this.placeholderToParameterIndexMap[outParamInfo.index - 1 /* JDBC is 1-based */]; + } + + this.setBytesNoEscapeNoQuotes(outParamIndex, StringUtils.getBytes(outParameterName, this.charConverter, this.charEncoding, this.connection Modified: branches/branch_5_0/connector-j/src/testsuite/regression/CallableStatementRegressionTest.java =================================================================== --- branches/branch_5_0/connector-j/src/testsuite/regression/CallableStatementRegressionTest.java 2006-03-09 20:56:13 UTC (rev 5034) +++ branches/branch_5_0/connector-j/src/testsuite/regression/CallableStatementRegressionTest.java 2006-03-09 21:00:13 UTC (rev 5035) @@ -590,4 +590,29 @@ } } } + + /** + * Tests fix for BUG#17898 - registerOutParameter not working when some + * parameters pre-populated. Still waiting for feedback from JDBC experts + * group to determine what correct parameter count from getMetaData() + * should be, however. + * + * @throws Exception if the test fails + */ + public void testBug17898() throws Exception { + if (versionMeetsMinimum(5, 0)) { + this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug17898"); + this.stmt.executeUpdate("CREATE PROCEDURE testBug17898(param1 VARCHAR(50), OUT param2 INT)\nBEGIN\nDECLARE rtn INT;\nSELECT 1 INTO rtn;\nSET param2=rtn;\nEND"); + + CallableStatement cstmt = this.conn.prepareCall("{CALL testBug17898('foo', ?)}"); + cstmt.registerOutParameter(1, Types.INTEGER); + cstmt.execute(); + assertEquals(1, cstmt.getInt(1)); + + cstmt.clearParameters(); + cstmt.registerOutParameter("param2", Types.INTEGER); + cstmt.execute(); + assertEquals(1, cstmt.getInt(1)); + } + } } Modified: branches/branch_5_1/connector-j/src/com/mysql/jdbc/CallableStatement.java =================================================================== --- branches/branch_5_1/connector-j/src/com/mysql/jdbc/CallableStatement.java 2006-03-09 20:56:13 UTC (rev 5034) +++ branches/branch_5_1/connector-j/src/com/mysql/jdbc/CallableStatement.java 2006-03-09 21:00:13 UTC (rev 5035) @@ -396,8 +396,52 @@ super(conn, catalog, null); determineParameterTypes(); + generateParameterMap(); } + private int[] placeholderToParameterIndexMap; + + private void generateParameterMap() throws SQLException { + // if the user specified some parameters as literals, we need to + // provide a map from the specified placeholders to the actual + // parameter numbers + + if (this.parameterCount != this.paramInfo.getParameterCount()) { + this.placeholderToParameterIndexMap = new int[this.parameterCount]; + + int startPos = StringUtils.indexOfIgnoreCase(this.originalSql, + "CALL"); + if (startPos != -1) { + int parenOpenPos = this.originalSql.indexOf('(', startPos + 4); + + if (parenOpenPos != -1) { + int parenClosePos = StringUtils.indexOfIgnoreCaseRespectQuotes(parenOpenPos, + this.originalSql, ")", '\'', true); + + if (parenClosePos != -1) { + List parsedParameters = StringUtils.split(this.originalSql.substring(parenOpenPos + 1, parenClosePos), ",", "'\"", "'\"", true); + + int numParsedParameters = parsedParameters.size(); + + // sanity check + + if (numParsedParameters != this.parameterCount) { + // bail? + } + + int placeholderCount = 0; + + for (int i = 0; i < numParsedParameters; i++) { + if (((String)parsedParameters.get(i)).equals("?")) { + this.placeholderToParameterIndexMap[placeholderCount++] = i; + } + } + } + } + } + } + } + /** * Creates a new CallableStatement * @@ -418,6 +462,7 @@ this.callingStoredFunction = isFunctionCall; determineParameterTypes(); + generateParameterMap(); } /* @@ -455,6 +500,10 @@ int localParamIndex = paramIndex - 1; + if (this.placeholderToParameterIndexMap != null) { + localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; + } + CallableStatementParam paramDescriptor = this.paramInfo .getParameter(localParamIndex); @@ -1178,7 +1227,18 @@ SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } - return namedParamInfo.index + 1; // JDBC indices are 1-based + if (this.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) { + return i + 1; + } + } + + throw SQLError.createSQLException("Can't find local placeholder mapping for parameter named \"" + + paramName + "\".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } /** @@ -1642,6 +1702,10 @@ int localParamIndex = paramIndex - 1; + if (this.placeholderToParameterIndexMap != null) { + localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; + } + int rsIndex = this.parameterIndexToRsIndex[localParamIndex]; if (rsIndex == NOT_OUTPUT_PARAMETER_INDICATOR) { @@ -1987,7 +2051,9 @@ /** * @see java.sql.CallableStatement#setLong(java.lang.String, long) */ - public void setLong(String parameterName, long x) throws SQLException { + public void setNCharacterStream(int parameterIndex, Reader value, + long length) throws SQLException { + throw new ToBeImplementedException(); setLong(getNamedParamIndex(parameterName, false), x); } @@ -2030,6 +2096,7 @@ * @see java.sql.CallableStatement#setNull(java.lang.String, int) */ public void setNull(String parameterName, int sqlType) throws SQLException { + setNull(getNamedParamIndex(parameterName, false), sqlType); } /** @@ -2038,6 +2105,7 @@ */ public void setNull(String parameterName, int sqlType, String typeName) throws SQLException { + setNull(getNamedParamIndex(parameterName, false), sqlType, typeName); } /** @@ -2075,7 +2143,15 @@ if (outParamInfo.isOut) { String outParameterName = mangleParameterName(outParamInfo.paramName); - this.setBytesNoEscapeNoQuotes(outParamInfo.index + 1, + int outParamIndex; + + if (this.placeholderToParameterIndexMap == null) { + outParamIndex = outParamInfo.index + 1; + } else { + outParamIndex = this.placeholderToParameterIndexMap[outParamInfo.index - 1 /* JDBC is 1-based */]; + } + + this.setBytesNoEscapeNoQuotes(outParamIndex, StringUtils.getBytes(outParameterName, this.charConverter, this.charEncoding, this.connection Modified: branches/branch_5_1/connector-j/src/testsuite/regression/CallableStatementRegressionTest.java =================================================================== --- branches/branch_5_1/connector-j/src/testsuite/regression/CallableStatementRegressionTest.java 2006-03-09 20:56:13 UTC (rev 5034) +++ branches/branch_5_1/connector-j/src/testsuite/regression/CallableStatementRegressionTest.java 2006-03-09 21:00:13 UTC (rev 5035) @@ -590,4 +590,29 @@ } } } + + /** + * Tests fix for BUG#17898 - registerOutParameter not working when some + * parameters pre-populated. Still waiting for feedback from JDBC experts + * group to determine what correct parameter count from getMetaData() + * should be, however. + * + * @throws Exception if the test fails + */ + public void testBug17898() throws Exception { + if (versionMeetsMinimum(5, 0)) { + this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug17898"); + this.stmt.executeUpdate("CREATE PROCEDURE testBug17898(param1 VARCHAR(50), OUT param2 INT)\nBEGIN\nDECLARE rtn INT;\nSELECT 1 INTO rtn;\nSET param2=rtn;\nEND"); + + CallableStatement cstmt = this.conn.prepareCall("{CALL testBug17898('foo', ?)}"); + cstmt.registerOutParameter(1, Types.INTEGER); + cstmt.execute(); + assertEquals(1, cstmt.getInt(1)); + + cstmt.clearParameters(); + cstmt.registerOutParameter("param2", Types.INTEGER); + cstmt.execute(); + assertEquals(1, cstmt.getInt(1)); + } + } }