From 48219f28f8dab9855803d2c34f9d43c3da4c5393 Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Tue, 23 Nov 2021 15:35:38 +0000 Subject: [PATCH] Fix for Bug#105197 (33461744), Statement.executeQuery() may return non-navigable ResultSet. --- CHANGES | 2 + .../cj/LocalizedErrorMessages.properties | 2 +- .../mysql/cj/jdbc/result/ResultSetImpl.java | 80 ++++++++++++++----- .../regression/ResultSetRegressionTest.java | 63 +++++++++++++++ 4 files changed, 128 insertions(+), 19 deletions(-) diff --git a/CHANGES b/CHANGES index a73e03340..d3bf57bc4 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,8 @@ Version 8.0.28 + - Fix for Bug#105197 (33461744), Statement.executeQuery() may return non-navigable ResultSet. + - Fix for Bug#105323 (33507321), README.md contains broken links. - Fix for Bug#96900 (30355150), STATEMENT.CANCEL()CREATE A DATABASE CONNECTION BUT DOES NOT CLOSE THE CONNECTION. diff --git a/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties b/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties index 1e6e9026d..1e612ae26 100644 --- a/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties +++ b/src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties @@ -449,7 +449,7 @@ ResultSet.Operation_not_allowed_after_ResultSet_closed_144=Operation not allowed ResultSet.Before_start_of_result_set_146=Before start of result set ResultSet.After_end_of_result_set_148=After end of result set ResultSet.Query_generated_no_fields_for_ResultSet_133=Query generated no fields for ResultSet -ResultSet.ResultSet_is_from_UPDATE._No_Data_115=ResultSet is from UPDATE. No Data. +ResultSet.ResultSet_is_from_UPDATE._No_Data_115=Not a navigable ResultSet. ResultSet.Invalid_value_for_getFloat()_-____68=Invalid value for getFloat() - '' ResultSet.Invalid_value_for_getInt()_-____74=Invalid value for getInt() - '' diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/result/ResultSetImpl.java b/src/main/user-impl/java/com/mysql/cj/jdbc/result/ResultSetImpl.java index ff6643399..ff45a5592 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/result/ResultSetImpl.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/result/ResultSetImpl.java @@ -371,6 +371,11 @@ public void initializeWithMetadata() throws SQLException { @Override public boolean absolute(int row) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { + if (!hasRows()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + if (isStrictlyForwardOnly()) { throw ExceptionFactory.createException(Messages.getString("ResultSet.ForwardOnly")); } @@ -419,6 +424,11 @@ public boolean absolute(int row) throws SQLException { @Override public void afterLast() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { + if (!hasRows()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + if (isStrictlyForwardOnly()) { throw ExceptionFactory.createException(Messages.getString("ResultSet.ForwardOnly")); } @@ -435,6 +445,11 @@ public void afterLast() throws SQLException { @Override public void beforeFirst() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { + if (!hasRows()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + if (isStrictlyForwardOnly()) { throw ExceptionFactory.createException(Messages.getString("ResultSet.ForwardOnly")); } @@ -560,18 +575,6 @@ public void deleteRow() throws SQLException { throw new NotUpdatable(Messages.getString("NotUpdatable.0")); } - /* - * /** - * TODO: Required by JDBC spec - */ - /* - * protected void finalize() throws Throwable { - * if (!this.isClosed) { - * realClose(false); - * } - * } - */ - @Override public int findColumn(String columnName) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { @@ -590,6 +593,11 @@ public int findColumn(String columnName) throws SQLException { @Override public boolean first() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { + if (!hasRows()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + if (isStrictlyForwardOnly()) { throw ExceptionFactory.createException(Messages.getString("ResultSet.ForwardOnly")); } @@ -1590,6 +1598,11 @@ public java.sql.Ref getRef(String colName) throws SQLException { public int getRow() throws SQLException { checkClosed(); + if (!hasRows()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + int currentRowNumber = this.rowData.getPosition(); int row = 0; @@ -1692,15 +1705,22 @@ public void insertRow() throws SQLException { @Override public boolean isAfterLast() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { - boolean b = this.rowData.isAfterLast(); - - return b; + if (!hasRows()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + return this.rowData.isAfterLast(); } } @Override public boolean isBeforeFirst() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { + if (!hasRows()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + return this.rowData.isBeforeFirst(); } } @@ -1708,6 +1728,11 @@ public boolean isBeforeFirst() throws SQLException { @Override public boolean isFirst() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { + if (!hasRows()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + return this.rowData.isFirst(); } } @@ -1715,6 +1740,11 @@ public boolean isFirst() throws SQLException { @Override public boolean isLast() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { + if (!hasRows()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + return this.rowData.isLast(); } } @@ -1733,6 +1763,11 @@ protected boolean isStrictlyForwardOnly() { @Override public boolean last() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { + if (!hasRows()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + if (isStrictlyForwardOnly()) { throw ExceptionFactory.createException(Messages.getString("ResultSet.ForwardOnly")); } @@ -1765,14 +1800,13 @@ public void moveToInsertRow() throws SQLException { @Override public boolean next() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { - - boolean b; - if (!hasRows()) { throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); } + boolean b; + if (this.rowData.size() == 0) { b = false; } else { @@ -1839,6 +1873,11 @@ public boolean prev() throws java.sql.SQLException { @Override public boolean previous() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { + if (!hasRows()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + if (isStrictlyForwardOnly()) { throw ExceptionFactory.createException(Messages.getString("ResultSet.ForwardOnly")); } @@ -1961,6 +2000,11 @@ public void refreshRow() throws SQLException { @Override public boolean relative(int rows) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { + if (!hasRows()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + if (isStrictlyForwardOnly()) { throw ExceptionFactory.createException(Messages.getString("ResultSet.ForwardOnly")); } diff --git a/src/test/java/testsuite/regression/ResultSetRegressionTest.java b/src/test/java/testsuite/regression/ResultSetRegressionTest.java index dfc9bea70..0f3bf45b9 100644 --- a/src/test/java/testsuite/regression/ResultSetRegressionTest.java +++ b/src/test/java/testsuite/regression/ResultSetRegressionTest.java @@ -8058,4 +8058,67 @@ public void testBug33185116() throws Exception { assertEquals(boolValues[i], this.rs.getBoolean(2)); } } + + /** + * Tests for Bug#105197 (33461744), Statement.executeQuery() may return non-navigable ResultSet. + * + * @throws Exception + */ + @Test + public void testBug105197() throws Exception { + createProcedure("testBug105197Proc", "() BEGIN END"); + this.rs = this.stmt.executeQuery("CALL testBug105197Proc()"); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.absolute(1); + return null; + }); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.relative(1); + return null; + }); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.getRow(); + return null; + }); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.beforeFirst(); + return null; + }); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.isBeforeFirst(); + return null; + }); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.first(); + return null; + }); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.isFirst(); + return null; + }); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.previous(); + return null; + }); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.next(); + return null; + }); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.last(); + return null; + }); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.isLast(); + return null; + }); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.afterLast(); + return null; + }); + assertThrows(SQLException.class, "Not a navigable ResultSet\\.", () -> { + this.rs.isAfterLast(); + return null; + }); + } }