Skip to content

fix(partition-mapping): stop epoch partition mirroring from dropping rows on hourly/daily keys - #45846

Open
hughhhh wants to merge 13 commits into
masterfrom
plan/sc-130257-partition-followup
Open

hughhhh wants to merge 13 commits into
masterfrom
plan/sc-130257-partition-followup

Conversation

@hughhhh

@hughhhh hughhhh commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

SUMMARY

Charts on Hive/Impala datasets were silently dropping rows when a partition filter mapping used the suggested transform unix_timestamp(:value). If the partition key stores hour- or day-start epoch seconds, that transform turns a filter bound like 10:47:23 into the raw instant rather than its bucket boundary, so the mirrored partition predicate excludes rows the original filter keeps. QA reproduced this on Hive 4.0.1 and Impala 4.4.1.

The fix:

  • Engine default removed. Hive/Impala (and their subclasses: Spark, Databricks Hive, Ascend) no longer suggest a transform. A BIGINT key doesn't tell Superset its bucket size or timezone.
  • Existing mappings protected. A temporal-to-numeric mapping whose transform is exactly the bare former default stops mirroring. Its filters still apply; only the extra partition predicate is dropped. The editor shows why. Stored expressions are not rewritten.
  • Any raw-instant spelling caught. On Hive/Impala-family engines, the transform is also evaluated at two instants 2,843 seconds apart within one hour (a separate, cached probe). If the answers keep that distance (or 2,843,000 for milliseconds), the transform returns raw instants, so mirroring is declined. This catches CAST(unix_timestamp(:value) AS BIGINT), + 0, * 1, floor(unix_timestamp(:value)) and STRING timestamp columns. A key that really stores raw epoch seconds is declined too: full scan, correct rows. Other engines are unaffected, since a raw epoch column there is an ordinary index.
  • Explicit data check. The editor's new Validate against data action takes one to three existing partition keys. It reads up to 50 rows from each, evaluates the transform fresh and compares each output with that same row's stored key. Results are matched, mismatch (with examples) or inconclusive. The read is a raw LIMIT 50 select (no GROUP BY) through QueryContext with raise_for_access() and RLS, with no COUNT(*) and no time filter. Probe caches and mirror verdicts are bypassed. A sampled match is reported as exactly that, not as proof for the whole dataset.
  • Honest preview label. The expression-only preview reads "Expression evaluated — data not checked" instead of "Valid".
  • Docs. The partition-filter-mapping guide and UPDATING.md give correct hourly/daily floor recipes and explain timezone and validation semantics.

Operator widening and the existing timezone normalization (adjust_time_bounds) are unchanged.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

  • New Hive/Impala mapping: transform prefilled/suggested as unix_timestamp(:value) → no suggestion; placeholder asks for the expression that populates the key.
  • Saved bare unix_timestamp(:value) on an hourly key: predicates mirrored, rows lost → not mirrored, with a warning explaining the missing bucket.
  • Preview success: "Valid" → "Expression evaluated — data not checked", plus Validate against data.

TESTING INSTRUCTIONS

Proof: backend unit tests for sql/parse, connectors/sqla, datasets, partition mirroring and the Hive/Impala specs pass (3,938 total). Jest PartitionFilterMapping/ passes (119). Staged-file pre-commit passes, including mypy, pylint and the frontend type check. New tests execute generated SQL and compare mirrored and unmirrored row identities for hourly and daily keys across =, IN, ranges and inequalities. They also confirm that the sampling query keeps RLS and partition restrictions and issues no COUNT(*). A further executed-SQL test runs five raw-instant spellings on TIMESTAMP and STRING sources: they are declined on Hive and still mirror on SQLite. With the check disabled, 9 of the 10 cases fail. QA ran an earlier head (2b4db94) on real Hive 4.0.1 and Impala 4.4.1: all 8 story scenarios returned correct counts. QA's remaining High (the raw-instant spellings above) is addressed by the later commits, which have not yet been re-run on live engines.

