Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
4dc3406
planner: cannot call checkCanPushDownToMPP twice
hawkingrei Dec 25, 2025
90742e1
refactor the code
hawkingrei Jan 22, 2026
683b0bd
enable more linter
hawkingrei Jan 22, 2026
19d9bc4
planner: fix cascades tiflash propagation
hawkingrei Feb 4, 2026
15c4031
planner: gate TiFlash flag by MPP allow
hawkingrei Feb 5, 2026
d03199c
planner: use PossiblePropertiesInfo in logical aggregation
hawkingrei Feb 7, 2026
e9e92f8
planner: support PossiblePropertiesInfo in shallow-ref generator
hawkingrei Feb 7, 2026
98c4041
planner: satisfy revive early-return in shallow-ref generator
hawkingrei Feb 7, 2026
0696a7f
planner: guard tiflash pushdown warnings by has tiflash
hawkingrei Feb 7, 2026
6b07c49
simple code for convertToTableScan
hawkingrei Feb 9, 2026
c21759e
planner: gate TiFlash pushdown checks by availability
hawkingrei Feb 9, 2026
06392b9
planner: guard possible properties nils
hawkingrei Feb 10, 2026
5c56b7c
planner: propagate tiflash flags safely
hawkingrei Feb 10, 2026
ec9c6d6
planner: propagate tiflash for cte reader
hawkingrei Feb 10, 2026
e690597
planner: fix tiflash propagation in cascades
hawkingrei Feb 11, 2026
440463b
planner: align tiflash flag semantics
hawkingrei Feb 11, 2026
c4a667d
planner: gate tiflash flags on mpp
hawkingrei Feb 11, 2026
1d3987e
planner: restore hypo tiflash helper
hawkingrei Feb 11, 2026
26f1641
chore: gofmt logical_datasource
hawkingrei Feb 11, 2026
f96c861
planner: fix tiflash propagation for selection and union scan
hawkingrei Feb 11, 2026
e934c20
planner: remove hasTiflash accessors from logical plan
hawkingrei Feb 11, 2026
7375a50
planner: drop PossiblePropertiesInfo equals helper
hawkingrei Feb 11, 2026
1031bc9
planner: propagate tiflash for logical sequence
hawkingrei Feb 11, 2026
d397b62
planner: gate tiflash pushdown checks by read engines
hawkingrei Feb 11, 2026
5e998ba
planner: propagate tiflash through expand
hawkingrei Feb 11, 2026
220fb7d
planner: propagate tiflash in base logical plan
hawkingrei Feb 11, 2026
db1350b
test: update analyze suite outputs
hawkingrei Feb 11, 2026
a3a70b4
planner: update MPP test outputs
hawkingrei Feb 11, 2026
e808ce7
planner: force MPP for tpch Q3
hawkingrei Feb 11, 2026
35cc4a5
planner: align tiflash availability and tpch outputs
hawkingrei Feb 11, 2026
e8e2ca2
test: update casetest outputs
hawkingrei Feb 11, 2026
2626068
update
hawkingrei Feb 12, 2026
c586f6d
update
hawkingrei Feb 12, 2026
1029aef
update
hawkingrei Feb 12, 2026
43cf58c
update
hawkingrei Feb 12, 2026
a2d1dce
test: update pushdown outputs
hawkingrei Feb 12, 2026
6b8cc93
test: update verbose explain outputs
hawkingrei Feb 12, 2026
d506236
update
hawkingrei Feb 12, 2026
8a11ed7
test: update pushdown projection outputs
hawkingrei Feb 12, 2026
c2c5188
test: update tiflash late materialization outputs
hawkingrei Feb 12, 2026
cbebb20
test: update enforcempp/hint/pushdown outputs
hawkingrei Feb 12, 2026
c4f9a5d
test: update enforcempp and pushdown outputs
hawkingrei Feb 12, 2026
7b77e97
update
hawkingrei Feb 12, 2026
e2e7e83
planner, unistore: fix tiflash partition pruning metadata and mpp gating
hawkingrei Feb 12, 2026
724cc85
test: refresh TestReadFromStorageHint expected plans
hawkingrei Feb 12, 2026
09d2839
planner: fix MPP task gating and sync TiFlash testdata
hawkingrei Feb 12, 2026
d56d5f1
planner/core: update verbose explain and TiFlash LM testdata
hawkingrei Feb 12, 2026
9e6f59b
planner: gate hasTiflash for TiKV-only hints
hawkingrei Feb 12, 2026
4556b11
planner: refresh cbotest TiFlash cost model plan ids
hawkingrei Feb 12, 2026
558156f
planner: keep TPCH Q3 MPP join candidates and sync tpch outputs
hawkingrei Feb 12, 2026
5c640c8
planner: gate mpp join enumeration by tiflash availability
hawkingrei Feb 12, 2026
469ae5a
planner: rely on LogicalJoin hasTiflash for MPP join gating
hawkingrei Feb 12, 2026
e41ba99
planner: preserve HasTiflash in LogicalTopN possible properties
hawkingrei Feb 13, 2026
cd9bfc2
planner: harden possible properties propagation
hawkingrei Feb 13, 2026
7c125c4
update
hawkingrei Feb 13, 2026
1056dda
update
hawkingrei Feb 13, 2026
bb586ab
update bazel
hawkingrei Feb 13, 2026
ad5961f
update
hawkingrei Feb 13, 2026
18ce622
planner: rename possible property orders field
hawkingrei Feb 25, 2026
a71fcac
planner/sessionctx: fix stmtctx tiflash flag access after rebase
hawkingrei Mar 1, 2026
4c1805d
planner/casetest: fix MPP hint warnings in TestMPPHintsScope
hawkingrei Mar 1, 2026
6eb15f5
planner/cascades: fix old optimize test property map type
hawkingrei Mar 2, 2026
08ea8c0
update bazel
hawkingrei Mar 2, 2026
ea04afd
tests/integrationtest: revert result files to master
hawkingrei Mar 2, 2026
e36f8f0
tests: refresh integration explain outputs
hawkingrei Mar 2, 2026
7e17bde
tests: stabilize plan cache partition explain assertions
hawkingrei Mar 2, 2026
1652cff
tests: stabilize enforcempp explain plan assertions
hawkingrei Mar 2, 2026
0add956
tests: refresh predicate pushdown warning expectations
hawkingrei Mar 2, 2026
415ac9b
tests: stabilize ranger explain assertion
hawkingrei Mar 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/note/planner/rule/rule_ai_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,35 @@ Test and verification:
- Record with: `go test ./pkg/planner/core/casetest/rule -run TestConstantPropagateWithCollation -tags=intest,deadlock -record`.
- Add integration test to `tests/integrationtest/t/select.test` and record via `pushd tests/integrationtest && ./run-tests.sh -r select && popd` (integration tests use `-r`, not `-record`).

