Skip to content

feat(sqllab): Add optional pre-run query cost warnings - #45313

Open
elizabeth-ilina wants to merge 70 commits into
apache:masterfrom
elizabeth-ilina:feat/bigquery-cost-warnings
Open

elizabeth-ilina wants to merge 70 commits into
apache:masterfrom
elizabeth-ilina:feat/bigquery-cost-warnings

Conversation

@elizabeth-ilina

@elizabeth-ilina elizabeth-ilina commented Oct 15, 2026 •

Copy link
Copy Markdown

SUMMARY

Add optional pre-run query cost warnings to SQL Lab for BigQuery and PostgreSQL. Administrators configure a threshold per connection; users can cancel or continue when an estimate exceeds it or cannot establish whether usage is within it. Warnings are advisory, not enforced spending or resource limits.

Enable SQLLAB_QUERY_COST_WARNINGS and ESTIMATE_QUERY_COST, then open Settings > Database Connections > Edit > Advanced > SQL Lab. Enable query cost estimation and Enable pre-run warnings, enter a positive threshold, and save. Warnings are off by default, with no automatic threshold. Settings use extra.query_cost_warning; no database migration is required.

Database Assessment and threshold
BigQuery Dry-run bytes processed. Enter thresholds in Bytes, MB or GB using decimal units. This estimates usage, not the final bill; SQL LIMIT does not cap bytes scanned.
PostgreSQL Total planner cost from EXPLAIN (FORMAT JSON) without ANALYZE. Planner units are relative, not money, memory or execution time.

A precise estimate or upper bound at or below the threshold proceeds normally. Above-threshold estimates prompt for confirmation, including conservative warnings for upper bounds. A lower bound at or below the threshold is inconclusive and also requires confirmation. Comparisons use raw values; details preserve native units and label bounds as At least or Up to.

The existing ConfirmModal displays Query warning, an explanation, optional details, and Cancel / Run anyway. Missing or unreliable estimates and known timeouts explain why assessment is unavailable. Invalid settings, permission failures, malformed assessment responses and unexpected errors stop the attempt.

Run keeps its normal appearance and shortcut tooltip. Enabled checks add an assessment request before execution; latency depends on the query, database and network. Disabled warnings send no assessment request. Manual estimation retains its existing formatted response.

The check uses the captured execution request and effective row limit. Cancel, Stop or relevant input changes invalidate a pending attempt; repeated confirmation submits the captured query once. Stop prevents execution but does not forcibly cancel the database estimate.

The estimate endpoint's check_warning option returns a shared proceed / confirm assessment. Engine checks own settings validation, prerequisites and assessment; Run and the dialog do not interpret database metrics. Both shipped engines use a shared numeric check. A test-only nonnumeric check demonstrates extension without changing the execution workflow. Future settings types may need their own settings UI.

Scope includes deliberate SQL Lab runs, selection, current-statement shortcuts, history reruns and asynchronous execution. Dashboards, scheduled queries, retries and CTAS/CVAS are excluded. Templates, multiple statements and custom SQL transformations receive an unavailable-assessment explanation. Existing authorization and native database limits still apply.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Before: SQL Lab offers manual estimation, but Run does not compare it with a connection threshold.

After: administrators configure a warning rule, and Run asks for confirmation when required.

BigQuery settings

image

PostgreSQL settings

02-postgresql-settings

PostgreSQL warning

03-postgresql-warning

BigQuery warning

05-bigquery-warning

Unavailable assessment

04-unavailable-assessment

TESTING INSTRUCTIONS

  1. Enable both flags and configure a supported connection. Save and reopen settings. For BigQuery, changing display units must retain the byte amount.
  2. Run a single read-only SELECT with a precise estimate or upper bound at or below the threshold. It should execute normally. Set the threshold below its estimate and repeat: Cancel must submit no execution; repeated Run anyway clicks must submit exactly one.
  3. Try multiple statements with warnings enabled. Confirm the unavailable explanation and the same Cancel / Run anyway actions.
  4. Change SQL, selection, connection or editor tab while checking. A late response must not execute the old request. Stop must prevent execution too.
  5. Disable warnings and refresh connection metadata: no assessment request should be sent. Valid saved values should remain, and manual estimation should still work.
  6. Exercise selection, current-statement and history entry points. Repeat cancellation, confirmation and disabled-warning cases with asynchronous execution using a separate Celery worker and results backend.

Automated tests cover settings/import validation, engine adapters, bounds and precision, authorization, SQL preparation, timeouts, malformed responses, stale inputs and single execution after confirmation. The nonnumeric fixture checks the shared extension contract. CI results are available in the PR checks.

The screenshots use real BigQuery dry-run and PostgreSQL EXPLAIN responses through SQL Lab, plus a real unavailable assessment for multiple statements. Each dialog was cancelled; no execution request was submitted during these captures. The displayed thresholds are demonstration values, not defaults.

ADDITIONAL INFORMATION

  • Has associated issue: [SIP-180] Implement a "Cost Estimation" Pre-Query Check #34384
  • Required feature flags: SQLLAB_QUERY_COST_WARNINGS; the shipped numeric checks also require ESTIMATE_QUERY_COST
  • 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

