diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 5d95473f2..67eeb5a5a 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -20,6 +20,7 @@ - When a Statement is re-executed, the previous server-side operation is now explicitly closed before starting the new execution, preventing orphaned server-side operations when Statements are reused. ### Fixed +- Fixed `DatabaseMetaData.getTables()` in Thrift mode returning rows when called with an empty `types` array. Per JDBC spec, empty types means "no types selected" and now correctly returns zero rows (matching SEA mode). - Fixed `?` characters inside SQL comments, string literals, and quoted identifiers being incorrectly counted as parameter placeholders when `supportManyParameters=1`. `SQLInterpolator` now uses `SqlCommentParser` to locate only real placeholders. Fixes #1331. - Fixed `MetadataOperationTimeout` not being applied when metadata operations use SHOW commands. Operations like `getTables`, `getSchemas`, and `getColumns` now respect the `MetadataOperationTimeout` connection property instead of hanging indefinitely with no timeout. - Reclassify transient server errors to standard SQL states (08S01, 40001) across all Thrift error sites. This ensures UC unavailability and concurrent modification errors surface consistently for better retry handling. Note: Dashboards and branching logic keyed on legacy XXUCC or 42000 must be updated. diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java b/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java index 0057c7faa..c3b1bcb8a 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClient.java @@ -476,6 +476,11 @@ public DatabricksResultSet listTables( session.toString(), catalog, schemaNamePattern, tableNamePattern); LOGGER.debug(context); + // Per JDBC spec: null types = return all types; empty array = return nothing + if (tableTypes != null && tableTypes.length == 0) { + return metadataResultSetBuilder.getTablesResult(catalog, tableTypes, new ArrayList<>()); + } + if (!metadataResultSetBuilder.shouldAllowCatalogAccess(catalog, null, session)) { return metadataResultSetBuilder.getTablesResult(catalog, tableTypes, new ArrayList<>()); } diff --git a/src/test/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClientTest.java b/src/test/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClientTest.java index 583438d5b..62b44a662 100644 --- a/src/test/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClientTest.java +++ b/src/test/java/com/databricks/jdbc/dbclient/impl/thrift/DatabricksThriftServiceClientTest.java @@ -13,6 +13,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -590,6 +591,21 @@ void testListTables() throws SQLException { assertEquals(resultSet.getStatementStatus().getState(), StatementState.SUCCEEDED); } + @Test + void testListTablesWithEmptyTypesReturnsEmptyWithoutServerCall() throws SQLException { + // Per JDBC spec: empty types array means "no types selected" → return no rows. + // The driver must short-circuit and NOT send the Thrift request to the server. + DatabricksThriftServiceClient client = + new DatabricksThriftServiceClient(thriftAccessor, connectionContext); + + DatabricksResultSet resultSet = + client.listTables(session, TEST_CATALOG, TEST_SCHEMA, TEST_TABLE, new String[0]); + + assertEquals(StatementState.SUCCEEDED, resultSet.getStatementStatus().getState()); + assertFalse(resultSet.next(), "Empty types array must yield zero rows"); + verify(thriftAccessor, never()).getThriftResponse(any()); + } + @Test void testListColumns() throws SQLException { DatabricksThriftServiceClient client =