Skip to content

fix(explore): prevent verbose_name from overriding column_name in filter resolution#38689

Open
Mayankaggarwal8055 wants to merge 2 commits intoapache:masterfrom
Mayankaggarwal8055:fix/filter-verbose-name-mapping
Open

fix(explore): prevent verbose_name from overriding column_name in filter resolution#38689
Mayankaggarwal8055 wants to merge 2 commits intoapache:masterfrom
Mayankaggarwal8055:fix/filter-verbose-name-mapping

Conversation

@Mayankaggarwal8055
Copy link
Contributor

@Mayankaggarwal8055 Mayankaggarwal8055 commented Mar 17, 2026

What problem does this solve?

Filtering can break when a column has a verbose_name that conflicts with an existing column name.

In the current implementation, both column_name and verbose_name are added to the columns_by_name mapping. If a verbose_name matches an existing key, it can override the actual column mapping. This leads to incorrect column resolution during query building, causing filters to fail or behave unexpectedly.

What does this PR do?

  • Ensures column_name remains the primary and authoritative key in columns_by_name
  • Adds verbose_name as a secondary alias only when it does not conflict with existing keys
  • Prevents overriding of actual column mappings, keeping query generation consistent

Why is this change needed?

column_name is the true identifier used in SQL queries, while verbose_name is intended for display purposes. Allowing verbose_name to override column_name breaks this distinction and can result in invalid or missing filters.

This change ensures predictable and correct filter behavior even when verbose_name is present.

Testing

  • Verified filtering works correctly using both column_name and non-conflicting verbose_name
  • Confirmed no regressions in existing query behavior

Related

Related to #38339

This addresses part of the issue where filter resolution fails due to mismatches between displayed labels and actual column identifiers. Additional handling may still be required for adhoc column labels.

@bito-code-review
Copy link
Contributor

bito-code-review bot commented Mar 17, 2026

Code Review Agent Run #ed78e8

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: 736d44f..736d44f
    • superset/models/helpers.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 change:backend Requires changing the backend explore:filter Related to filters in Explore labels Mar 17, 2026
@codeant-ai-for-open-source codeant-ai-for-open-source bot added the size:S This PR changes 10-29 lines, ignoring generated files label Mar 17, 2026
@codeant-ai-for-open-source
Copy link
Contributor

Sequence Diagram

This PR updates query building so real column names are always the primary mapping keys, while verbose names are added only as non-conflicting aliases. This keeps filter resolution predictable and prevents verbose labels from overriding actual SQL column identifiers.

sequenceDiagram
    participant Client
    participant QueryBuilder
    participant ColumnMap
    participant SQLBuilder

    Client->>QueryBuilder: Build query with filter field
    QueryBuilder->>ColumnMap: Register each column_name as primary key
    QueryBuilder->>ColumnMap: Add verbose_name only when key is unused
    QueryBuilder->>ColumnMap: Resolve filter field to mapped column
    QueryBuilder->>SQLBuilder: Build filter using resolved real column
    SQLBuilder-->>Client: Return query with correct filtering
Loading

Generated by CodeAnt AI

@codecov
Copy link

codecov bot commented Mar 17, 2026

Codecov Report

❌ Patch coverage is 66.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.31%. Comparing base (1c8224f) to head (736d44f).

Files with missing lines Patch % Lines
superset/models/helpers.py 66.66% 1 Missing and 1 partial ⚠️

❌ Your project check has failed because the head coverage (99.92%) is below the target coverage (100.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #38689      +/-   ##
==========================================
- Coverage   65.10%   64.31%   -0.80%     
==========================================
  Files        1819     2532     +713     
  Lines       72670   129707   +57037     
  Branches    23222    29981    +6759     
==========================================
+ Hits        47312    83416   +36104     
- Misses      25358    44838   +19480     
- Partials        0     1453    +1453     
Flag Coverage Δ
hive 40.59% <0.00%> (?)
mysql 61.59% <66.66%> (?)
postgres 61.66% <66.66%> (?)
presto 40.61% <0.00%> (?)
python 63.27% <66.66%> (?)
sqlite 61.29% <66.66%> (?)
unit 100.00% <ø> (?)

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

☔ View full report in Codecov by Sentry.
📢 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.

Comment on lines +2721 to +2724
# Add verbose_name ONLY if it doesn't conflict
for col in self.columns:
if col.verbose_name and col.verbose_name not in columns_by_name:
columns_by_name[col.verbose_name] = col
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: verbose_name aliases are added without checking metric names, so a verbose alias that matches a metric name will be resolved as a column first. This bypasses metric-filter handling and applies the filter in WHERE instead of HAVING, producing incorrect query results. Exclude aliases that collide with metric names when building columns_by_name. [logic error]

Severity Level: Major ⚠️
- ❌ HAVING metric filters downgraded into WHERE predicates.
- ⚠️ `POST /api/v1/chart/data` may return wrong aggregates.
Suggested change
# Add verbose_name ONLY if it doesn't conflict
for col in self.columns:
if col.verbose_name and col.verbose_name not in columns_by_name:
columns_by_name[col.verbose_name] = col
# Add verbose_name ONLY if it doesn't conflict
metric_names = {metric.metric_name for metric in self.metrics}
for col in self.columns:
if (
col.verbose_name
and col.verbose_name not in columns_by_name
and col.verbose_name not in metric_names
):
columns_by_name[col.verbose_name] = col
Steps of Reproduction ✅
1. Call chart data execution via `POST /api/v1/chart/data` in
`superset/charts/data/api.py:199-260`, which builds `ChartDataCommand` and executes query
payloads.

2. Request execution flows through `ChartDataCommand.run()`
(`superset/commands/chart/data/get_data_command.py:40-48`) to
`QueryContextProcessor.get_payload()`
(`superset/common/query_context_processor.py:325-361`) and then `_get_full()`
(`superset/common/query_actions.py:153-161`) which calls
`query_context.get_df_payload(...)`.

3. `get_df_payload()` calls datasource query generation through `get_query_result()`
(`superset/common/query_context_processor.py:131,235-243`) and eventually
`ExploreMixin.get_sqla_query()` (`superset/models/helpers.py:2630+`).

4. In `get_sqla_query()`, `columns_by_name` includes `verbose_name` aliases
(`superset/models/helpers.py:2721-2724`). If a column verbose alias equals a metric name
(cross-type collision is allowed; only per-table uniqueness exists in
`superset/connectors/sqla/models.py:1` for columns and `:246` for metrics), then filter
resolution checks column first (`helpers.py:3015`) and metric second
(`helpers.py:3018-3027`), so `is_metric_filter` stays false.

5. The filter is then appended to `WHERE` (`helpers.py:3046-3048`) instead of `HAVING`,
producing semantically wrong SQL for metric filters (e.g., metric-name filter intended for
aggregate expression applied to base column predicate).
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/models/helpers.py
**Line:** 2721:2724
**Comment:**
	*Logic Error: `verbose_name` aliases are added without checking metric names, so a verbose alias that matches a metric name will be resolved as a column first. This bypasses metric-filter handling and applies the filter in `WHERE` instead of `HAVING`, producing incorrect query results. Exclude aliases that collide with metric names when building `columns_by_name`.

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.
👍 | 👎

@Mayankaggarwal8055 Mayankaggarwal8055 changed the title fix: support filtering using verbose_name without overriding column_name fix(explore): prevent verbose_name from overriding column_name in filter resolution Mar 17, 2026
…ns_by_name

- Preserve column_name as source of truth
- Add verbose_name only when no conflict with columns or metrics
- Fix incorrect filter resolution caused by alias collision
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 explore:filter Related to filters in Explore size/S size:S This PR changes 10-29 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant