diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index ea019e959..59e177133 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -77,6 +77,7 @@ upgrading. These changes do not affect metadata on All-Purpose Clusters. - 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. +- Fixed `setCatalog()` and `setSchema()` producing invalid SQL (e.g. `SET CATALOG ``name``) when the catalog or schema name was passed already wrapped in backticks. Backticks are now stripped before wrapping, and `getCatalog()`/`getSchema()` return the bare identifier name. - Fixed telemetry HTTP client socket leak that prevented CRaC checkpoint. After `Connection.close()`, delayed telemetry flush tasks could re-create HTTP clients that were never closed, leaking TCP sockets. Fixes #1325. - Fixed client-side enforcement of `maxRows` limit. When `statement.setMaxRows()` is set, `ResultSet.next()` now returns false once the row limit is reached, even if the server returns more rows. Applies to all result types (Thrift, SEA, inline, CloudFetch). - Bump shaded `bouncycastle` (`bcprov-jdk18on`, `bcpkix-jdk18on`) from 1.79 to 1.84 to address [CVE-2026-5598](https://github.com/advisories/GHSA-p93r-85wp-75v3) (covert timing channel, severity 8.9) and two related MEDIUM CVEs (GHSA-wg6q-6289-32hp, GHSA-c3fc-8qff-9hwx). All three are unsurfaced by NVD-CPE scanners but visible to GHSA-backed scanners like OSV. @@ -84,4 +85,4 @@ upgrading. These changes do not affect metadata on All-Purpose Clusters. --- *Note: When making changes, please add your change under the appropriate section -with a brief description.* +with a brief description.* \ No newline at end of file diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksConnection.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksConnection.java index 44523b9e9..ca39430b9 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/DatabricksConnection.java +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksConnection.java @@ -489,8 +489,9 @@ public void setCatalog(String catalog) throws SQLException { return; } Statement statement = this.createStatement(); - statement.execute("SET CATALOG `" + catalog + "`"); - this.session.setCatalog(catalog); + String cleanCatalog = stripBackticks(catalog); + statement.execute("SET CATALOG `" + cleanCatalog + "`"); + this.session.setCatalog(cleanCatalog); } @Override @@ -865,8 +866,9 @@ public Struct createStruct(String typeName, Object[] attributes) throws SQLExcep @Override public void setSchema(String schema) throws SQLException { Statement statement = this.createStatement(); - statement.execute("USE SCHEMA `" + schema + "`"); - session.setSchema(schema); + String cleanSchema = stripBackticks(schema); + statement.execute("USE SCHEMA `" + cleanSchema + "`"); + session.setSchema(cleanSchema); } @Override @@ -1090,4 +1092,14 @@ private void closeStatementSafely(Statement statement) { } } } + + private static String stripBackticks(String identifier) { + if (identifier != null + && identifier.startsWith("`") + && identifier.endsWith("`") + && identifier.length() >= 2) { + return identifier.substring(1, identifier.length() - 1); + } + return identifier; + } } diff --git a/src/test/java/com/databricks/jdbc/api/impl/DatabricksConnectionTest.java b/src/test/java/com/databricks/jdbc/api/impl/DatabricksConnectionTest.java index f57a769fa..4828de2d0 100644 --- a/src/test/java/com/databricks/jdbc/api/impl/DatabricksConnectionTest.java +++ b/src/test/java/com/databricks/jdbc/api/impl/DatabricksConnectionTest.java @@ -134,6 +134,60 @@ public void testGetAndSetSchemaAndCatalog() throws SQLException { assertEquals(connection.getSchema(), DEFAULT_SCHEMA); } + @Test + public void testSetCatalogAndSchemaWithPreWrappedBackticks() throws SQLException { + when(databricksClient.createSession( + new Warehouse(WAREHOUSE_ID), CATALOG, SCHEMA, new HashMap<>())) + .thenReturn(IMMUTABLE_SESSION_INFO); + connection = new DatabricksConnection(connectionContext, databricksClient); + connection.open(); + + String backtickWrappedCatalog = "`catalog-with-hyphen`"; + when(databricksClient.executeStatement( + eq("SET CATALOG `catalog-with-hyphen`"), + eq(new Warehouse(WAREHOUSE_ID)), + eq(new HashMap<>()), + eq(StatementType.SQL), + any(), + any(), + any())) + .thenReturn(resultSet); + connection.setCatalog(backtickWrappedCatalog); + assertEquals(connection.getCatalog(), "catalog-with-hyphen"); + + String backtickWrappedSchema = "`schema-with-hyphen`"; + when(databricksClient.executeStatement( + eq("USE SCHEMA `schema-with-hyphen`"), + eq(new Warehouse(WAREHOUSE_ID)), + eq(new HashMap<>()), + eq(StatementType.SQL), + any(), + any(), + any())) + .thenReturn(resultSet); + connection.setSchema(backtickWrappedSchema); + assertEquals(connection.getSchema(), "schema-with-hyphen"); + + verify(databricksClient) + .executeStatement( + eq("SET CATALOG `catalog-with-hyphen`"), + eq(new Warehouse(WAREHOUSE_ID)), + eq(new HashMap<>()), + eq(StatementType.SQL), + any(), + any(), + any()); + verify(databricksClient) + .executeStatement( + eq("USE SCHEMA `schema-with-hyphen`"), + eq(new Warehouse(WAREHOUSE_ID)), + eq(new HashMap<>()), + eq(StatementType.SQL), + any(), + any(), + any()); + } + @Test public void testSetCatalogAndSchemaWithHyphenatedIdentifiers() throws SQLException { when(databricksClient.createSession(