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(""));
+    }
+  }
 }

Reply via email to