With PARTITION_FILTER_MAPPING enabled on a Hive or Impala dataset with a TIMESTAMP column mapped to an hour-start BIGINT partition key:

  1. Set the transform to unix_timestamp(:value), save, and reopen. Expect a "not mirrored" warning, and no partition predicate in View query.
  2. Enter existing partition keys and click Validate against data. Expect "Partition mismatch found" with the source value, stored key and transform output.
  3. Change the transform to CAST(floor(unix_timestamp(:value) / 3600) * 3600 AS BIGINT) and validate again. Expect "Matched N sampled rows across M partitions".
  4. Compare chart counts for =, IN and partial-hour ranges with and without the mapping. They should be equal.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags: PARTITION_FILTER_MAPPING
  • Changes UI
  • Includes DB Migration
  • Introduces new feature or API: optional validation_partition_values on the partition-mapping preview endpoint, data_validation in its response, safety_refusal in the dataset's partition summary
  • Removes existing feature or API: Hive/Impala partition_value_transform_default (the response field stays, now null)

Reviewed with sol-loop: 4 rounds with Claude Opus (GPT Sol unavailable: Codex usage limit). Verdict: the last finding (reverting a failure cache that could have switched the check off for 24 hours) is fixed as requested.

🤖 Generated with Claude Code

@netlify

netlify Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 73ea97a
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6ae506ce74cfe84a278be27a
😎 Deploy Preview https://deploy-preview-45846--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 29, 2026
@codeant-ai-for-open-source

Copy link
Copy Markdown
Contributor

CodeAnt  CodeAnt PR Risk

🟢  Low Risk

  • The PR appears safe to merge; no actionable issues were identified in the review.

Assessed commit: 299eba59f361

@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 i18n:chinese Translation related to Chinese language i18n:japanese Translation related to Japanese language i18n:russian Translation related to Russian language i18n:korean Translation related to Korean language i18n:dutch i18n:slovak i18n:ukrainian i18n:portuguese i18n:brazilian i18n:traditional-chinese i18n:persian i18n:czech i18n:latvian labels Oct 29, 2026
@codecov

codecov Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.56047% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.75%. Comparing base (aa7b2bc) to head (73ea97a).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
superset/connectors/sqla/partition_validation.py 83.78% 7 Missing and 5 partials ⚠️
superset/connectors/sqla/partition_mapping.py 92.92% 4 Missing and 4 partials ⚠️
...PartitionFilterMapping/PartitionDataValidation.tsx 90.14% 7 Missing ⚠️
superset/datasets/api.py 44.44% 4 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #45846      +/-   ##
==========================================
+ Coverage   84.73%   84.75%   +0.01%     
==========================================
  Files        3069     3071       +2     
  Lines      205781   206128     +347     
  Branches    48391    48511     +120     
==========================================
+ Hits       174366   174696     +330     
- Misses      28122    28132      +10     
- Partials     3293     3300       +7     
Flag Coverage Δ
hive 34.46% <14.76%> (-0.05%) ⬇️
javascript 81.01% <93.13%> (+0.02%) ⬆️
mysql 52.52% <14.76%> (-0.08%) ⬇️
postgres 52.52% <14.76%> (-0.09%) ⬇️
presto 36.18% <14.76%> (-0.05%) ⬇️
python 87.77% <89.45%> (+<0.01%) ⬆️
sqlite 52.29% <14.76%> (-0.08%) ⬇️
unit 82.54% <89.45%> (+0.01%) ⬆️

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.

@hughhhh
hughhhh requested a review from geido October 29, 2026 16:56
@hughhhh
hughhhh force-pushed the plan/sc-130257-partition-followup branch 2 times, most recently from f83cdd6 to 54a8442 Compare October 29, 2026 19:20
hughhhh and others added 4 commits October 29, 2026 15:55
…keted epoch keys

Hive/Impala suggested `unix_timestamp(:value)` as the partition transform.
On hour- or day-bucketed epoch keys it yields the raw instant rather than
the bucket boundary, so mirrored predicates silently excluded matching rows.

- Remove the Hive/Impala default; decline mirroring for the bare expression
  on temporal-to-numeric mappings and surface the refusal in the editor.
- Add an explicit "Validate against data" check that compares the transform
  of sampled business values with the same rows' stored partition keys.
