Skip to content

Commit cc9fd88

Browse files
chore: improve DML check (apache#30417)
1 parent 96b0bcf commit cc9fd88

File tree

6 files changed

+43
-12
lines changed

6 files changed

+43
-12
lines changed

Diff for: pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ dependencies = [
8989
"slack_sdk>=3.19.0, <4",
9090
"sqlalchemy>=1.4, <2",
9191
"sqlalchemy-utils>=0.38.3, <0.39",
92-
"sqlglot>=23.0.2,<24",
92+
"sqlglot>=25.24.0,<26",
9393
"sqlparse>=0.5.0",
9494
"tabulate>=0.8.9, <0.9",
9595
"typing-extensions>=4, <5",

Diff for: requirements/base.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ sqlalchemy-utils==0.38.3
350350
# via
351351
# apache-superset
352352
# flask-appbuilder
353-
sqlglot==23.6.3
353+
sqlglot==25.24.0
354354
# via apache-superset
355355
sqlparse==0.5.0
356356
# via apache-superset

Diff for: superset/sql/parse.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ def get_settings(self) -> dict[str, str | bool]:
362362
363363
"""
364364
return {
365-
eq.this.sql(): eq.expression.sql()
365+
eq.this.sql(comments=False): eq.expression.sql(comments=False)
366366
for set_item in self._parsed.find_all(exp.SetItem)
367367
for eq in set_item.find_all(exp.EQ)
368368
}

Diff for: superset/sql_lab.py

+21-8
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
OAuth2RedirectError,
4747
SupersetErrorException,
4848
SupersetErrorsException,
49+
SupersetParseError,
4950
)
5051
from superset.extensions import celery_app, event_logger
5152
from superset.models.core import Database
@@ -236,15 +237,27 @@ def execute_sql_statement( # pylint: disable=too-many-statements, too-many-loca
236237
# We are testing to see if more rows exist than the limit.
237238
increased_limit = None if query.limit is None else query.limit + 1
238239

239-
parsed_statement = SQLStatement(sql_statement, engine=db_engine_spec.engine)
240-
if parsed_statement.is_mutating() and not database.allow_dml:
241-
raise SupersetErrorException(
242-
SupersetError(
243-
message=__("Only SELECT statements are allowed against this database."),
244-
error_type=SupersetErrorType.DML_NOT_ALLOWED_ERROR,
245-
level=ErrorLevel.ERROR,
240+
if not database.allow_dml:
241+
try:
242+
parsed_statement = SQLStatement(sql_statement, engine=db_engine_spec.engine)
243+
disallowed = parsed_statement.is_mutating()
244+
except SupersetParseError:
245+
# if we fail to parse teh query, disallow by default
246+
disallowed = True
247+
248+
if disallowed:
249+
raise SupersetErrorException(
250+
SupersetError(
251+
message=__(
252+
"This database does not allow for DDL/DML, and the query "
253+
"could not be parsed to confirm it is a read-only query. Please "
254+
"contact your administrator for more assistance."
255+
),
256+
error_type=SupersetErrorType.DML_NOT_ALLOWED_ERROR,
257+
level=ErrorLevel.ERROR,
258+
)
246259
)
247-
)
260+
248261
if apply_ctas:
249262
if not query.tmp_table_name:
250263
start_dttm = datetime.fromtimestamp(query.start_time)

Diff for: tests/integration_tests/sqllab_tests.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,11 @@ def test_sql_json_dml_disallowed(self):
137137
assert data == {
138138
"errors": [
139139
{
140-
"message": "Only SELECT statements are allowed against this database.",
140+
"message": (
141+
"This database does not allow for DDL/DML, and the query "
142+
"could not be parsed to confirm it is a read-only query. Please "
143+
"contact your administrator for more assistance."
144+
),
141145
"error_type": SupersetErrorType.DML_NOT_ALLOWED_ERROR,
142146
"level": ErrorLevel.ERROR,
143147
"extra": {

Diff for: tests/unit_tests/sql/parse_tests.py

+14
Original file line numberDiff line numberDiff line change
@@ -918,3 +918,17 @@ def test_has_mutation(engine: str, sql: str, expected: bool) -> None:
918918
Test the `has_mutation` method.
919919
"""
920920
assert SQLScript(sql, engine).has_mutation() == expected
921+
922+
923+
def test_get_settings() -> None:
924+
"""
925+
Test `get_settings` in some edge cases.
926+
"""
927+
sql = """
928+
set
929+
-- this is a tricky comment
930+
search_path -- another one
931+
= bar;
932+
SELECT * FROM some_table;
933+
"""
934+
assert SQLScript(sql, "postgresql").get_settings() == {"search_path": "bar"}

0 commit comments

Comments
 (0)