Skip to content

[fix](fe) Fix timezone-sensitive MV rewrite and TIMESTAMPTZ MTMV partition issues - #66795

Draft
starocean999 wants to merge 1 commit into
apache:masterfrom
starocean999:master_0403
Draft

[fix](fe) Fix timezone-sensitive MV rewrite and TIMESTAMPTZ MTMV partition issues#66795
starocean999 wants to merge 1 commit into
apache:masterfrom
starocean999:master_0403

Conversation

@starocean999

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

SessionVariable.time_zone was not marked affectQueryResultInPlan, so it was never persisted/compared when deciding whether a materialized view can be rewritten, and time-zone sensitive expressions were not wrapped with a SessionVarGuardExpr, so a detected session mismatch did not disable the rewrite.

Issue Number: close #xxx

Related PR: #xxx

Problem Summary:

Release note

None

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason
  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@starocean999

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

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.

Requesting changes: the three-round review found 12 substantiated issues, consolidated into 9 inline comments.

The patch is focused, but its stated goal is only partially achieved. Wrong-result paths remain in alias-free and complex-type TIMESTAMPTZ expressions, stored generated/sync-MV writes, both rolling-upgrade directions, stale manual-partition refreshes, and colliding generated partition names. The shared session-variable map also introduces avoidable rewrite loss, and both new timezone regressions have false-negative gaps.

The required three rounds are complete. Because two distinct findings arrived in the final round, the accurate completion status is capped/incomplete, not converged. A valid historical physical partition was traced successfully end to end while its descriptor remains current. No separate security, transaction, lock-order, FE/BE schema, delete-bitmap, memory-safety, or observability issue was substantiated. No builds or tests were run; this review used the authoritative bundle and source inspection only.

// truncate and append a stable hash of the full name; no time suffix so repeated generation
// (e.g. MTMV creation vs. partition refresh validation) always produces the same name.
// Cast to long before abs so Integer.MIN_VALUE (whose abs stays negative) cannot slip through.
partitionName = partitionName.substring(0, 30) + Math.abs((long) Objects.hash(partitionName));

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.

[P1] Make the deterministic partition identity collision-safe

The suffix has only 32 bits. Two valid long values with the same first 30 sanitized characters and the standard Java Aa/BB hash collision generate the same physical name. Initial MTMV creation then fails with a duplicate partition name; if the second base partition appears later, the IF NOT EXISTS add silently no-ops and leaves it permanently unrepresented. Please use a collision-resistant identity and explicitly reject a same-name/different-description add.

