Skip to content

fix(sql): cap parser input length via SQL_MAX_PARSE_LENGTH config#40499

Merged
sha174n merged 25 commits into
apache:masterfrom
sha174n:fix/improve-validation-1779971563
Jul 1, 2026
Merged

fix(sql): cap parser input length via SQL_MAX_PARSE_LENGTH config#40499
sha174n merged 25 commits into
apache:masterfrom
sha174n:fix/improve-validation-1779971563

Conversation

@sha174n

@sha174n sha174n commented May 28, 2026

Copy link
Copy Markdown
Contributor

SUMMARY

Adds a configurable upper bound on the size of SQL scripts accepted by the SQL parser. Scripts whose UTF-8 byte length exceeds SQL_MAX_PARSE_LENGTH (default 1,000,000 bytes) are rejected before being passed to sqlglot, which bounds parser memory and CPU usage. Set SQL_MAX_PARSE_LENGTH = None to disable.

The check sits at every call site in superset/sql/parse.py that hands a string to sqlglot.parse or sqlglot.parse_one:

  • SQLStatement._parse, which covers SQL Lab execute, format, RLS rewriting, dataset SQL, and engine spec helpers (including the MySQL-backtick fallback inside the same function)
  • SQLStatement.parse_predicate
  • the extract_tables_from_statement helper that builds a pseudo SELECT from a SQL Command literal
  • the standalone transpile_to_dialect entry point used by chart query rendering

Putting one helper at all four sites means a direct caller cannot bypass the bound. The cap is in UTF-8 bytes rather than code points, so multi-byte payloads cannot exceed the intended memory cap.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A. Backend-only change.

TESTING INSTRUCTIONS

  • Default config: SQL scripts up to 1,000,000 UTF-8 bytes parse as before.
  • Scripts longer than the configured cap raise SupersetParseError before reaching sqlglot. Verified with a mocker.spy(sqlglot, "parse") that asserts zero parse calls for over-cap input.
  • Setting SQL_MAX_PARSE_LENGTH = None in superset_config.py disables the gate.
  • Existing parser unit tests pass unchanged.

New regression tests in tests/unit_tests/sql/parse_tests.py:

  • accept exactly at the cap (boundary)
  • reject one byte over the cap
  • reject when code-point count is under the cap but byte count is over
  • SQL_MAX_PARSE_LENGTH = None disables the gate
  • app-config value overrides the module fallback
  • SQLScript short-circuits sqlglot.parse on over-cap input (covers the MySQL-backtick double-parse path)
  • SQLStatement.parse_predicate is gated
  • transpile_to_dialect is gated

Run with:

pytest tests/unit_tests/sql/parse_tests.py -k "check_script_length or parse_predicate_length or transpile_to_dialect_length or sqlscript_gate"

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

Adds a configurable upper bound on the size of SQL scripts accepted by
the SQL parser. Scripts longer than SQL_MAX_PARSE_LENGTH (default
1,000,000 characters) are rejected before being passed to sqlglot. The
check sits in SQLStatement._parse, so it applies to every code path that
goes through SQLScript, including SQL Lab execute, format, RLS rewriting,
dataset SQL, and database engine spec helpers. Set SQL_MAX_PARSE_LENGTH
to None to disable.
@codecov

codecov Bot commented May 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 62.50000% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.54%. Comparing base (1e130fe) to head (bbbe312).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
superset/sql/parse.py 60.00% 4 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #40499      +/-   ##
==========================================
- Coverage   64.54%   64.54%   -0.01%     
==========================================
  Files        2665     2665              
  Lines      146630   146644      +14     
  Branches    33878    33880       +2     
==========================================
+ Hits        94647    94651       +4     
- Misses      50267    50273       +6     
- Partials     1716     1720       +4     
Flag Coverage Δ
hive 39.16% <37.50%> (+<0.01%) ⬆️
mysql 57.86% <62.50%> (+<0.01%) ⬆️
postgres 57.93% <62.50%> (-0.01%) ⬇️
presto 40.73% <37.50%> (+<0.01%) ⬆️
python 59.35% <62.50%> (-0.01%) ⬇️
sqlite 57.56% <62.50%> (+<0.01%) ⬆️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Follow-up on the parse-length gate. Three blocking gaps:

1. The gate at the top of SQLStatement._parse missed three other
   call sites in the same module that hand strings directly to
   sqlglot.parse_one: SQLStatement.parse_predicate, the
   extract_tables_from_statement helper that builds a pseudo SELECT
   from an exp.Command literal, and the standalone
   transpile_to_dialect entry point. Any of these could be reached
   without going through SQLStatement._parse, so the previous
   single-site check was bypassable.

   Pulled the check out of SQLStatement and into a module-level
   helper, then called it from all four sqlglot.parse/parse_one
   sites so the bound cannot be bypassed by a direct caller.

2. The cap was in Unicode code points, not bytes. A 1M-codepoint
   string of four-byte characters is up to 4MB of payload that the
   parser still has to ingest. Switched to UTF-8 byte length so the
   bound directly reflects parser memory and CPU exposure.

3. The "current_app.config.get + except RuntimeError" pattern is
   not the codebase idiom for "config-with-fallback-outside-app".
   Replaced with `has_app_context()`, which matches the pattern
   already used in sql_lab.py, models/core.py, and others.

Tests added in tests/unit_tests/sql/parse_tests.py:
- accept exactly at the cap (boundary)
- reject one byte over the cap
- reject when codepoint count is under the cap but byte count is over
- SQL_MAX_PARSE_LENGTH=None disables the gate
- app-config value overrides the module fallback
- SQLScript short-circuits sqlglot.parse on over-cap input (spy asserts
  zero calls, covers the MySQL-backtick double-parse path)
