Skip to content

fix: fallback UPDATE planner produces nondeterministic project layout - #24794

Merged
mergify[bot] merged 6 commits into
matrixorigin:mainfrom
cpegeric:update_fallback_ut_failed
Jun 3, 2026
Merged

fix: fallback UPDATE planner produces nondeterministic project layout#24794
mergify[bot] merged 6 commits into
matrixorigin:mainfrom
cpegeric:update_fallback_ut_failed

Conversation

@cpegeric

@cpegeric cpegeric commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

What type of PR is this?

  • API-change
  • BUG
  • Improvement
  • Documentation
  • Feature
  • Test and CI
  • Code Refactoring

Which issue(s) this PR fixes:

issue ##24718

What this PR does / why we need it:

Root cause

In pkg/sql/plan/build_constraint_util.go inside getUpdateTableInfo (around line 218):

// remove unused table
newTblInfo := &dmlTableInfo{...}
for alias, columns := range usedTbl { // ← non-deterministic map iteration
idx := tblInfo.alias[alias]
...
newTblInfo.tableDefs = append(newTblInfo.tableDefs, tblDef)
newTblInfo.alias[alias] = len(newTblInfo.tableDefs) - 1
...
}

usedTbl is a map[string]map[string]tree.Expr populated from the SET list. For UPDATE emp, dept SET emp.comm=1, dept.loc=... it has two keys ("emp", "dept"). Iterating it with range randomizes
the order per Go map semantics, so newTblInfo.tableDefs ends up as either [emp, dept] or [dept, emp].

The downstream fallback UPDATE builder (selectUpdateTables / rewriteGeneratedColumnsForUpdate in build_update.go) faithfully follows whatever order it receives — placing each target's columns,
SET expressions, and recomputed generated-column projections in a contiguous block. The plan it produces is internally correct in both orderings.

The test TestUpdateFallbackGeneratedColumnsUseOnUpdateAfterRewrite then asserts a fixed position in the project list:

generatedExpr := requireFallbackSourceProjectExpr(t, logicPlan.GetQuery(),
len(...emp.Cols)+2+len(...dept.Cols)+1, // total projectLen=16
len(...emp.Cols)+1, "on-update-marker") // pos=10 (expects emp first)

Position 10 holds the recomputed emp.ename only when emp is the first block. When the map iteration yields [dept, emp], the emp block lives at offsets 5..14, the recomputed ename lands at index
15, and position 10 is emp.sal — exactly what the failure trace shows.

Verdict

Real underlying bug in upstream main (introduced by 7ff125d): newTblInfo.tableDefs ordering depends on Go map iteration randomness. Should iterate tblInfo.tableDefs in index order (or sort
by tblInfo.alias[alias]) instead of ranging over usedTbl.
The new test added in the same commit only happens to pass most of the time because [emp, dept] is randomly chosen ~50%.
Not related to your gpu_plugin_all branch — neither getUpdateTableInfo nor the test exists there independently; both came in from main via merge.
Suggested upstream fix: replace the range usedTbl loop with an iteration over tblInfo.tableDefs by index, looking up usedTbl[alias] per table to preserve source order.

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@cpegeric
cpegeric requested review from XuPeng-SH and removed request for XuPeng-SH June 3, 2026 16:32

@XuPeng-SH XuPeng-SH 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.

LGTM after reviewing the current head from 3 angles.

  1. Logic/correctness: the fix removes both nondeterministic table-block ordering and nondeterministic per-column block ordering in fallback UPDATE planning, and the downstream consumers bind updated values by column name via updateColPosMap rather than depending on the prior random layout.
  2. Behavior/compatibility: this is a layout-stabilization change, not a semantic rewrite of UPDATE results; it makes planner output deterministic across builds without changing the intended column-to-value mapping.
  3. Tests: the new deterministic planner unit test directly covers the reported regression path. A SQL-level BVT could be a nice follow-up hardening, but I do not see a blocking gap for this fix.

Approve.

@mergify mergify Bot added the queued label Jun 3, 2026
@mergify

mergify Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • Entered queue2026-06-03 16:45 UTC · Rule: main
  • Checks passed · in-place
  • Merged2026-06-03 17:51 UTC · at be2b87de120373be85a1dc4d987a84cee4fa7c09 · squash

This pull request spent 1 hour 6 minutes 14 seconds in the queue, including 1 hour 4 minutes 30 seconds running CI.

Required conditions to merge
  • #approved-reviews-by >= 1 [🛡 GitHub branch protection]
  • #review-threads-unresolved = 0 [🛡 GitHub branch protection]
  • github-review-decision = APPROVED [🛡 GitHub branch protection]
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
    • check-neutral = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
    • check-skipped = Matrixone Compose CI / multi cn e2e bvt test docker compose(PESSIMISTIC)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
    • check-neutral = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
    • check-skipped = Matrixone Standlone CI / Multi-CN e2e BVT Test on Linux/x64(LAUNCH, PROXY)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
    • check-neutral = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
    • check-skipped = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH, PESSIMISTIC)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone CI / SCA Test on Ubuntu/x86
    • check-neutral = Matrixone CI / SCA Test on Ubuntu/x86
    • check-skipped = Matrixone CI / SCA Test on Ubuntu/x86
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone CI / UT Test on Ubuntu/x86
    • check-neutral = Matrixone CI / UT Test on Ubuntu/x86
    • check-skipped = Matrixone CI / UT Test on Ubuntu/x86
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
    • check-neutral = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
    • check-skipped = Matrixone Compose CI / multi cn e2e bvt test docker compose(Optimistic/PUSH)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
    • check-neutral = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
    • check-skipped = Matrixone Standlone CI / e2e BVT Test on Linux/x64(LAUNCH,Optimistic)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
    • check-neutral = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
    • check-skipped = Matrixone Upgrade CI / Compatibility Test With Target on Linux/x64(LAUNCH)
  • any of [🛡 GitHub branch protection]:
    • check-success = Matrixone Utils CI / Coverage
    • check-neutral = Matrixone Utils CI / Coverage
    • check-skipped = Matrixone Utils CI / Coverage

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

Labels

kind/bug Something isn't working size/S Denotes a PR that changes [10,99] lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants