Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

planner: distinguish the source of join hint information (#41440) #45642

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions planner/core/exhaust_physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -2010,6 +2010,9 @@ func (p *LogicalJoin) canPushToCop(storeTp kv.StoreType) bool {
// If the hint is not matched, it will get other candidates.
// If the hint is not figured, we will pick all candidates.
func (p *LogicalJoin) exhaustPhysicalPlans(prop *property.PhysicalProperty) ([]PhysicalPlan, bool, error) {
if p.ctx.GetSessionVars().EnableAdvancedJoinHint {
p.setPreferredJoinType4PhysicalOp()
}
failpoint.Inject("MockOnlyEnableIndexHashJoin", func(val failpoint.Value) {
if val.(bool) && !p.ctx.GetSessionVars().InRestrictedSQL {
indexJoins, _ := p.tryToGetIndexJoin(prop)
Expand Down
18 changes: 18 additions & 0 deletions planner/core/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,24 @@ func TestKeepOrderHint(t *testing.T) {
}
}

func TestSplitJoinHint(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec(`create table t(a int, b int, index idx_a(a), index idx_b(b));`)

tk.MustExec(`set @@tidb_opt_advanced_join_hint=0`)
tk.MustExec("select /*+ hash_join(t1) merge_join(t2) */ * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.a=t3.a")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 Join hints are conflict, you can only specify one type of join"))

tk.MustExec(`set @@tidb_opt_advanced_join_hint=1`)
tk.MustExec("select /*+ hash_join(t1) merge_join(t2) */ * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.a=t3.a")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 Join hints conflict after join reorder phase, you can only specify one type of join"))

tk.MustExec(`set @@tidb_opt_advanced_join_hint=0`)
}

func TestKeepOrderHintWithBinding(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
Expand Down
119 changes: 114 additions & 5 deletions planner/core/logical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,49 +591,89 @@ func (p *LogicalJoin) setPreferredJoinTypeAndOrder(hintInfo *tableHintInfo) {

lhsAlias := extractTableAlias(p.children[0], p.blockOffset)
rhsAlias := extractTableAlias(p.children[1], p.blockOffset)
if hintInfo.ifPreferMergeJoin(lhsAlias, rhsAlias) {
if hintInfo.ifPreferMergeJoin(lhsAlias) {
p.preferJoinType |= preferMergeJoin
p.leftPreferJoinType |= preferMergeJoin
}
if hintInfo.ifPreferBroadcastJoin(lhsAlias, rhsAlias) {
if hintInfo.ifPreferMergeJoin(rhsAlias) {
p.preferJoinType |= preferMergeJoin
p.rightPreferJoinType |= preferMergeJoin
}
if hintInfo.ifPreferBroadcastJoin(lhsAlias) {
p.preferJoinType |= preferBCJoin
p.leftPreferJoinType |= preferBCJoin
}
if hintInfo.ifPreferShuffleJoin(lhsAlias, rhsAlias) {
if hintInfo.ifPreferBroadcastJoin(rhsAlias) {
p.preferJoinType |= preferBCJoin
p.rightPreferJoinType |= preferBCJoin
}
if hintInfo.ifPreferShuffleJoin(lhsAlias) {
p.preferJoinType |= preferShuffleJoin
p.leftPreferJoinType |= preferShuffleJoin
}
if hintInfo.ifPreferHashJoin(lhsAlias, rhsAlias) {
if hintInfo.ifPreferShuffleJoin(rhsAlias) {
p.preferJoinType |= preferShuffleJoin
p.rightPreferJoinType |= preferShuffleJoin
}
if hintInfo.ifPreferHashJoin(lhsAlias) {
p.preferJoinType |= preferHashJoin
p.leftPreferJoinType |= preferHashJoin
}
if hintInfo.ifPreferHashJoin(rhsAlias) {
p.preferJoinType |= preferHashJoin
p.rightPreferJoinType |= preferHashJoin
}
if hintInfo.ifPreferINLJ(lhsAlias) {
p.preferJoinType |= preferLeftAsINLJInner
p.leftPreferJoinType |= preferINLJ
}
if hintInfo.ifPreferINLJ(rhsAlias) {
p.preferJoinType |= preferRightAsINLJInner
p.rightPreferJoinType |= preferINLJ
}
if hintInfo.ifPreferINLHJ(lhsAlias) {
p.preferJoinType |= preferLeftAsINLHJInner
p.leftPreferJoinType |= preferINLHJ
}
if hintInfo.ifPreferINLHJ(rhsAlias) {
p.preferJoinType |= preferRightAsINLHJInner
p.rightPreferJoinType |= preferINLHJ
}
if hintInfo.ifPreferINLMJ(lhsAlias) {
p.preferJoinType |= preferLeftAsINLMJInner
p.leftPreferJoinType |= preferINLMJ
}
if hintInfo.ifPreferINLMJ(rhsAlias) {
p.preferJoinType |= preferRightAsINLMJInner
p.rightPreferJoinType |= preferINLMJ
}
if hintInfo.ifPreferHJBuild(lhsAlias) {
p.preferJoinType |= preferLeftAsHJBuild
p.leftPreferJoinType |= preferHJBuild
}
if hintInfo.ifPreferHJBuild(rhsAlias) {
p.preferJoinType |= preferRightAsHJBuild
p.rightPreferJoinType |= preferHJBuild
}
if hintInfo.ifPreferHJProbe(lhsAlias) {
p.preferJoinType |= preferLeftAsHJProbe
p.leftPreferJoinType |= preferHJProbe
}
if hintInfo.ifPreferHJProbe(rhsAlias) {
p.preferJoinType |= preferRightAsHJProbe
p.rightPreferJoinType |= preferHJProbe
}
if containDifferentJoinTypes(p.preferJoinType) {
hasConflict := false
if !p.ctx.GetSessionVars().EnableAdvancedJoinHint || p.ctx.GetSessionVars().StmtCtx.StraightJoinOrder {
if containDifferentJoinTypes(p.preferJoinType) {
hasConflict = true
}
} else if p.ctx.GetSessionVars().EnableAdvancedJoinHint {
if containDifferentJoinTypes(p.leftPreferJoinType) || containDifferentJoinTypes(p.rightPreferJoinType) {
hasConflict = true
}
}
if hasConflict {
errMsg := "Join hints are conflict, you can only specify one type of join"
warning := ErrInternal.GenWithStack(errMsg)
p.ctx.GetSessionVars().StmtCtx.AppendWarning(warning)
Expand All @@ -649,6 +689,75 @@ func (p *LogicalJoin) setPreferredJoinTypeAndOrder(hintInfo *tableHintInfo) {
}
}

// setPreferredJoinType4PhysicalOp generates hint information for the logicalJoin based on the hint information of its left and right children.
// This information is used for selecting the physical operator.
func (p *LogicalJoin) setPreferredJoinType4PhysicalOp() {
leftHintInfo := p.leftPreferJoinType
rightHintInfo := p.rightPreferJoinType
if leftHintInfo == 0 && rightHintInfo == 0 {
return
}
if leftHintInfo != 0 && rightHintInfo != 0 && leftHintInfo != rightHintInfo {
// The hint information on the left and right child nodes is different. It causes the conflict.
errMsg := "Join hints conflict after join reorder phase, you can only specify one type of join"
warning := ErrInternal.GenWithStack(errMsg)
p.ctx.GetSessionVars().StmtCtx.AppendWarning(warning)
p.preferJoinType = 0
} else {
if leftHintInfo != 0 {
p.preferJoinType = leftHintInfo
} else {
p.preferJoinType = rightHintInfo
}
preferJoinType := uint(0)
// Some implementations of physical operators are dependent on the direction,
// and adjustments need to be made based on the direction.
switch p.preferJoinType {
case preferINLJ:
if leftHintInfo != 0 {
preferJoinType |= preferLeftAsINLJInner
}
if rightHintInfo != 0 {
preferJoinType |= preferRightAsINLJInner
}
case preferINLHJ:
if leftHintInfo != 0 {
preferJoinType |= preferLeftAsINLHJInner
}
if rightHintInfo != 0 {
preferJoinType |= preferLeftAsINLHJInner
}
case preferINLMJ:
if leftHintInfo != 0 {
preferJoinType |= preferLeftAsINLMJInner
}
if rightHintInfo != 0 {
preferJoinType |= preferLeftAsINLMJInner
}
case preferHJBuild:
if leftHintInfo != 0 {
preferJoinType |= preferLeftAsHJBuild
}
if rightHintInfo != 0 {
preferJoinType |= preferLeftAsHJBuild
}
case preferHJProbe:
if leftHintInfo != 0 {
preferJoinType |= preferLeftAsHJProbe
}
if rightHintInfo != 0 {
preferJoinType |= preferLeftAsHJProbe
}
default:
preferJoinType = p.preferJoinType
}
p.preferJoinType = preferJoinType
}
// Clear information from left and right child nodes to prevent multiple calls to this function.
p.leftPreferJoinType = 0
p.rightPreferJoinType = 0
}

func (ds *DataSource) setPreferredStoreType(hintInfo *tableHintInfo) {
if hintInfo == nil {
return
Expand Down
30 changes: 21 additions & 9 deletions planner/core/logical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,21 +110,31 @@ func (tp JoinType) String() string {
}

const (
preferLeftAsINLJInner uint = 1 << iota
// Hint flag for join
preferINLJ uint = 1 << iota
preferINLHJ
preferINLMJ
preferHJBuild
preferHJProbe
preferHashJoin
preferMergeJoin
preferBCJoin
preferShuffleJoin
preferRewriteSemiJoin

// Hint flag to specify the join with direction
preferLeftAsINLJInner
preferRightAsINLJInner
preferLeftAsINLHJInner
preferRightAsINLHJInner
preferLeftAsINLMJInner
preferRightAsINLMJInner
preferHashJoin
preferLeftAsHJBuild
preferRightAsHJBuild
preferLeftAsHJProbe
preferRightAsHJProbe
preferMergeJoin
preferBCJoin
preferShuffleJoin
preferRewriteSemiJoin

// Hint flag for Agg
preferHashAgg
preferStreamAgg
preferMPP1PhaseAgg
Expand All @@ -146,9 +156,11 @@ type LogicalJoin struct {
StraightJoin bool

// hintInfo stores the join algorithm hint information specified by client.
hintInfo *tableHintInfo
preferJoinType uint
preferJoinOrder bool
hintInfo *tableHintInfo
preferJoinType uint
preferJoinOrder bool
leftPreferJoinType uint
rightPreferJoinType uint

EqualConditions []*expression.ScalarFunction
NAEQConditions []*expression.ScalarFunction
Expand Down
3 changes: 3 additions & 0 deletions sessionctx/variable/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,9 @@ type SessionVars struct {
// EnableReuseCheck indicates request chunk whether use chunk alloc
EnableReuseCheck bool

// EnableAdvancedJoinHint indicates whether the join method hint is compatible with join order hint.
EnableAdvancedJoinHint bool

// preuseChunkAlloc indicates whether pre statement use chunk alloc
// like select @@last_sql_use_alloc
preUseChunkAlloc bool
Expand Down
4 changes: 4 additions & 0 deletions sessionctx/variable/sysvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,10 @@ var defaultSysVars = []*SysVar{
s.RangeMaxSize = TidbOptInt64(val, DefTiDBOptRangeMaxSize)
return nil
}},
{Scope: ScopeGlobal | ScopeSession, Name: TiDBOptAdvancedJoinHint, Value: BoolToOnOff(DefTiDBOptAdvancedJoinHint), Type: TypeBool, SetSession: func(s *SessionVars, val string) error {
s.EnableAdvancedJoinHint = TiDBOptOn(val)
return nil
}},
{Scope: ScopeGlobal | ScopeSession, Name: TiDBAnalyzePartitionConcurrency, Value: strconv.FormatInt(DefTiDBAnalyzePartitionConcurrency, 10),
MinValue: 1, MaxValue: uint64(config.GetGlobalConfig().Performance.AnalyzePartitionConcurrencyQuota), SetSession: func(s *SessionVars, val string) error {
s.AnalyzePartitionConcurrency = int(TidbOptInt64(val, DefTiDBAnalyzePartitionConcurrency))
Expand Down
4 changes: 4 additions & 0 deletions sessionctx/variable/tidb_vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,9 @@ const (
// limit for ranges.
TiDBOptRangeMaxSize = "tidb_opt_range_max_size"

// TiDBOptAdvancedJoinHint indicates whether the join method hint is compatible with join order hint.
TiDBOptAdvancedJoinHint = "tidb_opt_advanced_join_hint"

// TiDBAnalyzePartitionConcurrency indicates concurrency for save/read partitions stats in Analyze
TiDBAnalyzePartitionConcurrency = "tidb_analyze_partition_concurrency"
// TiDBMergePartitionStatsConcurrency indicates the concurrency when merge partition stats into global stats
Expand Down Expand Up @@ -1131,6 +1134,7 @@ const (
DefTiDBSysProcScanConcurrency = 1
DefTiDBRcWriteCheckTs = false
DefTiDBForeignKeyChecks = false
DefTiDBOptAdvancedJoinHint = false
DefTiDBAnalyzePartitionConcurrency = 1
DefTiDBOptRangeMaxSize = 64 * int64(size.MB) // 64 MB
DefTiDBCostModelVer = 2
Expand Down