From 88fedf18c18915bc99dd2ef5bb961b15cb0d67a1 Mon Sep 17 00:00:00 2001 From: hailanwhu <2013301500074@whu.edu.cn> Date: Wed, 27 Nov 2019 18:44:25 +1100 Subject: [PATCH] planner: support a hint to force using a IndexMerge path (#12843) --- cmd/explaintest/r/explain_indexmerge.result | 30 ++++++++++ cmd/explaintest/t/explain_indexmerge.test | 11 ++++ executor/executor.go | 3 +- planner/core/find_best_task.go | 2 + planner/core/hints.go | 20 +++++++ planner/core/logical_plan_builder.go | 26 ++++++++- planner/core/logical_plans.go | 3 +- planner/core/physical_plan_test.go | 50 ++++++++++++++++ planner/core/planbuilder.go | 1 + planner/core/stats.go | 64 +++++++++++++++++---- planner/core/stringer.go | 9 +++ planner/core/testdata/plan_suite_in.json | 9 +++ planner/core/testdata/plan_suite_out.json | 29 ++++++++++ session/session_test.go | 4 +- sessionctx/stmtctx/stmtctx.go | 3 +- sessionctx/variable/session.go | 5 +- 16 files changed, 247 insertions(+), 22 deletions(-) diff --git a/cmd/explaintest/r/explain_indexmerge.result b/cmd/explaintest/r/explain_indexmerge.result index e903a02a14961..fb26773144db6 100644 --- a/cmd/explaintest/r/explain_indexmerge.result +++ b/cmd/explaintest/r/explain_indexmerge.result @@ -81,3 +81,33 @@ label = "cop" "IndexMerge_12" -> "Selection_11" } +set session tidb_enable_index_merge = off; +explain select /*+ use_index_merge(t, tb, tc) */ * from t where b < 50 or c < 5000000; +id count task operator info +IndexMerge_8 5000000.00 root +├─IndexScan_5 49.00 cop[tikv] table:t, index:b, range:[-inf,50), keep order:false +├─IndexScan_6 4999999.00 cop[tikv] table:t, index:c, range:[-inf,5000000), keep order:false +└─TableScan_7 5000000.00 cop[tikv] table:t, keep order:false +explain select /*+ use_index_merge(t, tb, tc) */ * from t where (b < 10000 or c < 10000) and (a < 10 or d < 10) and f < 10; +id count task operator info +IndexMerge_9 0.00 root +├─IndexScan_5 9999.00 cop[tikv] table:t, index:b, range:[-inf,10000), keep order:false +├─IndexScan_6 9999.00 cop[tikv] table:t, index:c, range:[-inf,10000), keep order:false +└─Selection_8 0.00 cop[tikv] lt(Column#6, 10), or(lt(Column#1, 10), lt(Column#4, 10)) + └─TableScan_7 19998.00 cop[tikv] table:t, keep order:false +explain select /*+ use_index_merge(t, tb) */ * from t where b < 50 or c < 5000000; +id count task operator info +TableReader_7 4000000.00 root data:Selection_6 +└─Selection_6 4000000.00 cop[tikv] or(lt(Column#2, 50), lt(Column#3, 5000000)) + └─TableScan_5 5000000.00 cop[tikv] table:t, range:[-inf,+inf], keep order:false +explain select /*+ no_index_merge(), use_index_merge(t, tb, tc) */ * from t where b < 50 or c < 5000000; +id count task operator info +TableReader_7 4000000.00 root data:Selection_6 +└─Selection_6 4000000.00 cop[tikv] or(lt(Column#2, 50), lt(Column#3, 5000000)) + └─TableScan_5 5000000.00 cop[tikv] table:t, range:[-inf,+inf], keep order:false +explain select /*+ use_index_merge(t, primary, tb) */ * from t where a < 50 or b < 5000000; +id count task operator info +IndexMerge_8 5000000.00 root +├─TableScan_5 49.00 cop[tikv] table:t, range:[-inf,50), keep order:false +├─IndexScan_6 4999999.00 cop[tikv] table:t, index:b, range:[-inf,5000000), keep order:false +└─TableScan_7 5000000.00 cop[tikv] table:t, keep order:false diff --git a/cmd/explaintest/t/explain_indexmerge.test b/cmd/explaintest/t/explain_indexmerge.test index 507e7559a0979..7989c22426ddb 100644 --- a/cmd/explaintest/t/explain_indexmerge.test +++ b/cmd/explaintest/t/explain_indexmerge.test @@ -3,8 +3,10 @@ create table t (a int primary key, b int, c int, d int, e int, f int); create index tb on t (b); create index tc on t (c); create index td on t (d); +# generate a, b, c, d, e, f from 0 to 5000000 and a = b = c = d = e = f load stats 's/explain_indexmerge_stats_t.json'; set session tidb_enable_index_merge = on; +# choose the best plan based on cost explain select * from t where a < 50 or b < 50; explain select * from t where (a < 50 or b < 50) and f > 100; explain select * from t where a < 50 or b < 5000000; @@ -13,3 +15,12 @@ explain select * from t where b < 50 or c < 5000000; explain select * from t where a < 50 or b < 50 or c < 50; explain select * from t where (b < 10000 or c < 10000) and (a < 10 or d < 10) and f < 10; explain format="dot" select * from t where (a < 50 or b < 50) and f > 100; +set session tidb_enable_index_merge = off; +# be forced to use IndexMerge +explain select /*+ use_index_merge(t, tb, tc) */ * from t where b < 50 or c < 5000000; +explain select /*+ use_index_merge(t, tb, tc) */ * from t where (b < 10000 or c < 10000) and (a < 10 or d < 10) and f < 10; +explain select /*+ use_index_merge(t, tb) */ * from t where b < 50 or c < 5000000; +# no_index_merge hint +explain select /*+ no_index_merge(), use_index_merge(t, tb, tc) */ * from t where b < 50 or c < 5000000; +# tableScan can be a partial path to fetch handle +explain select /*+ use_index_merge(t, primary, tb) */ * from t where a < 50 or b < 5000000; diff --git a/executor/executor.go b/executor/executor.go index 16d80817410f6..ff4acd00a981d 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -1468,8 +1468,7 @@ func handleStmtHints(hints []*ast.TableOptimizerHint) (stmtHints stmtctx.StmtHin warn := errors.New("There are multiple NO_INDEX_MERGE hints, only the last one will take effect") warns = append(warns, warn) } - stmtHints.HasEnableIndexMergeHint = true - stmtHints.EnableIndexMerge = false + stmtHints.NoIndexMergeHint = true } // Handle READ_CONSISTENT_REPLICA if readReplicaHintCnt != 0 { diff --git a/planner/core/find_best_task.go b/planner/core/find_best_task.go index ab9be912ec8de..886064fea4f6f 100644 --- a/planner/core/find_best_task.go +++ b/planner/core/find_best_task.go @@ -408,6 +408,7 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty) (t task, err t = invalidTask candidates := ds.skylinePruning(prop) + for _, candidate := range candidates { path := candidate.path if path.partialIndexPaths != nil { @@ -450,6 +451,7 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty) (t task, err t = idxTask } } + return } diff --git a/planner/core/hints.go b/planner/core/hints.go index 6def3a392ff7d..490f06ab9b107 100644 --- a/planner/core/hints.go +++ b/planner/core/hints.go @@ -300,6 +300,26 @@ func genHintsFromPhysicalPlan(p PhysicalPlan, nodeType nodeType) (res []*ast.Tab Tables: []ast.HintTable{{DBName: index.DBName, TableName: getTableName(index.Table.Name, index.TableAsName)}}, Indexes: []model.CIStr{index.Index.Name}, }) + case *PhysicalIndexMergeReader: + Indexs := make([]model.CIStr, 0, 2) + var tableName model.CIStr + var tableAsName *model.CIStr + for _, partialPlan := range pp.PartialPlans { + if index, ok := partialPlan[0].(*PhysicalIndexScan); ok { + Indexs = append(Indexs, index.Index.Name) + tableName = index.Table.Name + tableAsName = index.TableAsName + } else { + indexName := model.NewCIStr("PRIMARY") + Indexs = append(Indexs, indexName) + } + } + res = append(res, &ast.TableOptimizerHint{ + QBName: generateQBName(nodeType, pp.blockOffset), + HintName: model.NewCIStr(HintIndexMerge), + Tables: []ast.HintTable{{TableName: getTableName(tableName, tableAsName)}}, + Indexes: Indexs, + }) case *PhysicalHashAgg: res = append(res, &ast.TableOptimizerHint{ QBName: generateQBName(nodeType, pp.blockOffset), diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index e724fa1a8b086..2b262d45dfe1d 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -81,6 +81,8 @@ const ( HintTiFlash = "tiflash" // HintTiKV is a label represents the tikv storage type. HintTiKV = "tikv" + // HintIndexMerge is a hint to enforce using some indexes at the same time. + HintIndexMerge = "use_index_merge" ) const ( @@ -2127,7 +2129,7 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, nodeType n hints = b.hintProcessor.getCurrentStmtHints(hints, nodeType, currentLevel) var ( sortMergeTables, INLJTables, INLHJTables, INLMJTables, hashJoinTables []hintTableInfo - indexHintList []indexHintInfo + indexHintList, indexMergeHintList []indexHintInfo tiflashTables, tikvTables []hintTableInfo aggHints aggHintInfo ) @@ -2188,6 +2190,17 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, nodeType n if hint.StoreType.L == HintTiKV { tikvTables = tableNames2HintTableInfo(b.ctx, hint.Tables, b.hintProcessor, nodeType, currentLevel) } + case HintIndexMerge: + if len(hint.Tables) != 0 { + indexMergeHintList = append(indexMergeHintList, indexHintInfo{ + tblName: hint.Tables[0].TableName, + indexHint: &ast.IndexHint{ + IndexNames: hint.Indexes, + HintType: ast.HintUse, + HintScope: ast.HintForScan, + }, + }) + } default: // ignore hints that not implemented } @@ -2200,6 +2213,7 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, nodeType n tiflashTables: tiflashTables, tikvTables: tikvTables, aggHints: aggHints, + indexMergeHintList: indexMergeHintList, }) } @@ -2529,6 +2543,15 @@ func (b *PlanBuilder) buildDataSource(ctx context.Context, tn *ast.TableName, as statisticTable = getStatsTable(b.ctx, tbl.Meta(), tbl.Meta().ID) } + // extract the IndexMergeHint + var indexMergeHints []*ast.IndexHint + if hints := b.TableHints(); hints != nil { + for _, hint := range hints.indexMergeHintList { + if hint.tblName.L == tblName.L { + indexMergeHints = append(indexMergeHints, hint.indexHint) + } + } + } ds := DataSource{ DBName: dbName, TableAsName: asName, @@ -2536,6 +2559,7 @@ func (b *PlanBuilder) buildDataSource(ctx context.Context, tn *ast.TableName, as tableInfo: tableInfo, statisticTable: statisticTable, indexHints: tn.IndexHints, + indexMergeHints: indexMergeHints, possibleAccessPaths: possiblePaths, Columns: make([]*model.ColumnInfo, 0, len(columns)), partitionNames: tn.PartitionNames, diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index 1bd9b02872160..a9d4c656db41e 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -388,7 +388,8 @@ type DataSource struct { DBName model.CIStr TableAsName *model.CIStr - + // indexMergeHints are the hint for indexmerge. + indexMergeHints []*ast.IndexHint // pushedDownConds are the conditions that will be pushed down to coprocessor. pushedDownConds []expression.Expression // allConds contains all the filters on this table. For now it's maintained diff --git a/planner/core/physical_plan_test.go b/planner/core/physical_plan_test.go index 9ebd2bf29e6ba..5ddd92ca54124 100644 --- a/planner/core/physical_plan_test.go +++ b/planner/core/physical_plan_test.go @@ -21,6 +21,7 @@ import ( "github.com/pingcap/parser/model" "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/domain" + "github.com/pingcap/tidb/executor" "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/planner" @@ -966,6 +967,55 @@ func (s *testPlanSuite) TestIndexHint(c *C) { } } +func (s *testPlanSuite) TestIndexMergeHint(c *C) { + defer testleak.AfterTest(c)() + store, dom, err := newStoreWithBootstrap() + c.Assert(err, IsNil) + defer func() { + dom.Close() + store.Close() + }() + se, err := session.CreateSession4Test(store) + c.Assert(err, IsNil) + _, err = se.Execute(context.Background(), "use test") + c.Assert(err, IsNil) + + var input []string + var output []struct { + SQL string + Best string + HasWarn bool + Hints string + } + s.testData.GetTestCases(c, &input, &output) + ctx := context.Background() + for i, test := range input { + comment := Commentf("case:%v sql:%s", i, test) + se.GetSessionVars().StmtCtx.SetWarnings(nil) + stmt, err := s.ParseOneStmt(test, "", "") + c.Assert(err, IsNil, comment) + sctx := se.(sessionctx.Context) + err = executor.ResetContextOfStmt(sctx, stmt) + c.Assert(err, IsNil) + p, _, err := planner.Optimize(ctx, se, stmt, s.is) + c.Assert(err, IsNil) + s.testData.OnRecord(func() { + output[i].SQL = test + output[i].Best = core.ToString(p) + output[i].HasWarn = len(se.GetSessionVars().StmtCtx.GetWarnings()) > 0 + output[i].Hints = core.GenHintsFromPhysicalPlan(p) + }) + c.Assert(core.ToString(p), Equals, output[i].Best, comment) + warnings := se.GetSessionVars().StmtCtx.GetWarnings() + if output[i].HasWarn { + c.Assert(warnings, HasLen, 1, comment) + } else { + c.Assert(warnings, HasLen, 0, comment) + } + c.Assert(core.GenHintsFromPhysicalPlan(p), Equals, output[i].Hints, comment) + } +} + func (s *testPlanSuite) TestQueryBlockHint(c *C) { defer testleak.AfterTest(c)() store, dom, err := newStoreWithBootstrap() diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 8c5488784b530..0f1e35e50aa2b 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -70,6 +70,7 @@ type tableHintInfo struct { tiflashTables []hintTableInfo tikvTables []hintTableInfo aggHints aggHintInfo + indexMergeHintList []indexHintInfo } type hintTableInfo struct { diff --git a/planner/core/stats.go b/planner/core/stats.go index ad97df5283a43..6cceed0abf0b4 100644 --- a/planner/core/stats.go +++ b/planner/core/stats.go @@ -16,6 +16,7 @@ package core import ( "math" + "github.com/pingcap/errors" "github.com/pingcap/parser/ast" "github.com/pingcap/parser/mysql" "github.com/pingcap/tidb/expression" @@ -196,21 +197,42 @@ func (ds *DataSource) DeriveStats(childStats []*property.StatsInfo, selfSchema * } } // Consider the IndexMergePath. Now, we just generate `IndexMergePath` in DNF case. - if len(ds.pushedDownConds) > 0 && len(ds.possibleAccessPaths) > 1 && ds.ctx.GetSessionVars().GetEnableIndexMerge() { - needConsiderIndexMerge := true - for i := 1; i < len(ds.possibleAccessPaths); i++ { - if len(ds.possibleAccessPaths[i].accessConds) != 0 { - needConsiderIndexMerge = false - break - } - } - if needConsiderIndexMerge { - ds.generateIndexMergeOrPaths() + isPossibleIdxMerge := len(ds.pushedDownConds) > 0 && len(ds.possibleAccessPaths) > 1 + sessionAndStmtPermission := (ds.ctx.GetSessionVars().GetEnableIndexMerge() || ds.indexMergeHints != nil) && !ds.ctx.GetSessionVars().StmtCtx.NoIndexMergeHint + // If there is an index path, we current do not consider `IndexMergePath`. + needConsiderIndexMerge := true + for i := 1; i < len(ds.possibleAccessPaths); i++ { + if len(ds.possibleAccessPaths[i].accessConds) != 0 { + needConsiderIndexMerge = false + break } } + if isPossibleIdxMerge && sessionAndStmtPermission && needConsiderIndexMerge { + ds.generateAndPruneIndexMergePath() + } else if ds.indexMergeHints != nil { + ds.indexMergeHints = nil + ds.ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("IndexMerge is inapplicable or disabled")) + } return ds.stats, nil } +func (ds *DataSource) generateAndPruneIndexMergePath() { + regularPathCount := len(ds.possibleAccessPaths) + ds.generateIndexMergeOrPaths() + // If without hints, it means that `enableIndexMerge` is true + if ds.indexMergeHints == nil { + return + } + // With hints and without generated IndexMerge paths + if regularPathCount == len(ds.possibleAccessPaths) { + ds.indexMergeHints = nil + ds.ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("IndexMerge is inapplicable or disabled")) + return + } + // Do not need to consider the regular paths in find_best_task(). + ds.possibleAccessPaths = ds.possibleAccessPaths[regularPathCount:] +} + // DeriveStats implement LogicalPlan DeriveStats interface. func (ts *TableScan) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, childSchema []*expression.Schema) (_ *property.StatsInfo, err error) { // PushDownNot here can convert query 'not (a != 1)' to 'a = 1'. @@ -272,12 +294,31 @@ func (ds *DataSource) generateIndexMergeOrPaths() { } } +// isInIndexMergeHints checks whether current index or primary key is in IndexMerge hints. +func (ds *DataSource) isInIndexMergeHints(name string) bool { + if ds.indexMergeHints == nil || + (len(ds.indexMergeHints) == 1 && ds.indexMergeHints[0].IndexNames == nil) { + return true + } + for _, hint := range ds.indexMergeHints { + for _, index := range hint.IndexNames { + if name == index.L { + return true + } + } + } + return false +} + // accessPathsForConds generates all possible index paths for conditions. func (ds *DataSource) accessPathsForConds(conditions []expression.Expression, usedIndexCount int) []*accessPath { var results = make([]*accessPath, 0, usedIndexCount) for i := 0; i < usedIndexCount; i++ { path := &accessPath{} if ds.possibleAccessPaths[i].isTablePath { + if !ds.isInIndexMergeHints("primary") { + continue + } path.isTablePath = true noIntervalRanges, err := ds.deriveTablePathStats(path, conditions, true) if err != nil { @@ -292,6 +333,9 @@ func (ds *DataSource) accessPathsForConds(conditions []expression.Expression, us } } else { path.index = ds.possibleAccessPaths[i].index + if !ds.isInIndexMergeHints(path.index.Name.L) { + continue + } noIntervalRanges, err := ds.deriveIndexPathStats(path, conditions, true) if err != nil { logutil.BgLogger().Debug("can not derive statistics of a path", zap.Error(err)) diff --git a/planner/core/stringer.go b/planner/core/stringer.go index 532f8610703da..6091aab37f756 100644 --- a/planner/core/stringer.go +++ b/planner/core/stringer.go @@ -179,6 +179,15 @@ func toString(in Plan, strs []string, idxs []int) ([]string, []int) { str = fmt.Sprintf("IndexReader(%s)", ToString(x.indexPlan)) case *PhysicalIndexLookUpReader: str = fmt.Sprintf("IndexLookUp(%s, %s)", ToString(x.indexPlan), ToString(x.tablePlan)) + case *PhysicalIndexMergeReader: + str = "IndexMergeReader(PartialPlans->[" + for i, paritalPlan := range x.partialPlans { + if i > 0 { + str += ", " + } + str += ToString(paritalPlan) + } + str += "], TablePlan->" + ToString(x.tablePlan) + ")" case *PhysicalUnionScan: str = fmt.Sprintf("UnionScan(%s)", x.Conditions) case *PhysicalIndexJoin: diff --git a/planner/core/testdata/plan_suite_in.json b/planner/core/testdata/plan_suite_in.json index db0bc91334196..287d60a4b07ce 100644 --- a/planner/core/testdata/plan_suite_in.json +++ b/planner/core/testdata/plan_suite_in.json @@ -51,6 +51,15 @@ "select /*+ USE_INDEX(t, c_d_e, f), IGNORE_INDEX(t, c_d_e) */ c from t order by c" ] }, + { + "name": "TestIndexMergeHint", + "cases": [ + "select /*+ USE_INDEX_MERGE(t, c_d_e, f_g) */ * from t where c < 1 or f > 2", + "select /*+ USE_INDEX_MERGE(t, primary, f_g) */ * from t where a < 1 or f > 2", + "select /*+ USE_INDEX_MERGE(t, primary, f_g, c_d_e) */ * from t where a < 1 or f > 2", + "select /*+ NO_INDEX_MERGE(), USE_INDEX_MERGE(t, primary, f_g, c_d_e) */ * from t where a < 1 or f > 2" + ] + }, { "name": "TestDAGPlanBuilderSimpleCase", "cases":[ diff --git a/planner/core/testdata/plan_suite_out.json b/planner/core/testdata/plan_suite_out.json index b6b53bf28ba62..c04ef02c13d17 100644 --- a/planner/core/testdata/plan_suite_out.json +++ b/planner/core/testdata/plan_suite_out.json @@ -185,6 +185,35 @@ } ] }, + { + "Name": "TestIndexMergeHint", + "Cases": [ + { + "SQL": "select /*+ USE_INDEX_MERGE(t, c_d_e, f_g) */ * from t where c < 1 or f > 2", + "Best": "IndexMergeReader(PartialPlans->[Index(t.c_d_e)[[-inf,1)], Index(t.f_g)[(2,+inf]]], TablePlan->Table(t))", + "HasWarn": false, + "Hints": "USE_INDEX_MERGE(@`sel_1` `t` `c_d_e`, `f_g`)" + }, + { + "SQL": "select /*+ USE_INDEX_MERGE(t, primary, f_g) */ * from t where a < 1 or f > 2", + "Best": "IndexMergeReader(PartialPlans->[Table(t), Index(t.f_g)[(2,+inf]]], TablePlan->Table(t))", + "HasWarn": false, + "Hints": "USE_INDEX_MERGE(@`sel_1` `t` `PRIMARY`, `f_g`)" + }, + { + "SQL": "select /*+ USE_INDEX_MERGE(t, primary, f_g, c_d_e) */ * from t where a < 1 or f > 2", + "Best": "IndexMergeReader(PartialPlans->[Table(t), Index(t.f_g)[(2,+inf]]], TablePlan->Table(t))", + "HasWarn": false, + "Hints": "USE_INDEX_MERGE(@`sel_1` `t` `PRIMARY`, `f_g`)" + }, + { + "SQL": "select /*+ NO_INDEX_MERGE(), USE_INDEX_MERGE(t, primary, f_g, c_d_e) */ * from t where a < 1 or f > 2", + "Best": "TableReader(Table(t)->Sel([or(lt(Column#1, 1), gt(Column#9, 2))]))", + "HasWarn": true, + "Hints": "USE_INDEX(@`sel_1` `test`.`t` )" + } + ] + }, { "Name": "TestDAGPlanBuilderSimpleCase", "Cases": [ diff --git a/session/session_test.go b/session/session_test.go index 06f63a3799fa8..73b20e16e786a 100644 --- a/session/session_test.go +++ b/session/session_test.go @@ -2860,10 +2860,10 @@ func (s *testSessionSuite) TestStmtHints(c *C) { // Test NO_INDEX_MERGE hint tk.Se.GetSessionVars().SetEnableIndexMerge(true) tk.MustExec("select /*+ NO_INDEX_MERGE() */ 1;") - c.Assert(tk.Se.GetSessionVars().GetEnableIndexMerge(), IsFalse) + c.Assert(tk.Se.GetSessionVars().StmtCtx.NoIndexMergeHint, IsTrue) tk.MustExec("select /*+ NO_INDEX_MERGE(), NO_INDEX_MERGE() */ 1;") c.Assert(tk.Se.GetSessionVars().StmtCtx.GetWarnings(), HasLen, 1) - c.Assert(tk.Se.GetSessionVars().GetEnableIndexMerge(), IsFalse) + c.Assert(tk.Se.GetSessionVars().GetEnableIndexMerge(), IsTrue) // Test USE_TOJA hint tk.Se.GetSessionVars().SetAllowInSubqToJoinAndAgg(true) diff --git a/sessionctx/stmtctx/stmtctx.go b/sessionctx/stmtctx/stmtctx.go index 6a0f382189d66..b3b9807ec98d4 100644 --- a/sessionctx/stmtctx/stmtctx.go +++ b/sessionctx/stmtctx/stmtctx.go @@ -146,13 +146,12 @@ type StatementContext struct { type StmtHints struct { // Hint flags HasAllowInSubqToJoinAndAggHint bool - HasEnableIndexMergeHint bool HasMemQuotaHint bool HasReplicaReadHint bool // Hint Information AllowInSubqToJoinAndAgg bool - EnableIndexMerge bool + NoIndexMergeHint bool MemQuotaQuery int64 ReplicaRead byte } diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index 725e8ab4db67a..3ec99b0d87cd2 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -594,11 +594,8 @@ func (s *SessionVars) SetAllowInSubqToJoinAndAgg(val bool) { s.allowInSubqToJoinAndAgg = val } -// GetEnableIndexMerge get EnableIndexMerge from sql hints and SessionVars.enableIndexMerge. +// GetEnableIndexMerge get EnableIndexMerge from SessionVars.enableIndexMerge. func (s *SessionVars) GetEnableIndexMerge() bool { - if s.StmtCtx.HasEnableIndexMergeHint { - return s.StmtCtx.EnableIndexMerge - } return s.enableIndexMerge }