Skip to content

Commit a11339d

Browse files
authored
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](apache/arrow#38404), which made `handle.signature` mutable and shared across the `prepareAndExecute` → `executeFlightInfoQuery` path. Closes #44.
1 parent 3a1498b commit a11339d

3 files changed

Lines changed: 32 additions & 0 deletions

File tree

flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowFlightStatement.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public FlightInfo executeFlightInfoQuery() throws SQLException {
5252
}
5353

5454
final Schema resultSetSchema = preparedStatement.getDataSetSchema();
55+
signature.columns.clear();
5556
signature.columns.addAll(
5657
ConvertUtils.convertArrowFieldsToColumnMetaDataList(resultSetSchema.getFields()));
5758
setSignature(signature);

flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ResultSetMetadataTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,23 @@ public void testShouldIsSearchable() throws SQLException {
220220
public void testShouldGetColumnTypesFromOutOfBoundIndex() {
221221
assertThrows(IndexOutOfBoundsException.class, () -> metadata.getColumnType(4));
222222
}
223+
224+
/**
225+
* Regression test for <a href="https://github.com/apache/arrow-java/issues/44">issue #44</a>:
226+
* {@code ArrowFlightStatement#executeFlightInfoQuery} appends schema columns to the reused {@code
227+
* Meta.Signature} without clearing the existing list. When the result has at least one endpoint,
228+
* the {@code FlightStream} consumption path overwrites {@code signature.columns} from the actual
229+
* stream schema and hides the duplication. With an empty endpoint list — the scenario reported
230+
* against both Rust- and Denodo-based Flight SQL servers — that overwrite never runs and {@code
231+
* ResultSetMetaData#getColumnCount()} reports double the schema width.
232+
*/
233+
@Test
234+
public void testShouldNotDuplicateColumnsWhenFlightInfoHasNoEndpoints() throws SQLException {
235+
try (Connection conn = FLIGHT_SERVER_TEST_EXTENSION.getConnection(false);
236+
Statement st = conn.createStatement();
237+
ResultSet rs =
238+
st.executeQuery(CoreMockedSqlProducers.LEGACY_REGULAR_NO_ENDPOINTS_SQL_CMD)) {
239+
assertThat(rs.getMetaData().getColumnCount(), equalTo(1));
240+
}
241+
}
223242
}

flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/CoreMockedSqlProducers.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ public final class CoreMockedSqlProducers {
6767
public static final String LEGACY_METADATA_SQL_CMD = "SELECT * FROM METADATA";
6868
public static final String LEGACY_CANCELLATION_SQL_CMD = "SELECT * FROM TAKES_FOREVER";
6969
public static final String LEGACY_REGULAR_WITH_EMPTY_SQL_CMD = "SELECT * FROM TEST_EMPTIES";
70+
public static final String LEGACY_REGULAR_NO_ENDPOINTS_SQL_CMD =
71+
"SELECT * FROM TEST_NO_ENDPOINTS";
7072

7173
public static final String UUID_SQL_CMD = "SELECT * FROM UUID_TABLE";
7274
public static final String UUID_PREPARED_SELECT_SQL_CMD =
@@ -100,12 +102,22 @@ public static MockFlightSqlProducer getLegacyProducer() {
100102
addLegacyMetadataSqlCmdSupport(producer);
101103
addLegacyCancellationSqlCmdSupport(producer);
102104
addQueryWithEmbeddedEmptyRoot(producer);
105+
addQueryWithNoEndpoints(producer);
103106
addUuidSqlCmdSupport(producer);
104107
addUuidPreparedSelectSqlCmdSupport(producer);
105108
addUuidPreparedUpdateSqlCmdSupport(producer);
106109
return producer;
107110
}
108111

112+
private static void addQueryWithNoEndpoints(final MockFlightSqlProducer producer) {
113+
final Schema querySchema =
114+
new Schema(
115+
ImmutableList.of(
116+
new Field("ID", new FieldType(true, new ArrowType.Int(64, true), null), null)));
117+
producer.addSelectQuery(
118+
LEGACY_REGULAR_NO_ENDPOINTS_SQL_CMD, querySchema, Collections.emptyList());
119+
}
120+
109121
/**
110122
* Gets a {@link MockFlightSqlProducer} configured with UUID test data.
111123
*

0 commit comments

Comments
 (0)