Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
- `DatabaseMetaData.getColumns(...)` with a `null` catalog now issues a single `SHOW COLUMNS IN ALL CATALOGS` statement (consistent with `getSchemas`/`getTables`) instead of enumerating every catalog and issuing a per-catalog `SHOW COLUMNS`. Older DBR versions that do not support the syntax transparently fall back to the previous enumerate-and-fan-out behavior.

### Fixed
- Invalid or incomplete Databricks JDBC URLs now fail with a descriptive `DatabricksSQLException`
instead of leaking a `NullPointerException` when required connection parameters are missing.

- Fixed `IdleConnectionEvictor` thread leak in long-running applications. Driver-side resources (HTTP client, background threads) are now always released when `Connection.close()` is called, even if statement cleanup or server-side session termination fails.

- Throw `DatabricksSQLException` instead of an unchecked `ClassCastException` when a complex-type getter (`getArray`, `getStruct`, `getMap`) is called on a column of a different complex type.
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/com/databricks/jdbc/common/util/ValidationUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ public static void checkHTTPError(HttpResponse response)
* @return true if the URL is valid, false otherwise
*/
public static boolean isValidJdbcUrl(String url) {
if (url == null) {
return false;
}

final List<Pattern> PATH_PATTERNS =
List.of(
HTTP_CLUSTER_PATH_PATTERN,
Expand Down Expand Up @@ -176,11 +180,30 @@ public static boolean isValidJdbcUrl(String url) {
*/
public static void validateInputProperties(Map<String, String> parameters)
throws DatabricksValidationException {
validateRequiredConnectionParameters(parameters);
// Fail fast on an unsupported AuthMech before the client-configurator machinery runs.
validateAuthMech(parameters);
validateUidParameter(parameters);
}

/**
* Validates parameters that must be present in every connection configuration. URL parameters and
* {@link java.util.Properties} are merged before this method is called, so required values may be
* supplied through either mechanism.
*
* @param parameters merged JDBC connection parameters
* @throws DatabricksValidationException if any required parameter is missing or blank
*/
private static void validateRequiredConnectionParameters(Map<String, String> parameters)
throws DatabricksValidationException {
String parameterName = DatabricksJdbcUrlParams.HTTP_PATH.getParamName().toLowerCase();
String httpPath = parameters.get(parameterName);
if (httpPath == null || httpPath.isBlank()) {
throw new DatabricksValidationException(
"Missing required connection parameter: " + parameterName);
}
}

/**
* Validates the AuthMech parameter. Reuses {@link AuthMech#fromValue} as the single source of
* truth for supported values, so adding a new AuthMech only requires updating {@code AuthMech}.
Expand Down
24 changes: 24 additions & 0 deletions src/test/java/com/databricks/client/jdbc/DriverTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.databricks.client.jdbc;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.databricks.jdbc.exception.DatabricksSQLException;
import java.util.Properties;
import org.junit.jupiter.api.Test;

class DriverTest {

@Test
void connectRejectsMissingRequiredConnectionParameters() {
DatabricksSQLException exception =
assertThrows(
DatabricksSQLException.class,
() ->
Driver.getInstance().connect("jdbc:databricks://localhost:8080", new Properties()));

assertEquals("INPUT_VALIDATION_ERROR", exception.getSQLState());
assertTrue(exception.getMessage().contains("httppath"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

class DatabricksConnectionContextTest {

Expand Down Expand Up @@ -85,6 +86,26 @@ public void testParseInvalid() {
assertThrows(
DatabricksParsingException.class,
() -> DatabricksConnectionContext.parse(TestConstants.INVALID_URL_2, properties));
assertThrows(
DatabricksParsingException.class,
() -> DatabricksConnectionContext.parse(null, properties));
}

@ParameterizedTest
@ValueSource(
strings = {
"jdbc:databricks://localhost:8080",
"jdbc:databricks://localhost:8080;httpPath=",
"jdbc:databricks://localhost:8080;httpPath= "
})
public void testParseRejectsMissingRequiredConnectionParameters(String url) {
DatabricksSQLException exception =
assertThrows(
DatabricksSQLException.class,
() -> DatabricksConnectionContext.parse(url, new Properties()));

assertEquals("INPUT_VALIDATION_ERROR", exception.getSQLState());
assertTrue(exception.getMessage().contains("httppath"));
}

@Test
Expand Down
Loading