From f8149afb63b9a66e89119290c594b50e599f351a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Mon, 18 Jan 2021 00:38:46 +0100 Subject: [PATCH] fix: getting resultset metadata twice could skip row (#323) * fix: getting resultset metadata twice could skip row If the client application would call ResultSet#getMetaData() more than once **before** calling ResultSet#next(), the ResultSet would skip a row when the rows would be consumed. Fixes #322 * test: add test for getMetaData twice --- .../cloud/spanner/jdbc/JdbcResultSet.java | 2 +- .../cloud/spanner/jdbc/JdbcResultSetTest.java | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java index 3f10b0c1..c4ca6b5e 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java @@ -547,7 +547,7 @@ public InputStream getBinaryStream(String columnLabel) throws SQLException { @Override public JdbcResultSetMetaData getMetaData() throws SQLException { checkClosed(); - if (isBeforeFirst()) { + if (isBeforeFirst() && !nextCalledForMetaData) { // do a call to next() on the underlying resultset to initialize metadata nextCalledForMetaData = true; nextCalledForMetaDataResult = spanner.next(); diff --git a/src/test/java/com/google/cloud/spanner/jdbc/JdbcResultSetTest.java b/src/test/java/com/google/cloud/spanner/jdbc/JdbcResultSetTest.java index 9429288d..2ad4a6d4 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/JdbcResultSetTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/JdbcResultSetTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.cloud.ByteArray; import com.google.cloud.Date; @@ -802,6 +803,32 @@ public void testGetMetaData() throws SQLException { assertNotNull(metadata); } + @Test + public void testGetMetaDataBeforeNext() throws SQLException { + ResultSet spannerResultSet = mock(ResultSet.class); + when(spannerResultSet.next()).thenReturn(true, false); + + JdbcResultSet resultSet = JdbcResultSet.of(spannerResultSet); + assertNotNull(resultSet.getMetaData()); + assertTrue(resultSet.next()); + assertFalse(resultSet.next()); + } + + @Test + public void testGetMetaDataTwiceBeforeNext() throws SQLException { + ResultSet spannerResultSet = mock(ResultSet.class); + when(spannerResultSet.next()).thenReturn(true, false); + + JdbcResultSet resultSet = JdbcResultSet.of(spannerResultSet); + assertNotNull(resultSet.getMetaData()); + assertNotNull(resultSet.getMetaData()); + + // This would have returned false before the fix in + // https://github.com/googleapis/java-spanner-jdbc/pull/323 + assertTrue(resultSet.next()); + assertFalse(resultSet.next()); + } + @Test public void testFindColumn() throws SQLException { assertEquals(2, subject.findColumn(STRING_COL_NOT_NULL));