{"record":{"id":"a9ab325e5f386cd0","repo":"apache/superset","slug":"custom-sql-fields-cannot-be-parsed-as-a-single-sql","errorCode":null,"errorMessage":"Custom SQL fields cannot be parsed as a single SQL statement.","messagePattern":"Custom SQL fields cannot be parsed as a single SQL statement\\.","errorType":"http","errorClass":"SupersetSecurityException","httpStatus":403,"severity":"error","filePath":"superset/connectors/sqla/models.py","lineNumber":1004,"sourceCode":"    is still inspected; structural attacks smuggled in the non-templated\n    portion of an otherwise-templated expression are still rejected.\n    Expressions whose substituted skeleton is unparseable (typically due to\n    ``{% if %}`` control-flow templating) fall back to deferring validation\n    to query time, when the template processor has a real context.\n    \"\"\"\n    if not expression:\n        return\n    skeleton = _JINJA_BLOCK_RE.sub(\" NULL \", expression)\n    contains_jinja = skeleton != expression\n    engine = database.backend\n    wrapped = f\"SELECT {skeleton}\"\n\n    try:\n        parsed = SQLStatement(wrapped, engine)\n    except SupersetParseError as ex:\n        if contains_jinja:\n            return\n        raise SupersetSecurityException(\n            SupersetError(\n                error_type=SupersetErrorType.ADHOC_SUBQUERY_NOT_ALLOWED_ERROR,\n                message=_(\n                    \"Custom SQL fields cannot be parsed as a single SQL statement.\"\n                ),\n                level=ErrorLevel.ERROR,\n            )\n        ) from ex\n\n    if parsed.is_set_operation():\n        raise SupersetSecurityException(\n            SupersetError(\n                error_type=SupersetErrorType.ADHOC_SUBQUERY_NOT_ALLOWED_ERROR,\n                message=_(\"Custom SQL fields cannot contain set operations.\"),\n                level=ErrorLevel.ERROR,\n            )\n        )\n","sourceCodeStart":986,"sourceCodeEnd":1022,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/connectors/sqla/models.py#L986-L1022","documentation":"SupersetSecurityException (ADHOC_SUBQUERY_NOT_ALLOWED_ERROR) raised in validate_adhoc_subquery's expression validator (models.py:1004) when a custom SQL expression cannot be parsed by sqlglot as a single statement after wrapping it as SELECT <expr>. Jinja-containing expressions are exempt (skeleton replaces Jinja blocks with NULL), so this fires only for plain SQL that fails parsing — for example unbalanced parentheses or garbage tokens in a metric/column SQL field.","triggerScenarios":"Entering a Custom SQL adhoc metric or column whose expression is not a valid single SQL expression (e.g. 'SUM(x' or 'SELECT * FROM t') — set operations and multi-statement input are caught by sibling checks; this one catches pure parse failures of the wrapped SELECT.","commonSituations":"Typos in the adhoc metric SQL box in Explore; pasting a full query instead of an expression into a metric field; dialect-specific syntax sqlglot cannot parse for the chosen database engine.","solutions":["Fix the expression so it parses as one statement: it must be an expression (not a full query), with balanced parentheses and engine-valid syntax.","Verify the expression alone: wrap mentally in SELECT <expr> and run it against the target database.","If the syntax is valid in your engine but sqlglot rejects it, simplify (e.g. avoid exotic operators) or file/track a sqlglot parsing issue; do not bypass the validator."],"exampleFix":"-- before (adhoc metric SQL)\nSUM(CASE WHEN region = 'EMEA' THEN sales\n\n-- after\nSUM(CASE WHEN region = 'EMEA' THEN sales ELSE 0 END)","handlingStrategy":"validation","validationCode":"from sqlglot import parse_one\nfrom sqlglot.errors import ParseError\n\ndef parses_as_single_expression(expr: str) -> bool:\n    try:\n        parse_one(f\"SELECT {expr}\")\n        return True\n    except ParseError:\n        return False","typeGuard":null,"tryCatchPattern":"from superset.exceptions import SupersetSecurityException\n\ntry:\n    validate_adhoc_subquery(sql_expr, database, catalog, schema)\nexcept SupersetSecurityException as ex:\n    show_user_error(\"Custom SQL must be a single valid SQL expression\")\n    raise","preventionTips":["Validate custom SQL client-side with sqlglot parse_one(f'SELECT {expr}') before saving.","Enter expressions, not full queries, in adhoc metric/column fields.","Add a lint step in dataset-import tooling that parses every stored expression."],"tags":["adhoc","sql","security","sqlglot","validation"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}