- Label expression-only preview as evaluated, not verified.
- Document bucket, timezone and validation semantics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…atement

Direct sqlglot use is disallowed here, and it let a tokenizer error from a
half-typed transform escape the dataset summary. Compare projections via
SQLStatement, which already maps tokenizer errors to parse errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hughhhh and others added 3 commits October 29, 2026 15:55
…etected epoch spellings

An empty metrics list made the sample a GROUP BY over the whole partition
before LIMIT. Docs now say which spellings the refusal does not catch,
replace raw-instant examples with bucketed ones, and warn about non-UTC
sessions such as IST.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tants

The text match caught only the bare former default; CAST(...), + 0, * 1,
floor(...) and STRING timestamp columns still mirrored raw instants onto
hour/day keys and dropped rows. Probe two canary instants 2843 seconds
apart for temporal-to-numeric mappings and decline when the answers keep
that distance. The canaries are a separate, cached probe, so filter-value
probes and their cache keys are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Elsewhere a raw epoch column is an ordinary index to prune on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hughhhh
hughhhh force-pushed the plan/sc-130257-partition-followup branch from 54a8442 to 30f0769 Compare October 29, 2026 19:55
hughhhh and others added 5 commits October 29, 2026 19:07
QA's rerun on Hive 4.0.1 and Impala 4.4.1 found three ways a raw-instant
transform still mirrored and dropped rows:

- a STRING timestamp column left at the default is_dttm=false was never
  checked;
- microsecond instants (`* 1000000`, Impala's `utc_to_unix_micros`) were
  not recognised;
- text in ISO layout with a `T` was asked only with a space, answered
  NULL, and skipped the check.

Text columns are now asked in each common layout plus their own
python_date_format, flagged temporal or not, and the step matches
seconds through nanoseconds. If an engine raises on one layout, each
pair is probed alone. The dataset API reports a refusal that a probe
has already cached, so safety_refusal stops reading null for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From QA's rerun of #45846 on Hive and Impala:

- N-FIX-02: an ordinary comment in a transform (`-- hourly`) made every
  probe decline with a "not a plain projection" warning. The probe gate
  rejects any comment in the compiled SQL, since that is how a bound value
  hides what follows it. Comments the owner wrote are now removed with the
  engine's own lexer before the probe compiles. A `:value` inside a comment
  is still refused as stored.
- A transform the mapping refuses could get a green "Matched" from
  Validate when the chosen keys only held hour-boundary rows. Validate now
  reports it as refused and reads no rows.
- VAL-2: an inconclusive result names the partition keys at fault.
- VAL-4: partition keys must be 64-bit integers. A trailing newline no
  longer passes the `$` anchor, and the UI and the API agree on length.
- VAL-5: a disabled Validate button says why, and "1 partition" is
  singular.
- VAL-6: a refused mapping no longer says its filters are mirrored above
  "Filters will not be mirrored".
- O-REG-01: Validate is not offered for a text partition key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The dataset editor crashed on load: the build lowers `**` to
  `Math.pow`, which throws on BigInt operands, so the 64-bit key bounds
  are now spelled out as literals. Jest keeps the native operator, so
  only the served bundle showed it.
- A rejected partition key was reported one character at a time:
  marshmallow treats a lazy string as a list of messages. The message is
  now a plain string, and the test checks the message, not only that it
  raised.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Ask the column's python_date_format canary pair only when its text
  round-trips as a literal, the same gate every filter value passes.
  The format is the dataset owner's text; on Hive a backslash closes
  the literal SQLAlchemy rendered.
- strip_comments returns "" for comment-only input, and its re-lex
  refusal branches are tested, restoring 100% coverage on superset/sql.
- Document the refused status and inconclusive_keys in the validation
  response, and regenerate openapi.json.
- The panel says a rejected transform is not mirrored rather than
  refused, since an operator or engine reason is not a refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the requires:rebase Requires rebasing on top of current master label Oct 30, 2026
…ition-followup

# Conflicts:
#	UPDATING.md
#	superset/translations/de/LC_MESSAGES/messages.po

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 requires:rebase Requires rebasing on top of current master size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant