Skip to content

fix(api): include query lifecycle timing in /api/v1/chart/data response#37516

Open
YuriyKrasilnikov wants to merge 47 commits into
apache:masterfrom
YuriyKrasilnikov:feat/chart-data-timing-breakdown
Open

fix(api): include query lifecycle timing in /api/v1/chart/data response#37516
YuriyKrasilnikov wants to merge 47 commits into
apache:masterfrom
YuriyKrasilnikov:feat/chart-data-timing-breakdown

Conversation

@YuriyKrasilnikov

@YuriyKrasilnikov YuriyKrasilnikov commented Jan 28, 2026

Copy link
Copy Markdown
Contributor

SUMMARY

Fixes #37507: Chart data API now returns per-phase timing breakdown.

Before: Timing computed server-side → sent to STATS_LOGGER → discarded by default (DummyStatsLogger). No timing in API response.
After: timing object included in response with per-phase breakdown, controlled by CHART_DATA_INCLUDE_TIMING config.

Problem Analysis

Root Cause (from issue #37507)

Problem Description
Inconsistency SQL Lab shows execution time, /api/v1/chart/data does not
Computed data discarded @statsd_metrics computes timing → sends to STATS_LOGGER → not in response
No user diagnostics Slow chart → loading spinner → no way to identify bottleneck
Silent metric loss Default DummyStatsLogger discards all timing() calls

Key Code Location

QueryContextProcessor.get_df_payload() in superset/common/query_context_processor.py — the main query lifecycle method that handles validation, cache lookup, DB execution, and result processing.

Solution: Per-Phase Timing Instrumentation

Architecture

Wrap each phase in get_df_payload() with time.perf_counter():

get_df_payload()
├── Phase: validate        → timing["validate_ms"]
├── Phase: cache_lookup    → timing["cache_lookup_ms"]
├── Phase: db_execution    → timing["db_execution_ms"]  (null on cache hit)
├── Phase: result_processing → timing["result_processing_ms"]
├── Total                  → timing["total_ms"]
└── Cache status           → timing["is_cached"]

Configuration

Config Default Description
CHART_DATA_INCLUDE_TIMING False Include timing breakdown in API responses. When False, timing is still collected for STATS_LOGGER and slow-query logging but omitted from the response.
CHART_DATA_SLOW_QUERY_THRESHOLD_MS None When set, queries exceeding the threshold emit WARNING with full timing breakdown.

Response Format

When CHART_DATA_INCLUDE_TIMING = True:

{
  "result": [{
    "cache_key": "...",
    "data": [...],
    "timing": {
      "validate_ms": 2.1,
      "cache_lookup_ms": 5.3,
      "db_execution_ms": 850.0,
      "result_processing_ms": 12.5,
      "total_ms": 870.1,
      "is_cached": false
    }
  }]
}
  • db_execution_ms is null on cache hit, numeric on cache miss
  • All numeric values are >= 0, rounded to 2 decimal places
  • When CHART_DATA_INCLUDE_TIMING = False (default), the timing key is absent from the response

Slow Query Logging

When CHART_DATA_SLOW_QUERY_THRESHOLD_MS is set, queries exceeding the threshold emit WARNING with full timing breakdown (independent of CHART_DATA_INCLUDE_TIMING):

WARNING: Slow chart query: total=1250ms validate=2ms cache_lookup=5ms db_execution=1200ms result_processing=43ms is_cached=False

Per-Phase Metrics

Each phase emitted to STATS_LOGGER.timing() in milliseconds (independent of CHART_DATA_INCLUDE_TIMING):
chart_data.validate_ms, chart_data.cache_lookup_ms, etc.

Changes Summary

File Change
superset/common/query_context_processor.py Wrap phases with time.perf_counter(), emit metrics, slow query log, config-gated response
superset/common/query_actions.py Pass timing through RESULTS result_type
superset/config.py Add CHART_DATA_INCLUDE_TIMING and CHART_DATA_SLOW_QUERY_THRESHOLD_MS configs
superset/charts/schemas.py Add ChartDataTimingSchema with db_execution_ms as allow_none=True
tests/unit_tests/common/test_query_context_processor_timing.py 6 new tests

New Tests

Test Scenario
test_timing_present_in_payload timing dict exists in result when config enabled
test_timing_omitted_when_config_disabled timing absent when CHART_DATA_INCLUDE_TIMING = False
test_timing_values_are_non_negative All ms values >= 0
test_timing_no_db_execution_on_cache_hit db_execution_ms is None on cache hit
test_timing_has_db_execution_on_cache_miss db_execution_ms present on cache miss
test_slow_query_logging WARNING emitted when threshold exceeded

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Not applicable (API-only change, no UI)

TESTING INSTRUCTIONS

  1. Set CHART_DATA_INCLUDE_TIMING = True in superset_config.py
  2. Start Superset
  3. Open any dashboard with charts
  4. Inspect network → /api/v1/chart/data response
  5. Before: No timing field
  6. After: timing object with phase breakdown

ADDITIONAL INFORMATION

Related discussions: #18431, #13044

@bito-code-review

bito-code-review Bot commented Jan 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #5007cd

Actionable Suggestions - 0
Review Details
  • Files reviewed - 4 · Commit Range: fe91173..fe91173
    • superset/common/query_actions.py
    • superset/common/query_context_processor.py
    • superset/config.py
    • tests/unit_tests/common/test_query_context_processor_timing.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

@dosubot dosubot Bot added api Related to the REST API change:backend Requires changing the backend labels Jan 28, 2026
@netlify

netlify Bot commented Jan 28, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit c8ec7b3
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a58ce481889310008748e07
😎 Deploy Preview https://deploy-preview-37516--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.

Comment thread superset/common/query_context_processor.py Outdated
Comment thread tests/unit_tests/common/test_query_context_processor_timing.py Outdated
Comment thread tests/unit_tests/common/test_query_context_processor_timing.py Outdated
Comment thread tests/unit_tests/common/test_query_context_processor_timing.py Outdated
@github-actions github-actions Bot removed the api Related to the REST API label Jan 28, 2026
@YuriyKrasilnikov

Copy link
Copy Markdown
Contributor Author

Fixed in b8d12f3

1. bool as int — spurious metric emission

isinstance(True, (int, float)) evaluates to True (bool is a subclass of int), so timing["is_cached"] was emitted as stats_logger.timing("chart_data.is_cached", 0.001).

Fix:

if isinstance(value, (int, float)) and not isinstance(value, bool):
    stats_logger.timing(f"chart_data.{phase}", value / 1000)

2. STATS_LOGGER KeyError

Direct dict access current_app.config["STATS_LOGGER"] raises KeyError when the key is absent.

Fix:

stats_logger = current_app.config.get("STATS_LOGGER")
if stats_logger and hasattr(stats_logger, "timing"):

3. Tests — explicit current_app mock

All 5 tests now explicitly mock current_app.config instead of relying on implicit app context:

with patch("superset.common.query_context_processor.current_app") as mock_app:
    mock_app.config = {
        "STATS_LOGGER": MagicMock(),
        "CHART_DATA_SLOW_QUERY_THRESHOLD_MS": None,
    }

All 5 tests pass.

@bito-code-review

bito-code-review Bot commented Jan 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #a58248

Actionable Suggestions - 0
Review Details
  • Files reviewed - 4 · Commit Range: fe91173..b8d12f3
    • superset/common/query_actions.py
    • superset/common/query_context_processor.py
    • superset/config.py
    • tests/unit_tests/common/test_query_context_processor_timing.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

@github-actions github-actions Bot added the doc Namespace | Anything related to documentation label Jan 28, 2026
@bito-code-review

bito-code-review Bot commented Jan 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #f0aa4a

Actionable Suggestions - 0
Review Details
  • Files reviewed - 5 · Commit Range: b8d12f3..12af077
    • docs/docs/configuration/event-logging.mdx
    • superset/common/query_actions.py
    • superset/common/query_context_processor.py
    • superset/config.py
    • tests/unit_tests/common/test_query_context_processor_timing.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ 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

@YuriyKrasilnikov

Copy link
Copy Markdown
Contributor Author

Added in bd6a3d6

TypeScript interface for timing field

Added timing to ChartDataResponseResult interface in superset-frontend/packages/superset-ui-core/src/query/types/QueryResponse.ts.

This keeps frontend TypeScript types in sync with the backend Python schema (ChartDataTimingSchema), enabling:

  • Type safety for frontend code accessing response.timing
  • IDE autocomplete for developers
  • Compile-time type checking

The field is optional (timing?:) so existing code/tests continue to work without changes.

@bito-code-review

bito-code-review Bot commented Jan 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #d72779

Actionable Suggestions - 0
Review Details
  • Files reviewed - 7 · Commit Range: 12af077..bd6a3d6
    • docs/docs/configuration/event-logging.mdx
    • superset-frontend/packages/superset-ui-core/src/query/types/QueryResponse.ts
    • superset/charts/schemas.py
    • superset/common/query_actions.py
    • superset/common/query_context_processor.py
    • superset/config.py
    • tests/unit_tests/common/test_query_context_processor_timing.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ 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

@codecov

codecov Bot commented Jan 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 62.84289% with 447 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.12%. Comparing base (157ef61) to head (e5d6a29).

Files with missing lines Patch % Lines
superset/common/query_context_processor.py 42.39% 145 Missing and 14 partials ⚠️
superset/common/chart_data_timing.py 71.09% 95 Missing and 27 partials ⚠️
superset/common/query_actions.py 56.70% 37 Missing and 5 partials ⚠️
superset/common/utils/query_cache_manager.py 45.07% 35 Missing and 4 partials ⚠️
superset/charts/data/api.py 55.76% 22 Missing and 1 partial ⚠️
superset/charts/data/timing.py 76.11% 12 Missing and 4 partials ⚠️
superset/utils/cache.py 43.47% 11 Missing and 2 partials ⚠️
superset/commands/chart/data/get_data_command.py 71.79% 8 Missing and 3 partials ⚠️
...et/commands/chart/data/streaming_export_command.py 57.89% 7 Missing and 1 partial ⚠️
superset/models/helpers.py 79.48% 6 Missing and 2 partials ⚠️
... and 3 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #37516      +/-   ##
==========================================
- Coverage   65.13%   65.12%   -0.01%     
==========================================
  Files        2753     2755       +2     
  Lines      154573   155534     +961     
  Branches    35453    35572     +119     
==========================================
+ Hits       100674   101296     +622     
- Misses      51985    52269     +284     
- Partials     1914     1969      +55     
Flag Coverage Δ
hive 38.93% <29.69%> (-0.07%) ⬇️
javascript 70.72% <100.00%> (+<0.01%) ⬆️
mysql 57.80% <61.24%> (+0.07%) ⬆️
postgres 57.85% <61.24%> (+0.07%) ⬆️
presto 41.19% <55.20%> (+0.21%) ⬆️
python 59.28% <62.50%> (+0.06%) ⬆️
sqlite 57.47% <61.24%> (+0.07%) ⬆️
unit 100.00% <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.

@YuriyKrasilnikov

Copy link
Copy Markdown
Contributor Author

CI Failures Analysis

1. pre-commit (current/next/previous)

yarn registry returned 500 Internal Server Error — infrastructure issue, unrelated to this PR.

2. sharded-jest-tests (7)

Jest worker crash on DatasourceControl.test.tsx — flaky test, unrelated to this PR.

3. Python Integration Tests (test-sqlite, test-mysql, test-postgres)

Problem:

FAILED tests/integration_tests/base_api_tests.py::TestOpenApiSpec::test_open_api_spec
OpenAPIValidationError: None for not nullable

Root cause: ChartDataTimingSchema uses Nested field with allow_none=True. Superset uses apispec==6.6.1 which has a bug with nullable nested schemas.

References:

Options:

Option Description Pro Con
1 Upgrade apispec to 6.8.0+ Fixes root cause Requires separate PR, out of scope
2 Make timing always present Simple fix, timing is always computed anyway Field cannot be null
3 Replace Nested with fields.Dict() Field can be nullable OpenAPI docs show generic "object" instead of field details

Which option is preferred?

Comment thread superset-frontend/packages/superset-ui-core/src/query/types/QueryResponse.ts Outdated
@YuriyKrasilnikov

Copy link
Copy Markdown
Contributor Author

Fixed in eba466a

OpenAPI spec validation fix

Problem: test_open_api_spec was failing with None for not nullable.

Root cause analysis:

The issue was NOT in apispec version — it was in load_default=None on the timing field.

# Before (broken)
timing = fields.Nested(
    ChartDataTimingSchema,
    metadata={...},
    allow_none=True,
    load_default=None,  # ← causes "default": null in OpenAPI
)

When load_default=None is present, apispec generates "default": null in the schema. The validator then checks if null is valid against the allOf schema — it's not, so validation fails.

Testing results:

Configuration apispec 6.6.1
allow_none=True
+ metadata
+ load_default=None

Fix:

Removed load_default=None — marshmallow returns None for absent fields with allow_none=True anyway.

# After (works)
timing = fields.Nested(
    ChartDataTimingSchema,
    metadata={...},
    allow_none=True,
)

@bito-code-review

bito-code-review Bot commented Jan 30, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #d5da15

Actionable Suggestions - 0
Review Details
  • Files reviewed - 7 · Commit Range: bd6a3d6..c5907dd
    • docs/docs/configuration/event-logging.mdx
    • superset-frontend/packages/superset-ui-core/src/query/types/QueryResponse.ts
    • superset/charts/schemas.py
    • superset/common/query_actions.py
    • superset/common/query_context_processor.py
    • superset/config.py
    • tests/unit_tests/common/test_query_context_processor_timing.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ 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

YuriyKrasilnikov added a commit to YuriyKrasilnikov/superset that referenced this pull request Feb 19, 2026
@bito-code-review

bito-code-review Bot commented Feb 19, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #97f5c2

Actionable Suggestions - 0
Review Details
  • Files reviewed - 7 · Commit Range: c5907dd..55de7e7
    • docs/docs/configuration/event-logging.mdx
    • superset-frontend/packages/superset-ui-core/src/query/types/QueryResponse.ts
    • superset/charts/schemas.py
    • superset/common/query_actions.py
    • superset/common/query_context_processor.py
    • superset/config.py
    • tests/unit_tests/common/test_query_context_processor_timing.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

Copy link
Copy Markdown
Member

Running CI, and did a quick Claude Code review for good measure:

Blocking concerns

1. Security: Timing data always exposed, no way to disable

Per-phase timing is unconditionally returned in every /api/v1/chart/data response. In multi-tenant deployments, this leaks operational details (cache hit/miss status, DB execution time, validation overhead) to all authenticated users. This should be behind a feature flag or at minimum a config toggle, especially since Superset is used in security-sensitive environments.

2. db_execution_ms inconsistency across layers

The three layers disagree on how to represent "no DB execution":

  • TypeScript type: db_execution_ms: number | null (field present, value null)
  • Marshmallow schema: allow_none=True (field present, value null)
  • Python code: key simply absent from the dict when cached

This will cause undefined vs null confusion on the frontend. The Python code should set timing["db_execution_ms"] = None on cache hits to match the schema/type contract.

3. Stats logger units likely wrong

stats_logger.timing(f"chart_data.{phase}", value / 1000)

This divides milliseconds by 1000, sending seconds to stats_logger.timing(). But BaseStatsLogger.timing() and statsd's timing metric conventionally expect milliseconds. This would cause all metrics to appear 1000x smaller than actual.

@YuriyKrasilnikov

Copy link
Copy Markdown
Contributor Author

Thanks for the review @rusackas. CHART_DATA_INCLUDE_TIMING is a great idea — all three points addressed in 8725a7e:

1. Security — timing always exposed
Added CHART_DATA_INCLUDE_TIMING config option (default False). Timing data is still collected internally for STATS_LOGGER and slow-query logging, but omitted from the API response unless explicitly enabled.

2. db_execution_ms inconsistency
Fixed. On cache hit, db_execution_ms is now explicitly set to None instead of being absent from the dict. This matches both the Marshmallow schema (allow_none=True) and the TypeScript type (number | null).

3. stats_logger units
Fixed. Removed the /1000 division — values are now sent as milliseconds, consistent with BaseStatsLogger.timing() convention.

Comment thread superset/common/query_context_processor.py Outdated
Comment thread superset/common/query_context_processor.py Outdated
@bito-code-review

bito-code-review Bot commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #ec367f

Actionable Suggestions - 0
Review Details
  • Files reviewed - 7 · Commit Range: 55de7e7..a0fb3e9
    • docs/docs/configuration/event-logging.mdx
    • superset-frontend/packages/superset-ui-core/src/query/types/QueryResponse.ts
    • superset/charts/schemas.py
    • superset/common/query_actions.py
    • superset/common/query_context_processor.py
    • superset/config.py
    • tests/unit_tests/common/test_query_context_processor_timing.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

@sadpandajoe
sadpandajoe requested a review from villebro March 6, 2026 18:48
Fixes apache#37507

Add per-phase timing breakdown to get_df_payload() using
time.perf_counter(). The timing dict includes validate_ms,
cache_lookup_ms, db_execution_ms (cache miss only),
result_processing_ms, total_ms, and is_cached flag.

Each phase is also emitted to STATS_LOGGER.timing().
New config CHART_DATA_SLOW_QUERY_THRESHOLD_MS enables WARNING
logging when total_ms exceeds the threshold.
@bito-code-review

bito-code-review Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #75cdce

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/semantic_layers/mapper.py - 1
    • Type annotation consistency · Line 139-141
      Added explicit type annotation `pd.DataFrame` for `main_df` at line 139, improving type safety per AGENTS.md and dev-standard.mdc requirements. Note that lines 176 and 293 use identical patterns without annotations — for consistency, consider applying the same pattern there.
Review Details
  • Files reviewed - 23 · Commit Range: abe464b..8f7c628
    • superset/charts/data/api.py
    • superset/commands/chart/data/get_data_command.py
    • superset/common/chart_data_timing.py
    • superset/common/query_actions.py
    • superset/common/query_context_processor.py
    • superset/common/utils/query_cache_manager.py
    • superset/semantic_layers/mapper.py
    • tests/unit_tests/charts/commands/data/test_get_data_command.py
    • tests/unit_tests/charts/test_chart_data_api.py
    • tests/unit_tests/common/test_query_context_processor.py
    • tests/unit_tests/common/test_query_context_processor_timing.py
    • superset/commands/chart/warm_up_cache.py
    • superset/views/core.py
    • tests/integration_tests/core_tests.py
    • tests/unit_tests/commands/chart/warm_up_cache_test.py
    • superset/commands/chart/data/streaming_export_command.py
    • tests/integration_tests/charts/data/api_tests.py
    • tests/integration_tests/model_tests.py
    • tests/unit_tests/semantic_layers/mapper_test.py
    • tests/integration_tests/tasks/async_queries_tests.py
    • docs/admin_docs/configuration/event-logging.mdx
    • superset/charts/data/timing.py
    • superset/models/helpers.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ 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/charts/data/api.py
Comment thread superset/charts/data/api.py
Comment thread superset/charts/data/api.py
Comment thread superset/common/query_context_processor.py
Comment thread superset/connectors/sqla/models.py Outdated
@YuriyKrasilnikov

Copy link
Copy Markdown
Contributor Author

Pushed two follow-up fixes after reviewing the latest CodeAnt
comments and tracing both findings through the execution, cache, and response
contracts.

The first fix separates a contribution total's internal dependency from its
public producer response. The processor previously normalized the producer into
an all-record totals query, acquired it once, and reused that acquisition as the
producer's response. That reuse is only valid when the normalized dependency and
the requested producer have the same acquisition identity. With a row limit,
offset, ordering, series limit, annotations, or a result type with its own query
preparation (SAMPLES / DRILL_DETAIL), the public response could therefore
silently contain the internal dependency result instead of the result requested
for that producer.

The updated contract is:

  • the contribution dependency is a canonical FULL all-record query;
  • the public producer is acquired independently when its execution or result
    preparation differs from that dependency;
  • exact FULL, RESULTS, and POST_PROCESSED producer acquisitions can still be
    reused because their acquisition identity is unchanged;
  • dependency and producer cache identities remain distinct when their queries
    differ;
  • a failed dependency fails the contribution consumer without replacing or
    suppressing the producer's own response;
  • neither the consumer nor producer QueryObject is mutated.

The second fix handles a partially resolved native annotation request explicitly.
AnnotationLayerDAO.find_by_ids() may return fewer objects than requested; direct
dictionary indexing then raised KeyError, which escaped as a 500. A missing layer
now raises QueryObjectValidationError with the layer ID and reference name, and
the existing chart-data command/API mapping returns a controlled 400. I also added
the explicit type for the initially empty annotation payload; the other suggested
local annotations are already inferred and the changed files pass MyPy.

Regression coverage now includes exact reuse in both materialized and cache-only
modes, separate acquisition for normalized and specially prepared producers,
cache-key separation, dependency failure, no-mutation assertions, a partial
annotation DAO result, and the API 400 contract.

Verification:

  • 137 backend tests passed across the processor, chart-data command/API, currency,
    and dependency suites;
  • the final focused processor/API rerun passed at 118 tests;
  • the table buildQuery suite passed at 25 tests;
  • changed-file Ruff, MyPy, and Pylint passed (Pylint: 10.00/10);
  • repository-wide Prettier, Oxlint, frontend custom rules, Stylelint, docs ESLint,
    backend MyPy, metadata validation, feature-flag sync, and Zizmor passed.

The repository-wide Ruff/Pylint hooks still report only the unrelated existing
execute_sql.py C901 finding and migration W9001 warnings.

Comment thread superset/common/query_object.py
Comment thread superset/connectors/sqla/models.py Outdated
Comment thread superset/commands/chart/data/streaming_export_command.py
@bito-code-review

bito-code-review Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #d7b5f6

Actionable Suggestions - 0
Filtered by Review Rules

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

  • tests/unit_tests/common/test_query_context_processor_timing.py - 1
Review Details
  • Files reviewed - 4 · Commit Range: 8f7c628..c540423
    • superset/common/query_context_processor.py
    • tests/unit_tests/common/test_query_context_processor.py
    • tests/unit_tests/common/test_query_context_processor_timing.py
    • tests/unit_tests/charts/test_chart_data_api.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ 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

@YuriyKrasilnikov

YuriyKrasilnikov commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Reviewed the additional CodeAnt comments and addressed the
underlying issues in 1c403bb.

The contribution cache-identity finding was valid, but I did not add
contribution_totals_query_index to QueryObject.to_dict(). The index is a
query-plan position, not a semantic identity: moving the same totals producer
would cause an unnecessary cache miss, while placing a different producer at
the same index could still collide. Instead, the canonical totals acquisition
now passes its actual producer cache key into the contribution consumer's cache
identity. This keeps the consumer key stable when only the helper-query position
changes and changes it when the selected producer query, datasource, RLS, or
extra cache inputs change. The selector remains internal query-context planning
metadata and the public QueryObject serialization contract is unchanged.

The unhashable extra-cache-key finding was also valid, although it was not a new
regression from dict.fromkeys: the previous set(extra_cache_keys) failed for
the same inputs. cache_key_wrapper() accepts arbitrary JSON-serializable
values, so list/dict values are legitimate cache-key components. Deduplication
is now order-preserving and uses the same canonical JSON identity as final cache
key generation. I also updated the datasource/explorable type contract from
Hashable to Any so the annotations match the runtime API instead of hiding
the mismatch.

The streaming validation finding was correct as well. The command intentionally
raises QueryObjectValidationError when a large CSV export cannot produce
executable SQL, but the exception was raised while constructing the response,
outside the existing synchronous API error mapping. _get_data_response() now
owns that boundary for both command execution and response construction, so the
client receives a 400 with the validation message instead of a generic 500. The
streaming command remains independent of HTTP concerns.

Regression coverage verifies producer-position stability, producer-identity
separation, propagation through the acquisition path, list/dict cache-key
deduplication, and the streaming 400 response including its message.

Verification:

  • 142 expanded processor/query-object/streaming unit tests passed;
  • 12 SqlaTable/Jinja extra-cache-key integration tests passed;
  • the final focused rerun passed at 63 tests;
  • changed-file Ruff, MyPy, and Pylint passed (Pylint: 10.00/10);
  • repository-wide MyPy, Prettier, Oxlint, frontend custom rules, Stylelint,
    docs ESLint, frontend type-checking, Helm docs, metadata validation,
    feature-flag sync, and Zizmor passed.

@bito-code-review

bito-code-review Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #2c1bcf

Actionable Suggestions - 0
Review Details
  • Files reviewed - 14 · Commit Range: c540423..1c403bb
    • superset/charts/data/api.py
    • superset/common/query_actions.py
    • superset/common/query_context.py
    • superset/common/query_context_processor.py
    • superset/connectors/sqla/models.py
    • superset/explorables/base.py
    • superset/models/helpers.py
    • superset/models/sql_lab.py
    • superset/semantic_layers/models.py
    • superset/superset_typing.py
    • tests/unit_tests/charts/data/streaming_filename_test.py
    • tests/unit_tests/common/test_query_actions_currency.py
    • tests/unit_tests/common/test_query_context_processor_timing.py
    • tests/unit_tests/models/query_dependency_test.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ 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/common/chart_data_timing.py Outdated
Comment thread superset/common/utils/query_cache_manager.py Outdated
Comment thread superset/connectors/sqla/models.py Outdated
Comment thread superset/common/query_actions.py Outdated
Comment thread superset/common/query_actions.py Outdated
Comment thread superset/common/query_actions.py Outdated
Comment thread superset/commands/chart/data/streaming_export_command.py Outdated
Comment thread superset/commands/chart/data/streaming_export_command.py Outdated
Comment thread superset/common/utils/query_cache_manager.py
Query-specific cache validation could discard a loaded legacy entry after the cache manager had already accepted a cache-only request. Forced refresh paths could likewise return an unloaded cache object, allowing datasource execution even though the caller required cached data.

- Validate filter metadata before any datasource acquisition.
- Reject unusable legacy entries and forced refreshes when cache-only execution is requested.
- Preserve stale-entry refresh behavior for requests that permit source execution.

Regression coverage requires both legacy filtered entries and explicitly forced refreshes to fail without contacting the datasource.
The drill-detail filter regression test still targeted a private helper removed by the typed acquisition refactor, causing the master merge to fail during collection instead of exercising the maintained execution path.

- Exercise DRILL_DETAIL through acquire_query_data.
- Assert the prepared query is isolated from the caller and retains filters.
- Verify the prepared query reaches the cache-aware acquisition boundary.

The characterization boundary remains limited to backend preparation and does not claim frontend filter propagation.
Several values introduced along the timing, cache, SQL planning, and streaming export paths relied on inference even though project policy requires explicit types for new Python variables. The implicit declarations also obscured contracts at shared execution boundaries.

- Declare timing limits and cached dataframe state.
- Type query materialization and streaming export locals.
- Use the concrete SqlaQuery result type for deferred SQL planning.

Static analysis requires these declarations to remain compatible with MyPy, Ruff, and the project Pylint plugins.
@bito-code-review

bito-code-review Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #0c7c9f

Actionable Suggestions - 0
Filtered by Review Rules

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

  • superset/common/query_context_processor.py - 1
Review Details
  • Files reviewed - 8 · Commit Range: 1c403bb..bf3589e
    • superset/common/query_context_processor.py
    • superset/common/utils/query_cache_manager.py
    • tests/unit_tests/common/test_query_context_processor.py
    • tests/unit_tests/common/test_query_actions.py
    • superset/commands/chart/data/streaming_export_command.py
    • superset/common/chart_data_timing.py
    • superset/common/query_actions.py
    • superset/connectors/sqla/models.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ 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

Upstream time-comparison changes overlapped query-context processor coverage after contribution totals moved from in-place mutation to dependency-aware acquisition. Retaining the obsolete totals test would restore a contract that no longer exists, while taking only the branch side would discard new time-offset guarantees.

- Retain quarter, zero-shift, and invalid-offset coverage with the corresponding parser and alignment behavior.\n- Preserve immutable contribution planning and typed chart-data timing coverage.\n- Incorporate the remaining upstream fixes without altering their behavior.
@contextmanager
def chart_timing_phase(phase: ChartTimingPhase) -> Iterator[None]:
"""Measure one fixed chart API phase when request collection is active."""
timing = _request_timing.get()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: Add an explicit local type annotation for this request timing value to satisfy the type-hint requirement for relevant variables. [custom_rule]

Severity Level: Minor 🧹

Why it matters? ⭐

The new Python code introduces a local variable that is inferable but not explicitly annotated. Under the type-hint rule, this is a relevant variable that can be annotated, so the suggestion identifies a real violation.

Rule source 📖

.cursor/rules/dev-standard.mdc (line 28)

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/charts/data/timing.py
**Line:** 70:70
**Comment:**
	*Custom Rule: Add an explicit local type annotation for this request timing value to satisfy the type-hint requirement for relevant variables.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎


def _emit_request_metrics(timing: ChartRequestTiming, total_ns: int) -> None:
try:
stats_logger = current_app.config.get("STATS_LOGGER")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: Add a concrete type annotation for this logger-like local variable so the new code includes type hints for relevant variables. [custom_rule]

Severity Level: Minor 🧹

Why it matters? ⭐

This is newly added Python code and the local variable is used as a logger-like object but has no explicit type annotation. That fits the type-hint requirement for relevant variables, so the suggestion is valid.

Rule source 📖

.cursor/rules/dev-standard.mdc (line 28)

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/charts/data/timing.py
**Line:** 130:130
**Comment:**
	*Custom Rule: Add a concrete type annotation for this logger-like local variable so the new code includes type hints for relevant variables.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +109 to +111
for query_result in execution.queries:
error = query_result.payload.get("error")
status = query_result.payload.get("status")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: Add explicit type annotations for the newly introduced local variables in the execution-results loop to satisfy the type-hint requirement for relevant variables. [custom_rule]

Severity Level: Minor 🧹

Why it matters? ⭐

The modified Python code introduces two local variables, error and status, without type annotations even though their types can be inferred and annotated. This matches the type-hint rule for relevant variables in new or changed code.

Rule source 📖

.cursor/rules/dev-standard.mdc (line 28)

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/commands/chart/warm_up_cache.py
**Line:** 109:111
**Comment:**
	*Custom Rule: Add explicit type annotations for the newly introduced local variables in the execution-results loop to satisfy the type-hint requirement for relevant variables.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +1900 to +1901
# Each offset must start from the original request state.
query_object_clone = copy.copy(query_object)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: Add an explicit variable type annotation for this cloned query object to satisfy the type-hint requirement for newly introduced variables. [custom_rule]

Severity Level: Minor 🧹

Why it matters? ⭐

This is a newly added local variable in modified Python code and it can be annotated with QueryObject; the rule requires type hints for relevant variables that can be annotated, so the omission is a real violation.

Rule source 📖

.cursor/rules/dev-standard.mdc (line 28)

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/models/helpers.py
**Line:** 1900:1901
**Comment:**
	*Custom Rule: Add an explicit variable type annotation for this cloned query object to satisfy the type-hint requirement for newly introduced variables.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +2122 to +2123
with source_timing(SourceKind.TIME_OFFSET, SourceProvider.SQL):
result = self.query(query_object_clone_dct)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: Annotate this new local result variable with its expected return type to comply with the rule requiring type hints in modified code. [custom_rule]

Severity Level: Minor 🧹

Why it matters? ⭐

This is a newly introduced local variable in modified Python code, and it can be annotated as QueryResult; the code currently omits that type hint, matching the rule violation.

Rule source 📖

.cursor/rules/dev-standard.mdc (line 28)

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/models/helpers.py
**Line:** 2122:2123
**Comment:**
	*Custom Rule: Annotate this new local result variable with its expected return type to comply with the rule requiring type hints in modified code.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +4263 to +4268
if defer_source_queries:
prequery = self.get_query_str_extended(
prequery_obj,
mutate=False,
defer_source_queries=True,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: Add a type annotation to this newly introduced local variable so its expected structure is explicit and type-checkable. [custom_rule]

Severity Level: Minor 🧹

Why it matters? ⭐

This branch introduces a new local variable whose return type is known (QueryStringExtended), so it should be annotated under the type-hint rule; the existing code omits that annotation.

Rule source 📖

.cursor/rules/dev-standard.mdc (line 28)

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/models/helpers.py
**Line:** 4263:4268
**Comment:**
	*Custom Rule: Add a type annotation to this newly introduced local variable so its expected structure is explicit and type-checkable.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

class SourceTimingCollector:
"""Collect a bounded source tree without retaining query content."""

def __init__(self, clock: Callable[[], int] = time.perf_counter_ns) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: Add an explicit instance attribute type annotation for this assigned callable so static type checking can validate collector clock usage. [custom_rule]

Severity Level: Minor 🧹

Why it matters? ⭐

The attribute is assigned in init without an explicit instance annotation even though its type is known from the constructor signature. This matches the custom rule requiring type hints for relevant annotatable variables in new Python code.

Rule source 📖

.cursor/rules/dev-standard.mdc (line 28)

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/common/chart_data_timing.py
**Line:** 386:386
**Comment:**
	*Custom Rule: Add an explicit instance attribute type annotation for this assigned callable so static type checking can validate collector clock usage.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

) -> tuple[QueryAcquisitionResult, str | None, int]:
"""Acquire AUTO currency metadata through the normal cache-aware path."""
datasource = _get_datasource(query_context, query_obj)
form_data = query_context.form_data or {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: Add an explicit type annotation for this new local variable so static type checking can validate accesses to form data fields. [custom_rule]

Severity Level: Minor 🧹

Why it matters? ⭐

The new local variable form_data is assigned a dictionary-like value but has no explicit type annotation. This matches the custom rule requiring type hints on relevant variables that can be annotated, so the violation is real.

Rule source 📖

.cursor/rules/dev-standard.mdc (line 28)

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/common/query_actions.py
**Line:** 156:156
**Comment:**
	*Custom Rule: Add an explicit type annotation for this new local variable so static type checking can validate accesses to form data fields.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

from_dttm=query_obj.from_dttm,
to_dttm=query_obj.to_dttm,
extras=query_obj.extras,
currency_query = copy.copy(query_obj)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: Add a concrete type hint for this copied query object variable to keep the new query-preparation flow fully typed. [custom_rule]

Severity Level: Minor 🧹

Why it matters? ⭐

The copied query object is a newly introduced local variable and is used as a QueryObject, but it is not explicitly annotated. This is a valid instance of the type-hint rule violation described in the custom rule.

Rule source 📖

.cursor/rules/dev-standard.mdc (line 28)

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/common/query_actions.py
**Line:** 179:179
**Comment:**
	*Custom Rule: Add a concrete type hint for this copied query object variable to keep the new query-preparation flow fully typed.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +447 to +448
with chart_timing_phase("authorize"):
command.validate()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: The new authorization step in the cache endpoint calls validate() but does not handle QueryObjectValidationError, so malformed/invalid cached query contexts will now bubble up as 500s instead of a client error response. Add a QueryObjectValidationError handler (consistent with the other chart data endpoints) around this validation path and return a 400 response. [api mismatch]

Severity Level: Major ⚠️
- ❌ Async chart results endpoint errors on invalid cached queries.
- ⚠️ Clients see 500 instead of actionable validation message.
Steps of Reproduction ✅
1. Enable global async queries so that chart async jobs use the Celery task
`load_chart_data_into_cache` at `superset/tasks/async_queries.py:8-40`, which on success
stores a query context in the cache and returns a URL `/api/v1/chart/data/<cache_key>`
(lines 20-36).

2. In a controlled test or development environment, construct a cached query context entry
whose `form_data` produces a `QueryObject` with invalid configuration, for example
duplicate metric/column labels that cause `QueryObject.validate()` to raise
`QueryObjectValidationError` as implemented in `superset/common/query_object.py:85-110`
(duplicate labels path at lines 100-110), and store it under a cache key that
`QueryContextCacheLoader.load()` will return (loader defined in
`superset/charts/data/query_context_cache_loader.py:23-30`).

3. Issue an HTTP GET request to `/api/v1/chart/data/<cache_key>`, which is handled by
`ChartDataRestApi.data_from_cache` in `superset/charts/data/api.py:135-199`; inside this
method, the try-block at lines 181-187 loads the cached form data via
`_load_query_context_form_from_cache`, builds a `QueryContext` with
`_create_query_context_from_form`, and constructs `command =
ChartDataCommand(query_context)`.

4. Still inside `data_from_cache`, the new authorization phase at lines 188-189 calls
`command.validate()` within `with chart_timing_phase("authorize")`, which routes to
`QueryContext.raise_for_access()` (`superset/common/query_context.py:52-53`) and then
`QueryContextProcessor.raise_for_access()`
(`superset/common/query_context_processor.py:25-40`) that iterates queries and calls
`query.validate()`, causing `QueryObject.validate()` to raise `QueryObjectValidationError`
for the invalid query; this exception is not caught by the surrounding `except` clauses in
`data_from_cache` (which only handle `ChartDataCacheLoadError`,
`SupersetSecurityException`, and `ValidationError` at
`superset/charts/data/api.py:190-197), so it propagates and results in a 500 Internal
Server Error instead of the 400 response used by the other chart data endpoints
(`get_data` and `data` both catch `QueryObjectValidationError` at
`superset/charts/data/api.py:241-252` and 90-101 respectively).

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/charts/data/api.py
**Line:** 447:448
**Comment:**
	*Api Mismatch: The new authorization step in the cache endpoint calls `validate()` but does not handle `QueryObjectValidationError`, so malformed/invalid cached query contexts will now bubble up as 500s instead of a client error response. Add a `QueryObjectValidationError` handler (consistent with the other chart data endpoints) around this validation path and return a 400 response.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +369 to 370
def get_extra_cache_keys(self, query_obj: QueryObjectDict) -> list[Any]:
return []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: Returning an unconditional empty list here makes cache keys for SQL-query datasources ignore user-dependent Jinja/ExtraCache context. If a query uses user-scoped macros (for example current-user values) and result caching is enabled, different users can collide on the same cache key and receive each other’s cached results. Build extra cache keys from the query SQL/template context (like SqlaTable does) so user-specific inputs are part of the cache key. [cache]

Severity Level: Critical 🚨
- ❌ Query-based charts with Jinja macros leak user-specific data.
- ❌ Cached /api/v1/chart/data results shared between different users.
- ⚠️ Query datasources ignore ExtraCache user context entirely.
- ⚠️ Per-user caching relies solely on global impersonation flags.
Steps of Reproduction ✅
1. In `superset/common/query_context_processor.py:59-77`,
`QueryContextProcessor.query_cache_key` calls
`datasource.get_extra_cache_keys(query_obj.to_dict())` and passes the result into
`query_obj.cache_key(...)` as the `extra_cache_keys` argument.

2. When a chart uses a SQL Lab saved query as its datasource (datasource type
`DatasourceType.QUERY`), `self._qc_datasource` in `QueryContextProcessor` is an instance
of `Query` from `superset/models/sql_lab.py:16-27`.

3. The `Query` class implementation of `get_extra_cache_keys` at
`superset/models/sql_lab.py:369-370` simply returns an empty list, so `extra_cache_keys`
passed into `QueryObject.cache_key` (see `superset/common/query_object.py:489-500`) is
always `[]` for query-based datasources.

4. Create a SQL Lab query that uses user-scoped Jinja macros (for example `{{
current_user_id() }}` or `{{ url_param('tenant_id') }}` from
`superset/jinja_context.py:46-60, 240-37`), save it, and build a chart on top of this
saved query; then enable result caching and hit `/api/v1/chart/data` as two different
users. Because `Query.get_extra_cache_keys` ignores any user-specific context, both users
share the same cache key and the second user receives the first user’s cached results
despite different macro inputs.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/models/sql_lab.py
**Line:** 369:370
**Comment:**
	*Cache: Returning an unconditional empty list here makes cache keys for SQL-query datasources ignore user-dependent Jinja/ExtraCache context. If a query uses user-scoped macros (for example current-user values) and result caching is enabled, different users can collide on the same cache key and receive each other’s cached results. Build extra cache keys from the query SQL/template context (like SqlaTable does) so user-specific inputs are part of the cache key.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

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 change:backend Requires changing the backend doc Namespace | Anything related to documentation packages plugins size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chart data API lacks timing information despite computing metrics internally

2 participants