Skip to content

[FlightSQL][JDBC] NullPointerException in DatabaseMetadata methods when SqlInfo is unavailable #994

@merzbird

Description

@merzbird

Describe the bug

Multiple DatabaseMetadata methods in the Arrow Flight SQL JDBC driver throw NullPointerException when the server doesn't provide certain SqlInfo values. The ArrowDatabaseMetadata.getSqlInfoAndCacheIfCacheIsEmpty() method returns null, but calling code doesn't handle null defensively before invoking methods like .intValue(), .booleanValue(), or .isEmpty().

Affected Methods

All 68+ DatabaseMetadata methods that rely on getSqlInfoAndCacheIfCacheIsEmpty() are affected, including:

Integer-returning methods (20 methods)

  • getMaxBinaryLiteralLength()
  • getMaxCharLiteralLength()
  • getMaxColumnNameLength()
  • getMaxColumnsInGroupBy()
  • getMaxColumnsInIndex()
  • getMaxColumnsInOrderBy()
  • getMaxColumnsInSelect()
  • getMaxColumnsInTable()
  • getMaxConnections()
  • getMaxCursorNameLength()
  • getMaxIndexLength()
  • getMaxSchemaNameLength()
  • getMaxProcedureNameLength()
  • getMaxCatalogNameLength()
  • getMaxRowSize()
  • getMaxStatementLength()
  • getMaxStatements()
  • getMaxTableNameLength()
  • getMaxTablesInSelect()
  • getMaxUserNameLength()

Boolean-returning methods (30 methods)

  • supportsColumnAliasing()
  • nullPlusNonNullIsNull()
  • supportsTableCorrelationNames()
  • supportsDifferentTableCorrelationNames()
  • supportsExpressionsInOrderBy()
  • supportsOrderByUnrelated()
  • supportsLikeEscapeClause()
  • supportsNonNullableColumns()
  • supportsIntegrityEnhancementFacility()
  • isCatalogAtStart()
  • supportsSelectForUpdate()
  • supportsStoredProcedures()
  • supportsCorrelatedSubqueries()
  • doesMaxRowSizeIncludeBlobs()
  • supportsTransactions()
  • dataDefinitionCausesTransactionCommit()
  • dataDefinitionIgnoredInTransactions()
  • supportsBatchUpdates()
  • supportsSavepoints()
  • supportsNamedParameters()
  • locatorsUpdateCopy()
  • supportsStoredFunctionsUsingCallSyntax()

Support level methods (18 methods)

  • getDefaultTransactionIsolation()
  • supportsGroupBy()
  • supportsGroupByUnrelated()
  • supportsMinimumSQLGrammar()
  • supportsCoreSQLGrammar()
  • supportsExtendedSQLGrammar()
  • supportsANSI92EntryLevelSQL()
  • supportsANSI92IntermediateSQL()
  • supportsANSI92FullSQL()
  • supportsOuterJoins()
  • supportsFullOuterJoins()
  • supportsLimitedOuterJoins()
  • supportsSchemasInProcedureCalls()
  • supportsSchemasInIndexDefinitions()
  • supportsSchemasInPrivilegeDefinitions()
  • supportsCatalogsInIndexDefinitions()
  • supportsCatalogsInPrivilegeDefinitions()
  • supportsPositionedDelete()
  • supportsPositionedUpdate()
  • supportsSubqueriesInComparisons()
  • supportsSubqueriesInExists()
  • supportsSubqueriesInIns()
  • supportsSubqueriesInQuantifieds()
  • supportsUnion()
  • supportsUnionAll()

Map-returning methods

  • supportsConvert()

Component(s)

  • FlightSQL
  • JDBC

To Reproduce

import java.sql.*;
import java.util.Properties;

public class DatabaseMetadataTest {
    public static void main(String[] args) throws SQLException {
        String url = "jdbc:arrow-flight-sql://...";
        Properties props = new Properties();
        Driver driver = DriverManager.getDriver(url);
        try (Connection conn = driver.connect(url, props)) {
            DatabaseMetaData metaData = conn.getMetaData();

            // All of these throw NullPointerException
            System.out.println(metaData.getMaxBinaryLiteralLength());
            System.out.println(metaData.supportsTransactions());
            System.out.println(metaData.supportsGroupBy());
        }
    }
}

Error Output

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Long.intValue()"
because the return value of "org.apache.arrow.driver.jdbc.ArrowDatabaseMetadata.getSqlInfoAndCacheIfCacheIsEmpty
(org.apache.arrow.driver.jdbc.shaded.org.apache.arrow.flight.sql.impl.FlightSql$SqlInfo, java.lang.Class)" is null
	at org.apache.arrow.driver.jdbc.ArrowDatabaseMetadata.getMaxBinaryLiteralLength(ArrowDatabaseMetadata.java:xxx)

Expected behavior

  1. Option A: Return sensible defaults when SqlInfo is unavailable:

    • Integer methods: return 0 (as per JDBC spec for "no limit" or "unknown")
    • Boolean methods: return false (conservative/safe default)
    • Support level methods: return appropriate default enums
    • Map methods: return empty map
  2. Option B: Throw SQLException with a descriptive message indicating the server doesn't support the requested SqlInfo

  3. Methods should NOT throw unchecked NullPointerException - this violates JDBC contracts and the principle of least surprise

Environment

Arrow Version:

  • 18.3.0

Java Version:

  • Java 11+

OS:

  • All platforms

JDBC Specification Reference

Per JDBC 4.3 specification:

  • Methods returning int should return 0 when the limit is unknown or there is no limit
  • Methods returning boolean should return false for unsupported features
  • Methods should throw SQLException for errors, not unchecked exceptions

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type: bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions