From ff996e7068db04bc162bc2400a41cca51917bbbe Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Tue, 30 May 2023 14:46:41 +0800 Subject: [PATCH] planner: allow to use dynamic mode to access partitioning tables without global-stats (#44264) close pingcap/tidb#44262 --- planner/core/casetest/integration_test.go | 29 +++++++++++++++++++++++ planner/core/logical_plan_builder.go | 8 ++++++- sessionctx/variable/session.go | 16 +++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/planner/core/casetest/integration_test.go b/planner/core/casetest/integration_test.go index a81f3ad20e..f31d830cfa 100644 --- a/planner/core/casetest/integration_test.go +++ b/planner/core/casetest/integration_test.go @@ -3513,3 +3513,32 @@ func TestFixControl(t *testing.T) { require.Equal(t, output[i].Variable, 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(t2_part) */ or /*+ TIDB_INLJ(t2_part) */ 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 +} diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index e0ed32f713..817e1487a2 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -4584,8 +4584,14 @@ 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 3a5be16901..bfcd4d17ce 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -1485,6 +1485,22 @@ type SessionVars struct { HypoIndexes map[string]map[string]map[string]*model.IndexInfo // dbName -> tblName -> idxName -> idxInfo } +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 +} + // planReplayerSessionFinishedTaskKeyLen is used to control the max size for the finished plan replayer task key in session // in order to control the used memory const planReplayerSessionFinishedTaskKeyLen = 128