From 129de9f5e3dd73db301414f8ac59a68e8446524e Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Wed, 31 May 2023 10:30:42 +0800 Subject: [PATCH] planner: allow to use dynamic mode to access partitioning tables without global-stats (#44284) close pingcap/tidb#44262 --- planner/core/integration_test.go | 29 ++++++++++++++++++++++++++++ planner/core/logical_plan_builder.go | 7 ++++++- sessionctx/variable/session.go | 19 ++++++++++++++++++ sessionctx/variable/sysvar.go | 27 ++++++++++++++++++++++++++ sessionctx/variable/tidb_vars.go | 3 +++ 5 files changed, 84 insertions(+), 1 deletion(-) diff --git a/planner/core/integration_test.go b/planner/core/integration_test.go index ae06750fe9624..8f2cc79867c97 100644 --- a/planner/core/integration_test.go +++ b/planner/core/integration_test.go @@ -7933,6 +7933,35 @@ func TestExplainAnalyzeDMLCommit(t *testing.T) { tk.MustQuery("select * from t").Check(testkit.Rows()) } +func TestFixControl44262(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.MustExec(`set tidb_partition_prune_mode='dynamic'`) + tk.MustExec(`create table t1 (a int, b int)`) + tk.MustExec(`create table t2_part (a int, b int, key(a)) partition by hash(a) partitions 4`) + + testJoin := func(q, join string) { + found := false + for _, x := range tk.MustQuery(`explain ` + q).Rows() { + if strings.Contains(x[0].(string), join) { + found = true + } + } + if !found { + t.Fatal(q, join) + } + } + + testJoin(`select /*+ TIDB_INLJ(t2_part@sel_2) */ * from t1 where t1.b<10 and not exists (select 1 from t2_part where t1.a=t2_part.a and t2_part.b<20)`, "HashJoin") + tk.MustQuery(`show warnings`).Sort().Check(testkit.Rows( + `Warning 1105 disable dynamic pruning due to t2_part has no global stats`, + `Warning 1815 Optimizer Hint INL_JOIN or TIDB_INLJ is inapplicable`)) + tk.MustExec(`set @@tidb_opt_fix_control = "44262:ON"`) + testJoin(`select /*+ TIDB_INLJ(t2_part@sel_2) */ * from t1 where t1.b<10 and not exists (select 1 from t2_part where t1.a=t2_part.a and t2_part.b<20)`, "IndexJoin") + tk.MustQuery(`show warnings`).Sort().Check(testkit.Rows()) // no warning +} + func TestIndexJoinRangeFallback(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index aa16c1d83bc71..35abfc1d25a38 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -4464,8 +4464,13 @@ func (b *PlanBuilder) buildDataSource(ctx context.Context, tn *ast.TableName, as tblStats := h.GetTableStats(tableInfo) isDynamicEnabled := b.ctx.GetSessionVars().IsDynamicPartitionPruneEnabled() globalStatsReady := tblStats.IsInitialized() + allowDynamicWithoutStats := false + fixValue, ok := b.ctx.GetSessionVars().GetOptimizerFixControlValue(variable.TiDBOptFixControl44262) + if ok && variable.TiDBOptOn(fixValue) { + allowDynamicWithoutStats = true + } // If dynamic partition prune isn't enabled or global stats is not ready, we won't enable dynamic prune mode in query - usePartitionProcessor := !isDynamicEnabled || !globalStatsReady + usePartitionProcessor := !isDynamicEnabled || (!globalStatsReady && !allowDynamicWithoutStats) failpoint.Inject("forceDynamicPrune", func(val failpoint.Value) { if val.(bool) { diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index cbed5b3863eaf..3e170f6655f83 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -1325,6 +1325,25 @@ type SessionVars struct { // EnableINLJoinInnerMultiPattern indicates whether enable multi pattern for index join inner side EnableINLJoinInnerMultiPattern bool + + // OptimizerFixControl control some details of the optimizer behavior through the tidb_opt_fix_control variable. + OptimizerFixControl map[uint64]string +} + +var ( + // variables below are for the optimizer fix control. + + // TiDBOptFixControl44262 controls whether to allow to use dynamic-mode to access partitioning tables without global-stats (#44262). + TiDBOptFixControl44262 uint64 = 44262 +) + +// GetOptimizerFixControlValue returns the specified value of the optimizer fix control. +func (s *SessionVars) GetOptimizerFixControlValue(key uint64) (value string, exist bool) { + if s.OptimizerFixControl == nil { + return "", false + } + value, exist = s.OptimizerFixControl[key] + return } // GetNewChunkWithCapacity Attempt to request memory from the chunk pool diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index dc95e507a7302..c33401c5d83ba 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -2260,6 +2260,33 @@ var defaultSysVars = []*SysVar{ return BoolToOnOff(s.EnableINLJoinInnerMultiPattern), nil }, }, + {Scope: ScopeGlobal | ScopeSession, Name: TiDBOptFixControl, Value: "", Type: TypeStr, IsHintUpdatable: true, + SetSession: func(s *SessionVars, val string) error { + newMap := make(map[uint64]string) + for _, singleFixCtrl := range strings.Split(val, ",") { + if len(singleFixCtrl) == 0 { + continue + } + colonIdx := strings.Index(singleFixCtrl, ":") + if colonIdx < 0 { + return errors.New("invalid fix control: colon not found") + } + k := strings.TrimSpace(singleFixCtrl[0:colonIdx]) + v := strings.TrimSpace(singleFixCtrl[colonIdx+1:]) + num, err := strconv.ParseUint(k, 10, 64) + if err != nil { + return err + } + originalV, ok := newMap[num] + if ok { + s.StmtCtx.AppendWarning( + errors.Errorf("found repeated fix control: %d:%s is overwritten with %s", num, originalV, v)) + } + newMap[num] = v + } + s.OptimizerFixControl = newMap + return nil + }}, } // FeedbackProbability points to the FeedbackProbability in statistics package. diff --git a/sessionctx/variable/tidb_vars.go b/sessionctx/variable/tidb_vars.go index c1f82a93cd86f..7c62b07abe10f 100644 --- a/sessionctx/variable/tidb_vars.go +++ b/sessionctx/variable/tidb_vars.go @@ -795,6 +795,9 @@ const ( // TiDBEnableINLJoinInnerMultiPattern indicates whether enable multi pattern for inner side of inl join TiDBEnableINLJoinInnerMultiPattern = "tidb_enable_inl_join_inner_multi_pattern" + + // TiDBOptFixControl makes the user able to control some details of the optimizer behavior. + TiDBOptFixControl = "tidb_opt_fix_control" ) // TiDB vars that have only global scope