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..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 @@ -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,24 @@ 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 +150,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));