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-java.git


The following commit(s) were added to refs/heads/main by this push:
     new a11339d1b GH-44: Clear signature columns before re-populating in 
ArrowFlightStatement#executeFlightInfoQuery (#1135)
a11339d1b is described below

commit a11339d1bdea4ec398df223907625f6b7c83ab59
Author: Jan Kadlec <[email protected]>
AuthorDate: Fri Aug 28 07:52:57 2026 +0200

    GH-44: Clear signature columns before re-populating in 
ArrowFlightStatement#executeFlightInfoQuery (#1135)
    
    ## Rationale
    
    `ArrowFlightStatement#executeFlightInfoQuery` appends the dataset
    schema columns to the statement's `Meta.Signature` without clearing
    them, so the column list doubles on every invocation. When the
    `FlightInfo` has at least one endpoint, `populateData` overwrites
    `signature.columns` from the actual stream schema and hides the
    duplication. With an empty endpoint list, that overwrite never
    runs. Avatica then refuses the metadata with `Cannot have more columns
    with the same name`.
    
    This is a regression introduced in 15.0.0 by the prepared-statement
    parameter binding work in
    [GH-33475](https://github.com/apache/arrow/pull/38404), which made
    `handle.signature` mutable and shared across the `prepareAndExecute` →
    `executeFlightInfoQuery` path.
    
    Closes #44.
---
 .../arrow/driver/jdbc/ArrowFlightStatement.java       |  1 +
 .../arrow/driver/jdbc/ResultSetMetadataTest.java      | 19 +++++++++++++++++++
 .../driver/jdbc/utils/CoreMockedSqlProducers.java     | 12 ++++++++++++
 3 files changed, 32 insertions(+)

diff --git 
a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowFlightStatement.java
 
b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowFlightStatement.java
index 577aee3b4..ff3d060c5 100644
--- 
a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowFlightStatement.java
+++ 
b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowFlightStatement.java
@@ -52,6 +52,7 @@ public class ArrowFlightStatement extends AvaticaStatement 
implements ArrowFligh
     }
 
     final Schema resultSetSchema = preparedStatement.getDataSetSchema();
+    signature.columns.clear();
     signature.columns.addAll(
         
ConvertUtils.convertArrowFieldsToColumnMetaDataList(resultSetSchema.getFields()));
     setSignature(signature);
diff --git 
a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ResultSetMetadataTest.java
 
b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ResultSetMetadataTest.java
index 4583194f5..700011310 100644
--- 
a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ResultSetMetadataTest.java
+++ 
b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ResultSetMetadataTest.java
@@ -220,4 +220,23 @@ public class ResultSetMetadataTest {
   public void testShouldGetColumnTypesFromOutOfBoundIndex() {
     assertThrows(IndexOutOfBoundsException.class, () -> 
metadata.getColumnType(4));
   }
+
+  /**
+   * Regression test for <a 
href="https://github.com/apache/arrow-java/issues/44";>issue #44</a>:
+   * {@code ArrowFlightStatement#executeFlightInfoQuery} appends schema 
columns to the reused {@code
+   * Meta.Signature} without clearing the existing list. When the result has 
at least one endpoint,
+   * the {@code FlightStream} consumption path overwrites {@code 
signature.columns} from the actual
+   * stream schema and hides the duplication. With an empty endpoint list — 
the scenario reported
+   * against both Rust- and Denodo-based Flight SQL servers — that overwrite 
never runs and {@code
+   * ResultSetMetaData#getColumnCount()} reports double the schema width.
+   */
+  @Test
+  public void testShouldNotDuplicateColumnsWhenFlightInfoHasNoEndpoints() 
throws SQLException {
+    try (Connection conn = FLIGHT_SERVER_TEST_EXTENSION.getConnection(false);
+        Statement st = conn.createStatement();
+        ResultSet rs =
+            
st.executeQuery(CoreMockedSqlProducers.LEGACY_REGULAR_NO_ENDPOINTS_SQL_CMD)) {
+      assertThat(rs.getMetaData().getColumnCount(), equalTo(1));
+    }
+  }
 }
diff --git 
a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/CoreMockedSqlProducers.java
 
b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/CoreMockedSqlProducers.java
index 7c1775569..9c5d972b1 100644
--- 
a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/CoreMockedSqlProducers.java
+++ 
b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/CoreMockedSqlProducers.java
@@ -67,6 +67,8 @@ public final class CoreMockedSqlProducers {
   public static final String LEGACY_METADATA_SQL_CMD = "SELECT * FROM 
METADATA";
   public static final String LEGACY_CANCELLATION_SQL_CMD = "SELECT * FROM 
TAKES_FOREVER";
   public static final String LEGACY_REGULAR_WITH_EMPTY_SQL_CMD = "SELECT * 
FROM TEST_EMPTIES";
+  public static final String LEGACY_REGULAR_NO_ENDPOINTS_SQL_CMD =
+      "SELECT * FROM TEST_NO_ENDPOINTS";
 
   public static final String UUID_SQL_CMD = "SELECT * FROM UUID_TABLE";
   public static final String UUID_PREPARED_SELECT_SQL_CMD =
@@ -100,12 +102,22 @@ public final class CoreMockedSqlProducers {
     addLegacyMetadataSqlCmdSupport(producer);
     addLegacyCancellationSqlCmdSupport(producer);
     addQueryWithEmbeddedEmptyRoot(producer);
+    addQueryWithNoEndpoints(producer);
     addUuidSqlCmdSupport(producer);
     addUuidPreparedSelectSqlCmdSupport(producer);
     addUuidPreparedUpdateSqlCmdSupport(producer);
     return producer;
   }
 
+  private static void addQueryWithNoEndpoints(final MockFlightSqlProducer 
producer) {
+    final Schema querySchema =
+        new Schema(
+            ImmutableList.of(
+                new Field("ID", new FieldType(true, new ArrowType.Int(64, 
true), null), null)));
+    producer.addSelectQuery(
+        LEGACY_REGULAR_NO_ENDPOINTS_SQL_CMD, querySchema, 
Collections.emptyList());
+  }
+
   /**
    * Gets a {@link MockFlightSqlProducer} configured with UUID test data.
    *

Reply via email to