fix(plan): sanitize non-finite plan stats - #24643
Conversation
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
## What type of PR is this? - [ ] API-change - [x] BUG - [ ] Improvement - [ ] Documentation - [ ] Feature - [ ] Test and CI - [ ] Code Refactoring ## Which issue(s) this PR fixes: issue #24520 ## What this PR does / why we need it: Cherry-pick PR #24643 to 4.0-dev. This PR prevents non-finite planner statistics from leaking into plan JSON and cost estimation paths by: - guarding stats ratio calculations against division by zero and non-finite results; - sanitizing non-finite float values before plan JSON marshaling; - clamping expression and join selectivity to the valid [0, 1] range; - clamping ANTI JOIN right-side selectivity before computing output rows; - adding regression coverage for zero stats, year(...) equality selectivity, NOT/JOIN propagation, ANTI JOIN, and JSON marshal sanitization. --------- Co-authored-by: XuPeng-SH <xupeng3112@163.com>
|
Correction after tracing the timeline for #26175: #24643 added the in-place reflective sanitizer, but it was not the recent trigger by itself. The regression was introduced by #25843 (merge commit b8a190a, 2026-07-23), which changed NewJsonPlanHandler from eagerly calling h.Marshal(ctx) to retaining marshalHandler and invoking Marshal later during asynchronous statement export. The retained ExplainData still shallow-copies PhyPlan, so PhyOperator.OpStats remains shared with the compile graph. The execution path calls RecordExecPlan and then Compile.Release(); release/reuse can reset OperatorStats (including OperatorMetrics) before deferred Marshal runs. The sanitizer from #24643 then writes the stale/shared map through SetMapIndex. A reset/reuse between IsNil and SetMapIndex yields assignment to entry in nil map. Root cause: #25843 introduced deferred serialization without snapshotting the shared PhyPlan. #24643 is the pre-existing unsafe mutator that makes the ownership violation visible. Fix: deep-copy PhyPlan (including OperatorStats maps and BackgroundQueries) before retaining it for deferred marshal, and add a regression test that releases/resets the source plan before Marshal. |
What type of PR is this?
Which issue(s) this PR fixes:
issue #24520
What this PR does / why we need it:
+InforNaN.json: unsupported value: +Inf.Cost/TableCntstats paths and marshal-plan non-finite stats.Test Plan
go test ./pkg/sql/plan -run 'TestSafeStatsRatiosAvoidNonFiniteSelectivity|TestSerializePlanToJson|TestDMLToJson' -count=1go test ./pkg/frontend -run 'TestSerializePlanToJson|TestMarshalPlanHandlerSanitizesNonFinitePlanStats' -count=1go test ./pkg/sql/plan -count=1go test ./pkg/frontend -count=1git diff --checkmakemake static-check