Skip to content

Commit d8d6ac1

Browse files
committed
fix co-pilot comments
1 parent ce90696 commit d8d6ac1

2 files changed

Lines changed: 59 additions & 14 deletions

File tree

libs/foundry-dev-tools/src/foundry_dev_tools/clients/foundry_sql_server.py

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -412,10 +412,15 @@ def query_foundry_sql(
412412
Raises:
413413
FoundrySqlQueryFailedError: If the query fails
414414
FoundrySqlQueryClientTimedOutError: If the query times out
415+
TypeError: If an invalid sql_dialect or arrow_compression_codec is provided
416+
ValueError: If an unsupported return_type is provided
415417
416418
""" # noqa: E501
417419
if experimental_use_trino:
418-
query = query.replace("SELECT ", "SELECT /*+ backend(trino) */ ", 1)
420+
# Case-insensitive replacement of first SELECT keyword
421+
import re
422+
423+
query = re.sub(r"\bSELECT\b", "SELECT /*+ backend(trino) */", query, count=1, flags=re.IGNORECASE)
419424

420425
response_json = self.api_query(
421426
query=query,
@@ -428,7 +433,6 @@ def query_foundry_sql(
428433
query_handle = self._extract_query_handle(response_json)
429434
start_time = time.time()
430435

431-
# Poll for completion
432436
while response_json.get("status", {}).get("type") != "ready":
433437
time.sleep(0.2)
434438
response = self.api_status(query_handle)
@@ -439,20 +443,14 @@ def query_foundry_sql(
439443
if time.time() > start_time + timeout:
440444
raise FoundrySqlQueryClientTimedOutError(response, timeout=timeout)
441445

442-
# Extract tickets from successful response
443446
ticket = self._extract_ticket(response_json)
444447

445-
# Fetch Arrow data using tickets
446448
arrow_stream_reader = self.read_stream_results_arrow(ticket)
447449

448450
if return_type == "pandas":
449451
return arrow_stream_reader.read_pandas()
450452

451453
if return_type == "polars":
452-
# The FakeModule implementation used in the _optional packages
453-
# throws an ImportError when trying to access attributes of the module.
454-
# This ImportError is caught below to fall back to query_foundry_sql_legacy
455-
# which will again raise an ImportError when polars is not installed.
456454
from foundry_dev_tools._optional.polars import pl
457455

458456
arrow_table = arrow_stream_reader.read_all()
@@ -484,8 +482,26 @@ def _extract_query_handle(self, response_json: dict[str, Any]) -> dict[str, Any]
484482
Returns:
485483
Query handle dict
486484
485+
Raises:
486+
KeyError: If the response JSON doesn't contain the expected structure
487+
487488
"""
488-
return response_json[response_json["type"]]["queryHandle"]
489+
response_type = response_json.get("type")
490+
if not response_type:
491+
msg = f"Response JSON missing 'type' field. Response: {response_json}"
492+
raise KeyError(msg)
493+
494+
type_data = response_json.get(response_type)
495+
if not type_data:
496+
msg = f"Response JSON missing '{response_type}' field. Response: {response_json}"
497+
raise KeyError(msg)
498+
499+
query_handle = type_data.get("queryHandle")
500+
if not query_handle:
501+
msg = f"Response JSON missing 'queryHandle' in '{response_type}'. Response: {response_json}"
502+
raise KeyError(msg)
503+
504+
return query_handle
489505

490506
def _extract_ticket(self, response_json: dict[str, Any]) -> dict[str, Any]:
491507
"""Extract tickets from success response.
@@ -496,16 +512,26 @@ def _extract_ticket(self, response_json: dict[str, Any]) -> dict[str, Any]:
496512
Returns:
497513
List of tickets for fetching results
498514
515+
Raises:
516+
KeyError: If the response JSON doesn't contain the expected structure
517+
499518
"""
519+
try:
520+
status = response_json["status"]
521+
ready = status["ready"]
522+
ticket_groups = ready["tickets"]
523+
except KeyError as exc:
524+
msg = (
525+
f"Response JSON missing expected structure. "
526+
f"Expected path: status.ready.tickets. Response: {response_json}"
527+
)
528+
raise KeyError(msg) from exc
529+
500530
# we combine all tickets into one to get the full data
501531
# if performance is a concern this should be done in parallel
502532
return {
503533
"id": 0,
504-
"tickets": [
505-
ticket
506-
for ticket_group in response_json["status"]["ready"]["tickets"]
507-
for ticket in ticket_group["tickets"]
508-
],
534+
"tickets": [ticket for ticket_group in ticket_groups for ticket in ticket_group["tickets"]],
509535
"type": "furnace",
510536
}
511537

tests/integration/clients/test_foundry_sql_server.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,22 @@ def test_v2_arrow_compression_codecs():
265265

266266
pd.testing.assert_frame_equal(result_lz4, result_zstd)
267267
pd.testing.assert_frame_equal(result_lz4, result_none)
268+
269+
270+
def test_v2_trino_engine_in_response(mocker):
271+
"""Test that when experimental_use_trino=True, the API response indicates trino engine."""
272+
# Spy on the api_query method to capture the initial response
273+
api_query_spy = mocker.spy(TEST_SINGLETON.ctx.foundry_sql_server_v2, "api_query")
274+
275+
# Execute query with trino enabled using parquet dataset (trino works with parquet)
276+
result = TEST_SINGLETON.ctx.foundry_sql_server_v2.query_foundry_sql(
277+
query=f"SELECT sepal_length FROM `{TEST_SINGLETON.iris_parquet.rid}` LIMIT 1",
278+
experimental_use_trino=True,
279+
)
280+
281+
assert result.shape == (1, 1)
282+
283+
# Verify the API response indicates TRINO backend
284+
response_json = api_query_spy.spy_return.json()
285+
backend = response_json[response_json["type"]]["queryStructure"]["metadata"]["backend"]
286+
assert backend == "TRINO"

0 commit comments

Comments
 (0)