Skip to content

Commit

Permalink
Merge branch 'master' into forbidBaselineEvolution
Browse files Browse the repository at this point in the history
  • Loading branch information
ti-chi-bot authored Aug 4, 2021
2 parents 0a8ef1e + cb7cd9c commit eae9ae7
Show file tree
Hide file tree
Showing 11 changed files with 477 additions and 228 deletions.
65 changes: 54 additions & 11 deletions bindinfo/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"time"

. "github.com/pingcap/check"
"github.com/pingcap/failpoint"
"github.com/pingcap/parser"
"github.com/pingcap/parser/auth"
"github.com/pingcap/parser/model"
Expand Down Expand Up @@ -61,6 +62,7 @@ func TestT(t *testing.T) {
}

var _ = Suite(&testSuite{})
var _ = SerialSuites(&testSerialSuite{})

type testSuite struct {
cluster testutils.Cluster
Expand Down Expand Up @@ -154,6 +156,44 @@ func (s *testSuite) cleanBindingEnv(tk *testkit.TestKit) {
s.domain.BindHandle().Clear()
}

type testSerialSuite struct {
cluster testutils.Cluster
store kv.Storage
domain *domain.Domain
}

func (s *testSerialSuite) SetUpSuite(c *C) {
flag.Lookup("mockTikv")
useMockTikv := *mockTikv
if useMockTikv {
store, err := mockstore.NewMockStore(
mockstore.WithClusterInspector(func(c testutils.Cluster) {
mockstore.BootstrapWithSingleStore(c)
s.cluster = c
}),
)
c.Assert(err, IsNil)
s.store = store
session.SetSchemaLease(0)
session.DisableStats4Test()
}
bindinfo.Lease = 0
d, err := session.BootstrapSession(s.store)
c.Assert(err, IsNil)
d.SetStatsUpdating(true)
s.domain = d
}

func (s *testSerialSuite) TearDownSuite(c *C) {
s.domain.Close()
s.store.Close()
}

func (s *testSerialSuite) cleanBindingEnv(tk *testkit.TestKit) {
tk.MustExec("delete from mysql.bind_info where source != 'builtin'")
s.domain.BindHandle().Clear()
}

func normalizeWithDefaultDB(c *C, sql, db string) (string, string) {
testParser := parser.New()
stmt, err := testParser.ParseOneStmt(sql, "", "")
Expand Down Expand Up @@ -1284,20 +1324,11 @@ func (s *testSuite) TestRuntimeHintsInEvolveTasks(c *C) {
tk.MustExec("set @@tidb_evolve_plan_baselines=1")
tk.MustExec("create table t(a int, b int, c int, index idx_a(a), index idx_b(b), index idx_c(c))")

// these runtime hints which don't be contained by the original binding should be ignored
tk.MustExec("create global binding for select * from t where a >= 1 and b >= 1 and c = 0 using select * from t use index(idx_a) where a >= 1 and b >= 1 and c = 0")
tk.MustQuery("select /*+ MAX_EXECUTION_TIME(5000) */* from t where a >= 4 and b >= 1 and c = 0")
tk.MustExec("admin flush bindings")
rows := tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 2)
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`)*/ * FROM `test`.`t` WHERE `a` >= 4 AND `b` >= 1 AND `c` = 0") // MAX_EXECUTION_TIME is ignored

s.cleanBindingEnv(tk)
tk.MustExec("create global binding for select * from t where a >= 1 and b >= 1 and c = 0 using select /*+ MAX_EXECUTION_TIME(5000) */* from t use index(idx_a) where a >= 1 and b >= 1 and c = 0")
tk.MustQuery("select /*+ MAX_EXECUTION_TIME(5000) */* from t where a >= 4 and b >= 1 and c = 0")
tk.MustExec("admin flush bindings")
rows = tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 2)
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`), max_execution_time(5000)*/ * FROM `test`.`t` WHERE `a` >= 4 AND `b` >= 1 AND `c` = 0")
}

Expand Down Expand Up @@ -2107,13 +2138,13 @@ func (s *testSuite) TestIssue20417(c *C) {
rows = tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 2)
c.Assert(rows[1][0], Equals, "select * from `test` . `t` where `c` = ?")
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`), use_index(`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541")
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541")
c.Assert(rows[1][3], Equals, "pending verify")
tk.MustExec("admin evolve bindings")
rows = tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 2)
c.Assert(rows[1][0], Equals, "select * from `test` . `t` where `c` = ?")
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`), use_index(`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541")
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541")
status := rows[1][3].(string)
c.Assert(status == "using" || status == "rejected", IsTrue)
tk.MustExec("set @@tidb_evolve_plan_baselines=0")
Expand Down Expand Up @@ -2277,3 +2308,15 @@ func (s *testSuite) TestGCBindRecord(c *C) {
tk.MustQuery("show global bindings").Check(testkit.Rows())
tk.MustQuery("select status from mysql.bind_info where original_sql = 'select * from `test` . `t` where `a` = ?'").Check(testkit.Rows())
}

func (s *testSerialSuite) TestOptimizeOnlyOnce(c *C) {
tk := testkit.NewTestKit(c, s.store)
s.cleanBindingEnv(tk)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int, b int, index idxa(a))")
tk.MustExec("create global binding for select * from t using select * from t use index(idxa)")
c.Assert(failpoint.Enable("github.com/pingcap/tidb/planner/checkOptimizeCountOne", "return"), IsNil)
tk.MustQuery("select * from t").Check(testkit.Rows())
c.Assert(failpoint.Disable("github.com/pingcap/tidb/planner/checkOptimizeCountOne"), IsNil)
}
2 changes: 1 addition & 1 deletion planner/core/exhaust_physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -1045,7 +1045,7 @@ func (p *LogicalJoin) constructInnerIndexScanTask(
Columns: ds.TblCols,
ColumnNames: ds.names,
}
if !ds.isCoveringIndex(ds.schema.Columns, path.FullIdxCols, path.FullIdxColLens, is.Table) {
if !path.IsSingleScan {
// On this way, it's double read case.
ts := PhysicalTableScan{
Columns: ds.Columns,
Expand Down
32 changes: 14 additions & 18 deletions planner/core/find_best_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,6 @@ type candidatePath struct {
path *util.AccessPath
accessCondsColSet *intsets.Sparse // accessCondsColSet is the set of columns that occurred in the access conditions.
indexFiltersColSet *intsets.Sparse // indexFiltersColSet is the set of columns that occurred in the index filters.
isSingleScan bool
isMatchProp bool
}

Expand Down Expand Up @@ -452,8 +451,8 @@ func compareBool(l, r bool) int {
}

func compareIndexBack(lhs, rhs *candidatePath) (int, bool) {
result := compareBool(lhs.isSingleScan, rhs.isSingleScan)
if result == 0 && !lhs.isSingleScan {
result := compareBool(lhs.path.IsSingleScan, rhs.path.IsSingleScan)
if result == 0 && !lhs.path.IsSingleScan {
// if both lhs and rhs need to access table after IndexScan, we use the set of columns that occurred in IndexFilters
// to compare how many table rows will be accessed.
return compareColumnSet(lhs.indexFiltersColSet, rhs.indexFiltersColSet)
Expand Down Expand Up @@ -542,11 +541,10 @@ func (ds *DataSource) getTableCandidate(path *util.AccessPath, prop *property.Ph
candidate := &candidatePath{path: path}
candidate.isMatchProp = ds.isMatchProp(path, prop)
candidate.accessCondsColSet = expression.ExtractColumnSet(path.AccessConds)
candidate.isSingleScan = true
return candidate
}

func (ds *DataSource) getIndexCandidate(path *util.AccessPath, prop *property.PhysicalProperty, isSingleScan bool) *candidatePath {
func (ds *DataSource) getIndexCandidate(path *util.AccessPath, prop *property.PhysicalProperty) *candidatePath {
candidate := &candidatePath{path: path}
all, _ := prop.AllSameOrder()
// When the prop is empty or `all` is false, `isMatchProp` is better to be `false` because
Expand All @@ -564,7 +562,6 @@ func (ds *DataSource) getIndexCandidate(path *util.AccessPath, prop *property.Ph
candidate.isMatchProp = ds.isMatchProp(path, prop)
candidate.accessCondsColSet = expression.ExtractColumnSet(path.AccessConds)
candidate.indexFiltersColSet = expression.ExtractColumnSet(path.IndexFilters)
candidate.isSingleScan = isSingleScan
return candidate
}

Expand Down Expand Up @@ -607,14 +604,13 @@ func (ds *DataSource) skylinePruning(prop *property.PhysicalProperty) []*candida
continue
}
} else {
coveredByIdx := ds.isCoveringIndex(ds.schema.Columns, path.FullIdxCols, path.FullIdxColLens, ds.tableInfo)
if len(path.AccessConds) > 0 || !prop.IsEmpty() || path.Forced || coveredByIdx {
if len(path.AccessConds) > 0 || !prop.IsEmpty() || path.Forced || path.IsSingleScan {
// We will use index to generate physical plan if any of the following conditions is satisfied:
// 1. This path's access cond is not nil.
// 2. We have a non-empty prop to match.
// 3. This index is forced to choose.
// 4. The needed columns are all covered by index columns(and handleCol).
currentCandidate = ds.getIndexCandidate(path, prop, coveredByIdx)
currentCandidate = ds.getIndexCandidate(path, prop)
} else {
continue
}
Expand Down Expand Up @@ -1110,7 +1106,7 @@ func (ts *PhysicalTableScan) appendExtraHandleCol(ds *DataSource) (*expression.C

// convertToIndexScan converts the DataSource to index scan with idx.
func (ds *DataSource) convertToIndexScan(prop *property.PhysicalProperty, candidate *candidatePath) (task task, err error) {
if !candidate.isSingleScan {
if !candidate.path.IsSingleScan {
// If it's parent requires single read task, return max cost.
if prop.TaskTp == property.CopSingleReadTaskType {
return invalidTask, nil
Expand All @@ -1123,7 +1119,7 @@ func (ds *DataSource) convertToIndexScan(prop *property.PhysicalProperty, candid
return invalidTask, nil
}
path := candidate.path
is, cost, _ := ds.getOriginalPhysicalIndexScan(prop, path, candidate.isMatchProp, candidate.isSingleScan)
is, cost, _ := ds.getOriginalPhysicalIndexScan(prop, path, candidate.isMatchProp, candidate.path.IsSingleScan)
cop := &copTask{
indexPlan: is,
tblColHists: ds.TblColHists,
Expand All @@ -1135,7 +1131,7 @@ func (ds *DataSource) convertToIndexScan(prop *property.PhysicalProperty, candid
Columns: ds.TblCols,
ColumnNames: ds.names,
}
if !candidate.isSingleScan {
if !candidate.path.IsSingleScan {
// On this way, it's double read case.
ts := PhysicalTableScan{
Columns: ds.Columns,
Expand Down Expand Up @@ -1701,8 +1697,8 @@ func (ds *DataSource) convertToPointGet(prop *property.PhysicalProperty, candida
if !prop.IsEmpty() && !candidate.isMatchProp {
return invalidTask
}
if prop.TaskTp == property.CopDoubleReadTaskType && candidate.isSingleScan ||
prop.TaskTp == property.CopSingleReadTaskType && !candidate.isSingleScan {
if prop.TaskTp == property.CopDoubleReadTaskType && candidate.path.IsSingleScan ||
prop.TaskTp == property.CopSingleReadTaskType && !candidate.path.IsSingleScan {
return invalidTask
}

Expand Down Expand Up @@ -1758,7 +1754,7 @@ func (ds *DataSource) convertToPointGet(prop *property.PhysicalProperty, candida
pointGetPlan.IdxColLens = candidate.path.IdxColLens
pointGetPlan.IndexValues = candidate.path.Ranges[0].LowVal
pointGetPlan.PartitionInfo = partitionInfo
if candidate.isSingleScan {
if candidate.path.IsSingleScan {
cost = pointGetPlan.GetCost(candidate.path.IdxCols)
} else {
cost = pointGetPlan.GetCost(ds.TblCols)
Expand All @@ -1784,8 +1780,8 @@ func (ds *DataSource) convertToBatchPointGet(prop *property.PhysicalProperty, ca
if !prop.IsEmpty() && !candidate.isMatchProp {
return invalidTask
}
if prop.TaskTp == property.CopDoubleReadTaskType && candidate.isSingleScan ||
prop.TaskTp == property.CopSingleReadTaskType && !candidate.isSingleScan {
if prop.TaskTp == property.CopDoubleReadTaskType && candidate.path.IsSingleScan ||
prop.TaskTp == property.CopSingleReadTaskType && !candidate.path.IsSingleScan {
return invalidTask
}

Expand Down Expand Up @@ -1832,7 +1828,7 @@ func (ds *DataSource) convertToBatchPointGet(prop *property.PhysicalProperty, ca
batchPointGetPlan.KeepOrder = true
batchPointGetPlan.Desc = prop.SortItems[0].Desc
}
if candidate.isSingleScan {
if candidate.path.IsSingleScan {
cost = batchPointGetPlan.GetCost(candidate.path.IdxCols)
} else {
cost = batchPointGetPlan.GetCost(ds.TblCols)
Expand Down
25 changes: 25 additions & 0 deletions planner/core/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4152,3 +4152,28 @@ func (s *testIntegrationSuite) TestIssue26559(c *C) {
tk.MustExec("insert into t values('2020-07-29 09:07:01', '2020-07-27 16:57:36');")
tk.MustQuery("select greatest(a, b) from t union select null;").Sort().Check(testkit.Rows("2020-07-29 09:07:01", "<nil>"))
}

func (s *testIntegrationSuite) TestHeuristicIndexSelection(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1(a int, b int, c int, d int, e int, f int, g int, primary key (a), unique key c_d_e (c, d, e), unique key f (f), unique key f_g (f, g), key g (g))")
tk.MustExec("create table t2(a int, b int, c int, d int, unique index idx_a (a), unique index idx_b_c (b, c), unique index idx_b_c_a_d (b, c, a, d))")

var input []string
var output []struct {
SQL string
Plan []string
Warnings []string
}
s.testData.GetTestCases(c, &input, &output)
for i, tt := range input {
s.testData.OnRecord(func() {
output[i].SQL = tt
output[i].Plan = s.testData.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows())
output[i].Warnings = s.testData.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows())
})
tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].Plan...))
tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warnings...))
}
}
Loading

0 comments on commit eae9ae7

Please sign in to comment.