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

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


The following commit(s) were added to refs/heads/main by this push:
     new e69723a  fix(java/driver/jdbc): clean up buffer leaks (#533)
e69723a is described below

commit e69723a158276d973cf6b84127ea590844e1efb5
Author: David Li <[email protected]>
AuthorDate: Wed Mar 29 13:24:27 2023 -0400

    fix(java/driver/jdbc): clean up buffer leaks (#533)
    
    * Close the allocator in JdbcConnection.
    * Allow supplying an allocator to JdbcDriver.
    * Fix miscellaneous memory leaks discovered from those changes.
    
    Fixes #476.
    Fixes #477.
---
 .../adbc/driver/flightsql/FlightSqlQuirks.java     |  2 +-
 .../arrow/adbc/driver/jdbc/derby/DerbyQuirks.java  |  5 +++--
 .../driver/jdbc/postgresql/PostgresqlQuirks.java   |  5 +++--
 .../arrow/adbc/driver/jdbc/JdbcArrowReader.java    | 12 ++++++-----
 .../arrow/adbc/driver/jdbc/JdbcBindReader.java     | 19 ++++++++++--------
 .../arrow/adbc/driver/jdbc/JdbcConnection.java     | 10 +++++++++-
 .../apache/arrow/adbc/driver/jdbc/JdbcDriver.java  | 18 ++++++++++++-----
 .../testsuite/AbstractConnectionMetadataTest.java  |  4 ++--
 .../driver/testsuite/AbstractConnectionTest.java   |  8 ++++++--
 .../testsuite/AbstractPartitionDescriptorTest.java |  4 ++--
 .../driver/testsuite/AbstractStatementTest.java    | 23 ++++++++++++++++++++--
 .../driver/testsuite/AbstractTransactionTest.java  |  4 ++--
 .../adbc/driver/testsuite/SqlValidationQuirks.java |  3 ++-
 java/pom.xml                                       |  2 +-
 14 files changed, 83 insertions(+), 36 deletions(-)

diff --git 
a/java/driver/flight-sql-validation/src/test/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlQuirks.java
 
b/java/driver/flight-sql-validation/src/test/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlQuirks.java
index d2cd614..2f1840d 100644
--- 
a/java/driver/flight-sql-validation/src/test/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlQuirks.java
+++ 
b/java/driver/flight-sql-validation/src/test/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlQuirks.java
@@ -43,7 +43,7 @@ public class FlightSqlQuirks extends SqlValidationQuirks {
   }
 
   @Override
-  public AdbcDatabase initDatabase() throws AdbcException {
+  public AdbcDatabase initDatabase(BufferAllocator allocator) throws 
AdbcException {
     String url = getFlightLocation();
 
     final Map<String, Object> parameters = new HashMap<>();
diff --git 
a/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyQuirks.java
 
b/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyQuirks.java
index b8a4def..1df0398 100644
--- 
a/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyQuirks.java
+++ 
b/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyQuirks.java
@@ -29,6 +29,7 @@ import org.apache.arrow.adbc.core.AdbcDriver;
 import org.apache.arrow.adbc.core.AdbcException;
 import org.apache.arrow.adbc.driver.jdbc.JdbcDriver;
 import org.apache.arrow.adbc.driver.testsuite.SqlValidationQuirks;
+import org.apache.arrow.memory.BufferAllocator;
 
 public class DerbyQuirks extends SqlValidationQuirks {
   private final String jdbcUrl;
@@ -38,10 +39,10 @@ public class DerbyQuirks extends SqlValidationQuirks {
   }
 
   @Override
-  public AdbcDatabase initDatabase() throws AdbcException {
+  public AdbcDatabase initDatabase(BufferAllocator allocator) throws 
AdbcException {
     final Map<String, Object> parameters = new HashMap<>();
     parameters.put(AdbcDriver.PARAM_URL, jdbcUrl);
-    return JdbcDriver.INSTANCE.open(parameters);
+    return new JdbcDriver(allocator).open(parameters);
   }
 
   @Override
diff --git 
a/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/PostgresqlQuirks.java
 
b/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/PostgresqlQuirks.java
index c11f5d1..7018d75 100644
--- 
a/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/PostgresqlQuirks.java
+++ 
b/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/PostgresqlQuirks.java
@@ -29,6 +29,7 @@ import org.apache.arrow.adbc.core.AdbcException;
 import org.apache.arrow.adbc.driver.jdbc.JdbcDriver;
 import org.apache.arrow.adbc.driver.testsuite.SqlValidationQuirks;
 import org.apache.arrow.adbc.sql.SqlQuirks;
+import org.apache.arrow.memory.BufferAllocator;
 import org.apache.arrow.vector.types.pojo.ArrowType;
 import org.junit.jupiter.api.Assumptions;
 
@@ -49,7 +50,7 @@ public class PostgresqlQuirks extends SqlValidationQuirks {
   }
 
   @Override
-  public AdbcDatabase initDatabase() throws AdbcException {
+  public AdbcDatabase initDatabase(BufferAllocator allocator) throws 
AdbcException {
     String url = makeJdbcUrl();
 
     final Map<String, Object> parameters = new HashMap<>();
@@ -65,7 +66,7 @@ public class PostgresqlQuirks extends SqlValidationQuirks {
                   return 
SqlQuirks.DEFAULT_ARROW_TYPE_TO_SQL_TYPE_NAME_MAPPING.apply(arrowType);
                 }))
             .build());
-    return JdbcDriver.INSTANCE.open(parameters);
+    return new JdbcDriver(allocator).open(parameters);
   }
 
   @Override
diff --git 
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcArrowReader.java
 
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcArrowReader.java
index 4445729..1ddbf1c 100644
--- 
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcArrowReader.java
+++ 
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcArrowReader.java
@@ -78,11 +78,13 @@ public class JdbcArrowReader extends ArrowReader {
   @Override
   public boolean loadNextBatch() {
     if (!delegate.hasNext()) return false;
-    final VectorSchemaRoot root = delegate.next();
-    final VectorUnloader unloader = new VectorUnloader(root);
-    final ArrowRecordBatch recordBatch = unloader.getRecordBatch();
-    bytesRead += recordBatch.computeBodyLength();
-    loadRecordBatch(recordBatch);
+    try (final VectorSchemaRoot root = delegate.next()) {
+      final VectorUnloader unloader = new VectorUnloader(root);
+      try (final ArrowRecordBatch recordBatch = unloader.getRecordBatch()) {
+        bytesRead += recordBatch.computeBodyLength();
+        loadRecordBatch(recordBatch);
+      }
+    }
     return true;
   }
 
diff --git 
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcBindReader.java
 
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcBindReader.java
index 9b314d5..167d3f2 100644
--- 
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcBindReader.java
+++ 
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcBindReader.java
@@ -54,9 +54,10 @@ final class JdbcBindReader extends ArrowReader {
       }
     }
 
-    final VectorSchemaRoot root = currentSource.next();
-    try (final ArrowRecordBatch batch = new 
VectorUnloader(root).getRecordBatch()) {
-      loadRecordBatch(batch);
+    try (final VectorSchemaRoot root = currentSource.next()) {
+      try (final ArrowRecordBatch batch = new 
VectorUnloader(root).getRecordBatch()) {
+        loadRecordBatch(batch);
+      }
     }
     return true;
   }
@@ -68,11 +69,13 @@ final class JdbcBindReader extends ArrowReader {
 
   @Override
   protected void closeReadSource() throws IOException {
-    try {
-      // Do not close PreparedStatement so we can reuse it
-      currentResultSet.close();
-    } catch (SQLException e) {
-      throw new IOException(e);
+    if (currentResultSet != null) {
+      try {
+        // Do not close PreparedStatement so we can reuse it
+        currentResultSet.close();
+      } catch (SQLException e) {
+        throw new IOException(e);
+      }
     }
   }
 
diff --git 
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnection.java
 
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnection.java
index e5f4a6a..205f193 100644
--- 
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnection.java
+++ 
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnection.java
@@ -32,6 +32,7 @@ import org.apache.arrow.adbc.core.IsolationLevel;
 import org.apache.arrow.adbc.core.StandardSchemas;
 import org.apache.arrow.adbc.sql.SqlQuirks;
 import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.util.AutoCloseables;
 import org.apache.arrow.vector.VectorSchemaRoot;
 import org.apache.arrow.vector.ipc.ArrowReader;
 import org.apache.arrow.vector.types.pojo.ArrowType;
@@ -44,6 +45,13 @@ public class JdbcConnection implements AdbcConnection {
   private final Connection connection;
   private final SqlQuirks quirks;
 
+  /**
+   * Create a new connection.
+   *
+   * @param allocator The allocator to use. The connection will close the 
allocator when done.
+   * @param connection The JDBC connection.
+   * @param quirks Backend-specific quirks to account for.
+   */
   JdbcConnection(BufferAllocator allocator, Connection connection, SqlQuirks 
quirks) {
     this.allocator = allocator;
     this.connection = connection;
@@ -253,7 +261,7 @@ public class JdbcConnection implements AdbcConnection {
 
   @Override
   public void close() throws Exception {
-    connection.close();
+    AutoCloseables.close(connection, allocator);
   }
 
   private void checkAutoCommit() throws AdbcException, SQLException {
diff --git 
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriver.java
 
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriver.java
index 67db75a..7925acb 100644
--- 
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriver.java
+++ 
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriver.java
@@ -17,6 +17,7 @@
 package org.apache.arrow.adbc.driver.jdbc;
 
 import java.util.Map;
+import java.util.Objects;
 import org.apache.arrow.adbc.core.AdbcDatabase;
 import org.apache.arrow.adbc.core.AdbcDriver;
 import org.apache.arrow.adbc.core.AdbcException;
@@ -27,14 +28,21 @@ import org.apache.arrow.memory.RootAllocator;
 import org.apache.arrow.util.Preconditions;
 
 /** An ADBC driver wrapping the JDBC API. */
-public enum JdbcDriver implements AdbcDriver {
-  INSTANCE;
+public class JdbcDriver implements AdbcDriver {
+  public static final JdbcDriver INSTANCE = new JdbcDriver();
+
+  static {
+    
AdbcDriverManager.getInstance().registerDriver("org.apache.arrow.adbc.driver.jdbc",
 INSTANCE);
+  }
 
   private final BufferAllocator allocator;
 
-  JdbcDriver() {
-    allocator = new RootAllocator();
-    
AdbcDriverManager.getInstance().registerDriver("org.apache.arrow.adbc.driver.jdbc",
 this);
+  public JdbcDriver() {
+    this(new RootAllocator());
+  }
+
+  public JdbcDriver(BufferAllocator allocator) {
+    this.allocator = Objects.requireNonNull(allocator);
   }
 
   @Override
diff --git 
a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java
 
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java
index 5514097..5d14339 100644
--- 
a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java
+++ 
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java
@@ -68,9 +68,9 @@ public abstract class AbstractConnectionMetadataTest {
   @BeforeEach
   public void beforeEach() throws Exception {
     Preconditions.checkNotNull(quirks, "Must initialize quirks in subclass 
with @BeforeAll");
-    database = quirks.initDatabase();
-    connection = database.connect();
     allocator = new RootAllocator();
+    database = quirks.initDatabase(allocator);
+    connection = database.connect();
     util = new SqlTestUtil(quirks);
     tableName = quirks.caseFoldTableName("foo");
     mainTable = quirks.caseFoldTableName("product");
diff --git 
a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionTest.java
 
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionTest.java
index 80efac2..9915636 100644
--- 
a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionTest.java
+++ 
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionTest.java
@@ -21,6 +21,8 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 import org.apache.arrow.adbc.core.AdbcConnection;
 import org.apache.arrow.adbc.core.AdbcDatabase;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
 import org.apache.arrow.util.AutoCloseables;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -30,18 +32,20 @@ public abstract class AbstractConnectionTest {
   /** Must be initialized by the subclass. */
   protected static SqlValidationQuirks quirks;
 
+  protected BufferAllocator allocator;
   protected AdbcDatabase database;
   protected AdbcConnection connection;
 
   @BeforeEach
   public void beforeEach() throws Exception {
-    database = quirks.initDatabase();
+    allocator = new RootAllocator();
+    database = quirks.initDatabase(allocator);
     connection = database.connect();
   }
 
   @AfterEach
   public void afterEach() throws Exception {
-    AutoCloseables.close(connection, database);
+    AutoCloseables.close(connection, database, allocator);
   }
 
   @Test
diff --git 
a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractPartitionDescriptorTest.java
 
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractPartitionDescriptorTest.java
index 8a1dfb9..a0dee52 100644
--- 
a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractPartitionDescriptorTest.java
+++ 
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractPartitionDescriptorTest.java
@@ -55,9 +55,9 @@ public abstract class AbstractPartitionDescriptorTest {
   @BeforeEach
   public void beforeEach() throws Exception {
     Preconditions.checkNotNull(quirks, "Must initialize quirks in subclass 
with @BeforeAll");
-    database = quirks.initDatabase();
-    connection = database.connect();
     allocator = new RootAllocator();
+    database = quirks.initDatabase(allocator);
+    connection = database.connect();
     util = new SqlTestUtil(quirks);
     tableName = quirks.caseFoldTableName("bulktable");
     schema =
diff --git 
a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractStatementTest.java
 
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractStatementTest.java
index 4b8dd14..b264bf4 100644
--- 
a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractStatementTest.java
+++ 
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractStatementTest.java
@@ -59,9 +59,9 @@ public abstract class AbstractStatementTest {
   @BeforeEach
   public void beforeEach() throws Exception {
     Preconditions.checkNotNull(quirks, "Must initialize quirks in subclass 
with @BeforeAll");
-    database = quirks.initDatabase();
-    connection = database.connect();
     allocator = new RootAllocator();
+    database = quirks.initDatabase(allocator);
+    connection = database.connect();
     util = new SqlTestUtil(quirks);
     tableName = quirks.caseFoldTableName("bulktable");
     schema =
@@ -203,6 +203,25 @@ public abstract class AbstractStatementTest {
     }
   }
 
+  @Test
+  public void prepareQueryWithParametersNoOp() throws Exception {
+    final Schema expectedSchema = util.ingestTableIntsStrs(allocator, 
connection, tableName);
+    final Schema paramsSchema =
+        new 
Schema(Collections.singletonList(expectedSchema.getFields().get(0)));
+    try (final AdbcStatement stmt = connection.createStatement();
+        final VectorSchemaRoot params = VectorSchemaRoot.create(paramsSchema, 
allocator)) {
+      stmt.setSqlQuery(String.format("SELECT * FROM %s WHERE INTS = ?", 
tableName));
+      stmt.prepare();
+      stmt.bind(params);
+      IntVector param0 = (IntVector) params.getVector(0);
+      param0.setSafe(0, 1);
+      param0.setSafe(1, 2);
+      params.setRowCount(2);
+      // Ensure things still close properly if we never actually drain the 
result
+      stmt.executeQuery().close();
+    }
+  }
+
   @Test
   public void prepareQueryWithParameters() throws Exception {
     final Schema expectedSchema = util.ingestTableIntsStrs(allocator, 
connection, tableName);
diff --git 
a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractTransactionTest.java
 
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractTransactionTest.java
index 230afc9..29265ba 100644
--- 
a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractTransactionTest.java
+++ 
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractTransactionTest.java
@@ -52,9 +52,9 @@ public abstract class AbstractTransactionTest {
   @BeforeEach
   public void beforeEach() throws Exception {
     Preconditions.checkNotNull(quirks, "Must initialize quirks in subclass 
with @BeforeAll");
-    database = quirks.initDatabase();
-    connection = database.connect();
     allocator = new RootAllocator();
+    database = quirks.initDatabase(allocator);
+    connection = database.connect();
   }
 
   @AfterEach
diff --git 
a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/SqlValidationQuirks.java
 
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/SqlValidationQuirks.java
index ee60e8b..6126972 100644
--- 
a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/SqlValidationQuirks.java
+++ 
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/SqlValidationQuirks.java
@@ -21,10 +21,11 @@ import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.arrow.adbc.core.AdbcDatabase;
 import org.apache.arrow.adbc.core.AdbcException;
+import org.apache.arrow.memory.BufferAllocator;
 
 /** Account for driver/vendor-specific quirks in implementing validation 
tests. */
 public abstract class SqlValidationQuirks {
-  public abstract AdbcDatabase initDatabase() throws AdbcException;
+  public abstract AdbcDatabase initDatabase(BufferAllocator allocator) throws 
AdbcException;
 
   public void cleanupTable(String name) throws Exception {}
 
diff --git a/java/pom.xml b/java/pom.xml
index 29abf76..bb983ad 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -28,7 +28,7 @@
   <url>https://arrow.apache.org/</url>
 
   <properties>
-    <dep.arrow.version>10.0.0</dep.arrow.version>
+    <dep.arrow.version>11.0.0</dep.arrow.version>
     <adbc.version>0.4.0-SNAPSHOT</adbc.version>
   </properties>
 

Reply via email to