From d2e8bc66b9d82a8e3253bfc975a79cad2f1d1793 Mon Sep 17 00:00:00 2001 From: Mingcong Han Date: Fri, 6 Sep 2019 16:42:20 +0800 Subject: [PATCH] planner: choose TableScan when use an empty index hint (#12037) --- planner/core/logical_plan_builder.go | 2 +- planner/core/logical_plans.go | 10 ++++++++++ planner/core/physical_plan_test.go | 10 ++++++++++ planner/core/planbuilder.go | 11 +++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 70e40c67c55e5..387dbb7379bf4 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -1973,7 +1973,7 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, nodeType n case HintStreamAgg: preferAggType |= preferStreamAgg case HintIndex: - if len(hint.Tables) != 0 && len(hint.Indexes) != 0 { + if len(hint.Tables) != 0 { indexHintList = append(indexHintList, indexHintInfo{ tblName: hint.Tables[0].TableName, indexHint: &ast.IndexHint{ diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index 13bc4be413e1b..5631d00dbea77 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -397,6 +397,16 @@ type accessPath struct { partialIndexPaths []*accessPath } +// getTablePath finds the TablePath from a group of accessPaths. +func getTablePath(paths []*accessPath) *accessPath { + for _, path := range paths { + if path.isTablePath { + return path + } + } + return nil +} + // deriveTablePathStats will fulfill the information that the accessPath need. // And it will check whether the primary key is covered only by point query. func (ds *DataSource) deriveTablePathStats(path *accessPath, conds []expression.Expression) (bool, error) { diff --git a/planner/core/physical_plan_test.go b/planner/core/physical_plan_test.go index 52ff5645de9c3..2077e5e2e5f86 100644 --- a/planner/core/physical_plan_test.go +++ b/planner/core/physical_plan_test.go @@ -65,6 +65,10 @@ func (s *testPlanSuite) TestDAGPlanBuilderSimpleCase(c *C) { sql: "select * from t t1 use index(c_d_e)", best: "IndexLookUp(Index(t.c_d_e)[[NULL,+inf]], Table(t))", }, + { + sql: "select f from t use index() where f = 1", + best: "TableReader(Table(t)->Sel([eq(test.t.f, 1)]))", + }, // Test ts + Sort vs. DoubleRead + filter. { sql: "select a from t where a between 1 and 2 order by c", @@ -1772,6 +1776,12 @@ func (s *testPlanSuite) TestIndexHint(c *C) { best: "IndexLookUp(Index(t.f)[[NULL,+inf]], Table(t))", hasWarn: false, }, + // use TablePath when the hint only contains table. + { + sql: "select /*+ INDEX(t) */ f from t where f > 10", + best: "TableReader(Table(t)->Sel([gt(test.t.f, 10)]))", + hasWarn: false, + }, // there will be a warning instead of error when index not exist { sql: "select /*+ INDEX(t, no_such_index) */ * from t", diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index eca5e35eccb89..fb619967bc476 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -558,6 +558,17 @@ func (b *PlanBuilder) getPossibleAccessPaths(indexHints []*ast.IndexHint, tblInf } hasScanHint = true + + // It is syntactically valid to omit index_list for USE INDEX, which means “use no indexes”. + // Omitting index_list for FORCE INDEX or IGNORE INDEX is a syntax error. + // See https://dev.mysql.com/doc/refman/8.0/en/index-hints.html. + if hint.IndexNames == nil && hint.HintType != ast.HintIgnore { + if path := getTablePath(publicPaths); path != nil { + hasUseOrForce = true + path.forced = true + available = append(available, path) + } + } for _, idxName := range hint.IndexNames { path := getPathByIndexName(publicPaths, idxName, tblInfo) if path == nil {