/* Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/J is licensed under the terms of the GPLv2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FOSS License Exception <http://www.mysql.com/about/legal/licensing/foss-exception.html>. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package testsuite.regression; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.ParameterMetaData; import java.sql.PreparedStatement; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import java.util.Properties; import java.util.concurrent.Callable; import com.mysql.jdbc.NonRegisteringDriver; import com.mysql.jdbc.SQLError; import testsuite.BaseTestCase; /** * Tests fixes for bugs in CallableStatement code. */ public class CallableStatementRegressionTest extends BaseTestCase { public CallableStatementRegressionTest(String name) { super(name); } /** * Runs all test cases in this test suite * * @param args * ignored */ public static void main(String[] args) { junit.textui.TestRunner.run(CallableStatementRegressionTest.class); } /** * Tests fix for BUG#3539 getProcedures() does not return any procedures in * result set * * @throws Exception * if an error occurs. */ public void testBug3539() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createProcedure("testBug3539", "()\nBEGIN\nSELECT 1;end\n"); this.rs = this.conn.getMetaData().getProcedures(null, null, "testBug3539"); assertTrue(this.rs.next()); assertTrue("testBug3539".equals(this.rs.getString(3))); } /** * Tests fix for BUG#3540 getProcedureColumns doesn't work with wildcards * for procedure name * * @throws Exception * if an error occurs. */ public void testBug3540() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createProcedure("testBug3540", "(x int, out y int)\nBEGIN\nSELECT 1;end\n"); this.rs = this.conn.getMetaData().getProcedureColumns(null, null, "testBug3540%", "%"); assertTrue(this.rs.next()); assertEquals("testBug3540", this.rs.getString(3)); assertEquals("x", this.rs.getString(4)); assertTrue(this.rs.next()); assertEquals("testBug3540", this.rs.getString(3)); assertEquals("y", this.rs.getString(4)); assertTrue(!this.rs.next()); } /** * Tests fix for BUG#7026 - DBMD.getProcedures() doesn't respect catalog * parameter * * @throws Exception * if the test fails. */ public void testBug7026() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createProcedure("testBug7026", "(x int, out y int)\nBEGIN\nSELECT 1;end\n"); // // Should be found this time. // this.rs = this.conn.getMetaData().getProcedures(this.conn.getCatalog(), null, "testBug7026"); assertTrue(this.rs.next()); assertTrue("testBug7026".equals(this.rs.getString(3))); assertTrue(!this.rs.next()); // // This time, shouldn't be found, because not associated with this (bogus) catalog // this.rs = this.conn.getMetaData().getProcedures("abfgerfg", null, "testBug7026"); assertTrue(!this.rs.next()); // // Should be found this time as well, as we haven't specified a catalog. // this.rs = this.conn.getMetaData().getProcedures(null, null, "testBug7026"); assertTrue(this.rs.next()); assertTrue("testBug7026".equals(this.rs.getString(3))); assertTrue(!this.rs.next()); } /** * Tests fix for BUG#9319 -- Stored procedures with same name in different * databases confuse the driver when it tries to determine parameter * counts/types. * * @throws Exception * if the test fails */ public void testBug9319() throws Exception { if (!serverSupportsStoredProcedures()) { return; } boolean doASelect = true; // SELECT currently causes the server to hang on the last execution of this testcase, filed as BUG#9405 if (isAdminConnectionConfigured()) { Connection db2Connection = null; Connection db1Connection = null; db2Connection = getAdminConnection(); db1Connection = getAdminConnection(); Statement db1st = db1Connection.createStatement(); Statement db2st = db2Connection.createStatement(); createDatabase(db2st, "db_9319_2"); db2Connection.setCatalog("db_9319_2"); createProcedure(db2st, "COMPROVAR_USUARI", "(IN p_CodiUsuari VARCHAR(10),\nIN p_contrasenya VARCHAR(10),\nOUT p_userId INTEGER," + "\nOUT p_userName VARCHAR(30),\nOUT p_administrador VARCHAR(1),\nOUT p_idioma VARCHAR(2))\nBEGIN" + (doASelect ? "\nselect 2;" : "\nSELECT 2 INTO p_administrador;") + "\nEND"); createDatabase(db1st, "db_9319_1"); db1Connection.setCatalog("db_9319_1"); createProcedure(db1st, "COMPROVAR_USUARI", "(IN p_CodiUsuari VARCHAR(10),\nIN p_contrasenya VARCHAR(10),\nOUT p_userId INTEGER," + "\nOUT p_userName VARCHAR(30),\nOUT p_administrador VARCHAR(1))\nBEGIN" + (doASelect ? "\nselect 1;" : "\nSELECT 1 INTO p_administrador;") + "\nEND"); CallableStatement cstmt = db2Connection.prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?, ?) }"); cstmt.setString(1, "abc"); cstmt.setString(2, "def"); cstmt.registerOutParameter(3, java.sql.Types.INTEGER); cstmt.registerOutParameter(4, java.sql.Types.VARCHAR); cstmt.registerOutParameter(5, java.sql.Types.VARCHAR); cstmt.registerOutParameter(6, java.sql.Types.VARCHAR); cstmt.execute(); if (doASelect) { this.rs = cstmt.getResultSet(); assertTrue(this.rs.next()); assertEquals(2, this.rs.getInt(1)); } else { assertEquals(2, cstmt.getInt(5)); } cstmt = db1Connection.prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?, ?) }"); cstmt.setString(1, "abc"); cstmt.setString(2, "def"); cstmt.registerOutParameter(3, java.sql.Types.INTEGER); cstmt.registerOutParameter(4, java.sql.Types.VARCHAR); cstmt.registerOutParameter(5, java.sql.Types.VARCHAR); try { cstmt.registerOutParameter(6, java.sql.Types.VARCHAR); fail("Should've thrown an exception"); } catch (SQLException sqlEx) { assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); } cstmt = db1Connection.prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?) }"); cstmt.setString(1, "abc"); cstmt.setString(2, "def"); cstmt.registerOutParameter(3, java.sql.Types.INTEGER); cstmt.registerOutParameter(4, java.sql.Types.VARCHAR); cstmt.registerOutParameter(5, java.sql.Types.VARCHAR); cstmt.execute(); if (doASelect) { this.rs = cstmt.getResultSet(); assertTrue(this.rs.next()); assertEquals(1, this.rs.getInt(1)); } else { assertEquals(1, cstmt.getInt(5)); } String quoteChar = db2Connection.getMetaData().getIdentifierQuoteString(); cstmt = db2Connection.prepareCall( "{ call " + quoteChar + db1Connection.getCatalog() + quoteChar + "." + quoteChar + "COMPROVAR_USUARI" + quoteChar + "(?, ?, ?, ?, ?) }"); cstmt.setString(1, "abc"); cstmt.setString(2, "def"); cstmt.registerOutParameter(3, java.sql.Types.INTEGER); cstmt.registerOutParameter(4, java.sql.Types.VARCHAR); cstmt.registerOutParameter(5, java.sql.Types.VARCHAR); cstmt.execute(); if (doASelect) { this.rs = cstmt.getResultSet(); assertTrue(this.rs.next()); assertEquals(1, this.rs.getInt(1)); } else { assertEquals(1, cstmt.getInt(5)); } } } /* * public void testBug9319() throws Exception { boolean doASelect = false; * // SELECT currently causes the server to hang on the // last execution of * this testcase, filed as BUG#9405 * * if (versionMeetsMinimum(5, 0, 2)) { if (isAdminConnectionConfigured()) { * Connection db2Connection = null; Connection db1Connection = null; * * try { db2Connection = getAdminConnection(); * * db2Connection.createStatement().executeUpdate( "CREATE DATABASE IF NOT * EXISTS db_9319"); db2Connection.setCatalog("db_9319"); * * db2Connection.createStatement().executeUpdate( "DROP PROCEDURE IF EXISTS * COMPROVAR_USUARI"); * * db2Connection.createStatement().executeUpdate( "CREATE PROCEDURE * COMPROVAR_USUARI(IN p_CodiUsuari VARCHAR(10)," + "\nIN p_contrasenya * VARCHAR(10)," + "\nOUT p_userId INTEGER," + "\nOUT p_userName * VARCHAR(30)," + "\nOUT p_administrador VARCHAR(1)," + "\nOUT p_idioma * VARCHAR(2))" + "\nBEGIN" + (doASelect ? "\nselect 2;" : "\nSELECT 2 INTO * p_administrador;" ) + "\nEND"); * * this.stmt .executeUpdate("DROP PROCEDURE IF EXISTS COMPROVAR_USUARI"); * this.stmt .executeUpdate("CREATE PROCEDURE COMPROVAR_USUARI(IN * p_CodiUsuari VARCHAR(10)," + "\nIN p_contrasenya VARCHAR(10)," + "\nOUT * p_userId INTEGER," + "\nOUT p_userName VARCHAR(30)," + "\nOUT * p_administrador VARCHAR(1))" + "\nBEGIN" + (doASelect ? "\nselect 1;" : * "\nSELECT 1 INTO p_administrador;" ) + "\nEND"); * * CallableStatement cstmt = db2Connection .prepareCall("{ call * COMPROVAR_USUARI(?, ?, ?, ?, ?, ?) }"); cstmt.setString(1, "abc"); * cstmt.setString(2, "def"); cstmt.registerOutParameter(3, * java.sql.Types.INTEGER); cstmt.registerOutParameter(4, * java.sql.Types.VARCHAR); cstmt.registerOutParameter(5, * java.sql.Types.VARCHAR); * * cstmt.registerOutParameter(6, java.sql.Types.VARCHAR); * * cstmt.execute(); * * if (doASelect) { this.rs = cstmt.getResultSet(); * assertTrue(this.rs.next()); assertEquals(2, this.rs.getInt(1)); } else { * assertEquals(2, cstmt.getInt(5)); } * * cstmt = this.conn .prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?, ?) * }"); cstmt.setString(1, "abc"); cstmt.setString(2, "def"); * cstmt.registerOutParameter(3, java.sql.Types.INTEGER); * cstmt.registerOutParameter(4, java.sql.Types.VARCHAR); * cstmt.registerOutParameter(5, java.sql.Types.VARCHAR); * * try { cstmt.registerOutParameter(6, java.sql.Types.VARCHAR); * fail("Should've thrown an exception"); } catch (SQLException sqlEx) { * assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx .getSQLState()); * } * * cstmt = this.conn .prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?) * }"); cstmt.setString(1, "abc"); cstmt.setString(2, "def"); * cstmt.registerOutParameter(3, java.sql.Types.INTEGER); * cstmt.registerOutParameter(4, java.sql.Types.VARCHAR); * cstmt.registerOutParameter(5, java.sql.Types.VARCHAR); * * cstmt.execute(); * * if (doASelect) { this.rs = cstmt.getResultSet(); * assertTrue(this.rs.next()); assertEquals(1, this.rs.getInt(1)); } else { * assertEquals(1, cstmt.getInt(5)); } * * String quoteChar = * db2Connection.getMetaData().getIdentifierQuoteString(); * * cstmt = db2Connection .prepareCall("{ call " + quoteChar + * this.conn.getCatalog() + quoteChar + "." + quoteChar + "COMPROVAR_USUARI" * + quoteChar + "(?, ?, ?, ?, ?) }"); cstmt.setString(1, "abc"); * cstmt.setString(2, "def"); cstmt.registerOutParameter(3, * java.sql.Types.INTEGER); cstmt.registerOutParameter(4, * java.sql.Types.VARCHAR); cstmt.registerOutParameter(5, * java.sql.Types.VARCHAR); * * cstmt.execute(); * * if (doASelect) { this.rs = cstmt.getResultSet(); * assertTrue(this.rs.next()); assertEquals(1, this.rs.getInt(1)); } else { * assertEquals(1, cstmt.getInt(5)); } } finally { if (db2Connection != * null) { db2Connection.createStatement().executeUpdate( "DROP PROCEDURE IF * EXISTS COMPROVAR_USUARI"); // * db2Connection.createStatement().executeUpdate( // "DROP DATABASE IF * EXISTS db_9319"); } * * this.stmt .executeUpdate("DROP PROCEDURE IF EXISTS COMPROVAR_USUARI"); } * } } } */ /** * Tests fix for BUG#9682 - Stored procedures with DECIMAL parameters with * storage specifications that contained "," in them would fail. * * @throws Exception * if the test fails. */ public void testBug9682() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createProcedure("testBug9682", "(decimalParam DECIMAL(18,0))\nBEGIN\n SELECT 1;\nEND"); CallableStatement cStmt = null; try { cStmt = this.conn.prepareCall("Call testBug9682(?)"); cStmt.setDouble(1, 18.0); cStmt.execute(); } finally { if (cStmt != null) { cStmt.close(); } } } /** * Tests fix forBUG#10310 - Driver doesn't support {?=CALL(...)} for calling * stored functions. This involved adding support for function retrieval to * DatabaseMetaData.getProcedures() and getProcedureColumns() as well. * * @throws Exception * if the test fails. */ public void testBug10310() throws Exception { if (!serverSupportsStoredProcedures()) { return; } CallableStatement cStmt = null; try { this.stmt.executeUpdate("DROP FUNCTION IF EXISTS testBug10310"); this.stmt.executeUpdate("CREATE FUNCTION testBug10310(a float, b bigint, c int) RETURNS INT NO SQL\nBEGIN\nRETURN a;\nEND"); cStmt = this.conn.prepareCall("{? = CALL testBug10310(?,?,?)}"); cStmt.registerOutParameter(1, Types.INTEGER); cStmt.setFloat(2, 2); cStmt.setInt(3, 1); cStmt.setInt(4, 1); assertEquals(4, cStmt.getParameterMetaData().getParameterCount()); assertEquals(Types.INTEGER, cStmt.getParameterMetaData().getParameterType(1)); java.sql.DatabaseMetaData dbmd = this.conn.getMetaData(); this.rs = ((com.mysql.jdbc.DatabaseMetaData) dbmd).getFunctionColumns(this.conn.getCatalog(), null, "testBug10310", "%"); ResultSetMetaData rsmd = this.rs.getMetaData(); assertEquals(17, rsmd.getColumnCount()); assertEquals("FUNCTION_CAT", rsmd.getColumnName(1)); assertEquals("FUNCTION_SCHEM", rsmd.getColumnName(2)); assertEquals("FUNCTION_NAME", rsmd.getColumnName(3)); assertEquals("COLUMN_NAME", rsmd.getColumnName(4)); assertEquals("COLUMN_TYPE", rsmd.getColumnName(5)); assertEquals("DATA_TYPE", rsmd.getColumnName(6)); assertEquals("TYPE_NAME", rsmd.getColumnName(7)); assertEquals("PRECISION", rsmd.getColumnName(8)); assertEquals("LENGTH", rsmd.getColumnName(9)); assertEquals("SCALE", rsmd.getColumnName(10)); assertEquals("RADIX", rsmd.getColumnName(11)); assertEquals("NULLABLE", rsmd.getColumnName(12)); assertEquals("REMARKS", rsmd.getColumnName(13)); assertEquals("CHAR_OCTET_LENGTH", rsmd.getColumnName(14)); assertEquals("ORDINAL_POSITION", rsmd.getColumnName(15)); assertEquals("IS_NULLABLE", rsmd.getColumnName(16)); assertEquals("SPECIFIC_NAME", rsmd.getColumnName(17)); this.rs.close(); assertFalse(cStmt.execute()); assertEquals(2f, cStmt.getInt(1), .001); assertEquals("java.lang.Integer", cStmt.getObject(1).getClass().getName()); assertEquals(-1, cStmt.executeUpdate()); assertEquals(2f, cStmt.getInt(1), .001); assertEquals("java.lang.Integer", cStmt.getObject(1).getClass().getName()); cStmt.setFloat("a", 4); cStmt.setInt("b", 1); cStmt.setInt("c", 1); assertFalse(cStmt.execute()); assertEquals(4f, cStmt.getInt(1), .001); assertEquals("java.lang.Integer", cStmt.getObject(1).getClass().getName()); assertEquals(-1, cStmt.executeUpdate()); assertEquals(4f, cStmt.getInt(1), .001); assertEquals("java.lang.Integer", cStmt.getObject(1).getClass().getName()); // Check metadata while we're at it this.rs = dbmd.getProcedures(this.conn.getCatalog(), null, "testBug10310"); this.rs.next(); assertEquals("testBug10310", this.rs.getString("PROCEDURE_NAME")); assertEquals(java.sql.DatabaseMetaData.procedureReturnsResult, this.rs.getShort("PROCEDURE_TYPE")); cStmt.setNull(2, Types.FLOAT); cStmt.setInt(3, 1); cStmt.setInt(4, 1); assertFalse(cStmt.execute()); assertEquals(0f, cStmt.getInt(1), .001); assertEquals(true, cStmt.wasNull()); assertEquals(null, cStmt.getObject(1)); assertEquals(true, cStmt.wasNull()); assertEquals(-1, cStmt.executeUpdate()); assertEquals(0f, cStmt.getInt(1), .001); assertEquals(true, cStmt.wasNull()); assertEquals(null, cStmt.getObject(1)); assertEquals(true, cStmt.wasNull()); // Check with literals, not all parameters filled! cStmt = this.conn.prepareCall("{? = CALL testBug10310(4,5,?)}"); cStmt.registerOutParameter(1, Types.INTEGER); cStmt.setInt(2, 1); assertFalse(cStmt.execute()); assertEquals(4f, cStmt.getInt(1), .001); assertEquals("java.lang.Integer", cStmt.getObject(1).getClass().getName()); assertEquals(-1, cStmt.executeUpdate()); assertEquals(4f, cStmt.getInt(1), .001); assertEquals("java.lang.Integer", cStmt.getObject(1).getClass().getName()); assertEquals(2, cStmt.getParameterMetaData().getParameterCount()); assertEquals(Types.INTEGER, cStmt.getParameterMetaData().getParameterType(1)); assertEquals(Types.INTEGER, cStmt.getParameterMetaData().getParameterType(2)); } finally { if (this.rs != null) { this.rs.close(); this.rs = null; } if (cStmt != null) { cStmt.close(); } this.stmt.executeUpdate("DROP FUNCTION IF EXISTS testBug10310"); } } /** * Tests fix for Bug#12417 - stored procedure catalog name is case-sensitive * on Windows (this is actually a server bug, but we have a workaround in * place for it now). * * @throws Exception * if the test fails. */ public void testBug12417() throws Exception { if (serverSupportsStoredProcedures() && isServerRunningOnWindows()) { createProcedure("testBug12417", "()\nBEGIN\nSELECT 1;end\n"); Connection ucCatalogConn = null; try { ucCatalogConn = getConnectionWithProps((Properties) null); ucCatalogConn.setCatalog(this.conn.getCatalog().toUpperCase()); ucCatalogConn.prepareCall("{call testBug12417()}"); } finally { if (ucCatalogConn != null) { ucCatalogConn.close(); } } } } public void testBug15121() throws Exception { if (!this.DISABLED_testBug15121 /* needs to be fixed on server */) { if (versionMeetsMinimum(5, 0)) { createProcedure("p_testBug15121", "()\nBEGIN\nSELECT * from idonotexist;\nEND"); Properties props = new Properties(); props.setProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY, ""); Connection noDbConn = null; try { noDbConn = getConnectionWithProps(props); StringBuilder queryBuf = new StringBuilder("{call "); String quotedId = this.conn.getMetaData().getIdentifierQuoteString(); queryBuf.append(quotedId); queryBuf.append(this.conn.getCatalog()); queryBuf.append(quotedId); queryBuf.append(".p_testBug15121()}"); noDbConn.prepareCall(queryBuf.toString()).execute(); } finally { if (noDbConn != null) { noDbConn.close(); } } } } } /** * Tests fix for BUG#15464 - INOUT parameter does not store IN value. * * @throws Exception * if the test fails */ public void testBug15464() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createProcedure("testInOutParam", "(IN p1 VARCHAR(255), INOUT p2 INT)\nbegin\n DECLARE z INT;\n" + "SET z = p2 + 1;\nSET p2 = z;\nSELECT p1;\nSELECT CONCAT('zyxw', p1);\nend\n"); CallableStatement storedProc = null; storedProc = this.conn.prepareCall("{call testInOutParam(?, ?)}"); storedProc.setString(1, "abcd"); storedProc.setInt(2, 4); storedProc.registerOutParameter(2, Types.INTEGER); storedProc.execute(); assertEquals(5, storedProc.getInt(2)); } /** * 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 (!serverSupportsStoredProcedures()) { return; } createProcedure("testBug17898", "(param1 VARCHAR(50), OUT param2 INT)\nBEGIN\nDECLARE rtn INT;\n" + "SELECT 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)); } /** * Tests fix for BUG#21462 - JDBC (and ODBC) specifications allow * no-parenthesis CALL statements for procedures with no arguments, MySQL * server does not. * * @throws Exception * if the test fails. */ public void testBug21462() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createProcedure("testBug21462", "() BEGIN SELECT 1; END"); CallableStatement cstmt = null; try { cstmt = this.conn.prepareCall("{CALL testBug21462}"); cstmt.execute(); } finally { if (cstmt != null) { cstmt.close(); } } } /** * Tests fix for BUG#22024 - Newlines causing whitespace to span confuse * procedure parser when getting parameter metadata for stored procedures. * * @throws Exception * if the test fails */ public void testBug22024() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createProcedure("testBug22024_1", "(\r\n)\r\n BEGIN SELECT 1; END"); createProcedure("testBug22024_2", "(\r\na INT)\r\n BEGIN SELECT 1; END"); CallableStatement cstmt = null; try { cstmt = this.conn.prepareCall("{CALL testBug22024_1()}"); cstmt.execute(); cstmt = this.conn.prepareCall("{CALL testBug22024_2(?)}"); cstmt.setInt(1, 1); cstmt.execute(); } finally { if (cstmt != null) { cstmt.close(); } } } /** * Tests workaround for server crash when calling stored procedures via a * server-side prepared statement (driver now detects prepare(stored * procedure) and substitutes client-side prepared statement). * * @throws Exception * if the test fails */ public void testBug22297() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createTable("tblTestBug2297_1", "(id varchar(20) NOT NULL default '',Income double(19,2) default NULL)"); createTable("tblTestBug2297_2", "(id varchar(20) NOT NULL default '',CreatedOn datetime default NULL)"); createProcedure("testBug22297", "(pcaseid INT) BEGIN\nSET @sql = \"DROP TEMPORARY TABLE IF EXISTS tmpOrders\";" + " PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;" + "\nSET @sql = \"CREATE TEMPORARY TABLE tmpOrders SELECT id, 100 AS Income FROM tblTestBug2297_1 GROUP BY id\"; PREPARE stmt FROM @sql;" + " EXECUTE stmt; DEALLOCATE PREPARE stmt;\n SELECT id, Income FROM (SELECT e.id AS id ,COALESCE(prof.Income,0) AS Income" + "\n FROM tblTestBug2297_2 e LEFT JOIN tmpOrders prof ON e.id = prof.id\n WHERE e.CreatedOn > '2006-08-01') AS Final ORDER BY id;\nEND"); this.stmt.executeUpdate("INSERT INTO tblTestBug2297_1 (`id`,`Income`) VALUES ('a',4094.00),('b',500.00),('c',3462.17), ('d',500.00), ('e',600.00)"); this.stmt.executeUpdate("INSERT INTO tblTestBug2297_2 (`id`,`CreatedOn`) VALUES ('d','2006-08-31 00:00:00'),('e','2006-08-31 00:00:00')," + "('b','2006-08-31 00:00:00'),('c','2006-08-31 00:00:00'),('a','2006-08-31 00:00:00')"); this.pstmt = this.conn.prepareStatement("{CALL testBug22297(?)}"); this.pstmt.setInt(1, 1); this.rs = this.pstmt.executeQuery(); String[] ids = new String[] { "a", "b", "c", "d", "e" }; int pos = 0; while (this.rs.next()) { assertEquals(ids[pos++], this.rs.getString(1)); assertEquals(100, this.rs.getInt(2)); } assertTrue(this.pstmt.getClass().getName().indexOf("Server") == -1); } public void testHugeNumberOfParameters() throws Exception { if (!serverSupportsStoredProcedures()) { return; } StringBuilder procDef = new StringBuilder("(OUT param_0 VARCHAR(32)"); StringBuilder placeholders = new StringBuilder("?"); for (int i = 1; i < 274; i++) { procDef.append(", OUT param_" + i + " VARCHAR(32)"); placeholders.append(",?"); } procDef.append(")\nBEGIN\nSELECT 1;\nEND"); createProcedure("testHugeNumberOfParameters", procDef.toString()); CallableStatement cStmt = null; try { cStmt = this.conn.prepareCall("{call testHugeNumberOfParameters(" + placeholders.toString() + ")}"); cStmt.registerOutParameter(274, Types.VARCHAR); cStmt.execute(); } finally { if (cStmt != null) { cStmt.close(); } } } public void testPrepareOfMultiRs() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createProcedure("p", "() begin select 1; select 2; end;"); PreparedStatement ps = null; try { ps = this.conn.prepareStatement("call p()"); ps.execute(); this.rs = ps.getResultSet(); assertTrue(this.rs.next()); assertEquals(1, this.rs.getInt(1)); assertTrue(ps.getMoreResults()); this.rs = ps.getResultSet(); assertTrue(this.rs.next()); assertEquals(2, this.rs.getInt(1)); assertTrue(!ps.getMoreResults()); } finally { if (this.rs != null) { this.rs.close(); this.rs = null; } if (ps != null) { ps.close(); } } } /** * Tests fix for BUG#25379 - INOUT parameters in CallableStatements get * doubly-escaped. * * @throws Exception * if the test fails. */ public void testBug25379() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createTable("testBug25379", "(col char(40))"); createProcedure("sp_testBug25379", "(INOUT invalue char(255))\nBEGIN" + "\ninsert into testBug25379(col) values(invalue);\nEND"); CallableStatement cstmt = this.conn.prepareCall("{call sp_testBug25379(?)}"); cstmt.setString(1, "'john'"); cstmt.executeUpdate(); assertEquals("'john'", cstmt.getString(1)); assertEquals("'john'", getSingleValue("testBug25379", "col", "").toString()); } /** * Tests fix for BUG#25715 - CallableStatements with OUT/INOUT parameters * that are "binary" have extra 7 bytes (which happens to be the _binary * introducer!) * * @throws Exception * if the test fails. */ public void testBug25715() throws Exception { if (!serverSupportsStoredProcedures()) { return; // no stored procs } createProcedure("spbug25715", "(INOUT mblob MEDIUMBLOB) BEGIN SELECT 1 FROM DUAL WHERE 1=0;\nEND"); CallableStatement cstmt = null; try { cstmt = this.conn.prepareCall("{call spbug25715(?)}"); byte[] buf = new byte[65]; for (int i = 0; i < 65; i++) { buf[i] = 1; } int il = buf.length; int[] typesToTest = new int[] { Types.BIT, Types.BINARY, Types.BLOB, Types.JAVA_OBJECT, Types.LONGVARBINARY, Types.VARBINARY }; for (int i = 0; i < typesToTest.length; i++) { cstmt.setBinaryStream("mblob", new ByteArrayInputStream(buf), buf.length); cstmt.registerOutParameter("mblob", typesToTest[i]); cstmt.executeUpdate(); InputStream is = cstmt.getBlob("mblob").getBinaryStream(); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); int bytesRead = 0; byte[] readBuf = new byte[256]; while ((bytesRead = is.read(readBuf)) != -1) { bOut.write(readBuf, 0, bytesRead); } byte[] fromSelectBuf = bOut.toByteArray(); int ol = fromSelectBuf.length; assertEquals(il, ol); } cstmt.close(); } finally { if (cstmt != null) { cstmt.close(); } } } protected boolean serverSupportsStoredProcedures() throws SQLException { return versionMeetsMinimum(5, 0); } public void testBug26143() throws Exception { if (!serverSupportsStoredProcedures()) { return; // no stored procedure support } try { dropProcedure("testBug26143"); this.stmt.executeUpdate("CREATE DEFINER=CURRENT_USER PROCEDURE testBug26143(I INT) COMMENT 'abcdefg'\nBEGIN\nSELECT I * 10;\nEND"); this.conn.prepareCall("{call testBug26143(?)").close(); } finally { dropProcedure("testBug26143"); } } /** * Tests fix for BUG#26959 - comments confuse procedure parser. * * @throws Exception * if the test fails */ public void testBug26959() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createProcedure("testBug26959", "(_ACTION varchar(20),\n`/*dumb-identifier-1*/` int,\n`#dumb-identifier-2` int,\n`--dumb-identifier-3` int," + "\n_CLIENT_ID int, -- ABC\n_LOGIN_ID int, # DEF\n_WHERE varchar(2000),\n_SORT varchar(2000)," + "\n out _SQL varchar(/* inline right here - oh my gosh! */ 8000),\n _SONG_ID int,\n _NOTES varchar(2000),\n out _RESULT varchar(10)" + "\n /*\n , -- Generic result parameter" + "\n out _PERIOD_ID int, -- Returns the period_id. Useful when using @PREDEFLINK to return which is the last period" + "\n _SONGS_LIST varchar(8000),\n _COMPOSERID int,\n _PUBLISHERID int," + "\n _PREDEFLINK int -- If the user is accessing through a predefined link: 0=none 1=last period\n */) BEGIN SELECT 1; END"); createProcedure("testBug26959_1", "(`/*id*/` /* before type 1 */ varchar(20)," + "/* after type 1 */ OUT result2 DECIMAL(/*size1*/10,/*size2*/2) /* p2 */)BEGIN SELECT action, result; END"); this.conn.prepareCall("{call testBug26959(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)}").close(); this.rs = this.conn.getMetaData().getProcedureColumns(this.conn.getCatalog(), null, "testBug26959", "%"); String[] parameterNames = new String[] { "_ACTION", "/*dumb-identifier-1*/", "#dumb-identifier-2", "--dumb-identifier-3", "_CLIENT_ID", "_LOGIN_ID", "_WHERE", "_SORT", "_SQL", "_SONG_ID", "_NOTES", "_RESULT" }; int[] parameterTypes = new int[] { Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.INTEGER, Types.INTEGER, Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.VARCHAR, Types.VARCHAR }; int[] direction = new int[] { java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnOut, java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnOut }; int[] precision = new int[] { 20, 10, 10, 10, 10, 10, 2000, 2000, 8000, 10, 2000, 10 }; int index = 0; while (this.rs.next()) { assertEquals(parameterNames[index], this.rs.getString("COLUMN_NAME")); assertEquals(parameterTypes[index], this.rs.getInt("DATA_TYPE")); switch (index) { case 0: case 6: case 7: case 8: case 10: case 11: assertEquals(precision[index], this.rs.getInt("LENGTH")); break; default: assertEquals(precision[index], this.rs.getInt("PRECISION")); } assertEquals(direction[index], this.rs.getInt("COLUMN_TYPE")); index++; } this.rs.close(); index = 0; parameterNames = new String[] { "/*id*/", "result2" }; parameterTypes = new int[] { Types.VARCHAR, Types.DECIMAL }; precision = new int[] { 20, 10 }; direction = new int[] { java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnOut }; int[] scale = new int[] { 0, 2 }; this.conn.prepareCall("{call testBug26959_1(?, ?)}").close(); this.rs = this.conn.getMetaData().getProcedureColumns(this.conn.getCatalog(), null, "testBug26959_1", "%"); while (this.rs.next()) { assertEquals(parameterNames[index], this.rs.getString("COLUMN_NAME")); assertEquals(parameterTypes[index], this.rs.getInt("DATA_TYPE")); switch (index) { case 0: case 6: case 7: case 8: case 10: case 11: assertEquals(precision[index], this.rs.getInt("LENGTH")); break; default: assertEquals(precision[index], this.rs.getInt("PRECISION")); } assertEquals(scale[index], this.rs.getInt("SCALE")); assertEquals(direction[index], this.rs.getInt("COLUMN_TYPE")); index++; } } /** * Tests fix for BUG#27400 - CALL [comment] some_proc() doesn't work */ public void testBug27400() throws Exception { if (!serverSupportsStoredProcedures()) { return; // SPs not supported } createProcedure("testBug27400", "(a INT, b VARCHAR(32)) BEGIN SELECT 1; END"); CallableStatement cStmt = null; try { cStmt = this.conn.prepareCall("{CALL /* SOME COMMENT */ testBug27400( /* does this work too? */ ?, ?)} # and a commented ? here too"); assertTrue(cStmt.toString().indexOf("/*") != -1); // we don't want // to strip the // comments cStmt.setInt(1, 1); cStmt.setString(2, "bleh"); cStmt.execute(); } finally { if (cStmt != null) { cStmt.close(); } } } /** * Tests fix for BUG#28689 - CallableStatement.executeBatch() doesn't work * when connection property "noAccessToProcedureBodies" has been set to * "true". * * The fix involves changing the behavior of "noAccessToProcedureBodies", in * that the driver will now report all paramters as "IN" paramters but allow * callers to call registerOutParameter() on them. * * @throws Exception */ public void testBug28689() throws Exception { if (!versionMeetsMinimum(5, 0)) { return; // no stored procedures } createTable("testBug28689", "(" + "`id` int(11) NOT NULL auto_increment,`usuario` varchar(255) default NULL,PRIMARY KEY (`id`))"); this.stmt.executeUpdate("INSERT INTO testBug28689 (usuario) VALUES ('AAAAAA')"); createProcedure("sp_testBug28689", "(tid INT)\nBEGIN\nUPDATE testBug28689 SET usuario = 'BBBBBB' WHERE id = tid;\nEND"); Connection noProcedureBodiesConn = getConnectionWithProps("noAccessToProcedureBodies=true"); CallableStatement cStmt = null; try { cStmt = noProcedureBodiesConn.prepareCall("{CALL sp_testBug28689(?)}"); cStmt.setInt(1, 1); cStmt.addBatch(); cStmt.executeBatch(); assertEquals("BBBBBB", getSingleIndexedValueWithQuery(noProcedureBodiesConn, 1, "SELECT `usuario` FROM testBug28689 WHERE id=1")); } finally { if (cStmt != null) { cStmt.close(); } if (noProcedureBodiesConn != null) { noProcedureBodiesConn.close(); } } } /** * Tests fix for Bug#31823 - CallableStatement.setNull() on a stored * function would throw an ArrayIndexOutOfBounds when setting the last * parameter to null when calling setNull(). * * @throws Exception */ public void testBug31823() throws Exception { if (!versionMeetsMinimum(5, 0)) { return; // no stored functions } createTable("testBug31823", "(value_1 BIGINT PRIMARY KEY,value_2 VARCHAR(20))"); createFunction("f_testBug31823", "(value_1_v BIGINT,value_2_v VARCHAR(20)) RETURNS BIGINT " + "DETERMINISTIC MODIFIES SQL DATA BEGIN INSERT INTO testBug31823 VALUES (value_1_v,value_2_v); RETURN value_1_v; END;"); // Prepare the function call CallableStatement callable = null; try { callable = this.conn.prepareCall("{? = call f_testBug31823(?,?)}"); callable.registerOutParameter(1, Types.BIGINT); // Add row with non-null value callable.setLong(2, 1); callable.setString(3, "Non-null value"); callable.executeUpdate(); assertEquals(1, callable.getLong(1)); // Add row with null value callable.setLong(2, 2); callable.setNull(3, Types.VARCHAR); callable.executeUpdate(); assertEquals(2, callable.getLong(1)); Method[] setters = CallableStatement.class.getMethods(); for (int i = 0; i < setters.length; i++) { if (setters[i].getName().startsWith("set")) { Class<?>[] args = setters[i].getParameterTypes(); if (args.length == 2 && args[0].equals(Integer.TYPE)) { if (!args[1].isPrimitive()) { try { setters[i].invoke(callable, new Object[] { new Integer(2), null }); } catch (InvocationTargetException ive) { if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { throw ive; } } } else { if (args[1].getName().equals("boolean")) { try { setters[i].invoke(callable, new Object[] { new Integer(2), Boolean.FALSE }); } catch (InvocationTargetException ive) { if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { throw ive; } } } if (args[1].getName().equals("byte")) { try { setters[i].invoke(callable, new Object[] { new Integer(2), new Byte((byte) 0) }); } catch (InvocationTargetException ive) { if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { throw ive; } } } if (args[1].getName().equals("double")) { try { setters[i].invoke(callable, new Object[] { new Integer(2), new Double(0) }); } catch (InvocationTargetException ive) { if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { throw ive; } } } if (args[1].getName().equals("float")) { try { setters[i].invoke(callable, new Object[] { new Integer(2), new Float(0) }); } catch (InvocationTargetException ive) { if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { throw ive; } } } if (args[1].getName().equals("int")) { try { setters[i].invoke(callable, new Object[] { new Integer(2), new Integer(0) }); } catch (InvocationTargetException ive) { if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { throw ive; } } } if (args[1].getName().equals("long")) { try { setters[i].invoke(callable, new Object[] { new Integer(2), new Long(0) }); } catch (InvocationTargetException ive) { if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { throw ive; } } } if (args[1].getName().equals("short")) { try { setters[i].invoke(callable, new Object[] { new Integer(2), new Short((short) 0) }); } catch (InvocationTargetException ive) { if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { throw ive; } } } } } } } } finally { if (callable != null) { callable.close(); } } } /** * Tests fix for Bug#32246 - When unpacking rows directly, we don't hand off * error message packets to the internal method which decodes them * correctly, so no exception is rasied, and the driver than hangs trying to * read rows that aren't there... * * @throws Exception * if the test fails */ public void testBug32246() throws Exception { if (!versionMeetsMinimum(5, 0)) { return; } dropTable("test_table_2"); dropTable("test_table_1"); doBug32246(this.conn); dropTable("test_table_2"); dropTable("test_table_1"); doBug32246(getConnectionWithProps("useDirectRowUnpack=false")); } private void doBug32246(Connection aConn) throws SQLException { createTable("test_table_1", "(value_1 BIGINT PRIMARY KEY) ENGINE=InnoDB"); this.stmt.executeUpdate("INSERT INTO test_table_1 VALUES (1)"); createTable("test_table_2", "(value_2 BIGINT PRIMARY KEY) ENGINE=InnoDB"); this.stmt.executeUpdate("DROP FUNCTION IF EXISTS test_function"); createFunction("test_function", "() RETURNS BIGINT DETERMINISTIC MODIFIES SQL DATA BEGIN DECLARE max_value BIGINT; " + "SELECT MAX(value_1) INTO max_value FROM test_table_2; RETURN max_value; END;"); CallableStatement callable = null; try { callable = aConn.prepareCall("{? = call test_function()}"); callable.registerOutParameter(1, Types.BIGINT); try { callable.executeUpdate(); fail("impossible; we should never get here."); } catch (SQLException sqlEx) { assertEquals("42S22", sqlEx.getSQLState()); } createTable("test_table_1", "(value_1 BIGINT PRIMARY KEY) ENGINE=InnoDB"); this.stmt.executeUpdate("INSERT INTO test_table_1 VALUES (1)"); createTable("test_table_2", "(value_2 BIGINT PRIMARY KEY, " + " FOREIGN KEY (value_2) REFERENCES test_table_1 (value_1) ON DELETE CASCADE) ENGINE=InnoDB"); createFunction("test_function", "(value BIGINT) RETURNS BIGINT DETERMINISTIC MODIFIES SQL DATA BEGIN " + "INSERT INTO test_table_2 VALUES (value); RETURN value; END;"); callable = aConn.prepareCall("{? = call test_function(?)}"); callable.registerOutParameter(1, Types.BIGINT); callable.setLong(2, 1); callable.executeUpdate(); callable.setLong(2, 2); try { callable.executeUpdate(); fail("impossible; we should never get here."); } catch (SQLException sqlEx) { assertEquals("23000", sqlEx.getSQLState()); } } finally { if (callable != null) { callable.close(); } } } public void testBitSp() throws Exception { if (!versionMeetsMinimum(5, 0)) { return; } createTable("`Bit_Tab`", "( `MAX_VAL` tinyint(1) default NULL, `MIN_VAL` tinyint(1) default NULL, `NULL_VAL` tinyint(1) default NULL)"); createProcedure("Bit_Proc", "(out MAX_PARAM TINYINT, out MIN_PARAM TINYINT, out NULL_PARAM TINYINT)" + "begin select MAX_VAL, MIN_VAL, NULL_VAL into MAX_PARAM, MIN_PARAM, NULL_PARAM from Bit_Tab; end"); Boolean minBooleanVal; Boolean oRetVal; String Min_Val_Query = "SELECT MIN_VAL from Bit_Tab"; //String sMaxBooleanVal = "1"; // sMaxBooleanVal = "true"; //Boolean bool = Boolean.valueOf("true"); String Min_Insert = "insert into Bit_Tab values(1,0,null)"; // System.out.println("Value to insert=" + extractVal(Min_Insert,1)); CallableStatement cstmt; this.stmt.executeUpdate("delete from Bit_Tab"); this.stmt.executeUpdate(Min_Insert); cstmt = this.conn.prepareCall("{call Bit_Proc(?,?,?)}"); System.out.println("register the output parameters"); cstmt.registerOutParameter(1, java.sql.Types.BIT); cstmt.registerOutParameter(2, java.sql.Types.BIT); cstmt.registerOutParameter(3, java.sql.Types.BIT); System.out.println("execute the procedure"); cstmt.executeUpdate(); System.out.println("invoke getBoolean method"); boolean bRetVal = cstmt.getBoolean(2); oRetVal = new Boolean(bRetVal); minBooleanVal = new Boolean("false"); this.rs = this.stmt.executeQuery(Min_Val_Query); if (oRetVal.equals(minBooleanVal)) { System.out.println("getBoolean returns the Minimum value "); } else { System.out.println("getBoolean() did not return the Minimum value, getBoolean Failed!"); } } public void testNotReallyCallableStatement() throws Exception { if (!versionMeetsMinimum(5, 0)) { return; } CallableStatement cstmt = null; try { this.stmt.executeUpdate("DROP TABLE IF EXISTS testNotReallyCallableStatement"); cstmt = this.conn.prepareCall("CREATE TABLE testNotReallyCallableStatement(field1 INT)"); } finally { this.stmt.executeUpdate("DROP TABLE IF EXISTS testNotReallyCallableStatement"); if (cstmt != null) { cstmt.close(); } } } public void testBug35199() throws Exception { if (!versionMeetsMinimum(5, 0)) { return; } createFunction("test_function", "(a varchar(40), b bigint(20), c varchar(80)) RETURNS bigint(20) LANGUAGE SQL DETERMINISTIC " + "MODIFIES SQL DATA COMMENT 'bbb' BEGIN RETURN 1; END; "); CallableStatement callable = null; try { callable = this.conn.prepareCall("{? = call test_function(?,101,?)}"); callable.registerOutParameter(1, Types.BIGINT); callable.setString(2, "FOO"); callable.setString(3, "BAR"); callable.executeUpdate(); } finally { if (callable != null) { callable.close(); } } } public void testBug49831() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createTable("testBug49831", "(val varchar(32))"); createProcedure("pTestBug49831", "(testval varchar(32)) BEGIN insert into testBug49831 (val) values (testval);END;"); execProcBug49831(this.conn); this.stmt.execute("TRUNCATE TABLE testBug49831"); assertEquals(0, getRowCount("testBug49831")); Connection noBodiesConn = getConnectionWithProps("noAccessToProcedureBodies=true,jdbcCompliantTruncation=false,characterEncoding=utf8,useUnicode=yes"); try { execProcBug49831(noBodiesConn); } finally { noBodiesConn.close(); } } private void execProcBug49831(Connection c) throws Exception { CallableStatement cstmt = c.prepareCall("{call pTestBug49831(?)}"); cstmt.setObject(1, "abc", Types.VARCHAR, 32); cstmt.addBatch(); cstmt.setObject(1, "def", Types.VARCHAR, 32); cstmt.addBatch(); cstmt.executeBatch(); assertEquals(2, getRowCount("testBug49831")); this.rs = this.stmt.executeQuery("SELECT * from testBug49831 ORDER BY VAL ASC"); this.rs.next(); assertEquals("abc", this.rs.getString(1)); this.rs.next(); assertEquals("def", this.rs.getString(1)); } public void testBug43576() throws Exception { createTable("TMIX91P", "(F01SMALLINT SMALLINT NOT NULL, F02INTEGER INTEGER,F03REAL REAL," + "F04FLOAT FLOAT,F05NUMERIC31X4 NUMERIC(31,4), F06NUMERIC16X16 NUMERIC(16,16), F07CHAR_10 CHAR(10)," + " F08VARCHAR_10 VARCHAR(10), F09CHAR_20 CHAR(20), F10VARCHAR_20 VARCHAR(20), F11DATE DATE," + " F12DATETIME DATETIME, PRIMARY KEY (F01SMALLINT))"); this.stmt.executeUpdate("INSERT INTO TMIX91P VALUES (1,1,1234567.12,1234567.12,111111111111111111111111111.1111,.111111111111111,'1234567890'," + "'1234567890','CHAR20CHAR20','VARCHAR20ABCD','2001-01-01','2001-01-01 01:01:01.111')"); this.stmt.executeUpdate("INSERT INTO TMIX91P VALUES (7,1,1234567.12,1234567.12,22222222222.0001,.99999999999,'1234567896','1234567896','CHAR20'," + "'VARCHAR20ABCD','2001-01-01','2001-01-01 01:01:01.111')"); this.stmt.executeUpdate("INSERT INTO TMIX91P VALUES (12,12,1234567.12,1234567.12,111222333.4444,.1234567890,'2234567891','2234567891','CHAR20'," + "'VARCHAR20VARCHAR20','2001-01-01','2001-01-01 01:01:01.111')"); createProcedure("MSQSPR100", "\n( p1_in INTEGER , p2_in CHAR(20), OUT p3_out INTEGER, OUT p4_out CHAR(11))\nBEGIN " + "\n SELECT F01SMALLINT,F02INTEGER, F11DATE,F12DATETIME,F03REAL \n FROM TMIX91P WHERE F02INTEGER = p1_in; " + "\n SELECT F02INTEGER,F07CHAR_10,F08VARCHAR_10,F09CHAR_20 \n FROM TMIX91P WHERE F09CHAR_20 = p2_in ORDER BY F02INTEGER ; " + "\n SET p3_out = 144; \n SET p4_out = 'CHARACTER11'; \n SELECT p3_out, p4_out; END"); String sql = "{call MSQSPR100(1,'CHAR20',?,?)}"; CallableStatement cs = this.conn.prepareCall(sql); cs.registerOutParameter(1, Types.INTEGER); cs.registerOutParameter(2, Types.CHAR); cs.execute(); cs.close(); createProcedure("bug43576_1", "(OUT nfact VARCHAR(100), IN ccuenta VARCHAR(100),\nOUT ffact VARCHAR(100),\nOUT fdoc VARCHAR(100))\nBEGIN" + "\nSET nfact = 'ncfact string';\nSET ffact = 'ffact string';\nSET fdoc = 'fdoc string';\nEND"); createProcedure("bug43576_2", "(IN ccuent1 VARCHAR(100), IN ccuent2 VARCHAR(100),\nOUT nfact VARCHAR(100),\nOUT ffact VARCHAR(100)," + "\nOUT fdoc VARCHAR(100))\nBEGIN\nSET nfact = 'ncfact string';\nSET ffact = 'ffact string';\nSET fdoc = 'fdoc string';\nEND"); Properties props = new Properties(); props.put("jdbcCompliantTruncation", "true"); props.put("useInformationSchema", "true"); Connection conn1 = null; conn1 = getConnectionWithProps(props); try { CallableStatement callSt = conn1.prepareCall("{ call bug43576_1(?, ?, ?, ?) }"); callSt.setString(2, "xxx"); callSt.registerOutParameter(1, java.sql.Types.VARCHAR); callSt.registerOutParameter(3, java.sql.Types.VARCHAR); callSt.registerOutParameter(4, java.sql.Types.VARCHAR); callSt.execute(); assertEquals("ncfact string", callSt.getString(1)); assertEquals("ffact string", callSt.getString(3)); assertEquals("fdoc string", callSt.getString(4)); CallableStatement callSt2 = conn1.prepareCall("{ call bug43576_2(?, ?, ?, ?, ?) }"); callSt2.setString(1, "xxx"); callSt2.setString(2, "yyy"); callSt2.registerOutParameter(3, java.sql.Types.VARCHAR); callSt2.registerOutParameter(4, java.sql.Types.VARCHAR); callSt2.registerOutParameter(5, java.sql.Types.VARCHAR); callSt2.execute(); assertEquals("ncfact string", callSt2.getString(3)); assertEquals("ffact string", callSt2.getString(4)); assertEquals("fdoc string", callSt2.getString(5)); CallableStatement callSt3 = conn1.prepareCall("{ call bug43576_2(?, 'yyy', ?, ?, ?) }"); callSt3.setString(1, "xxx"); // callSt3.setString(2, "yyy"); callSt3.registerOutParameter(2, java.sql.Types.VARCHAR); callSt3.registerOutParameter(3, java.sql.Types.VARCHAR); callSt3.registerOutParameter(4, java.sql.Types.VARCHAR); callSt3.execute(); assertEquals("ncfact string", callSt3.getString(2)); assertEquals("ffact string", callSt3.getString(3)); assertEquals("fdoc string", callSt3.getString(4)); } finally { conn1.close(); } } /** * Tests fix for Bug#57022 - cannot execute a store procedure with output * parameters Problem was in CallableStatement.java, private void * determineParameterTypes() throws SQLException if (procName.indexOf(".") * == -1) { useCatalog = true; } The fix will be to "sanitize" db.sp call * just like in noAccessToProcedureBodies. * * @throws Exception * if the test fails */ public void testBug57022() throws Exception { if (!serverSupportsStoredProcedures()) { return; } String originalCatalog = this.conn.getCatalog(); createDatabase("bug57022"); createProcedure("bug57022.procbug57022", "(x int, out y int)\nbegin\ndeclare z int;\nset z = x+1, y = z;\nend\n"); CallableStatement cStmt = null; try { cStmt = this.conn.prepareCall("{call `bug57022`.`procbug57022`(?, ?)}"); cStmt.setInt(1, 5); cStmt.registerOutParameter(2, Types.INTEGER); cStmt.execute(); assertEquals(6, cStmt.getInt(2)); cStmt.clearParameters(); cStmt.close(); this.conn.setCatalog("bug57022"); cStmt = this.conn.prepareCall("{call bug57022.procbug57022(?, ?)}"); cStmt.setInt(1, 5); cStmt.registerOutParameter(2, Types.INTEGER); cStmt.execute(); assertEquals(6, cStmt.getInt(2)); cStmt.clearParameters(); cStmt.close(); this.conn.setCatalog("mysql"); cStmt = this.conn.prepareCall("{call `bug57022`.`procbug57022`(?, ?)}"); cStmt.setInt(1, 5); cStmt.registerOutParameter(2, Types.INTEGER); cStmt.execute(); assertEquals(6, cStmt.getInt(2)); } finally { if (cStmt != null) { cStmt.clearParameters(); cStmt.close(); } this.conn.setCatalog(originalCatalog); } } /** * Tests fix for BUG#60816 - Cannot pass NULL to an INOUT procedure parameter * * @throws Exception */ public void testBug60816() throws Exception { createProcedure("test60816_1", "(INOUT x INTEGER)\nBEGIN\nSET x = x + 1;\nEND"); createProcedure("test60816_2", "(x INTEGER, OUT y INTEGER)\nBEGIN\nSET y = x + 1;\nEND"); createProcedure("test60816_3", "(INOUT x INTEGER)\nBEGIN\nSET x = 10;\nEND"); CallableStatement call = this.conn.prepareCall("{ call test60816_1(?) }"); call.setInt(1, 1); call.registerOutParameter(1, Types.INTEGER); call.execute(); assertEquals(2, call.getInt(1)); call = this.conn.prepareCall("{ call test60816_2(?, ?) }"); call.setInt(1, 1); call.registerOutParameter(2, Types.INTEGER); call.execute(); assertEquals(2, call.getInt(2)); call = this.conn.prepareCall("{ call test60816_2(?, ?) }"); call.setNull(1, Types.INTEGER); call.registerOutParameter(2, Types.INTEGER); call.execute(); assertEquals(0, call.getInt(2)); assertTrue(call.wasNull()); call = this.conn.prepareCall("{ call test60816_1(?) }"); call.setNull(1, Types.INTEGER); call.registerOutParameter(1, Types.INTEGER); call.execute(); assertEquals(0, call.getInt(1)); assertTrue(call.wasNull()); call = this.conn.prepareCall("{ call test60816_3(?) }"); call.setNull(1, Types.INTEGER); call.registerOutParameter(1, Types.INTEGER); call.execute(); assertEquals(10, call.getInt(1)); } /** * Tests fix for Bug#79561 - NullPointerException when calling a fully qualified stored procedure */ public void testBug79561() throws Exception { createProcedure("testBug79561", "(OUT o VARCHAR(100)) BEGIN SELECT 'testBug79561 data' INTO o; END"); String dbName = this.conn.getCatalog(); String[] sql = new String[] { String.format("{CALL %s.testBug79561(?)}", dbName), String.format("{CALL `%s`.testBug79561(?)}", dbName), String.format("{CALL %s.`testBug79561`(?)}", dbName), String.format("{CALL `%s`.`testBug79561`(?)}", dbName) }; for (int i = 0; i < sql.length; i++) { for (int m = 0; m < 4; m++) { // Method call type: 0) by index; 1) by name; 2) by invalid index; 3) by invalid name; final String testCase = String.format("Case: [sql: %d, method: %d ]", i, m); final CallableStatement cstmt = this.conn.prepareCall(sql[i]); boolean dataExpected = true; // Register the output parameter using one of the different methods. if (m == 0) { cstmt.registerOutParameter(1, Types.VARCHAR); } else if (m == 1) { cstmt.registerOutParameter("o", Types.VARCHAR); } else if (m == 2) { assertThrows(testCase, SQLException.class, "Parameter index of 2 is out of range \\(1, 1\\)", new Callable<Void>() { public Void call() throws Exception { cstmt.registerOutParameter(2, Types.VARCHAR); return null; } }); dataExpected = false; } else { assertThrows(testCase, SQLException.class, "No parameter named 'oparam'", new Callable<Void>() { public Void call() throws Exception { cstmt.registerOutParameter("oparam", Types.VARCHAR); return null; } }); dataExpected = false; } // Check the returned data, if any expected, using different methods. if (dataExpected) { cstmt.execute(); assertEquals(testCase, "testBug79561 data", cstmt.getString(1)); assertEquals(testCase, "testBug79561 data", cstmt.getString("o")); assertThrows(testCase, SQLException.class, "Parameter index of 2 is out of range \\(1, 1\\)", new Callable<Void>() { public Void call() throws Exception { cstmt.getString(2); return null; } }); assertThrows(testCase, SQLException.class, "Column '@com_mysql_jdbc_outparam_oparam' not found\\.", new Callable<Void>() { public Void call() throws Exception { cstmt.getString("oparam"); return null; } }); } cstmt.close(); } } } /** * Tests fix for Bug#84324 - CallableStatement.extractProcedureName() not work when catalog name with dash. */ public void testBug84324() throws Exception { createDatabase("`testBug84324-db`"); /* * Test procedure. */ createProcedure("`testBug84324-db`.`testBug84324-proc`", "(IN a INT, INOUT b VARCHAR(100)) BEGIN SELECT a, b; END"); final CallableStatement cstmtP = this.conn.prepareCall("CALL testBug84324-db.testBug84324-proc(?, ?)"); ParameterMetaData pmd = cstmtP.getParameterMetaData(); assertEquals(2, pmd.getParameterCount()); // 1st parameter assertEquals("INT", pmd.getParameterTypeName(1)); assertEquals(Types.INTEGER, pmd.getParameterType(1)); assertEquals(Integer.class.getName(), pmd.getParameterClassName(1)); assertEquals(ParameterMetaData.parameterModeIn, pmd.getParameterMode(1)); // 2nd parameter assertEquals("VARCHAR", pmd.getParameterTypeName(2)); assertEquals(Types.VARCHAR, pmd.getParameterType(2)); assertEquals(String.class.getName(), pmd.getParameterClassName(2)); assertEquals(ParameterMetaData.parameterModeInOut, pmd.getParameterMode(2)); cstmtP.setInt(1, 1); cstmtP.setString(2, "foo"); assertThrows(SQLException.class, new Callable<Void>() { public Void call() throws Exception { cstmtP.execute(); return null; } }); // Although the procedure metadata could be obtained, the end query actually fails due to syntax errors. cstmtP.close(); /* * Test function. */ createFunction("`testBug84324-db`.`testBug84324-func`", "(a INT, b VARCHAR(123)) RETURNS INT DETERMINISTIC BEGIN RETURN a + LENGTH(b); END"); final CallableStatement cstmtF = this.conn.prepareCall("{? = CALL testBug84324-db.testBug84324-func(?, ?)}"); pmd = cstmtF.getParameterMetaData(); assertEquals(3, pmd.getParameterCount()); // 1st parameter assertEquals("INT", pmd.getParameterTypeName(1)); assertEquals(Types.INTEGER, pmd.getParameterType(1)); assertEquals(Integer.class.getName(), pmd.getParameterClassName(1)); assertEquals(ParameterMetaData.parameterModeOut, pmd.getParameterMode(1)); // 2nd parameter assertEquals("INT", pmd.getParameterTypeName(2)); assertEquals(Types.INTEGER, pmd.getParameterType(2)); assertEquals(Integer.class.getName(), pmd.getParameterClassName(2)); assertEquals(ParameterMetaData.parameterModeIn, pmd.getParameterMode(2)); // 3rd parameter assertEquals("VARCHAR", pmd.getParameterTypeName(3)); assertEquals(Types.VARCHAR, pmd.getParameterType(3)); assertEquals(String.class.getName(), pmd.getParameterClassName(3)); assertEquals(ParameterMetaData.parameterModeIn, pmd.getParameterMode(3)); cstmtF.registerOutParameter(1, Types.INTEGER); cstmtF.setInt(2, 1); cstmtF.setString(3, "foo"); assertThrows(SQLException.class, new Callable<Void>() { public Void call() throws Exception { cstmtF.execute(); return null; } }); // Although the function metadata could be obtained, the end query actually fails due to syntax errors. cstmtP.close(); cstmtF.close(); } /** * Tests fix for BUG#87704 (26771560) - THE STREAM GETS THE RESULT SET ?THE DRIVER SIDE GET WRONG ABOUT GETLONG(). * * @throws Exception * if an error occurs. */ public void testBug87704() throws Exception { if (!serverSupportsStoredProcedures()) { return; } createProcedure("testBug87704", "(IN PARAMIN BIGINT, OUT PARAM_OUT_LONG BIGINT, OUT PARAM_OUT_STR VARCHAR(100))\nBEGIN\nSET PARAM_OUT_LONG = PARAMIN + 100000;\nSET PARAM_OUT_STR = concat('STR' ,PARAM_OUT_LONG);end\n"); final Properties props = new Properties(); props.setProperty("useSSL", "false"); props.setProperty("useServerPrepStmts", "true"); props.setProperty("cachePrepStmts", "true"); props.setProperty("prepStmtCacheSize", "500"); props.setProperty("prepStmtCacheSqlLimit", "2048"); props.setProperty("useOldAliasMetadataBehavior", "true"); props.setProperty("rewriteBatchedStatements", "true"); props.setProperty("useCursorFetch", "true"); props.setProperty("defaultFetchSize", "100"); Connection con = getConnectionWithProps(props); CallableStatement callableStatement = null; try { callableStatement = con.prepareCall("call testBug87704(?,?,?)"); callableStatement.setLong(1, 30214567L); callableStatement.registerOutParameter(2, Types.BIGINT); callableStatement.registerOutParameter(3, Types.VARCHAR); callableStatement.execute(); System.out.println(callableStatement.getLong(2)); System.out.println(callableStatement.getString(3)); assertEquals(30314567L, callableStatement.getLong(2)); assertEquals("STR30314567", callableStatement.getString(3)); } finally { if (callableStatement != null) { callableStatement.close(); } if (con != null) { con.close(); } } } }