|
25 | 25 | UPDATE_ENDPOINT_HEADER, |
26 | 26 | UPDATE_PARAMETERS_HEADER, |
27 | 27 | CursorState, |
28 | | - ParameterStyle, |
29 | 28 | ) |
30 | 29 | from firebolt.common.cursor.base_cursor import ( |
31 | 30 | BaseCursor, |
|
38 | 37 | check_not_closed, |
39 | 38 | check_query_executed, |
40 | 39 | ) |
| 40 | +from firebolt.common.cursor.statement_planners import ( |
| 41 | + ExecutionPlan, |
| 42 | + StatementPlannerFactory, |
| 43 | +) |
41 | 44 | from firebolt.common.row_set.asynchronous.base import BaseAsyncRowSet |
42 | 45 | from firebolt.common.row_set.asynchronous.in_memory import InMemoryAsyncRowSet |
43 | 46 | from firebolt.common.row_set.asynchronous.streaming import StreamingAsyncRowSet |
@@ -222,117 +225,84 @@ async def _do_execute( |
222 | 225 | from firebolt.async_db import paramstyle |
223 | 226 |
|
224 | 227 | try: |
225 | | - parameter_style = ParameterStyle(paramstyle) |
226 | | - except ValueError: |
227 | | - raise ProgrammingError(f"Unsupported paramstyle: {paramstyle}") |
228 | | - try: |
229 | | - if parameter_style == ParameterStyle.FB_NUMERIC: |
230 | | - await self._execute_fb_numeric( |
231 | | - raw_query, parameters, timeout, async_execution, streaming |
232 | | - ) |
233 | | - else: |
234 | | - queries: List[Union[SetParameter, str]] = ( |
235 | | - [raw_query] |
236 | | - if skip_parsing |
237 | | - else self._formatter.split_format_sql(raw_query, parameters) |
238 | | - ) |
239 | | - timeout_controller = TimeoutController(timeout) |
240 | | - if len(queries) > 1 and async_execution: |
241 | | - raise FireboltError( |
242 | | - "Server side async does not support multi-statement queries" |
243 | | - ) |
244 | | - for query in queries: |
245 | | - await self._execute_single_query( |
246 | | - query, timeout_controller, async_execution, streaming |
247 | | - ) |
| 228 | + statement_planner = StatementPlannerFactory.create_planner( |
| 229 | + paramstyle, self._formatter |
| 230 | + ) |
| 231 | + |
| 232 | + plan = statement_planner.create_execution_plan( |
| 233 | + raw_query, parameters, skip_parsing, async_execution, streaming |
| 234 | + ) |
| 235 | + await self._execute_plan(plan, timeout) |
248 | 236 | self._state = CursorState.DONE |
249 | 237 | except Exception: |
250 | 238 | self._state = CursorState.ERROR |
251 | 239 | raise |
252 | 240 |
|
253 | | - async def _execute_fb_numeric( |
| 241 | + async def _execute_plan( |
254 | 242 | self, |
255 | | - query: str, |
256 | | - parameters: Sequence[Sequence[ParameterType]], |
| 243 | + plan: ExecutionPlan, |
257 | 244 | timeout: Optional[float], |
258 | | - async_execution: bool, |
259 | | - streaming: bool, |
260 | 245 | ) -> None: |
261 | | - Cursor._log_query(query) |
| 246 | + """Execute an execution plan.""" |
262 | 247 | timeout_controller = TimeoutController(timeout) |
263 | | - timeout_controller.raise_if_timeout() |
264 | | - query_params = self._build_fb_numeric_query_params( |
265 | | - parameters, streaming, async_execution |
266 | | - ) |
267 | | - resp = await self._api_request( |
268 | | - query, |
269 | | - query_params, |
270 | | - timeout=timeout_controller.remaining(), |
271 | | - ) |
272 | | - await self._raise_if_error(resp) |
273 | | - if async_execution: |
274 | | - await resp.aread() |
275 | | - self._parse_async_response(resp) |
276 | | - else: |
277 | | - await self._parse_response_headers(resp.headers) |
278 | | - await self._append_row_set_from_response(resp) |
| 248 | + |
| 249 | + for query in plan.queries: |
| 250 | + if isinstance(query, SetParameter): |
| 251 | + if plan.async_execution: |
| 252 | + raise FireboltError( |
| 253 | + "Server side async does not support set statements, " |
| 254 | + "please use execute to set this parameter" |
| 255 | + ) |
| 256 | + await self._validate_set_parameter( |
| 257 | + query, timeout_controller.remaining() |
| 258 | + ) |
| 259 | + else: |
| 260 | + # Regular query execution |
| 261 | + await self._execute_single_query( |
| 262 | + query, |
| 263 | + plan.query_params, |
| 264 | + timeout_controller, |
| 265 | + plan.async_execution, |
| 266 | + plan.streaming, |
| 267 | + ) |
279 | 268 |
|
280 | 269 | async def _execute_single_query( |
281 | 270 | self, |
282 | | - query: Union[SetParameter, str], |
| 271 | + query: str, |
| 272 | + query_params: Optional[Dict[str, Any]], |
283 | 273 | timeout_controller: TimeoutController, |
284 | 274 | async_execution: bool, |
285 | 275 | streaming: bool, |
286 | 276 | ) -> None: |
| 277 | + """Execute a single query.""" |
287 | 278 | start_time = time.time() |
288 | 279 | Cursor._log_query(query) |
289 | 280 | timeout_controller.raise_if_timeout() |
290 | 281 |
|
291 | | - if isinstance(query, SetParameter): |
292 | | - if async_execution: |
293 | | - raise FireboltError( |
294 | | - "Server side async does not support set statements, " |
295 | | - "please use execute to set this parameter" |
296 | | - ) |
297 | | - await self._validate_set_parameter(query, timeout_controller.remaining()) |
298 | | - else: |
299 | | - await self._handle_query_execution( |
300 | | - query, timeout_controller, async_execution, streaming |
301 | | - ) |
302 | | - |
303 | | - if not async_execution: |
304 | | - logger.info( |
305 | | - f"Query fetched {self.rowcount} rows in" |
306 | | - f" {time.time() - start_time} seconds." |
307 | | - ) |
308 | | - else: |
309 | | - logger.info("Query submitted for async execution.") |
| 282 | + final_params = query_params or {} |
310 | 283 |
|
311 | | - async def _handle_query_execution( |
312 | | - self, |
313 | | - query: str, |
314 | | - timeout_controller: TimeoutController, |
315 | | - async_execution: bool, |
316 | | - streaming: bool, |
317 | | - ) -> None: |
318 | | - query_params: Dict[str, Any] = { |
319 | | - "output_format": self._get_output_format(streaming) |
320 | | - } |
321 | | - if async_execution: |
322 | | - query_params["async"] = True |
323 | 284 | resp = await self._api_request( |
324 | 285 | query, |
325 | | - query_params, |
| 286 | + final_params, |
326 | 287 | timeout=timeout_controller.remaining(), |
327 | 288 | ) |
328 | 289 | await self._raise_if_error(resp) |
| 290 | + |
329 | 291 | if async_execution: |
330 | 292 | await resp.aread() |
331 | 293 | self._parse_async_response(resp) |
332 | 294 | else: |
333 | 295 | await self._parse_response_headers(resp.headers) |
334 | 296 | await self._append_row_set_from_response(resp) |
335 | 297 |
|
| 298 | + if not async_execution: |
| 299 | + logger.info( |
| 300 | + f"Query fetched {self.rowcount} rows in" |
| 301 | + f" {time.time() - start_time} seconds." |
| 302 | + ) |
| 303 | + else: |
| 304 | + logger.info("Query submitted for async execution.") |
| 305 | + |
336 | 306 | async def use_database(self, database: str, cache: bool = True) -> None: |
337 | 307 | """Switch the current database context with caching.""" |
338 | 308 | if cache: |
|
0 commit comments