## 2026-02-12 - TiFlash `HasTiflash` propagation and plan-shape regression triage

Background:
- A refactor centralized `HasTiflash` propagation and changed `PreparePossibleProperties` behavior, which triggered broad plan output changes in TiFlash-related suites.
- One real regression appeared in partition pruning: `Unknown column 'a' in expression` from `MakePartitionByFnCol` when the physical partition info was built from output names instead of real table column names.

Key takeaways:
- The primary fix should stay in the `PhysPlanPartInfo` producer path, not in ad-hoc downstream guards.
- For partition pruning, always build pruning expressions with reconstructed table column names (`ReconstructTableColNames`) rather than post-projection/output names.
- Avoid adding planner warning logs in this hot path for fallback behavior; keep the planning path deterministic and quiet unless there is actionable operator-level context.
- When moving logic into `BaseLogicalPlan.PreparePossibleProperties`, verify both `p.hasTiflash` propagation and return-value semantics against operators that previously had custom implementations.

Plan output triage checklist:
1. Classify diffs before recording:
- Plan ID only (`_5` vs `_6`) -> safe to update testdata.
- Engine/mode change (`cop[tiflash]` vs `mpp[tiflash]`) -> validate session knobs and expected default behavior.
- Structural/semantic shape change (new/removed operators or root/Mpp boundary shifts) -> treat as suspicious until proved intentional.
2. For suspicious changes, compare the same case against `master` before re-recording.
3. Keep lock-related expectations explicit:
- `FOR UPDATE` can keep `SelectLock` at root while child subtree runs on MPP; this shape can be valid.
4. Update only targeted result files after confirmation; avoid broad re-record without classification.

