This is an automated email from the ASF dual-hosted git repository.

lidavidm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new 2025673f0c ARROW-16035: [Java] Handling empty JDBC ResultSet
2025673f0c is described below

commit 2025673f0c49b8b8ccd41d5494b29653b57366e4
Author: Todd Farmer <[email protected]>
AuthorDate: Tue May 3 08:06:27 2022 -0400

    ARROW-16035: [Java] Handling empty JDBC ResultSet
    
    ArrowVectorIterator.hasNext() delegates to the underlying 
resultset.isAfterLast() method, but per JDBC specs, this [should return false 
in the case of empty 
ResultSets](https://docs.oracle.com/en/java/javase/11/docs/api/java.sql/java/sql/ResultSet.html#isAfterLast()).
  This causes hasNext() to return true for empty ResultSets, triggering 
infinite loops.
    
    Closes #13049 from toddfarmer/tofarmer/fix-hasnext-for-empty-resultset
    
    Authored-by: Todd Farmer <[email protected]>
    Signed-off-by: David Li <[email protected]>
---
 .../arrow/adapter/jdbc/ArrowVectorIterator.java    | 20 +++++++++++++----
 .../arrow/adapter/jdbc/h2/JdbcToArrowTest.java     | 25 +++++++++++++++++++---
 2 files changed, 38 insertions(+), 7 deletions(-)

diff --git 
a/java/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/ArrowVectorIterator.java
 
b/java/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/ArrowVectorIterator.java
index 1dfc462b5e..ba0b700415 100644
--- 
a/java/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/ArrowVectorIterator.java
+++ 
b/java/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/ArrowVectorIterator.java
@@ -53,6 +53,10 @@ public class ArrowVectorIterator implements 
Iterator<VectorSchemaRoot>, AutoClos
 
   private final int targetBatchSize;
 
+  // This is used to track whether the ResultSet has been fully read, and is 
needed spcifically for cases where there
+  // is a ResultSet having zero rows (empty):
+  private boolean readComplete = false;
+
   /**
    * Construct an instance.
    */
@@ -107,10 +111,15 @@ public class ArrowVectorIterator implements 
Iterator<VectorSchemaRoot>, AutoClos
           compositeConsumer.consume(resultSet);
           readRowCount++;
         }
+        readComplete = true;
       } else {
-        while (readRowCount < targetBatchSize && resultSet.next()) {
-          compositeConsumer.consume(resultSet);
-          readRowCount++;
+        while ((readRowCount < targetBatchSize) && !readComplete) {
+          if (resultSet.next()) {
+            compositeConsumer.consume(resultSet);
+            readRowCount++;
+          } else {
+            readComplete = true;
+          }
         }
       }
 
@@ -154,8 +163,11 @@ public class ArrowVectorIterator implements 
Iterator<VectorSchemaRoot>, AutoClos
 
   @Override
   public boolean hasNext() {
+    if (readComplete) {
+      return false;
+    }
     try {
-      return !resultSet.isAfterLast();
+      return !resultSet.isLast();
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
diff --git 
a/java/adapter/jdbc/src/test/java/org/apache/arrow/adapter/jdbc/h2/JdbcToArrowTest.java
 
b/java/adapter/jdbc/src/test/java/org/apache/arrow/adapter/jdbc/h2/JdbcToArrowTest.java
index ca1c0c00bf..32db254ac3 100644
--- 
a/java/adapter/jdbc/src/test/java/org/apache/arrow/adapter/jdbc/h2/JdbcToArrowTest.java
+++ 
b/java/adapter/jdbc/src/test/java/org/apache/arrow/adapter/jdbc/h2/JdbcToArrowTest.java
@@ -40,7 +40,7 @@ import static 
org.apache.arrow.adapter.jdbc.JdbcToArrowTestHelper.getDoubleValue
 import static 
org.apache.arrow.adapter.jdbc.JdbcToArrowTestHelper.getFloatValues;
 import static org.apache.arrow.adapter.jdbc.JdbcToArrowTestHelper.getIntValues;
 import static 
org.apache.arrow.adapter.jdbc.JdbcToArrowTestHelper.getLongValues;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -251,15 +251,31 @@ public class JdbcToArrowTest extends 
AbstractJdbcToArrowTest {
       allocator.close();
     }
 
-    assertEquals(x, targetRows);
+    assertEquals(targetRows, x);
+  }
+
+  @Test
+  public void testZeroRowResultSet() throws Exception {
+    BufferAllocator allocator = new RootAllocator(Integer.MAX_VALUE);
+    int x = 0;
+    final int targetRows = 0;
+    ResultSet rs = new FakeResultSet(targetRows);
+    JdbcToArrowConfig config = new JdbcToArrowConfigBuilder(
+            allocator, JdbcToArrowUtils.getUtcCalendar(), /* include metadata 
*/ false)
+            .setReuseVectorSchemaRoot(reuseVectorSchemaRoot).build();
+
+    ArrowVectorIterator iter = JdbcToArrow.sqlToArrowVectorIterator(rs, 
config);
+    assertFalse("Iterator on zero row ResultSet should not haveNext()", 
iter.hasNext());
   }
 
   private class FakeResultSet implements ResultSet {
 
     public int numRows;
+    public boolean empty;
 
     FakeResultSet(int numRows) {
       this.numRows = numRows;
+      this.empty = numRows <= 0;
     }
 
     @Override
@@ -629,6 +645,9 @@ public class JdbcToArrowTest extends 
AbstractJdbcToArrowTest {
 
     @Override
     public boolean isAfterLast() throws SQLException {
+      if (empty) {
+        return false;
+      }
       return numRows < 0;
     }
 
@@ -639,7 +658,7 @@ public class JdbcToArrowTest extends 
AbstractJdbcToArrowTest {
 
     @Override
     public boolean isLast() throws SQLException {
-      return false;
+      return numRows <= 0;
     }
 
     @Override

Reply via email to