apache/superset · error · SupersetSecurityException
Only `SELECT` statements are allowed
Error message
Only `SELECT` statements are allowed
What it means
Raised by get_virtual_table_metadata() when SQLScript.has_mutation() detects a mutating statement (INSERT/UPDATE/DELETE/CREATE/ALTER/DROP etc.) in a virtual dataset's SQL. Virtual datasets are metadata-only views; mutating SQL is rejected with a SupersetSecurityException of type DATASOURCE_SECURITY_ACCESS_ERROR before anything is executed.
Source
Thrown at superset/connectors/sqla/utils.py:154
parsed_script = SQLScript(sql, engine=db_engine_spec.engine)
except SupersetParseError as ex:
# ``SQLScript`` fails on any invalid SQL, including static SQL
# with no template dependency. Only soften when the input
# contained Jinja markers — in that case an "Invalid SQL"
# outcome is very likely a rendering artifact (e.g. an empty
# ``filter_values('x')`` producing ``WHERE col IN ()``) rather
# than a genuine defect in the user's SQL, and the row is
# already persisted by ``UpdateDatasetCommand``. Genuinely
# invalid static SQL must still hard-error. See #38012.
if _has_jinja_markers(original_sql):
raise SupersetVirtualTableParseException(
message=_("Invalid SQL: %(error)s", error=ex.error.message),
) from ex
raise SupersetGenericDBErrorException(
message=_("Invalid SQL: %(error)s", error=ex.error.message),
) from ex
if parsed_script.has_mutation():
raise SupersetSecurityException(
SupersetError(
error_type=SupersetErrorType.DATASOURCE_SECURITY_ACCESS_ERROR,
message=_("Only `SELECT` statements are allowed"),
level=ErrorLevel.ERROR,
)
)
if len(parsed_script.statements) > 1:
raise SupersetSecurityException(
SupersetError(
error_type=SupersetErrorType.DATASOURCE_SECURITY_ACCESS_ERROR,
message=_("Only single queries supported"),
level=ErrorLevel.ERROR,
)
)
return get_columns_description(
dataset.database,
dataset.catalog,
dataset.schema,View on GitHub (pinned to f4587218dd)
Solutions
- Make the dataset SQL a single pure SELECT; move mutations to a proper pipeline/job.
- If flagged falsely, rename non-mutating constructs that the parser mistakes for DML, or restructure the query (e.g. avoid a function named like an update call).
- Run the mutation outside Superset, then build the virtual dataset on top of the resulting table.
Example fix
-- before DELETE FROM audit_log WHERE ts < '2020-01-01'; SELECT * FROM audit_log; -- after SELECT * FROM audit_log WHERE ts >= '2020-01-01'
Defensive patterns
Strategy: validation
Validate before calling
def is_single_select(sql: str, engine: str) -> bool:
try:
script = SQLScript(sql, engine=engine)
return not script.has_mutation()
except SupersetParseError:
return False Try / catch
try:
get_virtual_table_metadata(dataset)
except SupersetSecurityException:
# mutation rejected before execution: rewrite SQL as pure SELECT
raise Prevention
- Policy: dataset SQL is always a single SELECT — enforce in review and linters.
- Never rely on datasets to run DML; use scheduled jobs.
- Watch for sqlglot false positives after upgrading and pin known-good behavior with tests.
When it happens
Trigger: Saving a dataset whose SQL is `INSERT INTO ...`, `UPDATE ...`, `DELETE FROM ...`, or DDL like `CREATE TABLE ...` / `DROP VIEW ...`; also `SELECT ...; DELETE ...` where the mutation check trips on any statement. The check runs on the rendered SQL during save/refresh of the dataset.
Common situations: Trying to smuggle a mutation through the dataset layer because direct DML via SQL Lab is restricted; pasting a whole script (including cleanup DDL) into a dataset definition; a CTE or function name that sqlglot's engine dialect misclassifies as a mutation.
Related errors
- Only single queries supported
- User doesn't have permission to create or update datasets
- Custom SQL fields cannot be parsed as a single SQL statement
- Custom SQL fields cannot contain set operations.
- Invalid SQL: %(error)s
AI-assisted analysis of apache/superset@f4587218dd (2026-08-14).
Data as JSON: /api/errors/cb3b633ae2299c94.
Report an issue: GitHub.