Suggested validation set after this class of changes:
- `go test -run TestTiflashPartitionTableScan --tags=intest ./pkg/executor/test/tiflashtest`
- `go test -run TestTiFlashCostModel --tags=intest ./pkg/planner/core/casetest/cbotest`
- `go test -run TestReadFromStorageHint --tags=intest ./pkg/planner/core/casetest/hint`
- `go test -run TestPushDownToTiFlashWithKeepOrder --tags=intest ./pkg/planner/core/casetest/pushdown`
- `go test -run TestPushDownProjectionForTiFlash --tags=intest ./pkg/planner/core/casetest/pushdown`

## 2026-02-16 - Index range dimension mismatch with appended handle column (issue #66291)

Background:
Expand Down
5 changes: 3 additions & 2 deletions pkg/executor/test/showtest/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,11 +303,12 @@ func TestShowWarningsForExprPushdown(t *testing.T) {
tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1105|Scalar function 'md5'(signature: MD5, return type: var_string(32)) is not supported to push down to tiflash now.", "Warning|1105|Aggregation can not be pushed to tiflash because arguments of AggFunc `max` contains unsupported exprs"))
tk.MustExec("explain format = 'brief' select /*+ read_from_storage(tiflash[show_warnings_expr_pushdown]) */ max(a) from show_warnings_expr_pushdown group by md5(value)")
require.Equal(t, uint16(2), tk.Session().GetSessionVars().StmtCtx.WarningCount())
tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1105|Scalar function 'md5'(signature: MD5, return type: var_string(32)) is not supported to push down to tiflash now.", "Warning|1105|Aggregation can not be pushed to tiflash because groupByItems contain unsupported exprs"))
tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|",
"Warning|1105|Scalar function 'md5'(signature: MD5, return type: var_string(32)) is not supported to push down to tiflash now.",
"Warning|1105|Aggregation can not be pushed to tiflash because groupByItems contain unsupported exprs"))
tk.MustExec("set tidb_opt_distinct_agg_push_down=0")
tk.MustExec("explain format = 'brief' select max(distinct a) from show_warnings_expr_pushdown group by value")
require.Equal(t, uint16(0), tk.Session().GetSessionVars().StmtCtx.WarningCount())
// tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1105|Aggregation can not be pushed to storage layer in non-mpp mode because it contains agg function with distinct"))
}

func TestShowGrantsPrivilege(t *testing.T) {
Expand Down
14 changes: 7 additions & 7 deletions pkg/executor/testdata/slow_query_suite_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,35 @@
},
{
"SQL": "select /*+ READ_FROM_STORAGE(TIKV[t]) */ * from t where a > 1 order by f",
"Result": "[{\"Level\":\"Warning\",\"Message\":\"Expression about 'test.t.h' can not be pushed to TiFlash because it contains unsupported calculation of type 'set'.\",\"IsExtra\":true},{\"Level\":\"Note\",\"Message\":\"[t,t(tiflash)] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true},{\"Level\":\"Warning\",\"Message\":\"Expression about 'test.t.h' can not be pushed to TiFlash because it contains unsupported calculation of type 'set'.\",\"IsExtra\":true}]"
"Result": "[{\"Level\":\"Note\",\"Message\":\"[t,t(tiflash)] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true}]"
},
{
"SQL": "select /*+ READ_FROM_STORAGE(TIKV[t]) */ * from t where f > 1",
"Result": "[{\"Level\":\"Warning\",\"Message\":\"Expression about 'test.t.h' can not be pushed to TiFlash because it contains unsupported calculation of type 'set'.\",\"IsExtra\":true}]"
"Result": ""
},
{
"SQL": "select /*+ READ_FROM_STORAGE(TIKV[t]) */ f from t where f > 1",
"Result": "[{\"Level\":\"Note\",\"Message\":\"[t(tiflash),f,f_g] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true}]"
},
{
"SQL": "select /*+ READ_FROM_STORAGE(TIKV[t]) */ * from t where f > 3 and g = 5",
"Result": "[{\"Level\":\"Warning\",\"Message\":\"Expression about 'test.t.h' can not be pushed to TiFlash because it contains unsupported calculation of type 'set'.\",\"IsExtra\":true},{\"Level\":\"Note\",\"Message\":\"[t(tiflash),f_g,g] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true}]"
"Result": "[{\"Level\":\"Note\",\"Message\":\"[t(tiflash),f_g,g] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true}]"
},
{
"SQL": "select /*+ READ_FROM_STORAGE(TIKV[t]) */ * from t where g = 5 order by f",
"Result": "[{\"Level\":\"Warning\",\"Message\":\"Expression about 'test.t.h' can not be pushed to TiFlash because it contains unsupported calculation of type 'set'.\",\"IsExtra\":true},{\"Level\":\"Note\",\"Message\":\"[t(tiflash),g] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true},{\"Level\":\"Warning\",\"Message\":\"Expression about 'test.t.h' can not be pushed to TiFlash because it contains unsupported calculation of type 'set'.\",\"IsExtra\":true},{\"Level\":\"Note\",\"Message\":\"[t,t(tiflash),f_g,g] remain after pruning paths for t given Prop{SortItems: [{test.t.f asc}], TaskTp: rootTask}\",\"IsExtra\":true}]"
"Result": "[{\"Level\":\"Note\",\"Message\":\"[t(tiflash),g] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true},{\"Level\":\"Note\",\"Message\":\"[t,t(tiflash),f_g,g] remain after pruning paths for t given Prop{SortItems: [{test.t.f asc}], TaskTp: rootTask}\",\"IsExtra\":true}]"
},
{
"SQL": "select /*+ READ_FROM_STORAGE(TIKV[t]) */ * from t where d = 3 order by c, e",
"Result": "[{\"Level\":\"Warning\",\"Message\":\"Expression about 'test.t.h' can not be pushed to TiFlash because it contains unsupported calculation of type 'set'.\",\"IsExtra\":true},{\"Level\":\"Note\",\"Message\":\"[t,t(tiflash)] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true},{\"Level\":\"Warning\",\"Message\":\"Expression about 'test.t.h' can not be pushed to TiFlash because it contains unsupported calculation of type 'set'.\",\"IsExtra\":true}]"
"Result": "[{\"Level\":\"Note\",\"Message\":\"[t,t(tiflash)] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true}]"
},
{
"SQL": "select /*+ READ_FROM_STORAGE(TIKV[t]) */ * from t where h = '11,22'",
"Result": "[{\"Level\":\"Warning\",\"Message\":\"Expression about 'test.t.h' can not be pushed to TiFlash because it contains unsupported calculation of type 'set'.\",\"IsExtra\":true},{\"Level\":\"Warning\",\"Message\":\"Expression about 'test.t.h' can not be pushed to TiFlash because it contains unsupported calculation of type 'set'.\",\"IsExtra\":true},{\"Level\":\"Note\",\"Message\":\"[t,t(tiflash)] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true}]"
"Result": "[{\"Level\":\"Note\",\"Message\":\"[t,t(tiflash)] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true}]"
},
{
"SQL": "select /*+ READ_FROM_STORAGE(TIKV[t]) */ * from t where a > rand()*100",
"Result": "[{\"Level\":\"Warning\",\"Message\":\"Scalar function 'rand'(signature: Rand, return type: double) is not supported to push down to storage layer now.\",\"IsExtra\":true},{\"Level\":\"Warning\",\"Message\":\"Expression about 'test.t.h' can not be pushed to TiFlash because it contains unsupported calculation of type 'set'.\",\"IsExtra\":true},{\"Level\":\"Warning\",\"Message\":\"Scalar function 'rand'(signature: Rand, return type: double) is not supported to push down to tiflash now.\",\"IsExtra\":true},{\"Level\":\"Note\",\"Message\":\"[t,t(tiflash)] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true}]"
"Result": "[{\"Level\":\"Warning\",\"Message\":\"Scalar function 'rand'(signature: Rand, return type: double) is not supported to push down to storage layer now.\",\"IsExtra\":true},{\"Level\":\"Note\",\"Message\":\"[t,t(tiflash)] remain after pruning paths for t given Prop{SortItems: [], TaskTp: rootTask}\",\"IsExtra\":true}]"
}
]
}
Expand Down
15 changes: 12 additions & 3 deletions pkg/planner/cascades/memo/group_expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,19 +223,23 @@ func (e *GroupExpression) DeriveLogicalProp() (err error) {
}
childStats := make([]*property.StatsInfo, 0, len(e.Inputs))
childSchema := make([]*expression.Schema, 0, len(e.Inputs))
childProperties := make([][][]*expression.Column, 0, len(e.Inputs))
childProperties := make([]*base.PossiblePropertiesInfo, 0, len(e.Inputs))
for _, childG := range e.Inputs {
childGProp := childG.GetLogicalProperty()
childStats = append(childStats, childGProp.Stats)
childSchema = append(childSchema, childGProp.Schema)
childProperties = append(childProperties, childGProp.PossibleProps)
childProperties = append(childProperties, &base.PossiblePropertiesInfo{
Orders: childGProp.PossibleProps,
HasTiflash: childGProp.HasTiflash,
})
}
Comment on lines +226 to 235
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

