apache/superset · error · SupersetSecurityException

Custom SQL fields cannot contain set operations.

Error message

Custom SQL fields cannot contain set operations.

What it means

SupersetSecurityException (ADHOC_SUBQUERY_NOT_ALLOWED_ERROR) raised in validate_adhoc_subquery (models.py:1015) when sqlglot's is_set_operation() reports that the wrapped expression contains a set operation (UNION/INTERSECT/EXCEPT [ALL]). Custom SQL fields for metrics/columns/filters must be single scalar expressions; set operations would allow smuggling subquery-like behavior, so Superset blocks them outright.

Source

Thrown at superset/connectors/sqla/models.py:1015

    wrapped = f"SELECT {skeleton}"

    try:
        parsed = SQLStatement(wrapped, engine)
    except SupersetParseError as ex:
        if contains_jinja:
            return
        raise SupersetSecurityException(
            SupersetError(
                error_type=SupersetErrorType.ADHOC_SUBQUERY_NOT_ALLOWED_ERROR,
                message=_(
                    "Custom SQL fields cannot be parsed as a single SQL statement."
                ),
                level=ErrorLevel.ERROR,
            )
        ) from ex

    if parsed.is_set_operation():
        raise SupersetSecurityException(
            SupersetError(
                error_type=SupersetErrorType.ADHOC_SUBQUERY_NOT_ALLOWED_ERROR,
                message=_("Custom SQL fields cannot contain set operations."),
                level=ErrorLevel.ERROR,
            )
        )

    validate_adhoc_subquery(
        wrapped,
        database,
        catalog,
        schema or "",
        engine,
    )
    sanitize_clause(wrapped, engine)


class TableColumn(AuditMixinNullable, ImportExportMixin, CertificationMixin, Model):

View on GitHub (pinned to f4587218dd)

Solutions

  1. Remove the set operation from the custom SQL field; compute the unioned data in a virtual dataset (its SQL supports full queries) and reference that dataset instead.
  2. Restructure the metric as a single expression, e.g. use SUM/CASE aggregation over joined rows rather than UNION.
  3. If you genuinely need set-operation SQL, put it in the dataset's virtual SQL or a derived view in the database, not in an adhoc field.

Example fix

-- before (adhoc metric SQL — rejected)
SELECT SUM(a) FROM t1 UNION SELECT SUM(b) FROM t2

-- after
-- create a virtual dataset with the union, then use a simple metric on it
SUM(value)  -- on dataset: SELECT a AS value FROM t1 UNION ALL SELECT b FROM t1
Defensive patterns

Strategy: validation

Validate before calling

from superset.db_engine_specs.presto import SQLStatement # illustrative
from sqlglot import parse_one

def contains_set_operation(expr: str) -> bool:
    tree = parse_one(f"SELECT {expr}")
    return any(node.key in {"union", "intersect", "except", "union_all", "union_distinct"} for node in tree.walk())

Try / catch

from superset.exceptions import SupersetSecurityException

try:
    validate_adhoc_subquery(expr, database, catalog, schema)
except SupersetSecurityException as ex:
    if "set operations" in str(ex):
        guide_user_to_virtual_dataset()
    raise

Prevention

When it happens

Trigger: Putting e.g. 'SELECT a FROM t UNION SELECT b FROM t2' or an expression whose parse tree contains UNION/INTERSECT/EXCEPT into a Custom SQL metric, column, or filter value field in Explore or the dataset editor.

Common situations: Users pasting union-based queries into adhoc metric SQL; attempts to simulate subqueries via set operators; Jinja that renders to SQL containing UNION (Jinja skeletons replace {% %}/{{ }} blocks, but fully rendered UNIONs are caught).

Related errors


AI-assisted analysis of apache/superset@f4587218dd (2026-08-14). Data as JSON: /api/errors/decf3589a147e7bc. Report an issue: GitHub.