From aa376f1dae9ba7a2dcc2755d4f1ac87109286b8c Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Mon, 1 Jun 2026 13:59:27 +0000 Subject: [PATCH 1/2] Use async wait timeout for SEA when direct results disabled When direct results are disabled, the SEA execute request used waitTimeout=10s (the old hybrid path) with onWaitTimeout=CONTINUE. For results that span multiple chunks but compress small (highly-compressible payloads), the server returns only the first chunk inline with no external links, and the driver returns just that chunk -- silently truncating the result. Switch to waitTimeout=0s (async) when direct results are disabled so the server delivers results via external links, which the driver fetches in full. This matches the intended contract: DirectResults=0 -> WaitTimeout=0 (async); DirectResults=1 -> WaitTimeout unset. Thrift is unaffected (no waitTimeout; it paginates). Related: ES-1714092 Co-authored-by: Isaac Signed-off-by: Sreekanth Vadigi --- NEXT_CHANGELOG.md | 1 + .../jdbc/dbclient/impl/sqlexec/DatabricksSdkClient.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 49ecff151..a972de65e 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixed - 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 metadata SQL generation for catalog, schema, and table identifiers containing backticks. +- Fixed SEA result truncation when direct results are disabled. Large, highly-compressible results that span multiple chunks were delivered inline via the old hybrid path and truncated to the first chunk. The SQL Execution path now uses an async (`0s`) wait timeout when direct results are disabled, so results are returned via external links and fetched in full. --- *Note: When making changes, please add your change under the appropriate section diff --git a/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksSdkClient.java b/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksSdkClient.java index 4b7398244..6be922cde 100644 --- a/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksSdkClient.java +++ b/src/main/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksSdkClient.java @@ -740,9 +740,9 @@ private ExecuteStatementRequest getRequest( if (executeAsync) { request.setWaitTimeout(ASYNC_TIMEOUT_VALUE); } else { - // Only set timeout if direct results mode is not enabled + // DirectResults off -> async (0s); avoids truncation (ES-1714092) if (!connectionContext.getDirectResultMode()) { - request.setWaitTimeout(SYNC_TIMEOUT_VALUE); + request.setWaitTimeout(ASYNC_TIMEOUT_VALUE); } request.setOnWaitTimeout(ExecuteStatementRequestOnWaitTimeout.CONTINUE); } From 3be4ea47b44cfa854ecf9cbcca4d7f83cc41834e Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Wed, 3 Jun 2026 07:38:50 +0000 Subject: [PATCH 2/2] Add unit tests for SEA wait-timeout on direct-results toggle Asserts getRequest() sets waitTimeout=0s when direct results are disabled and leaves it unset when enabled. Addresses review feedback on #1476. Co-authored-by: Isaac Signed-off-by: Sreekanth Vadigi --- .../impl/sqlexec/DatabricksSdkClientTest.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksSdkClientTest.java b/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksSdkClientTest.java index 71457c35e..5e88e6166 100644 --- a/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksSdkClientTest.java +++ b/src/test/java/com/databricks/jdbc/dbclient/impl/sqlexec/DatabricksSdkClientTest.java @@ -1402,4 +1402,62 @@ public void testCheckStatementAlive_exceptionWrapped() throws Exception { () -> databricksSdkClient.checkStatementAlive(STATEMENT_ID)); assertTrue(exception.getMessage().contains("Heartbeat status check failed")); } + + @Test + public void testWaitTimeout_directResultsDisabled_usesAsyncZero() throws Exception { + setupClientMocks(true, false); + // EnableDirectResults=0 -> getDirectResultMode() is false + IDatabricksConnectionContext connectionContext = + DatabricksConnectionContext.parse(JDBC_URL + "EnableDirectResults=0", new Properties()); + DatabricksSdkClient databricksSdkClient = + new DatabricksSdkClient(connectionContext, statementExecutionService, apiClient); + DatabricksConnection connection = + new DatabricksConnection(connectionContext, databricksSdkClient); + connection.open(); + DatabricksStatement statement = new DatabricksStatement(connection); + + databricksSdkClient.executeStatement( + STATEMENT, + warehouse, + sqlParams, + StatementType.QUERY, + connection.getSession(), + statement, + null); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(ExecuteStatementRequest.class); + verify(apiClient, atLeastOnce()).serialize(captor.capture()); + // Direct results disabled -> async (0s), not the hybrid 10s path that truncates (ES-1714092). + assertEquals("0s", captor.getValue().getWaitTimeout()); + } + + @Test + public void testWaitTimeout_directResultsEnabled_leftUnset() throws Exception { + setupClientMocks(true, false); + // Default JDBC_URL has direct results enabled -> getDirectResultMode() is true + IDatabricksConnectionContext connectionContext = + DatabricksConnectionContext.parse(JDBC_URL, new Properties()); + DatabricksSdkClient databricksSdkClient = + new DatabricksSdkClient(connectionContext, statementExecutionService, apiClient); + DatabricksConnection connection = + new DatabricksConnection(connectionContext, databricksSdkClient); + connection.open(); + DatabricksStatement statement = new DatabricksStatement(connection); + + databricksSdkClient.executeStatement( + STATEMENT, + warehouse, + sqlParams, + StatementType.QUERY, + connection.getSession(), + statement, + null); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(ExecuteStatementRequest.class); + verify(apiClient, atLeastOnce()).serialize(captor.capture()); + // Direct results enabled -> WaitTimeout left unset (true SEA direct results). + assertNull(captor.getValue().getWaitTimeout()); + } }