Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.0 #2

Merged
merged 48 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
807a027
fix: is_select with UNION (#25290)
betodealmeida Sep 14, 2023
2d1f1e3
fix: Add explicit ON DELETE CASCADE for dashboard_roles (#25320)
john-bodley Sep 18, 2023
8ca49d4
fix(chart): Supporting custom SQL as temporal x-axis column with filt…
Sep 18, 2023
61dcc70
fix: Use RLS clause instead of ID for cache key (#25229)
jfrag1 Sep 18, 2023
0d53446
fix: Improve the reliability of alerts & reports (#25239)
jfrag1 Sep 19, 2023
5390e2d
fix: DashboardRoles cascade operation (#25349)
michael-s-molina Sep 20, 2023
88e6f22
fix: datetime with timezone excel export (#25318)
betodealmeida Sep 21, 2023
c508a33
fix: Workaround for Cypress ECONNRESET error (#25399)
michael-s-molina Sep 25, 2023
721db8a
fix(sqllab): invalid persisted tab state (#25308) (#25398)
justinpark Sep 26, 2023
eacdbdd
fix: Rename on_delete parameter to ondelete (#25424)
john-bodley Sep 26, 2023
4a65d41
fix: preventing save button from flickering in SQL Lab (#25106)
fisjac Sep 26, 2023
1757ce4
fix: chart import (#25425)
betodealmeida Sep 27, 2023
0c6db23
fix: swagger UI CSP error (#25368)
dpgaspar Sep 27, 2023
8dfe95f
fix: smarter date formatter (#25404)
betodealmeida Sep 27, 2023
b83bd5d
fix(sqllab): invalid start date (#25437)
justinpark Sep 27, 2023
7b2b696
fix(nativeFilters): Speed up native filters by removing unnecessary r…
Always-prog Sep 28, 2023
731cd65
fix(SqlLab): make icon placement even (#25372)
CorbinBullard Sep 28, 2023
58778a7
fix: Duplicate items when pasting into Select (#25447)
michael-s-molina Sep 28, 2023
d8e87aa
fix: update the SQLAlchemy model definition at json column for Log ta…
cnabro Sep 29, 2023
f682dba
fix(helm chart): set chart appVersion to 3.0.0 (#25373)
celalettin1286 Sep 29, 2023
615d7f5
fix(mysql): handle string typed decimal results (#24241)
villebro Sep 29, 2023
0dd1a3b
fix: Styles not loading because of faulty CSP setting (#25468)
kgabryje Sep 29, 2023
455b3d8
fix(sqllab): error with lazy_gettext for tab titles (#25469)
nytai Sep 30, 2023
9d1ab46
fix: Address Mypy issue which is causing CI to fail (#25494)
john-bodley Oct 2, 2023
4ad2a05
chore: Adds 3.0.1 CHANGELOG
michael-s-molina Oct 3, 2023
ae700d1
fix: Unable to sync columns when database or dataset name contains `+…
mapledan Oct 3, 2023
1367d7b
fix(sqllab): Broken query containing 'children' (#25490)
justinpark Oct 4, 2023
220dc58
chore: Expand error detail on screencapture (#25519)
justinpark Oct 4, 2023
8b66603
fix: tags permissions error message (#25516)
Khrol Oct 4, 2023
dd769eb
fix: Apply normalization to all dttm columns (#25147)
kgabryje Oct 6, 2023
286c095
fix: REST API CSRF exempt list (#25590)
dpgaspar Oct 10, 2023
69c2378
fix(RLS): Fix Info Tooltip + Button Alignment on RLS Modal (#25400)
CorbinBullard Oct 10, 2023
53b84b9
fix: thubmnails loading - Talisman default config (#25486)
Khrol Oct 11, 2023
254cc36
fix(Presto): catch DatabaseError when testing Presto views (#25559)
zhaorui2022 Oct 11, 2023
732c5b1
fix(Charts): Set max row limit + removed the option to use an empty r…
CorbinBullard Oct 11, 2023
a0b2dc4
fix(window): unavailable localStorage and sessionStorage (#25599)
frassinier Oct 11, 2023
c44f1a3
fix: finestTemporalGrainFormatter (#25618)
betodealmeida Oct 11, 2023
cd1b7a4
fix: revert fix(sqllab): Force trino client async execution (#24859) …
villebro Oct 13, 2023
890bf59
chore: Updates 3.0.1 CHANGELOG
michael-s-molina Oct 13, 2023
d7cbdca
fix(sqllab): Mistitled for new tab after rename (#25523)
justinpark Oct 13, 2023
701ee30
fix(sqllab): template validation error within comments (#25626)
justinpark Oct 13, 2023
ec3bed7
fix: avoid 500 errors with SQLLAB_BACKEND_PERSISTENCE (#25553)
Khrol Oct 13, 2023
af1e713
fix(import): Make sure query context is overwritten for overwriting i…
jfrag1 Oct 16, 2023
236aef8
fix: permalink save/overwrites in explore (#25112)
hughhhh Oct 16, 2023
b95ff2d
fix(header navlinks): link navlinks to path prefix (#25495)
fisjac Oct 17, 2023
b0f229e
fix: improve upload ZIP file validation (#25658)
dpgaspar Oct 17, 2023
b380495
fix: warning of nth-child (#23638)
justinpark Oct 17, 2023
293568a
fix(dremio): Fixes issue with Dremio SQL generation for Charts with S…
OskarNS Oct 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: revert fix(sqllab): Force trino client async execution (apache#2…
…4859) (apache#25541)

(cherry picked from commit e56e0de)
  • Loading branch information
villebro authored and michael-s-molina committed Oct 13, 2023
commit cd1b7a4c06462fb284b0f2e696b0a108ce1834a8
18 changes: 0 additions & 18 deletions superset/db_engine_specs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1053,24 +1053,6 @@ def handle_cursor(cls, cursor: Any, query: Query, session: Session) -> None:
query object"""
# TODO: Fix circular import error caused by importing sql_lab.Query

@classmethod
def execute_with_cursor(
cls, cursor: Any, sql: str, query: Query, session: Session
) -> None:
"""
Trigger execution of a query and handle the resulting cursor.

For most implementations this just makes calls to `execute` and
`handle_cursor` consecutively, but in some engines (e.g. Trino) we may
need to handle client limitations such as lack of async support and
perform a more complicated operation to get information from the cursor
in a timely manner and facilitate operations such as query stop
"""
logger.debug("Query %d: Running query: %s", query.id, sql)
cls.execute(cursor, sql, async_=True)
logger.debug("Query %d: Handling cursor", query.id)
cls.handle_cursor(cursor, query, session)

@classmethod
def extract_error_message(cls, ex: Exception) -> str:
return f"{cls.engine} error: {cls._extract_error_message(ex)}"
Expand Down
66 changes: 6 additions & 60 deletions superset/db_engine_specs/trino.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
from __future__ import annotations

import logging
import threading
import time
from typing import Any, TYPE_CHECKING

import simplejson as json
Expand Down Expand Up @@ -156,22 +154,15 @@ def get_tracking_url(cls, cursor: Cursor) -> str | None:

@classmethod
def handle_cursor(cls, cursor: Cursor, query: Query, session: Session) -> None:
"""
Handle a trino client cursor.

WARNING: if you execute a query, it will block until complete and you
will not be able to handle the cursor until complete. Use
`execute_with_cursor` instead, to handle this asynchronously.
"""

# Adds the executed query id to the extra payload so the query can be cancelled
cancel_query_id = cursor.query_id
logger.debug("Query %d: queryId %s found in cursor", query.id, cancel_query_id)
query.set_extra_json_key(key=QUERY_CANCEL_KEY, value=cancel_query_id)

if tracking_url := cls.get_tracking_url(cursor):
query.tracking_url = tracking_url

# Adds the executed query id to the extra payload so the query can be cancelled
query.set_extra_json_key(
key=QUERY_CANCEL_KEY,
value=(cancel_query_id := cursor.stats["queryId"]),
)

session.commit()

# if query cancelation was requested prior to the handle_cursor call, but
Expand All @@ -185,51 +176,6 @@ def handle_cursor(cls, cursor: Cursor, query: Query, session: Session) -> None:

super().handle_cursor(cursor=cursor, query=query, session=session)

@classmethod
def execute_with_cursor(
cls, cursor: Any, sql: str, query: Query, session: Session
) -> None:
"""
Trigger execution of a query and handle the resulting cursor.

Trino's client blocks until the query is complete, so we need to run it
in another thread and invoke `handle_cursor` to poll for the query ID
to appear on the cursor in parallel.
"""
execute_result: dict[str, Any] = {}

def _execute(results: dict[str, Any]) -> None:
logger.debug("Query %d: Running query: %s", query.id, sql)

# Pass result / exception information back to the parent thread
try:
cls.execute(cursor, sql)
results["complete"] = True
except Exception as ex: # pylint: disable=broad-except
results["complete"] = True
results["error"] = ex

execute_thread = threading.Thread(target=_execute, args=(execute_result,))
execute_thread.start()

# Wait for a query ID to be available before handling the cursor, as
# it's required by that method; it may never become available on error.
while not cursor.query_id and not execute_result.get("complete"):
time.sleep(0.1)

logger.debug("Query %d: Handling cursor", query.id)
cls.handle_cursor(cursor, query, session)

# Block until the query completes; same behaviour as the client itself
logger.debug("Query %d: Waiting for query to complete", query.id)
while not execute_result.get("complete"):
time.sleep(0.5)

# Unfortunately we'll mangle the stack trace due to the thread, but
# throwing the original exception allows mapping database errors as normal
if err := execute_result.get("error"):
raise err

@classmethod
def prepare_cancel_query(cls, query: Query, session: Session) -> None:
if QUERY_CANCEL_KEY not in query.extra:
Expand Down
7 changes: 5 additions & 2 deletions superset/sql_lab.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def get_sql_results( # pylint: disable=too-many-arguments
return handle_query_error(ex, query, session)


def execute_sql_statement( # pylint: disable=too-many-arguments
def execute_sql_statement( # pylint: disable=too-many-arguments,too-many-statements
sql_statement: str,
query: Query,
session: Session,
Expand Down Expand Up @@ -271,7 +271,10 @@ def execute_sql_statement( # pylint: disable=too-many-arguments
)
session.commit()
with stats_timing("sqllab.query.time_executing_query", stats_logger):
db_engine_spec.execute_with_cursor(cursor, sql, query, session)
logger.debug("Query %d: Running query: %s", query.id, sql)
db_engine_spec.execute(cursor, sql, async_=True)
logger.debug("Query %d: Handling cursor", query.id)
db_engine_spec.handle_cursor(cursor, query, session)

with stats_timing("sqllab.query.time_fetching_results", stats_logger):
logger.debug(
Expand Down
31 changes: 1 addition & 30 deletions tests/unit_tests/db_engine_specs/test_trino.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ def test_handle_cursor_early_cancel(
query_id = "myQueryId"

cursor_mock = engine_mock.return_value.__enter__.return_value
cursor_mock.query_id = query_id
cursor_mock.stats = {"queryId": query_id}
session_mock = mocker.MagicMock()

query = Query()
Expand All @@ -366,32 +366,3 @@ def test_handle_cursor_early_cancel(
assert cancel_query_mock.call_args[1]["cancel_query_id"] == query_id
else:
assert cancel_query_mock.call_args is None


def test_execute_with_cursor_in_parallel(mocker: MockerFixture):
"""Test that `execute_with_cursor` fetches query ID from the cursor"""
from superset.db_engine_specs.trino import TrinoEngineSpec

query_id = "myQueryId"

mock_cursor = mocker.MagicMock()
mock_cursor.query_id = None

mock_query = mocker.MagicMock()
mock_session = mocker.MagicMock()

def _mock_execute(*args, **kwargs):
mock_cursor.query_id = query_id

mock_cursor.execute.side_effect = _mock_execute

TrinoEngineSpec.execute_with_cursor(
cursor=mock_cursor,
sql="SELECT 1 FROM foo",
query=mock_query,
session=mock_session,
)

mock_query.set_extra_json_key.assert_called_once_with(
key=QUERY_CANCEL_KEY, value=query_id
)
10 changes: 6 additions & 4 deletions tests/unit_tests/sql_lab_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ def test_execute_sql_statement(mocker: MockerFixture, app: None) -> None:
)

database.apply_limit_to_sql.assert_called_with("SELECT 42 AS answer", 2, force=True)
db_engine_spec.execute_with_cursor.assert_called_with(
cursor, "SELECT 42 AS answer LIMIT 2", query, session
db_engine_spec.execute.assert_called_with(
cursor, "SELECT 42 AS answer LIMIT 2", async_=True
)
SupersetResultSet.assert_called_with([(42,)], cursor.description, db_engine_spec)

Expand Down Expand Up @@ -106,8 +106,10 @@ def test_execute_sql_statement_with_rls(
101,
force=True,
)
db_engine_spec.execute_with_cursor.assert_called_with(
cursor, "SELECT * FROM sales WHERE organization_id=42 LIMIT 101", query, session
db_engine_spec.execute.assert_called_with(
cursor,
"SELECT * FROM sales WHERE organization_id=42 LIMIT 101",
async_=True,
)
SupersetResultSet.assert_called_with([(42,)], cursor.description, db_engine_spec)

Expand Down