You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by pm...@apache.org on 2026/09/21 13:48:19 UTC
(superset) branch master updated: fix(sql): classify ATTACH/DETACH and MySQL REPLACE/RENAME/SET PASSWORD as mutating (#44072)
This is an automated email from the ASF dual-hosted git repository.
sha174n pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new c11dc027969 fix(sql): classify ATTACH/DETACH and MySQL REPLACE/RENAME/SET PASSWORD as mutating (#44072)
c11dc027969 is described below
commit c11dc027969451567d7fe11a549e5baf241e9394
Author: Shaitan <10...@users.noreply.github.com>
AuthorDate: Mon Sep 21 14:48:12 2026 +0100
fix(sql): classify ATTACH/DETACH and MySQL REPLACE/RENAME/SET PASSWORD as mutating (#44072)
Co-authored-by: Claude Opus 4.8 <no...@anthropic.com>
---
superset/sql/parse.py | 14 ++++++++++++++
tests/unit_tests/sql/parse_tests.py | 17 +++++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/superset/sql/parse.py b/superset/sql/parse.py
index c7951e85c9a..a3835f780a4 100644
--- a/superset/sql/parse.py
+++ b/superset/sql/parse.py
@@ -963,6 +963,11 @@ class SQLStatement(BaseSQLStatement[exp.Expression]):
# MySQL LOAD DATA INFILE ingests server files into a table;
# PostgreSQL LOAD '/path/lib.so' dlopens a shared library.
"LOAD",
+ # MySQL REPLACE INTO is destructive DML and RENAME TABLE is DDL;
+ # both fall back to an opaque exp.Command with these heads (no
+ # structured node), and neither has a read-only form.
+ "REPLACE",
+ "RENAME",
# NOTE: `SHOW` is intentionally NOT included. It is a read (mutates
# nothing), so classifying it as mutating would be wrong for every
# is_mutating()/has_mutation() consumer (the commit decision, the
@@ -993,6 +998,10 @@ class SQLStatement(BaseSQLStatement[exp.Expression]):
{
Dialects.POSTGRES,
Dialects.STARROCKS,
+ # MySQL shares StarRocks' parser: ordinary `SET var = value` parses
+ # as exp.Set, so the opaque-Command fallback is reached only by the
+ # dangerous forms (SET PASSWORD FOR .../SET ROLE).
+ Dialects.MYSQL,
}
)
@@ -1278,6 +1287,11 @@ class SQLStatement(BaseSQLStatement[exp.Expression]):
# fires for dialects where this instead falls back to
# exp.Command.
exp.Refresh,
+ # ATTACH/DETACH (SQLite) connect or disconnect a database file;
+ # ATTACH can bring a writable database into scope. Structured
+ # nodes on SQLite, so the exp.Command fallback never sees them.
+ exp.Attach,
+ exp.Detach,
)
if self._parsed.find(*mutating_nodes):
diff --git a/tests/unit_tests/sql/parse_tests.py b/tests/unit_tests/sql/parse_tests.py
index fdf2ba59ede..edf19bb8db3 100644
--- a/tests/unit_tests/sql/parse_tests.py
+++ b/tests/unit_tests/sql/parse_tests.py
@@ -2031,6 +2031,14 @@ def test_is_mutating(sql: str, engine: str, expected: bool) -> None:
("EXPLAIN ANALYZE )))", "postgresql"),
# The flag need not be followed by whitespace.
("EXPLAIN ANALYZE(DELETE FROM t)", "postgresql"),
+ # SQLite ATTACH/DETACH and MySQL REPLACE INTO / RENAME TABLE /
+ # SET PASSWORD FOR fall past node-type matching (opaque command or
+ # dialect-specific structured nodes) and must be gated as mutating.
+ ("ATTACH DATABASE 'x.db' AS y", "sqlite"),
+ ("DETACH DATABASE y", "sqlite"),
+ ("REPLACE INTO t VALUES (1)", "mysql"),
+ ("RENAME TABLE a TO b", "mysql"),
+ ("SET PASSWORD FOR 'u'@'h' = 'p'", "mysql"),
],
)
def test_is_mutating_fails_closed_on_gate_blind_spots(sql: str, engine: str) -> None:
@@ -2042,6 +2050,15 @@ def test_is_mutating_fails_closed_on_gate_blind_spots(sql: str, engine: str) ->
assert SQLStatement(sql, engine).is_mutating()
+@pytest.mark.parametrize("engine", ["mysql", "sqlite"])
+def test_is_mutating_replace_function_is_read(engine: str) -> None:
+ """The REPLACE() string function inside a SELECT is a read; only the
+ REPLACE INTO statement form is mutating."""
+ assert not SQLStatement(
+ "SELECT REPLACE(name, 'a', 'b') FROM t", engine
+ ).is_mutating()
+
+
@pytest.mark.parametrize(
"sql, expected",
[