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.
*