From b4d251bbbbc8be38a38baa274b3ed84687e72824 Mon Sep 17 00:00:00 2001 From: Mario Juarros Date: Wed, 7 Oct 2026 14:13:21 -0600 Subject: [PATCH 1/2] fix(flight-sql-jdbc): preserve Arrow column nullability in JDBC metadata (GH-782) --- .../arrow/driver/jdbc/utils/ConvertUtils.java | 7 +++++- .../ArrowFlightPreparedStatementTest.java | 24 +++++++++++++++++++ .../driver/jdbc/utils/ConvertUtilsTest.java | 24 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java index dd51ee5361..4381e32e81 100644 --- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java +++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java @@ -16,6 +16,7 @@ */ package org.apache.arrow.driver.jdbc.utils; +import java.sql.ResultSetMetaData; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -78,7 +79,11 @@ public static List convertArrowFieldsToColumnMetaDataList( Common.ColumnMetaData.newBuilder() .setOrdinal(index) .setColumnName(field.getName()) - .setLabel(field.getName()); + .setLabel(field.getName()) + .setNullable( + field.isNullable() + ? ResultSetMetaData.columnNullable + : ResultSetMetaData.columnNoNulls); setOnColumnMetaDataBuilder(builder, field.getMetadata()); diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowFlightPreparedStatementTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowFlightPreparedStatementTest.java index 73ece46403..2ffa5f5cc7 100644 --- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowFlightPreparedStatementTest.java +++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowFlightPreparedStatementTest.java @@ -28,6 +28,7 @@ import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.time.LocalDate; import java.util.Arrays; @@ -259,6 +260,29 @@ public void testReturnColumnCount() throws SQLException { } } + @Test + public void testReturnColumnNullability() throws SQLException { + final String query = "SELECT nullable, required FROM nullability_test"; + final Schema schema = + new Schema( + Arrays.asList( + Field.nullable("nullable", ArrowType.Utf8.INSTANCE), + Field.notNullable("required", ArrowType.Utf8.INSTANCE))); + PRODUCER.addSelectQuery(query, schema, Collections.emptyList()); + try (final PreparedStatement psmt = connection.prepareStatement(query)) { + assertAll( + "Column nullability is as expected", + () -> assertThat("nullable", equalTo(psmt.getMetaData().getColumnName(1))), + () -> assertThat("required", equalTo(psmt.getMetaData().getColumnName(2))), + () -> + assertThat( + psmt.getMetaData().isNullable(1), equalTo(ResultSetMetaData.columnNullable)), + () -> + assertThat( + psmt.getMetaData().isNullable(2), equalTo(ResultSetMetaData.columnNoNulls))); + } + } + @Test public void testUpdateQuery() throws SQLException { String query = "Fake update"; diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java index f128ca7c73..0e1b5ed151 100644 --- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java +++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java @@ -20,6 +20,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import com.google.common.collect.ImmutableList; +import java.sql.ResultSetMetaData; import java.util.List; import org.apache.arrow.flight.sql.FlightSqlColumnMetadata; import org.apache.arrow.vector.types.pojo.ArrowType; @@ -92,6 +93,7 @@ public void testShouldConvertArrowFieldsToColumnMetaDataList() { .setSchemaName("schema1") .setTableName("table1") .setColumnName("col1") + .setNullable(ResultSetMetaData.columnNullable) .setType( Common.AvaticaType.newBuilder() .setId(SqlTypes.getSqlTypeIdFromArrowType(ArrowType.Utf8.INSTANCE)) @@ -104,6 +106,7 @@ public void testShouldConvertArrowFieldsToColumnMetaDataList() { .setSchemaName("schema1") .setTableName("table1") .setColumnName("col2") + .setNullable(ResultSetMetaData.columnNullable) .setType( Common.AvaticaType.newBuilder() .setId(SqlTypes.getSqlTypeIdFromArrowType(ArrowType.Utf8View.INSTANCE)) @@ -117,6 +120,26 @@ public void testShouldConvertArrowFieldsToColumnMetaDataList() { assertColumnMetaData(expectedColumnMetaData, actualColumnMetaData); } + @Test + public void testShouldPreserveArrowFieldNullability() { + + final List fields = + ImmutableList.of( + Field.nullable("nullable", ArrowType.Utf8.INSTANCE), + Field.notNullable("required", ArrowType.Utf8.INSTANCE)); + + final List columnMetaDataList = + ConvertUtils.convertArrowFieldsToColumnMetaDataList(fields); + + assertThat(columnMetaDataList.size(), equalTo(2)); + assertThat(columnMetaDataList.get(0).columnName, equalTo("nullable")); + assertThat( + columnMetaDataList.get(0).nullable, equalTo(ResultSetMetaData.columnNullable)); + assertThat(columnMetaDataList.get(1).columnName, equalTo("required")); + assertThat( + columnMetaDataList.get(1).nullable, equalTo(ResultSetMetaData.columnNoNulls)); + } + private void assertColumnMetaData( final List expected, final List actual) { assertThat(expected.size(), equalTo(actual.size())); @@ -129,6 +152,7 @@ private void assertColumnMetaData( assertThat(expectedColumnMetaData.tableName, equalTo(actualColumnMetaData.tableName)); assertThat(expectedColumnMetaData.columnName, equalTo(actualColumnMetaData.columnName)); assertThat(expectedColumnMetaData.type, equalTo(actualColumnMetaData.type)); + assertThat(expectedColumnMetaData.nullable, equalTo(actualColumnMetaData.nullable)); assertThat(expectedColumnMetaData.readOnly, equalTo(actualColumnMetaData.readOnly)); assertThat(expectedColumnMetaData.autoIncrement, equalTo(actualColumnMetaData.autoIncrement)); assertThat(expectedColumnMetaData.precision, equalTo(actualColumnMetaData.precision)); From dbcaa3cf979e1ae6b8f91a8e38be6599c73f8849 Mon Sep 17 00:00:00 2001 From: Mario Juarros Date: Wed, 7 Oct 2026 15:04:03 -0600 Subject: [PATCH 2/2] style(flight-sql-jdbc): format nullability assertions (GH-782) --- .../apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java index 0e1b5ed151..7330765f92 100644 --- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java +++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java @@ -133,11 +133,9 @@ public void testShouldPreserveArrowFieldNullability() { assertThat(columnMetaDataList.size(), equalTo(2)); assertThat(columnMetaDataList.get(0).columnName, equalTo("nullable")); - assertThat( - columnMetaDataList.get(0).nullable, equalTo(ResultSetMetaData.columnNullable)); + assertThat(columnMetaDataList.get(0).nullable, equalTo(ResultSetMetaData.columnNullable)); assertThat(columnMetaDataList.get(1).columnName, equalTo("required")); - assertThat( - columnMetaDataList.get(1).nullable, equalTo(ResultSetMetaData.columnNoNulls)); + assertThat(columnMetaDataList.get(1).nullable, equalTo(ResultSetMetaData.columnNoNulls)); } private void assertColumnMetaData(