2
2
from typing import Optional , Union , TYPE_CHECKING
3
3
from databricks .sql .results import ResultSet
4
4
5
+ from dataclasses import dataclass
5
6
6
7
if TYPE_CHECKING :
7
8
from databricks .sql .thrift_backend import ThriftBackend
14
15
from databricks .sql .thrift_api .TCLIService import ttypes
15
16
16
17
18
+ @dataclass
19
+ class FakeCursor :
20
+ active_op_handle : Optional [ttypes .TOperationHandle ]
21
+
22
+
17
23
class AsyncExecutionStatus (Enum ):
18
24
"""An enum that represents the status of an async execution"""
19
25
@@ -22,6 +28,8 @@ class AsyncExecutionStatus(Enum):
22
28
FINISHED = 2
23
29
CANCELED = 3
24
30
FETCHED = 4
31
+
32
+ # todo: when is this ever evaluated?
25
33
ABORTED = 5
26
34
27
35
@@ -44,21 +52,29 @@ class AsyncExecution:
44
52
"""
45
53
A class that represents an async execution of a query. Exposes just two methods:
46
54
get_result_or_status and cancel
55
+
56
+ AsyncExecutions are effectively connectionless. But because thrift_backend is entangled
57
+ with client.py, the AsyncExecution needs access to both a Connection and a ThriftBackend
58
+
59
+ This will need to be refactored for cleanliness in the future.
47
60
"""
48
61
49
62
_connection : "Connection"
63
+ _thrift_backend : "ThriftBackend"
50
64
_result_set : Optional ["ResultSet" ]
51
65
_execute_statement_response : Optional [ttypes .TExecuteStatementResp ]
52
66
53
67
def __init__ (
54
68
self ,
69
+ thrift_backend : "ThriftBackend" ,
55
70
connection : "Connection" ,
56
71
query_id : UUID ,
57
72
query_secret : UUID ,
58
73
status : AsyncExecutionStatus ,
59
74
execute_statement_response : Optional [ttypes .TExecuteStatementResp ] = None ,
60
75
):
61
76
self ._connection = connection
77
+ self ._thrift_backend = thrift_backend
62
78
self ._execute_statement_response = execute_statement_response
63
79
self .query_id = query_id
64
80
self .query_secret = query_secret
@@ -87,13 +103,13 @@ def cancel(self) -> None:
87
103
def _thrift_cancel_operation (self ) -> None :
88
104
"""Execute TCancelOperation"""
89
105
90
- _output = self ._connection . thrift_backend .async_cancel_command (self .t_operation_handle )
106
+ _output = self ._thrift_backend .async_cancel_command (self .t_operation_handle )
91
107
self .status = AsyncExecutionStatus .CANCELED
92
108
93
109
def _thrift_get_operation_status (self ) -> None :
94
110
"""Execute GetOperationStatusReq and map thrift execution status to DbsqlAsyncExecutionStatus"""
95
111
96
- _output = self ._connection . thrift_backend ._poll_for_status (self .t_operation_handle )
112
+ _output = self ._thrift_backend ._poll_for_status (self .t_operation_handle )
97
113
self .status = _toperationstate_to_ae_status (_output )
98
114
99
115
def _thrift_fetch_result (self ) -> None :
@@ -104,10 +120,10 @@ def _thrift_fetch_result(self) -> None:
104
120
# support JSON and Thrift binary result formats in addition to arrow.
105
121
106
122
# in the case of direct results this creates a second cursor...how can I avoid that?
107
- with self . _connection . cursor () as cursor :
108
- er = self ._connection . thrift_backend ._handle_execute_response (
109
- self ._execute_statement_response , cursor
110
- )
123
+
124
+ er = self ._thrift_backend ._handle_execute_response (
125
+ self ._execute_statement_response , FakeCursor ( None )
126
+ )
111
127
112
128
self ._result_set = ResultSet (
113
129
connection = self ._connection ,
@@ -123,18 +139,37 @@ def is_running(self) -> bool:
123
139
AsyncExecutionStatus .RUNNING ,
124
140
AsyncExecutionStatus .PENDING ,
125
141
]
126
-
142
+
127
143
@property
128
144
def t_operation_handle (self ) -> ttypes .TOperationHandle :
129
- """Return the current AsyncExecution as a Thrift TOperationHandle
130
- """
145
+ """Return the current AsyncExecution as a Thrift TOperationHandle"""
131
146
132
147
handle = ttypes .TOperationHandle (
133
148
operationId = ttypes .THandleIdentifier (
134
149
guid = self .query_id .bytes , secret = self .query_secret .bytes
135
150
),
136
151
operationType = ttypes .TOperationType .EXECUTE_STATEMENT ,
137
- hasResultSet = True
152
+ hasResultSet = True ,
138
153
)
139
154
140
155
return handle
156
+
157
+ @classmethod
158
+ def from_thrift_response (
159
+ cls ,
160
+ connection : "Connection" ,
161
+ thrift_backend : "ThriftBackend" ,
162
+ resp : ttypes .TExecuteStatementResp ,
163
+ ) -> "AsyncExecution" :
164
+ """This method is meant to be consumed by `client.py`"""
165
+
166
+ return cls (
167
+ connection = connection ,
168
+ thrift_backend = thrift_backend ,
169
+ query_id = UUID (bytes = resp .operationHandle .operationId .guid ),
170
+ query_secret = UUID (bytes = resp .operationHandle .operationId .secret ),
171
+ status = _toperationstate_to_ae_status (
172
+ resp .directResults .operationStatus .operationState
173
+ ),
174
+ execute_statement_response = resp ,
175
+ )
0 commit comments