Copilot commented on code in PR #4359:
URL: https://github.com/apache/arrow-adbc/pull/4359#discussion_r3353388181


##########
java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/JniStatement.java:
##########
@@ -53,23 +59,48 @@ public void setSqlQuery(String query) throws AdbcException {
 
   @Override
   public void bind(VectorSchemaRoot root) throws AdbcException {
+    clearBind();
     this.bindRoot = root;
   }
 
+  @Override
+  public void bind(ArrowReader reader) throws AdbcException {
+    clearBind();
+    this.bindRoot = null;
+    this.bindStream = reader;
+  }
+
+  private void clearBind() throws AdbcException {
+    if (this.bindStream != null) {
+      try {
+        this.bindStream.close();
+      } catch (IOException e) {
+        throw AdbcException.internal("[jni] failed to close previous bind 
stream").withCause(e);
+      } finally {
+        this.bindStream = null;
+      }
+    }
+    this.bindRoot = null;
+  }
+
   // The C Data export takes ownership of the data at bind time and ignores 
subsequent
   // client changes to the bound root. Defer the export until execution so we 
capture
   // the final state of the VectorSchemaRoot.
   private void exportBind() throws AdbcException {
-    if (bindRoot == null) {
-      return;
-    }
-    try (final ArrowArray batch = ArrowArray.allocateNew(allocator);
-        final ArrowSchema schema = ArrowSchema.allocateNew(allocator)) {
-      // TODO(lidavidm): we may need a way to separately provide a dictionary 
provider
-      Data.exportSchema(allocator, bindRoot.getSchema(), null, schema);
-      Data.exportVectorSchemaRoot(allocator, bindRoot, null, batch);
-
-      JniLoader.INSTANCE.statementBind(handle, batch, schema);
+    if (bindRoot != null) {
+      try (final ArrowArray batch = ArrowArray.allocateNew(allocator);
+          final ArrowSchema schema = ArrowSchema.allocateNew(allocator)) {
+        // TODO(lidavidm): we may need a way to separately provide a 
dictionary provider
+        Data.exportSchema(allocator, bindRoot.getSchema(), null, schema);
+        Data.exportVectorSchemaRoot(allocator, bindRoot, null, batch);
+
+        JniLoader.INSTANCE.statementBind(handle, batch, schema);
+      }
+    } else if (bindStream != null) {
+      try (final ArrowArrayStream stream = 
ArrowArrayStream.allocateNew(allocator)) {
+        Data.exportArrayStream(allocator, bindStream, stream);
+        JniLoader.INSTANCE.statementBindStream(handle, stream);
+      }

Review Comment:
   After exporting/binding an ArrowReader via AdbcStatementBindStream, the C 
driver takes ownership of the exported ArrowArrayStream and will invoke its 
release callback (which will close the underlying ArrowReader). Keeping 
`bindStream` set here means `clearBind()`/`close()` may later close the same 
reader again, or close it before the native statement releases it, violating 
the ownership contract of `AdbcStatementBindStream` (see adbc.h:2114-2116). 
Clear the Java reference once the bind succeeds so the statement no longer 
attempts to manage the reader lifecycle after ownership is transferred.



##########
java/driver/jni-validation/src/test/java/org/apache/arrow/adbc/driver/jni/PostgresIntegrationTest.java:
##########
@@ -569,6 +573,37 @@ void bindUpdateQuery() throws Exception {
     }
   }
 
+  @Test
+  void bindStream() throws Exception {
+    // Create temp Arrow file to get an ArrowReader
+    Schema schema = new Schema(List.of(Field.nullable("i", 
Types.MinorType.INT.getType())));

Review Comment:
   For stream binding, other tests in this file use parameter-style field names 
(e.g. "$1" in bindSelectQuery). If this test is updated to actually execute a 
statement (which it should), the stream schema should match that convention so 
the driver can map columns to bind parameters consistently.



##########
java/driver/jni-validation/src/test/java/org/apache/arrow/adbc/driver/jni/PostgresIntegrationTest.java:
##########
@@ -569,6 +573,37 @@ void bindUpdateQuery() throws Exception {
     }
   }
 
+  @Test
+  void bindStream() throws Exception {
+    // Create temp Arrow file to get an ArrowReader
+    Schema schema = new Schema(List.of(Field.nullable("i", 
Types.MinorType.INT.getType())));
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    try (VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator);
+        ArrowStreamWriter writer = new ArrowStreamWriter(root, null, baos)) {
+      writer.start();
+
+      IntVector iv = (IntVector) root.getVector(0);
+
+      iv.setSafe(0, 1);
+      iv.setSafe(1, 42);
+      iv.setNull(2);
+      root.setRowCount(3);
+      writer.writeBatch();
+
+      iv.setSafe(0, 10);
+      iv.setSafe(1, 20);
+      iv.setSafe(2, 30);
+      root.setRowCount(3);
+      writer.writeBatch();
+    }
+
+    ArrowStreamReader reader =
+        new ArrowStreamReader(new ByteArrayInputStream(baos.toByteArray()), 
allocator);
+    try (var stmt = conn.createStatement()) {
+      stmt.bind(reader);
+    }

Review Comment:
   This test currently never executes a query/update after binding the 
ArrowReader, so it doesn't exercise the new `statementBindStream` JNI path 
(exporting the ArrowReader to C and letting the driver consume it). Execute a 
simple bound query and assert results so regressions in stream binding are 
caught.



##########
java/driver/jni-validation/src/test/java/org/apache/arrow/adbc/driver/jni/SqlServerIntegrationTest.java:
##########
@@ -598,6 +602,37 @@ void bindUpdateQuery() throws Exception {
     }
   }
 
+  @Test
+  void bindStream() throws Exception {
+    // Create temp Arrow file to get an ArrowReader
+    Schema schema = new Schema(List.of(Field.nullable("i", 
Types.MinorType.INT.getType())));
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    try (VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator);
+        ArrowStreamWriter writer = new ArrowStreamWriter(root, null, baos)) {
+      writer.start();
+
+      IntVector iv = (IntVector) root.getVector(0);
+
+      iv.setSafe(0, 1);
+      iv.setSafe(1, 42);
+      iv.setNull(2);
+      root.setRowCount(3);
+      writer.writeBatch();
+
+      iv.setSafe(0, 10);
+      iv.setSafe(1, 20);
+      iv.setSafe(2, 30);
+      root.setRowCount(3);
+      writer.writeBatch();
+    }
+
+    ArrowStreamReader reader =
+        new ArrowStreamReader(new ByteArrayInputStream(baos.toByteArray()), 
allocator);
+    try (var stmt = conn.createStatement()) {
+      stmt.bind(reader);
+    }

Review Comment:
   This test only binds the ArrowReader and closes the statement; it never 
executes anything, so it doesn't exercise the new JNI stream-binding/export 
path. Execute a simple bound query (similar to bindSelectQuery) and assert the 
results so stream binding failures are caught.



##########
java/driver/jni-validation/src/test/java/org/apache/arrow/adbc/driver/jni/SqlServerIntegrationTest.java:
##########
@@ -598,6 +602,37 @@ void bindUpdateQuery() throws Exception {
     }
   }
 
+  @Test
+  void bindStream() throws Exception {
+    // Create temp Arrow file to get an ArrowReader
+    Schema schema = new Schema(List.of(Field.nullable("i", 
Types.MinorType.INT.getType())));

Review Comment:
   To meaningfully test stream binding (i.e., execute a statement using the 
bound values), the stream schema should follow the parameter-style naming used 
elsewhere in this file (e.g., "$1" in bindSelectQuery) so the driver can map 
stream columns to bind parameters consistently.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to