From 5e243b1ef61472c86d46c715d96d6e2fd098f5ee Mon Sep 17 00:00:00 2001 From: rusher Date: Fri, 5 Apr 2024 15:40:56 +0200 Subject: [PATCH] [CONJ-1125] follow JDBC spec PreparedStatement/PreparedStatement.executeQuery must throw exception when no returning result-set. This is the exact difference of when not using .execute(); --- .../mariadb/jdbc/ClientPreparedStatement.java | 7 ++-- .../mariadb/jdbc/ServerPreparedStatement.java | 18 +++----- src/main/java/org/mariadb/jdbc/Statement.java | 14 +++---- .../jdbc/client/result/CompleteResult.java | 2 +- .../jdbc/integration/MultiQueriesTest.java | 4 +- .../integration/PreparedStatementTest.java | 6 +-- .../jdbc/integration/ProcedureTest.java | 4 +- .../jdbc/integration/StatementTest.java | 42 +++++++++++++++++-- .../jdbc/integration/UpdateResultSetTest.java | 12 +++--- 9 files changed, 68 insertions(+), 41 deletions(-) diff --git a/src/main/java/org/mariadb/jdbc/ClientPreparedStatement.java b/src/main/java/org/mariadb/jdbc/ClientPreparedStatement.java index ff04dc9e6..782095755 100644 --- a/src/main/java/org/mariadb/jdbc/ClientPreparedStatement.java +++ b/src/main/java/org/mariadb/jdbc/ClientPreparedStatement.java @@ -7,9 +7,7 @@ import java.sql.*; import java.util.*; -import org.mariadb.jdbc.client.ColumnDecoder; import org.mariadb.jdbc.client.Completion; -import org.mariadb.jdbc.client.result.CompleteResult; import org.mariadb.jdbc.client.result.Result; import org.mariadb.jdbc.client.util.ClosableLock; import org.mariadb.jdbc.export.ExceptionFactory; @@ -309,7 +307,10 @@ public ResultSet executeQuery() throws SQLException { if (currResult instanceof Result) { return (Result) currResult; } - return new CompleteResult(new ColumnDecoder[0], new byte[0][], con.getContext(), resultSetType); + throw new SQLException( + "PrepareStatement.executeQuery() command does NOT return a result-set as expected. Either" + + " use PrepareStatement.execute(), PrepareStatement.executeUpdate(), or correct" + + " command"); } /** diff --git a/src/main/java/org/mariadb/jdbc/ServerPreparedStatement.java b/src/main/java/org/mariadb/jdbc/ServerPreparedStatement.java index f3ad13567..359a9f1b9 100644 --- a/src/main/java/org/mariadb/jdbc/ServerPreparedStatement.java +++ b/src/main/java/org/mariadb/jdbc/ServerPreparedStatement.java @@ -8,9 +8,7 @@ import java.sql.*; import java.util.*; import java.util.regex.Pattern; -import org.mariadb.jdbc.client.ColumnDecoder; import org.mariadb.jdbc.client.Completion; -import org.mariadb.jdbc.client.result.CompleteResult; import org.mariadb.jdbc.client.result.Result; import org.mariadb.jdbc.client.util.ClosableLock; import org.mariadb.jdbc.client.util.Parameters; @@ -436,17 +434,13 @@ public void setQueryTimeout(int seconds) throws SQLException { public ResultSet executeQuery() throws SQLException { executeInternal(); handleParameterOutput(); - if (!(currResult instanceof Result)) { - if (Boolean.parseBoolean( - con.getContext().getConf().nonMappedOptions().getProperty("permitNoResults", "false"))) { - // for compatibility with pre 3.4.0 version - return new CompleteResult( - new ColumnDecoder[0], new byte[0][], con.getContext(), resultSetType); - } - throw exceptionFactory() - .create("the given SQL statement have not produced a ResultSet object", "HY000"); + if ((currResult instanceof Result)) { + return (Result) currResult; } - return (Result) currResult; + throw new SQLException( + "PrepareStatement.executeQuery() command does NOT return a result-set as expected. Either" + + " use PrepareStatement.execute(), PrepareStatement.executeUpdate(), or correct" + + " command"); } /** diff --git a/src/main/java/org/mariadb/jdbc/Statement.java b/src/main/java/org/mariadb/jdbc/Statement.java index 2422d7b34..a5d7658bf 100644 --- a/src/main/java/org/mariadb/jdbc/Statement.java +++ b/src/main/java/org/mariadb/jdbc/Statement.java @@ -10,7 +10,6 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.mariadb.jdbc.client.ColumnDecoder; import org.mariadb.jdbc.client.Completion; import org.mariadb.jdbc.client.DataType; import org.mariadb.jdbc.client.result.CompleteResult; @@ -166,8 +165,12 @@ public void setLocalInfileInputStream(InputStream inputStream) throws SQLExcepti public ResultSet executeQuery(String sql) throws SQLException { executeInternal(sql, Statement.NO_GENERATED_KEYS); currResult = results.remove(0); - if (currResult instanceof Result) return (Result) currResult; - return new CompleteResult(new ColumnDecoder[0], new byte[0][], con.getContext(), resultSetType); + if (currResult instanceof Result) { + return (Result) currResult; + } + throw new SQLException( + "Statement.executeQuery() command does NOT return a result-set as expected. Either use" + + " Statement.execute(), Statement.executeUpdate(), or correct command"); } /** @@ -949,11 +952,6 @@ public ResultSet getGeneratedKeys() throws SQLException { } } - if (insertIds.isEmpty()) { - return new CompleteResult( - new ColumnDecoder[0], new byte[0][], con.getContext(), resultSetType); - } - String[][] ids = insertIds.toArray(new String[0][]); return CompleteResult.createResultSet( "insert_id", diff --git a/src/main/java/org/mariadb/jdbc/client/result/CompleteResult.java b/src/main/java/org/mariadb/jdbc/client/result/CompleteResult.java index 2c05b351b..729e2f545 100644 --- a/src/main/java/org/mariadb/jdbc/client/result/CompleteResult.java +++ b/src/main/java/org/mariadb/jdbc/client/result/CompleteResult.java @@ -111,7 +111,7 @@ private CompleteResult(ColumnDecoder[] metadataList, CompleteResult prev) { * @param context connection context * @param resultSetType result set type */ - public CompleteResult( + private CompleteResult( ColumnDecoder[] metadataList, byte[][] data, Context context, int resultSetType) { super(metadataList, data, context, resultSetType); } diff --git a/src/test/java/org/mariadb/jdbc/integration/MultiQueriesTest.java b/src/test/java/org/mariadb/jdbc/integration/MultiQueriesTest.java index 7b908538c..ee4ebbdbc 100644 --- a/src/test/java/org/mariadb/jdbc/integration/MultiQueriesTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/MultiQueriesTest.java @@ -99,7 +99,7 @@ public void quitWhileStreaming() throws SQLException { Connection connection = createCon("&allowMultiQueries=true"); Statement stmt = connection.createStatement(); stmt.setFetchSize(1); - stmt.executeQuery( + stmt.execute( "DO 2;SELECT * from AllowMultiQueriesTest;SELECT * from AllowMultiQueriesTest; DO 1; SELECT" + " 2"); connection.abort(Runnable::run); @@ -107,7 +107,7 @@ public void quitWhileStreaming() throws SQLException { connection = createCon("&allowMultiQueries=true"); stmt = connection.createStatement(); stmt.setFetchSize(1); - stmt.executeQuery("DO 2;DO 1;SELECT * from AllowMultiQueriesTest"); + stmt.execute("DO 2;DO 1;SELECT * from AllowMultiQueriesTest"); connection.abort(Runnable::run); } diff --git a/src/test/java/org/mariadb/jdbc/integration/PreparedStatementTest.java b/src/test/java/org/mariadb/jdbc/integration/PreparedStatementTest.java index 29c5f0a61..8138a0bd6 100644 --- a/src/test/java/org/mariadb/jdbc/integration/PreparedStatementTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/PreparedStatementTest.java @@ -235,7 +235,6 @@ public void executeUpdate() throws SQLException { preparedStatement.setInt(2, 10); assertEquals(1, preparedStatement.executeUpdate()); - // verification that query without resultset return an empty resultset preparedStatement.clearParameters(); Common.assertThrowsContains( SQLException.class, @@ -243,8 +242,7 @@ public void executeUpdate() throws SQLException { "Parameter at position 1 is not set"); preparedStatement.setInt(2, 11); preparedStatement.setInt(1, 6); - ResultSet rs0 = preparedStatement.executeQuery(); - assertFalse(rs0.next()); + preparedStatement.executeUpdate(); // verification ResultSet rs = stmt.executeQuery("SELECT * FROM prepare1"); @@ -1363,7 +1361,7 @@ public void textPrefix() throws SQLException { try (PreparedStatement prep = con.prepareStatement("/*client prepare*/SET @name := ?; SELECT @name ")) { prep.setString(1, "test"); - prep.executeQuery(); + prep.execute(); assertTrue(prep.getMoreResults(Statement.CLOSE_CURRENT_RESULT)); ResultSet rs = prep.getResultSet(); assertTrue(rs.next()); diff --git a/src/test/java/org/mariadb/jdbc/integration/ProcedureTest.java b/src/test/java/org/mariadb/jdbc/integration/ProcedureTest.java index 5a1456076..ce81c51c8 100644 --- a/src/test/java/org/mariadb/jdbc/integration/ProcedureTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/ProcedureTest.java @@ -53,7 +53,7 @@ public void settingParameterBeforeOutRegistration() throws SQLException { Common.assertThrowsContains( SQLException.class, () -> cstmt.executeQuery(), - "the given SQL statement have not produced a ResultSet object"); + "PrepareStatement.executeQuery() command does NOT return a result-set as expected"); } try (Connection con = createCon("&permitNoResults=true")) { try (CallableStatement cstmt = con.prepareCall("{ CALL multiply_by_2(?) }")) { @@ -65,7 +65,7 @@ public void settingParameterBeforeOutRegistration() throws SQLException { cstmt.executeUpdate(); assertEquals(86, cstmt.getLong(1)); cstmt.setLong(1, 44L); - cstmt.executeQuery(); + cstmt.execute(); assertEquals(88, cstmt.getLong(1)); } } diff --git a/src/test/java/org/mariadb/jdbc/integration/StatementTest.java b/src/test/java/org/mariadb/jdbc/integration/StatementTest.java index 9f118496e..6d7203754 100644 --- a/src/test/java/org/mariadb/jdbc/integration/StatementTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/StatementTest.java @@ -71,6 +71,42 @@ public void ensureGetGeneratedKeysReturnsEmptyResult() throws SQLException { stmt.execute("DROP TABLE key_test"); } + @Test + public void ensureJdbcErrorWhenNoResultset() throws SQLException { + Statement stmt = sharedConn.createStatement(); + stmt.execute("DO 1"); + assertThrowsContains( + SQLException.class, + () -> stmt.executeQuery("DO 1"), + "Statement.executeQuery() command does NOT return a result-set as expected. Either use" + + " Statement.execute(), Statement.executeUpdate(), or correct command"); + stmt.execute("DO 1"); + try (PreparedStatement ps = + sharedConn.prepareStatement("DO ?", Statement.RETURN_GENERATED_KEYS)) { + ps.setInt(1, 1); + ps.execute(); + assertThrowsContains( + SQLException.class, + () -> ps.executeQuery(), + "PrepareStatement.executeQuery() command does NOT return a result-set as expected. Either" + + " use PrepareStatement.execute(), PrepareStatement.executeUpdate(), or correct" + + " command"); + ps.execute(); + } + try (PreparedStatement ps = + sharedConnBinary.prepareStatement("DO ?", Statement.RETURN_GENERATED_KEYS)) { + ps.setInt(1, 1); + ps.execute(); + assertThrowsContains( + SQLException.class, + () -> ps.executeQuery(), + "PrepareStatement.executeQuery() command does NOT return a result-set as expected. Either" + + " use PrepareStatement.execute(), PrepareStatement.executeUpdate(), or correct" + + " command"); + ps.execute(); + } + } + @Test public void getUpdateCountValueOnFail() throws SQLException { try (Statement st = sharedConn.createStatement()) { @@ -471,13 +507,13 @@ public void executeLargeUpdate() throws SQLException { } @Test - public void executeQuery() throws SQLException { + public void executeUpdateNoResults() throws SQLException { Statement stmt = sharedConn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT 1"); assertTrue(rs.next()); if (!isXpand()) { - rs = stmt.executeQuery("DO 1"); - assertFalse(rs.next()); + stmt.executeUpdate("DO 1"); + assertNull(stmt.getResultSet()); } } diff --git a/src/test/java/org/mariadb/jdbc/integration/UpdateResultSetTest.java b/src/test/java/org/mariadb/jdbc/integration/UpdateResultSetTest.java index db90e605e..2b7bd555b 100644 --- a/src/test/java/org/mariadb/jdbc/integration/UpdateResultSetTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/UpdateResultSetTest.java @@ -143,8 +143,8 @@ public void testNoDatabase() throws Exception { public void testMultipleTable() throws Exception { Statement stmt = sharedConn.createStatement(); stmt.execute("START TRANSACTION"); // if MAXSCALE ensure using WRITER - stmt.executeQuery("INSERT INTO testMultipleTable1(t1) values ('1')"); - stmt.executeQuery("INSERT INTO testMultipleTable2(t1) values ('2')"); + stmt.executeUpdate("INSERT INTO testMultipleTable1(t1) values ('1')"); + stmt.executeUpdate("INSERT INTO testMultipleTable2(t1) values ('2')"); try (PreparedStatement preparedStatement = sharedConn.prepareStatement( @@ -166,7 +166,7 @@ public void testMultipleTable() throws Exception { public void testOneNoTable() throws Exception { Statement stmt = sharedConn.createStatement(); stmt.execute("START TRANSACTION"); // if MAXSCALE ensure using WRITER - stmt.executeQuery("INSERT INTO testOneNoTable(t1) values ('1')"); + stmt.executeUpdate("INSERT INTO testOneNoTable(t1) values ('1')"); try (PreparedStatement preparedStatement = sharedConn.prepareStatement( @@ -234,8 +234,8 @@ public void testMultipleDatabase() throws Exception { + " VARCHAR(50) NULL,PRIMARY KEY (`id2`))"); stmt.execute("START TRANSACTION"); // if MAXSCALE ensure using WRITER - stmt.executeQuery("INSERT INTO testMultipleDatabase(t1) values ('1')"); - stmt.executeQuery("INSERT INTO testConnectorJ.testMultipleDatabase(t2) values ('2')"); + stmt.executeUpdate("INSERT INTO testMultipleDatabase(t1) values ('1')"); + stmt.executeUpdate("INSERT INTO testConnectorJ.testMultipleDatabase(t2) values ('2')"); try (PreparedStatement preparedStatement = sharedConn.prepareStatement( @@ -263,7 +263,7 @@ public void testMeta() throws Exception { + "`t2` VARCHAR(50) NULL default 'default-value'," + "PRIMARY KEY (`id`))"); stmt.execute("START TRANSACTION"); // if MAXSCALE ensure using WRITER - stmt.executeQuery("INSERT INTO UpdateWithoutPrimary(t1,t2) values ('1-1','1-2')"); + stmt.executeUpdate("INSERT INTO UpdateWithoutPrimary(t1,t2) values ('1-1','1-2')"); try (PreparedStatement preparedStatement = sharedConn.prepareStatement(