- SQLStatement.parse_predicate is gated
- transpile_to_dialect is gated

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@pull-request-size pull-request-size Bot added size/L and removed size/M labels May 28, 2026
@sha174n
sha174n marked this pull request as ready for review May 28, 2026 12:51
@dosubot dosubot Bot added change:backend Requires changing the backend sqllab Namespace | Anything related to the SQL Lab labels May 28, 2026
@bito-code-review

bito-code-review Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #3c3c61

Actionable Suggestions - 0
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • tests/unit_tests/sql/parse_tests.py - 1
Review Details
  • Files reviewed - 3 · Commit Range: 5a6f1d5..b66c7a7
    • superset/config.py
    • superset/sql/parse.py
    • tests/unit_tests/sql/parse_tests.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment thread superset/sql/parse.py Outdated
@bito-code-review

Copy link
Copy Markdown
Contributor

The provided pr_comments.csv file appears to be empty or incomplete, as it only contains a header row and no actual comment data. Without the content of the file, I cannot validate the correctness of the flagged issue or provide a resolution. Please ensure the file contains the expected review comments and try again.

sha174n added 2 commits May 29, 2026 01:16
… contract

Move the length gate inside the try/except wrappers in transpile_to_dialect
and extract_tables_from_statement so the new SupersetParseError no longer
escapes paths that previously normalised parse failures into
QueryClauseValidationException (or swallowed them with `return set()`).
Callers like transpile_virtual_dataset_sql only catch the former and would
otherwise lose their graceful fallback on over-cap input.
@bito-code-review

bito-code-review Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #0991ff

Actionable Suggestions - 0
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset/sql/parse.py - 1
Review Details
  • Files reviewed - 2 · Commit Range: b66c7a7..cad8d0b
    • superset/sql/parse.py
    • tests/unit_tests/sql/parse_tests.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas rusackas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wiring looks correct (all sqlglot entry points, UTF-8 byte count, None disables, app-context fallback) and the transpile_to_dialect error contract is preserved. One thing to confirm before merge: the 1 MB default now rejects any single query exceeding it — e.g. a very large IN (...) list or a big virtual-dataset SQL — in SQL Lab and dashboard-generated queries. It's configurable, but worth sanity-checking 1 MB against real-world query sizes in target deployments.

…ation-1779971563

# Conflicts:
#	tests/unit_tests/sql/parse_tests.py

@rusackas rusackas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think since this MIGHT break some very large queries, it's worth an UPDATING.md entry... otherwise LGTM.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sha174n

sha174n commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@rusackas thanks for the review.

On the 1 MB default: typical SQL Lab and dashboard-generated queries are in the kilobytes range, so 1,000,000 bytes leaves wide headroom. It is fully configurable via SQL_MAX_PARSE_LENGTH, and setting it to None disables the gate, so deployments that legitimately run larger queries (big IN (...) lists, large virtual-dataset SQL) can raise or remove it.

Added an UPDATING.md entry in 1eafdc3 calling out the cap, the affected paths, and how to adjust it.

Comment thread superset/sql/parse.py Outdated
sha174n and others added 2 commits June 12, 2026 22:02
Read the SQL_MAX_PARSE_LENGTH fallback from superset.config instead of a
duplicated module constant, so the no-app-context path stays sourced from
configuration rather than a hardcoded literal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #8010ad

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: cad8d0b..90493ef
    • superset/sql/parse.py
    • tests/unit_tests/sql/parse_tests.py
  • Files skipped - 1
    • UPDATING.md - Reason: Filter setting
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

sha174n and others added 3 commits June 13, 2026 21:43
Keep both UPDATING.md changelog entries (SQL_MAX_PARSE_LENGTH cap +
upstream's webhook/Impala internal-host blocks).
Collapse the redundant local in the parse-length config read and drop a
tautological self-assertion in the byte-length test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #9335b1

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 90493ef..2e7ffde
    • superset/sql/parse.py
    • tests/unit_tests/sql/parse_tests.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas rusackas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM!

@rusackas rusackas moved this to Propose for Lazy Consensus in Apache Superset 7.0 Jun 15, 2026
@rusackas

Copy link
Copy Markdown
Member

@sha174n I approved, but added the next major version hold tag since this sounded breaking change-ish. @villebro and @michael-s-molina can be reached on slack if it's more urgent, or you don't see it as a breaking change. The proposals for the changes in 7.0 will kick off tomorrow.

@rusackas rusackas moved this from Propose for Lazy Consensus to Completed in Apache Superset 7.0 Jun 30, 2026
@rusackas rusackas added the merge-if-green If approved and tests are green, please go ahead and merge it for me label Jun 30, 2026
@sha174n
sha174n merged commit 3651020 into apache:master Jul 1, 2026
60 of 61 checks passed
@github-project-automation github-project-automation Bot moved this from Completed to Lazy Consensus Reached in Apache Superset 7.0 Jul 1, 2026
sha174n added a commit to sha174n/superset that referenced this pull request Jul 1, 2026
Resolve additive conflicts with apache#40499 (SQL_MAX_PARSE_LENGTH): keep both our
denylist entries/tests and upstream's parse-length gate. Suite green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:backend Requires changing the backend merge-if-green If approved and tests are green, please go ahead and merge it for me size/L sqllab Namespace | Anything related to the SQL Lab

Projects

Status: Lazy Consensus Reached

Development

Successfully merging this pull request may close these issues.

2 participants