childProperties is constructed with only Order and leaves HasTiflash at the zero value (false). Any PreparePossibleProperties implementation that depends on HasTiflash (many in this PR) will then compute hasTiflash=false for parent nodes under cascades, potentially disabling MPP/TiFlash plan generation even when TiFlash replicas exist.

Consider propagating HasTiflash through cascades logical properties (e.g., extend the group logical property to carry it) and populate it here; otherwise PreparePossibleProperties(...).HasTiflash will not be meaningful in cascades mode.

Copilot uses AI. Check for mistakes.
e.GetGroup().SetLogicalProperty(property.NewLogicalProp())
// currently the schemaProducer side logical op is still useful for group schema.
tmpFD := e.LogicalPlan.GetBaseLogicalPlan().(*logicalop.BaseLogicalPlan).FDs()
tmpSchema := e.LogicalPlan.Schema()
tmpStats := e.LogicalPlan.StatsInfo()
var tmpPossibleProps [][]*expression.Column
var tmpHasTiflash bool
// the leaves node may have already had their stats in join reorder est phase, while
// their group ndv signal is passed in CollectPredicateColumnsPoint which is applied
// behind join reorder rule, we should build their group ndv again (implied in DeriveStats).
Expand All @@ -252,12 +256,17 @@ func (e *GroupExpression) DeriveLogicalProp() (err error) {
// todo: extractFD should be refactored as take in childFDs, and return the new FDSet rather than depend on tree.
tmpFD = e.LogicalPlan.ExtractFD()
// prepare the possible sort columns for the group, which require fillIndexPath to fill index cols.
tmpPossibleProps = e.LogicalPlan.PreparePossibleProperties(tmpSchema, childProperties...)
tmp := e.LogicalPlan.PreparePossibleProperties(tmpSchema, childProperties...)
if tmp != nil {
tmpPossibleProps = tmp.Orders
tmpHasTiflash = tmp.HasTiflash
}
}
e.GetGroup().GetLogicalProperty().Schema = tmpSchema
e.GetGroup().GetLogicalProperty().Stats = tmpStats
e.GetGroup().GetLogicalProperty().FD = tmpFD
e.GetGroup().GetLogicalProperty().PossibleProps = tmpPossibleProps
e.GetGroup().GetLogicalProperty().HasTiflash = tmpHasTiflash
return nil
}