public Expression visit(Expression expr, Boolean insideGuard) {
Expression rewritten = rewriteChildren(this, expr, Boolean.FALSE);
if (rewritten instanceof NeedSessionVarGuard && !Boolean.TRUE.equals(insideGuard)) {
if (needsSessionVarGuard(rewritten) && !Boolean.TRUE.equals(insideGuard)) {

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.

[P1] Apply the guard rewriter outside aliases

rewritePlanTree reaches Filter, Join, Aggregate, and TopN expressions, but its executor's only rule matches Alias. A plan such as Project(id AS id) -> Filter(date_trunc(ts, 'day') = ...) -> Scan therefore leaves the predicate unchanged when the projected alias is unrelated. An MV built in UTC can remain structurally eligible in +08 even though rows around midnight differ. Please apply the visitor to every expression owned by these plan nodes, preserving named-output identity.

return false;
}
try {
return expr.anyMatch(e -> ((Expression) e).getDataType() instanceof TimeStampTzType);

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.

[P1] Classify the actual operation and nested type dependency

This top-level descendant test is wrong in both directions. It guards zone-invariant scalar operations such as COUNT(ts), MIN/MAX(ts), and ts IS NULL, disabling safe rewrites and rescanning nested trees repeatedly. Conversely, no node in array_join(array_sort(arr), '|') over ARRAY<TIMESTAMPTZ> has top-level TimeStampTzType, so the zone-dependent string conversion gets no guard at all; existing array output shows nested values rendered in the session zone. A UTC-materialized string can therefore rewrite in +08 and return the stored UTC rendering. Please model the operations that actually depend on timezone, including nested complex-type conversions.

});
for (String partition : partitions) {
if (!shouldExistPartitionNames.contains(partition)) {
if (!existPartitionNames.contains(partition)

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.

[P1] Revalidate manual partitions after alignment

Accepting any stored physical name also admits a stale one. If its base partition was dropped, or changes between analysis and the async task, alignMvPartition removes the MV partition but the manual request keeps the old name; the rebuilt mapping returns null and snapshot generation dereferences it. Please require the physical name's descriptor to remain current and revalidate the manual set after alignment before constructing snapshots or the overwrite sink.

@VarAttrDef.VarAttr(name = TIME_ZONE, needForward = true, affectQueryResultInExecution = true)
// affectQueryResultInPlan is required: TIMESTAMPTZ expressions (date_trunc/cast/floor on timestamptz)
// are evaluated in the session time zone, so the time zone must be captured when persisting session
// variables for views / materialized views / generated columns, and must be compared when deciding

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.

[P1] Preserve the captured zone through stored-expression execution

Adding time_zone to the persisted map changes binding, but not runtime materialization. BindSink adds a guard around a generated/sync-MV DateTrunc; the mandatory final MergeGuardExpr removes it because DateTrunc does not implement NeedSessionVarGuard, translation also unwraps guards, and BE receives the current insert session's zone. Thus an expression created in UTC and written in +08 materializes +08 values. Please make the creation-zone semantics survive execution, or reject unsupported stored expressions, and cover post-creation cross-zone writes.

// affectQueryResultInPlan is required: TIMESTAMPTZ expressions (date_trunc/cast/floor on timestamptz)
// are evaluated in the session time zone, so the time zone must be captured when persisting session
// variables for views / materialized views / generated columns, and must be compared when deciding
// whether a materialized view can be used for rewrite. Otherwise a MV built in one time zone may be

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.

[P1] Fence both metadata upgrade directions

Pre-change objects have no time_zone key: a new FE overlays the old map onto a fresh system-default session for background refresh, and old empty maps are treated as unconditional matches. In the reverse direction, an old read-serving FE accepts new metadata but neither registers this key nor detects TIMESTAMPTZ functions; its nominal mismatch cache stays unguarded and can rewrite across zones. Please introduce an explicit compatibility/migration fence (or require rebuild/recreation) and test old-metadata/new-FE plus new-metadata/old-FE operation.

// variables for views / materialized views / generated columns, and must be compared when deciding
// whether a materialized view can be used for rewrite. Otherwise a MV built in one time zone may be
// rewritten in a session with a different time zone and return stale materialized values.
@VarAttrDef.VarAttr(name = TIME_ZONE, needForward = true, affectQueryResultInPlan = 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.

[P2] Scope mismatches to the session dependency that changed

The complete maps are compared as raw strings and reduced to one mismatch Boolean. This treats equivalent spellings such as UTC, Etc/UTC, GMT, and +00:00 as different. A genuinely different but expression-irrelevant zone also activates every older NeedSessionVarGuard; for example, integer SUM has no TIMESTAMPTZ dependency but loses rewrite across zones. Please canonicalize timezone identity and propagate per-variable dependency differences instead of enabling all guard families from any map mismatch.


sql "SET enable_nereids_planner = true"
sql "SET enable_fallback_to_original_planner = false"
sql "SET time_zone = '+00:00'"

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.

[P2] Use a creation zone different from the FE default

This suite describes a non-default creation zone but selects +00:00; the regression runner and JVM default are UTC/Etc/UTC. Before this change, the fresh background context therefore evaluates under the same effective zone and can pass without restoring any persisted time_zone. Please choose a zone proven different from the FE default and keep a boundary row that makes the pre-fix refresh deterministically fail or materialize the wrong day.

WHERE ts IS NOT NULL
GROUP BY date_trunc(ts, 'day')
""")
def resSameTz = sql """

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.

[P2] Assert that the same-zone MV is actually selected

After recreating the MV in +08, this only compares query results. A base-table scan produces the same rows, so the suite still passes if TIMESTAMPTZ MV rewrite is disabled in every session. Please add mv_rewrite_success (or an equivalent plan assertion) for this positive same-zone case and retain the result check.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants