pingcap / pingcap/tidb

refactor: join_recorder refacor plan

Open
#62,866 3 comments 2 reactions 0 assignees View on GitHub
contribution first-time-contributor type/enhancement
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.