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.git
The following commit(s) were added to refs/heads/main by this push:
new 805327e48d GH-40249: [Java] Fix NPE in ArrowDatabaseMetadata (#40988)
805327e48d is described below
commit 805327e48d81cc1ccd3ae4de749547c4f1f4842c
Author: normanj-bitquill <[email protected]>
AuthorDate: Sun Apr 7 18:17:09 2024 -0500
GH-40249: [Java] Fix NPE in ArrowDatabaseMetadata (#40988)
### Rationale for this change
When retrieving database metadata using the JDBC driver, some data such as
SQL keywords could be null. Before this change, an NPE would be thrown when
trying to convert the list of SQL keywords into a String.
### What changes are included in this PR?
The following database metadata fields:
* SQL keywords
* Numeric functions
* String functions
* System functions
* Time/date functions
will convert to an empty string when they are null.
### Are these changes tested?
A unit test has been added to verify that the fields above are converted to
the empty string when null, with no exceptions thrown.
### Are there any user-facing changes?
The fields above will now return an empty string rather than throw an NPE.
* GitHub Issue: #40249
Authored-by: Norman Jordan <[email protected]>
Signed-off-by: David Li <[email protected]>
---
.../arrow/driver/jdbc/ArrowDatabaseMetadata.java | 24 +++++++++++++++-------
.../driver/jdbc/ArrowDatabaseMetadataTest.java | 19 ++++++++++++++++-
2 files changed, 35 insertions(+), 8 deletions(-)
diff --git
a/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadata.java
b/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadata.java
index d68b8070e2..4af3e55ee1 100644
---
a/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadata.java
+++
b/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadata.java
@@ -49,6 +49,7 @@ import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -197,31 +198,36 @@ public class ArrowDatabaseMetadata extends
AvaticaDatabaseMetaData {
@Override
public String getSQLKeywords() throws SQLException {
return convertListSqlInfoToString(
- getSqlInfoAndCacheIfCacheIsEmpty(SqlInfo.SQL_KEYWORDS, List.class));
+ getSqlInfoAndCacheIfCacheIsEmpty(SqlInfo.SQL_KEYWORDS, List.class))
+ .orElse("");
}
@Override
public String getNumericFunctions() throws SQLException {
return convertListSqlInfoToString(
- getSqlInfoAndCacheIfCacheIsEmpty(SqlInfo.SQL_NUMERIC_FUNCTIONS,
List.class));
+ getSqlInfoAndCacheIfCacheIsEmpty(SqlInfo.SQL_NUMERIC_FUNCTIONS,
List.class))
+ .orElse("");
}
@Override
public String getStringFunctions() throws SQLException {
return convertListSqlInfoToString(
- getSqlInfoAndCacheIfCacheIsEmpty(SqlInfo.SQL_STRING_FUNCTIONS,
List.class));
+ getSqlInfoAndCacheIfCacheIsEmpty(SqlInfo.SQL_STRING_FUNCTIONS,
List.class))
+ .orElse("");
}
@Override
public String getSystemFunctions() throws SQLException {
return convertListSqlInfoToString(
- getSqlInfoAndCacheIfCacheIsEmpty(SqlInfo.SQL_SYSTEM_FUNCTIONS,
List.class));
+ getSqlInfoAndCacheIfCacheIsEmpty(SqlInfo.SQL_SYSTEM_FUNCTIONS,
List.class))
+ .orElse("");
}
@Override
public String getTimeDateFunctions() throws SQLException {
return convertListSqlInfoToString(
- getSqlInfoAndCacheIfCacheIsEmpty(SqlInfo.SQL_DATETIME_FUNCTIONS,
List.class));
+ getSqlInfoAndCacheIfCacheIsEmpty(SqlInfo.SQL_DATETIME_FUNCTIONS,
List.class))
+ .orElse("");
}
@Override
@@ -753,8 +759,12 @@ public class ArrowDatabaseMetadata extends
AvaticaDatabaseMetaData {
return desiredType.cast(cachedSqlInfo.get(sqlInfoCommand));
}
- private String convertListSqlInfoToString(final List<?> sqlInfoList) {
- return
sqlInfoList.stream().map(Object::toString).collect(Collectors.joining(", "));
+ private Optional<String> convertListSqlInfoToString(final List<?>
sqlInfoList) {
+ if (sqlInfoList == null) {
+ return Optional.empty();
+ } else {
+ return
Optional.of(sqlInfoList.stream().map(Object::toString).collect(Collectors.joining(",
")));
+ }
}
private boolean getSqlInfoEnumOptionAndCacheIfCacheIsEmpty(
diff --git
a/java/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
b/java/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
index 0d930f4c44..51334c7748 100644
---
a/java/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
+++
b/java/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
@@ -95,9 +95,14 @@ import com.google.protobuf.Message;
public class ArrowDatabaseMetadataTest {
public static final boolean EXPECTED_MAX_ROW_SIZE_INCLUDES_BLOBS = false;
private static final MockFlightSqlProducer FLIGHT_SQL_PRODUCER = new
MockFlightSqlProducer();
+ private static final MockFlightSqlProducer FLIGHT_SQL_PRODUCER_EMPTY_SQLINFO
=
+ new MockFlightSqlProducer();
@ClassRule
public static final FlightServerTestRule FLIGHT_SERVER_TEST_RULE =
FlightServerTestRule
.createStandardTestRule(FLIGHT_SQL_PRODUCER);
+ @ClassRule
+ public static final FlightServerTestRule
FLIGHT_SERVER_EMPTY_SQLINFO_TEST_RULE =
+
FlightServerTestRule.createStandardTestRule(FLIGHT_SQL_PRODUCER_EMPTY_SQLINFO);
private static final int ROW_COUNT = 10;
private static final List<List<Object>> EXPECTED_GET_CATALOGS_RESULTS =
range(0, ROW_COUNT)
@@ -604,7 +609,7 @@ public class ArrowDatabaseMetadataTest {
@AfterClass
public static void tearDown() throws Exception {
- AutoCloseables.close(connection, FLIGHT_SQL_PRODUCER);
+ AutoCloseables.close(connection, FLIGHT_SQL_PRODUCER,
FLIGHT_SQL_PRODUCER_EMPTY_SQLINFO);
}
@@ -1420,4 +1425,16 @@ public class ArrowDatabaseMetadataTest {
Assert.assertEquals("\\*", ArrowDatabaseMetadata.sqlToRegexLike("*"));
Assert.assertEquals("T\\*E.S.*T",
ArrowDatabaseMetadata.sqlToRegexLike("T*E_S%T"));
}
+
+ @Test
+ public void testEmptySqlInfo() throws Exception {
+ try (final Connection testConnection =
FLIGHT_SERVER_EMPTY_SQLINFO_TEST_RULE.getConnection(false)) {
+ final DatabaseMetaData metaData = testConnection.getMetaData();
+ collector.checkThat(metaData.getSQLKeywords(), is(""));
+ collector.checkThat(metaData.getNumericFunctions(), is(""));
+ collector.checkThat(metaData.getStringFunctions(), is(""));
+ collector.checkThat(metaData.getSystemFunctions(), is(""));
+ collector.checkThat(metaData.getTimeDateFunctions(), is(""));
+ }
+ }
}