Skip to content

Commit de947bf

Browse files
fix: harden async result rowcount retrieval
Signed-off-by: Aakash Saravanan <aakash.saravanan@databricks.com>
1 parent 43a2c93 commit de947bf

3 files changed

Lines changed: 46 additions & 1 deletion

File tree

‎src/databricks/sql/backend/thrift_backend.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,11 @@ def _results_message_to_execute_response(
927927
def get_execution_result(
928928
self, command_id: CommandId, cursor: Cursor
929929
) -> "ResultSet":
930+
"""Fetch a completed async result and its latest operation metadata.
931+
932+
FetchResults does not carry ``numModifiedRows``, so Thrift refreshes
933+
operation status before constructing the result set.
934+
"""
930935
thrift_handle = command_id.to_thrift_handle()
931936
if not thrift_handle:
932937
raise ValueError("Not a valid Thrift command ID")

‎src/databricks/sql/client.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1701,7 +1701,9 @@ def get_async_execution_result(self):
17011701
self.active_command_id, self
17021702
)
17031703

1704-
num_modified_rows = self.active_result_set.num_modified_rows
1704+
num_modified_rows = getattr(
1705+
self.active_result_set, "num_modified_rows", None
1706+
)
17051707
self.rowcount = num_modified_rows if num_modified_rows is not None else -1
17061708

17071709
if self.active_result_set and self.active_result_set.is_staging_operation:

‎tests/unit/test_thrift_backend.py‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,44 @@ def test_async_result_reads_status_count_and_preserves_rows(count):
179179
assert result_set.call_args.kwargs["t_row_set"] is sentinel.counter_rows
180180

181181

182+
@pytest.mark.parametrize(
183+
"operation_state,exception_type",
184+
[
185+
(ttypes.TOperationState.ERROR_STATE, ServerOperationError),
186+
(ttypes.TOperationState.CLOSED_STATE, DatabaseError),
187+
],
188+
)
189+
def test_async_result_rejects_terminal_error_state_before_fetch(
190+
operation_state, exception_type
191+
):
192+
with patch(
193+
"databricks.sql.backend.thrift_backend.TCLIService.Client", autospec=True
194+
):
195+
backend = ThriftDatabricksClient(
196+
"foobar",
197+
443,
198+
"path",
199+
[],
200+
auth_provider=AuthProvider(),
201+
ssl_options=SSLOptions(),
202+
http_client=MagicMock(),
203+
)
204+
backend._poll_for_status = Mock(
205+
return_value=ttypes.TGetOperationStatusResp(
206+
status=ThriftBackendTestSuite.okay_status,
207+
operationState=operation_state,
208+
errorMessage="async operation failed",
209+
)
210+
)
211+
backend.make_request = Mock()
212+
cid = CommandId.from_thrift_handle(ThriftBackendTestSuite.operation_handle)
213+
214+
with pytest.raises(exception_type):
215+
backend.get_execution_result(cid, Mock())
216+
217+
backend.make_request.assert_not_called()
218+
219+
182220
@pytest.mark.skipif(pyarrow is None, reason="PyArrow is not installed")
183221
class ThriftBackendTestSuite(unittest.TestCase):
184222
okay_status = ttypes.TStatus(statusCode=ttypes.TStatusCode.SUCCESS_STATUS)

0 commit comments

Comments
 (0)