refactor: join_recorder refacor plan
- Dominant language
- Go
- Stars
- 40.5k
- Forks
- 6.2k
- PR merge metrics
- PR metrics pending
Description
related to [ISSUE#55231](https://github.com/pingcap/tidb/issues/55231)
## Refactoring Design Plan
### 1. Current State Analysis
`rule_join_reorder.go` is currently located in the `pkg/planner/core` packageand contains the following core components:
- The `JoinReOrderSolver` struct
- The `extractJoinGroup` function, which is used to extract join groups
- The `baseSingleGroupJoinOrderSolver`, the base solver
This file depends on several packages:
- `github.com/pingcap/tidb/pkg/planner/core/operator/logicalop` for logical operator type checking
- `github.com/pingcap/tidb/pkg/planner/core/base` for basic plan interfaces
### 2. Target Structure After Refactoring
```
pkg/planner/core/rule/
├── join_reorder.go # Main rule implementation
├── util/
│ ├── join_group.go # Join group extraction logic
│ ├── join_solver_base.go # Base solver
│ └── join_cost.go # Cost calculation utilities
└── logical_rules.go # Rule registration
```
### 3. Detailed Refactoring Steps
#### Step 1: Create Rule Interface Implementation
Implement the `LogicalOptRule` interface in `pkg/planner/core/rule/join_reorder.go`:
```go
// JoinReorderRule implements LogicalOptRule interface
type JoinReorderRule struct{}
// Match implements LogicalOptRule.Match
func (r *JoinReorderRule) Match(ctx context.Context, p base.LogicalPlan) (bool, error) {
// Check if reorderable joins exist
return containsReorderableJoins(p), nil
}
// Optimize implements LogicalOptRule.Optimize
func (r *JoinReorderRule) Optimize(ctx context.Context, p base.LogicalPlan, opt *optimizetrace.LogicalOptimizeOp) (base.LogicalPlan, bool, error) {
solver := &JoinReOrderSolver{}
return solver.Optimize(ctx, p, opt)
}
// Name implements LogicalOptRule.Name
func (r *JoinReorderRule) Name() string {
return "join_reorder"
}
```
#### Step 2: Move Core Solver Logic
Keep the `Optimize` method of `JoinReOrderSolver` in the rule file, but move the specific solving logic to the util package.
#### Step 3: Extract Shared Utilities to util Package
**pkg/planner/core/rule/util/join_group.go:**
```go
// ExtractJoinGroup extracts join groups for use by rules and other components
func ExtractJoinGroup(p base.LogicalPlan) *JoinGroupResult {
// Move extractJoinGroup function implementation here
}
type JoinGroupResult struct {
Group []base.LogicalPlan
EqEdges []*expression.ScalarFunction
OtherConds []expression.Expression
JoinTypes []*JoinTypeWithExtMsg
JoinOrderHintInfo []*h.PlanHints
JoinMethodHintInfo map[int]*JoinMethodHint
HasOuterJoin bool
}
```
**pkg/planner/core/rule/util/join_solver_base.go:**
```go
// BaseSingleGroupJoinOrderSolver base solver
type BaseSingleGroupJoinOrderSolver struct {
ctx base.PlanContext
eqEdges []*expression.ScalarFunction
otherConds []expression.Expression
joinTypes []*JoinTypeWithExtMsg
// ... other fields
}
// CheckConnection checks the connection relationship between two nodes
func (s *BaseSingleGroupJoinOrderSolver) CheckConnection(leftPlan, rightPlan base.LogicalPlan) (leftNode, rightNode base.LogicalPlan, usedEdges []*expression.ScalarFunction, joinType *JoinTypeWithExtMsg) {
// Move checkConnection method implementation here
}
// MakeJoin constructs a join
func (s *BaseSingleGroupJoinOrderSolver) MakeJoin(leftPlan, rightPlan base.LogicalPlan, eqEdges []*expression.ScalarFunction, joinType *JoinTypeWithExtMsg) (base.LogicalPlan, []expression.Expression) {
// Move makeJoin method implementation here
}
```
#### Step 4: Handle Dependencies
To avoid cyclic dependencies, you should:
1. **Move type definitions to util package:**
- `joinTypeWithExtMsg` [8](#1-7)
- `jrNode` [9](#1-8)
2. **Create interface abstraction:**
```go
// pkg/planner/core/rule/util/interfaces.go
type JoinReorderSolver interface {
Solve(joinGroup []base.LogicalPlan, tracer *JoinReorderTrace) (base.LogicalPlan, error)
}
```
3. **Maintain dependency on logicalop:**
The util package can safely depend on `pkg/planner/core/operator/logicalop` for type assertions.
#### Step 5: Update BUILD.bazel File
Update `pkg/planner/core/BUILD.bazel` to remove `rule_join_reorder.go` and create the corresponding BUILD file in the new rule package.
#### Step 6: Keep DP and Greedy Solvers
`rule_join_reorder_dp.go` and `rule_join_reorder_greedy.go` also need to be refactored accordingly and moved to the rule package.
### 4. Dependency Graph
Dependency relationships after refactoring:
```mermaid
graph TD
A[core] --> B[core/logicalop]
A --> C[core/rule]
C --> D[core/rule/util]
B --> D
D --> E[expression]
D --> F[base]
subgraph "rule package"
C1[join_reorder.go]
C2[join_reorder_dp.go]
C3[join_reorder_greedy.go]
end
subgraph "util package"
D1[join_group.go]
D2[join_solver_base.go]
D3[join_cost.go]
end
```
### 5. Rule Registration
Register the new rule in `pkg/planner/core/rule/logical_rules.go`:
```go
const (
FlagJoinReorder uint64 = 1 << iota
// ... other rule flags
)
func NewJoinReorderRule() LogicalOptRule {
return &JoinReorderRule{}
}
```
feature impl: https://github.com/undertaker86001/tidb/commit/8ee6d0357253466770377b1508c04682eb0902e2
Contributor guide
Assessment
This issue has not been assessed yet.