Repository: googleapis/java-spanner-jdbc (java)

Issue:
Calling `ResultSet#getMetaData()` more than once before calling `ResultSet#next()` skips a row
If the client application calls `ResultSet#getMetaData()` more than once **before** calling `ResultSet#next()`, the `ResultSet` will skip the first row of the results when `ResultSet#next()` is called. Calling `ResultSet#getMetaData()` once before calling `ResultSet#next()` will not have this effect.

Pull request description:
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


Patch:
diff --git a/pom.xml b/pom.xml
index ae04a08..9797798 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,7 +71,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>google-cloud-spanner-bom</artifactId>
-        <version>3.1.2</version>
+        <version>3.3.1</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index 6cef2b5..69cbd88 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -30,7 +30,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>libraries-bom</artifactId>
-        <version>16.2.1</version>
+        <version>16.3.0</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
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 3f10b0c..c4ca6b5 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 @@ class JdbcResultSet extends AbstractJdbcResultSet {
   @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();


Test patch:
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 9429288..2ad4a6d 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.assertNull;
 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 class JdbcResultSetTest {
     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));