Expand Down
28 changes: 21 additions & 7 deletions pkg/planner/cascades/old/optimize.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ func (opt *Optimizer) onPhaseImplementation(_ base.PlanContext, g *memo.Group) (
prop := &property.PhysicalProperty{
ExpectedCnt: math.MaxFloat64,
}
preparePossibleProperties(g, make(map[*memo.Group][][]*expression.Column))
preparePossibleProperties(g, make(map[*memo.Group]*base.PossiblePropertiesInfo))
// TODO replace MaxFloat64 costLimit by variable from sctx, or other sources.
impl, err := opt.implGroup(g, prop, math.MaxFloat64)
if err != nil {
Expand Down Expand Up @@ -355,18 +355,28 @@ func (opt *Optimizer) implGroupExpr(cur *memo.GroupExpr, reqPhysProp *property.P
// preparePossibleProperties recursively calls LogicalPlan PreparePossibleProperties
// interface. It will fulfill the the possible properties fields of LogicalAggregation
// and LogicalJoin.
func preparePossibleProperties(g *memo.Group, propertyMap map[*memo.Group][][]*expression.Column) [][]*expression.Column {
func preparePossibleProperties(g *memo.Group, propertyMap map[*memo.Group]*base.PossiblePropertiesInfo) *base.PossiblePropertiesInfo {
if prop, ok := propertyMap[g]; ok {
return prop
}
groupPropertyMap := make(map[string][]*expression.Column)
groupHasTiflash := false
for elem := g.Equivalents.Front(); elem != nil; elem = elem.Next() {
expr := elem.Value.(*memo.GroupExpr)
childrenProperties := make([][][]*expression.Column, len(expr.Children))
childrenProperties := make([]*base.PossiblePropertiesInfo, len(expr.Children))
for i, child := range expr.Children {
childrenProperties[i] = preparePossibleProperties(child, propertyMap)
childProps := preparePossibleProperties(child, propertyMap)
if childProps == nil {
childProps = &base.PossiblePropertiesInfo{}
}
childrenProperties[i] = childProps
}
exprInfo := expr.ExprNode.PreparePossibleProperties(expr.Schema(), childrenProperties...)
if exprInfo == nil {
continue
}
exprProperties := expr.ExprNode.PreparePossibleProperties(expr.Schema(), childrenProperties...)
groupHasTiflash = groupHasTiflash || exprInfo.HasTiflash
exprProperties := exprInfo.Orders
for _, newPropCols := range exprProperties {
// Check if the prop has already been in `groupPropertyMap`.
newProp := property.PhysicalProperty{SortItems: property.SortItemsFromCols(newPropCols, true)}
Expand All @@ -380,6 +390,10 @@ func preparePossibleProperties(g *memo.Group, propertyMap map[*memo.Group][][]*e
for _, prop := range groupPropertyMap {
resultProps = append(resultProps, prop)
}
propertyMap[g] = resultProps
return resultProps
result := &base.PossiblePropertiesInfo{
Orders: resultProps,
HasTiflash: groupHasTiflash,
}
Comment on lines +393 to +396
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

preparePossibleProperties for the old cascades optimizer drops HasTiflash when caching into propertyMap (it only stores Order). Since PreparePossibleProperties now relies on childrenProperties[i].HasTiflash to propagate TiFlash availability, this will cause parents to see HasTiflash=false and skip MPP/TiFlash plan enumeration. Consider storing/merging HasTiflash in the cached PossiblePropertiesInfo (e.g. AND across equivalences / children).

Copilot uses AI. Check for mistakes.
propertyMap[g] = result
return result
}
10 changes: 5 additions & 5 deletions pkg/planner/cascades/old/optimize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,18 +179,18 @@ func TestPreparePossibleProperties(t *testing.T) {
// IndexScan_8 table:t, index:f, g
// Group#4 Schema:[test.t.a,test.t.f]
// IndexScan_6 table:t, index:f
propMap := make(map[*memo.Group][][]*expression.Column)
propMap := make(map[*memo.Group]*base.PossiblePropertiesInfo)
aggProp := preparePossibleProperties(group, propMap)
// We only have one prop for Group0 : f
require.Len(t, aggProp, 1)
require.True(t, aggProp[0][0].EqualColumn(columnF))
require.Len(t, aggProp.Orders, 1)
require.True(t, aggProp.Orders[0][0].EqualColumn(columnF))

gatherGroup := group.Equivalents.Front().Value.(*memo.GroupExpr).Children[0]
gatherProp, ok := propMap[gatherGroup]
require.True(t, ok)
// We have 2 props for Group1: [f], [a]
require.Len(t, gatherProp, 2)
for _, prop := range gatherProp {
require.Len(t, gatherProp.Orders, 2)
for _, prop := range gatherProp.Orders {
require.Len(t, prop, 1)
require.True(t, prop[0].EqualColumn(columnA) || prop[0].EqualColumn(columnF))
}
Expand Down
62 changes: 61 additions & 1 deletion pkg/planner/core/base/plan_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ type LogicalPlan interface {

// PreparePossibleProperties is only used for join and aggregation. Like group by a,b,c, all permutation of (a,b,c) is
// valid, but the ordered indices in leaf plan is limited. So we can get all possible order properties by a pre-walking.
PreparePossibleProperties(schema *expression.Schema, childrenProperties ...[][]*expression.Column) [][]*expression.Column
PreparePossibleProperties(schema *expression.Schema, childrenProperties ...*PossiblePropertiesInfo) *PossiblePropertiesInfo

// ExtractCorrelatedCols extracts correlated columns inside the LogicalPlan.
ExtractCorrelatedCols() []*expression.CorrelatedColumn
Expand Down Expand Up @@ -378,3 +378,63 @@ type PhysicalJoin interface {
GetInnerChildIdx() int
GetJoinType() JoinType
}

// PossiblePropertiesInfo is used to store all possible order properties.
type PossiblePropertiesInfo struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

this is interesting

// all possible order properties
Orders [][]*expression.Column
// HasTiflash is a runtime pruning signal and is intentionally excluded from hash/equals.
HasTiflash bool
}

// Hash64 implements the HashEquals interface.
func (info *PossiblePropertiesInfo) Hash64(h base.Hasher) {
if info == nil {
h.HashByte(base.NilFlag)
return
}
h.HashByte(base.NotNilFlag)
if info.Orders == nil {
h.HashByte(base.NilFlag)
} else {
h.HashByte(base.NotNilFlag)
h.HashInt(len(info.Orders))
for _, one := range info.Orders {
h.HashInt(len(one))
for _, col := range one {
col.Hash64(h)
}
Comment on lines +402 to +406
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Hash64 should nil-guard columns, consistent with Equals.

Equals handles nil Orders entries, but Hash64 dereferences col unconditionally. A nil slot would panic during hashing.

💡 Proposed fix
 		for _, one := range info.Orders {
 			h.HashInt(len(one))
 			for _, col := range one {
-				col.Hash64(h)
+				if col == nil {
+					h.HashByte(base.NilFlag)
+					continue
+				}
+				h.HashByte(base.NotNilFlag)
+				col.Hash64(h)
 			}
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/planner/core/base/plan_base.go` around lines 402 - 406, The Hash64
implementation that iterates info.Orders should guard against nil columns like
Equals does: inside the outer loop over info.Orders and the inner loop over each
"col", check if col is nil before calling col.Hash64(h); for nil slots emit a
deterministic nil marker into the hash (e.g., hash a fixed integer/zero) so the
hash distinguishes nil entries from non-nil ones. Update the Hash64 method (the
loop shown over info.Orders/col.Hash64) to perform this nil check and emit the
marker when col == nil.

}
}
}

// Equals implements the HashEquals interface.
func (info *PossiblePropertiesInfo) Equals(other any) bool {
info2, ok := other.(*PossiblePropertiesInfo)
if !ok {
return false
}
if info == nil {
return info2 == nil
}
if info2 == nil {
return false
}
if (info.Orders == nil && info2.Orders != nil) || (info.Orders != nil && info2.Orders == nil) || len(info.Orders) != len(info2.Orders) {
return false
}
for i, one := range info.Orders {
if len(one) != len(info2.Orders[i]) {
return false
}
for j, col := range one {
if (col == nil && info2.Orders[i][j] != nil) || (col != nil && info2.Orders[i][j] == nil) {
return false
}
if col != nil && !col.Equals(info2.Orders[i][j]) {
return false
}
}
}
return true
}
Loading