@elizabeth-ilina
elizabeth-ilina marked this pull request as draft October 15, 2026 02:08
@netlify

netlify Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 461c48c
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6ae522becc0825de9cadaae3
😎 Deploy Preview https://deploy-preview-45313--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot added api Related to the REST API doc Namespace | Anything related to documentation labels Oct 15, 2026
@codeant-ai-for-open-source

Copy link
Copy Markdown
Contributor

CodeAnt PR Risk: Medium Risk

  • The PR needs attention before merging.
  • Changing the threshold unit rescales the stored byte threshold; selecting GB can turn a 100 MB warning limit into 100 GB.
  • If a keyboard shortcut clears selected SQL while estimation is pending, the input comparison may discard the estimate and prevent the selected statement from running.

Assessed commit: 874ee59a4a88

@github-actions github-actions Bot added i18n Namespace | Anything related to localization i18n:spanish Translation related to Spanish language i18n:italian Translation related to Italian language i18n:french Translation related to French language labels Oct 19, 2026
This reverts commit c01ed6b. Keep the shared build-system change separate from the query cost warning feature.
@github-actions github-actions Bot added the requires:rebase Requires rebasing on top of current master label Oct 26, 2026
Comment thread superset/commands/database/update.py Outdated
"""Validate replacement or retained settings without adding omitted extra."""
assert self._model
serialized_extra = self._properties.get("extra", self._model.extra) or "{}"
if "query_cost_warning" not in serialized_extra:

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.

This gate does a substring test on the raw extra string, so a JSON-escaped key ("\u0071uery_cost_warning") skips it. extra_validator only runs QueryWarningRuleSchema over the parsed value, so an update with threshold: -1 or a metric the engine doesn't support is stored without the engine/threshold validation, and the Run-time check then returns 400 for that connection. Could this test the parsed extra dict for the key instead (parsing first, as normalize_query_cost_warning already does)?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey, thanks for catching this. Fixed by parsing the JSON before checking for the warning setting. Added regression coverage for escaped keys in both submitted and retained settings, including invalid thresholds and unsupported metrics.

}
timeout = app.config["SQLLAB_QUERY_COST_ESTIMATE_TIMEOUT"]
try:
with utils.timeout(

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.

The new utils.timeout(...) wrapper around check.assess isn't covered. test_warning_known_timeout_is_unavailable has the mocked check raise SupersetTimeoutException directly, so it would still pass if this wrapper were removed or ignored SQLLAB_QUERY_COST_ESTIMATE_TIMEOUT, and a slow estimate would then block Run indefinitely. Could a command-level test patch utils.timeout and assert it is entered with the configured seconds, then that the confirm/unavailable result is returned when it raises?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks, agreed - the previous test only covered handling the exception. Updated it to check that the timeout context is entered with the configured duration, and that a timeout returns confirm/unavailable without calling the estimator.

@github-actions github-actions Bot removed the requires:rebase Requires rebasing on top of current master label Oct 29, 2026
{"query": []},
{"query": {"totalBytesProcessed": "1"}},
{"rowLevelSecurityStatistics": "masked"},
{"rowLevelSecurityStatistics": {"rowLevelSecurityApplied": "false"}},

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.

The string-valued rowLevelSecurityApplied case has no totalBytesProcessed or accuracy, so _query_cost_warning_estimate returns an unusable estimate for it even if the boolean check in bigquery.py is removed, and this assertion still passes. A regression there could let a dry run with malformed row-security metadata but a valid byte count return proceed. Could this case also include "totalBytesProcessed": "1" and "totalBytesProcessedAccuracy": "PRECISE" so it fails without the guard?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks, fixed. The case now includes a valid byte count and PRECISE accuracy, so it specifically tests the row-security guard. I also fixed a few other test issues: mock leakage between tests, cancellation assertions that ran too early, and malformed-response cases that weren't testing the intended guard.

MagicMock(),
False,
)
execute._set_query_limit_if_required(sql)

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.

These assertions check the prepared SQL limit but not limiting_factor, so they still pass if every assignment is removed from the shared set_query_limit. The only tests that assert those values (sqllab_tests.py) are skipped, so a wrong attribution would change the truncated-result guidance with nothing failing. Could a unit test on set_query_limit assert QUERY for SQL limit 5 / dropdown 100, DROPDOWN for 500 / 100, and QUERY_AND_DROPDOWN for 100 / 100?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks, added unit coverage for all three cases, asserting both the effective limit and limiting_factor.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api Related to the REST API doc Namespace | Anything related to documentation i18n:brazilian i18n:chinese Translation related to Chinese language i18n:czech i18n:dutch i18n:french Translation related to French language i18n:italian Translation related to Italian language i18n:japanese Translation related to Japanese language i18n:korean Translation related to Korean language i18n:latvian i18n:persian i18n:portuguese i18n:russian Translation related to Russian language i18n:slovak i18n:spanish Translation related to Spanish language i18n:traditional-chinese i18n:ukrainian i18n Namespace | Anything related to localization packages size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants