Skip to content

Conversation

@MagnetonBora
Copy link
Contributor

Description

DRAFT — need to add tests, requesting early feedback.

This PR fixes a driver crash that occurs when executing a query returning an INTERVAL type mentioned in #1083 issue.
Previously, PreparedStatement.getMetaData() failed with:

IllegalArgumentException: No enum constant ColumnInfoTypeName.INTERVAL DAY TO SECOND

This happened because the driver attempted to map interval types to an enum that did not support multi-word SQL type names such as INTERVAL DAY TO SECOND.

What has been done

  • Added support for INTERVAL types in metadata handling.
  • Ensured DatabricksPreparedStatement.getMetaData() and supporting code paths correctly parse and map interval type names.
  • Changed visibility of SIGNED_TYPES from private to public (just a suggestion — it seems useful and potentially reusable).

This PR addresses the issue described in Follow-up #1064 and fixes the metadata retrieval for interval expressions such as:

SELECT current_timestamp() - '2025-01-01 00:00:00.0'
SELECT INTERVAL '15' MINUTE

Testing

Still planning to add automated tests — this is a DRAFT.

Manual testing performed so far:

  • Used a small Java program (included below) that:

    • Prepares a statement returning an interval value.
    • Calls getMetaData() to verify that no exceptions are thrown.
    • Prints metadata fields (type, name, precision, scale, etc.).
    • Executes the query and validates that values can be retrieved via both getObject() and getString().

Tested on driver version 3.0.4.

Before this fix → driver crashes.
With this PR → metadata is returned successfully and the interval value can be fetched.

How to manually test the change

You can use the following standalone Java snippet to test the fix end-to-end:

public static void main(String[] args) {
  String url = "...";
  Properties props = new Properties();
  Driver driver = Driver.getInstance();

  try (Connection conn = driver.connect(url, props)) {
    try (PreparedStatement st = conn.prepareStatement("SELECT INTERVAL '15' MINUTE")) {
      ResultSetMetaData rsmd = st.getMetaData();

      System.out.println("=== ResultSetMetaData ===");
      System.out.println("Column count: " + rsmd.getColumnCount());

      for (int i = 1; i <= rsmd.getColumnCount(); i++) {
        System.out.println("\nColumn " + i + ":");
        System.out.println("  Name: " + rsmd.getColumnName(i));
        System.out.println("  Label: " + rsmd.getColumnLabel(i));
        System.out.println("  Type: " + rsmd.getColumnType(i));
        System.out.println("  Type Name: " + rsmd.getColumnTypeName(i));
        System.out.println("  Class Name: " + rsmd.getColumnClassName(i));
        System.out.println("  Display Size: " + rsmd.getColumnDisplaySize(i));
        System.out.println("  Precision: " + rsmd.getPrecision(i));
        System.out.println("  Scale: " + rsmd.getScale(i));
      }

      try (ResultSet rs = st.executeQuery()) {
        System.out.println("=== Query Results ===");

        while (rs.next()) {
          Object value = rs.getObject(1);
          String stringValue = rs.getString(1);

          System.out.println("Value (Object): " + value);
          System.out.println("Value (String): " + stringValue);
          System.out.println("Value class: " + (value != null ? value.getClass() : "null"));
        }
      }

    } catch (SQLException e) {
      System.err.println("SQL Error: " + e.getMessage());
      e.printStackTrace();
      throw new RuntimeException(e);
    }
  } catch (SQLException e) {
    System.err.println("Connection Error: " + e.getMessage());
    e.printStackTrace();
    throw new RuntimeException(e);
  }

  TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
  System.out.printf("\nThe driver {%s} has been initialized.%n", Driver.class);
}

Additional Notes to the Reviewer

  • This PR is a DRAFT — unit tests will be added before marking it ready.

  • Early feedback is welcome, especially regarding:

    • The chosen mapping strategy for interval types.
    • Whether exposing SIGNED_TYPES is acceptable.
    • Enum / type-parsing paths that may need to be refactored.
  • If there are other interval forms worth testing (e.g., INTERVAL DAY, INTERVAL SECOND, mixed units), please let me know.

  • I’m open to suggestions on where interval-related tests should live in the test suite.

@MagnetonBora
Copy link
Contributor Author

@samikshya-db could you please give your feedback? Does this PR make sense to you?

Copy link
Collaborator

@gopalldb gopalldb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change looks good, can you add test cases?

} else if (columnTypeText.equalsIgnoreCase(VARIANT)) {
columnTypeName = ColumnInfoTypeName.STRING;
columnTypeText = VARIANT;
} else if (INTERVAL_TYPES.contains(columnTypeText.toUpperCase())) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we also say startWith instead of contains? It can then work with any new variant in future as well

ColumnInfoTypeName.TINYINT,
ColumnInfoTypeName.BYTE,
ColumnInfoTypeName.BIGINT));
public static final ArrayList<String> INTERVAL_TYPES =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We can use Set instead of List

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants