Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into expression_index_…
Browse files Browse the repository at this point in the history
…crate_index
  • Loading branch information
wjhuang2016 committed Dec 19, 2019
2 parents 133428e + eef8c39 commit 5c5e4f9
Show file tree
Hide file tree
Showing 47 changed files with 57,419 additions and 368 deletions.
3 changes: 3 additions & 0 deletions bindinfo/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,10 +520,12 @@ func (s *testSuite) TestDropSingleBindings(c *C) {
rows = tk.MustQuery("show bindings").Rows()
c.Assert(len(rows), Equals, 1)
c.Assert(rows[0][1], Equals, "select * from t use index(idx_b)")
tk.MustExec("drop table t")
tk.MustExec("drop binding for select * from t using select * from t use index(idx_b)")
rows = tk.MustQuery("show bindings").Rows()
c.Assert(len(rows), Equals, 0)

tk.MustExec("create table t(a int, b int, c int, index idx_a(a), index idx_b(b))")
// Test drop global bindings.
tk.MustExec("create global binding for select * from t using select * from t use index(idx_a)")
tk.MustExec("create global binding for select * from t using select * from t use index(idx_b)")
Expand All @@ -535,6 +537,7 @@ func (s *testSuite) TestDropSingleBindings(c *C) {
rows = tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 1)
c.Assert(rows[0][1], Equals, "select * from t use index(idx_b)")
tk.MustExec("drop table t")
tk.MustExec("drop global binding for select * from t using select * from t use index(idx_b)")
rows = tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 0)
Expand Down
18 changes: 13 additions & 5 deletions bindinfo/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ type Binding struct {
id string
}

func (b *Binding) isSame(rb *Binding) bool {
if b.id != "" && rb.id != "" {
return b.id == rb.id
}
// Sometimes we cannot construct `id` because of the changed schema, so we need to compare by bind sql.
return b.BindSQL == rb.BindSQL
}

// cache is a k-v map, key is original sql, value is a slice of BindRecord.
type cache map[string][]*BindRecord

Expand Down Expand Up @@ -90,7 +98,7 @@ func (br *BindRecord) FindBinding(hint string) *Binding {
func (br *BindRecord) prepareHints(sctx sessionctx.Context, is infoschema.InfoSchema) error {
p := parser.New()
for i, bind := range br.Bindings {
if bind.Hint != nil || bind.id != "" {
if bind.Hint != nil || bind.id != "" || bind.Status == deleted {
continue
}
stmtNode, err := p.ParseOneStmt(bind.BindSQL, bind.Charset, bind.Collation)
Expand Down Expand Up @@ -119,7 +127,7 @@ func merge(lBindRecord, rBindRecord *BindRecord) *BindRecord {
for _, rbind := range rBindRecord.Bindings {
found := false
for j, lbind := range lBindRecord.Bindings {
if lbind.id == rbind.id {
if lbind.isSame(&rbind) {
found = true
if rbind.UpdateTime.Compare(lbind.UpdateTime) >= 0 {
result.Bindings[j] = rbind
Expand All @@ -142,7 +150,7 @@ func (br *BindRecord) remove(deleted *BindRecord) *BindRecord {
result := br.shallowCopy()
for _, deletedBind := range deleted.Bindings {
for i, bind := range result.Bindings {
if bind.id == deletedBind.id {
if bind.isSame(&deletedBind) {
result.Bindings = append(result.Bindings[:i], result.Bindings[i+1:]...)
break
}
Expand Down Expand Up @@ -205,8 +213,8 @@ func (br *BindRecord) metrics() ([]float64, []int) {
}

// size calculates the memory size of a bind info.
func (m *Binding) size() float64 {
res := len(m.BindSQL) + len(m.Status) + 2*int(unsafe.Sizeof(m.CreateTime)) + len(m.Charset) + len(m.Collation)
func (b *Binding) size() float64 {
res := len(b.BindSQL) + len(b.Status) + 2*int(unsafe.Sizeof(b.CreateTime)) + len(b.Charset) + len(b.Collation)
return float64(res)
}

Expand Down
49 changes: 18 additions & 31 deletions bindinfo/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,7 @@ func NewBindHandle(ctx sessionctx.Context) *BindHandle {
handle.bindInfo.parser = parser.New()
handle.invalidBindRecordMap.Value.Store(make(map[string]*bindRecordUpdate))
handle.invalidBindRecordMap.flushFunc = func(record *BindRecord) error {
// We do not need the first two parameters because they are only use to generate hint,
// and we already have the hint.
return handle.DropBindRecord(nil, nil, record)
return handle.DropBindRecord(record.OriginalSQL, record.Db, &record.Bindings[0])
}
handle.pendingVerifyBindRecordMap.Value.Store(make(map[string]*bindRecordUpdate))
handle.pendingVerifyBindRecordMap.flushFunc = func(record *BindRecord) error {
Expand Down Expand Up @@ -248,13 +246,9 @@ func (h *BindHandle) AddBindRecord(sctx sessionctx.Context, is infoschema.InfoSc
}

// DropBindRecord drops a BindRecord to the storage and BindRecord int the cache.
func (h *BindHandle) DropBindRecord(sctx sessionctx.Context, is infoschema.InfoSchema, record *BindRecord) (err error) {
err = record.prepareHints(sctx, is)
if err != nil {
return err
}
exec, _ := h.sctx.Context.(sqlexec.SQLExecutor)
func (h *BindHandle) DropBindRecord(originalSQL, db string, binding *Binding) (err error) {
h.sctx.Lock()
exec, _ := h.sctx.Context.(sqlexec.SQLExecutor)

_, err = exec.Execute(context.TODO(), "BEGIN")
if err != nil {
Expand All @@ -276,7 +270,11 @@ func (h *BindHandle) DropBindRecord(sctx sessionctx.Context, is infoschema.InfoS
return
}

err = h.removeBindRecord(parser.DigestNormalized(record.OriginalSQL), record)
record := &BindRecord{OriginalSQL: originalSQL, Db: db}
if binding != nil {
record.Bindings = append(record.Bindings, *binding)
}
err = h.removeBindRecord(parser.DigestNormalized(originalSQL), record)
}()

txn, err1 := h.sctx.Context.Txn(true)
Expand All @@ -289,21 +287,13 @@ func (h *BindHandle) DropBindRecord(sctx sessionctx.Context, is infoschema.InfoS
Type: mysql.TypeDatetime,
Fsp: 3,
}
oldBindRecord := h.GetBindRecord(parser.DigestNormalized(record.OriginalSQL), record.OriginalSQL, record.Db)
bindingSQLs := make([]string, 0, len(record.Bindings))
for i := range record.Bindings {
record.Bindings[i].Status = deleted
record.Bindings[i].UpdateTime = updateTs
if oldBindRecord == nil {
continue
}
binding := oldBindRecord.FindBinding(record.Bindings[i].id)
if binding != nil {
bindingSQLs = append(bindingSQLs, binding.BindSQL)
}

bindSQL := ""
if binding != nil {
bindSQL = binding.BindSQL
}

_, err = exec.Execute(context.TODO(), h.logicalDeleteBindInfoSQL(record.OriginalSQL, record.Db, updateTs, bindingSQLs))
_, err = exec.Execute(context.TODO(), h.logicalDeleteBindInfoSQL(originalSQL, db, updateTs, bindSQL))
return err
}

Expand Down Expand Up @@ -522,19 +512,16 @@ func (h *BindHandle) insertBindInfoSQL(orignalSQL string, db string, info Bindin
)
}

func (h *BindHandle) logicalDeleteBindInfoSQL(originalSQL, db string, updateTs types.Time, bindingSQLs []string) string {
func (h *BindHandle) logicalDeleteBindInfoSQL(originalSQL, db string, updateTs types.Time, bindingSQL string) string {
sql := fmt.Sprintf(`UPDATE mysql.bind_info SET status=%s,update_time=%s WHERE original_sql=%s and default_db=%s`,
expression.Quote(deleted),
expression.Quote(updateTs.String()),
expression.Quote(originalSQL),
expression.Quote(db))
if len(bindingSQLs) == 0 {
if bindingSQL == "" {
return sql
}
for i, sql := range bindingSQLs {
bindingSQLs[i] = expression.Quote(sql)
}
return sql + fmt.Sprintf(` and bind_sql in (%s)`, strings.Join(bindingSQLs, ","))
return sql + fmt.Sprintf(` and bind_sql = %s`, expression.Quote(bindingSQL))
}

// GenHintsFromSQL is used to generate hints from SQL.
Expand Down Expand Up @@ -744,7 +731,7 @@ func (h *BindHandle) HandleEvolvePlanTask(sctx sessionctx.Context) error {
// since it is still in the bind record. Now we just drop it and if it is actually retryable,
// we will hope for that we can capture this evolve task again.
if err != nil {
return h.DropBindRecord(sctx, sctx.GetSessionVars().TxnCtx.InfoSchema.(infoschema.InfoSchema), &BindRecord{OriginalSQL: originalSQL, Db: db, Bindings: []Binding{binding}})
return h.DropBindRecord(originalSQL, db, &binding)
}
// If the accepted plan timeouts, it is hard to decide the timeout for verify plan.
// Currently we simply mark the verify plan as `using` if it could run successfully within maxTime.
Expand All @@ -754,7 +741,7 @@ func (h *BindHandle) HandleEvolvePlanTask(sctx sessionctx.Context) error {
sctx.GetSessionVars().UsePlanBaselines = false
verifyPlanTime, err := h.getRunningDuration(sctx, db, binding.BindSQL, maxTime)
if err != nil {
return h.DropBindRecord(sctx, sctx.GetSessionVars().TxnCtx.InfoSchema.(infoschema.InfoSchema), &BindRecord{OriginalSQL: originalSQL, Db: db, Bindings: []Binding{binding}})
return h.DropBindRecord(originalSQL, db, &binding)
}
if verifyPlanTime < 0 {
binding.Status = Rejected
Expand Down
12 changes: 6 additions & 6 deletions bindinfo/session_handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,13 @@ func (h *SessionHandle) AddBindRecord(sctx sessionctx.Context, is infoschema.Inf
}

// DropBindRecord drops a BindRecord in the cache.
func (h *SessionHandle) DropBindRecord(sctx sessionctx.Context, is infoschema.InfoSchema, record *BindRecord) error {
err := record.prepareHints(sctx, is)
if err != nil {
return err
}
oldRecord := h.GetBindRecord(record.OriginalSQL, record.Db)
func (h *SessionHandle) DropBindRecord(originalSQL, db string, binding *Binding) error {
oldRecord := h.GetBindRecord(originalSQL, db)
var newRecord *BindRecord
record := &BindRecord{OriginalSQL: originalSQL, Db: db}
if binding != nil {
record.Bindings = append(record.Bindings, *binding)
}
if oldRecord != nil {
newRecord = oldRecord.remove(record)
} else {
Expand Down
32 changes: 32 additions & 0 deletions cmd/explaintest/r/explain_union_scan.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
drop table if exists city;
CREATE TABLE `city` (
`id` varchar(70) NOT NULL,
`province_id` int(15) DEFAULT NULL,
`city_name` varchar(90) DEFAULT NULL,
`description` varchar(90) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
load stats "s/explain_union_scan.json";
insert into city values("06766b3ef41d484d8878606393f1ed0b", 88, "chongqing", "chongqing city");
begin;
update city set province_id = 77 where id="06766b3ef41d484d8878606393f1ed0b";
explain select t1.*, t2.province_id as provinceID, t2.city_name as cityName, t3.description as description from city t1 inner join city t2 on t1.id = t2.id left join city t3 on t1.province_id = t3.province_id where t1.province_id > 1 and t1.province_id < 100 limit 10;
id count task operator info
Projection_17 10.00 root test.city.id, test.city.province_id, test.city.city_name, test.city.description, test.city.province_id, test.city.city_name, test.city.description
└─Limit_20 10.00 root offset:0, count:10
└─HashLeftJoin_22 10.00 root left outer join, inner:Limit_25 (REVERSED), equal:[eq(test.city.province_id, test.city.province_id)]
├─Limit_25 10.00 root offset:0, count:10
│ └─IndexJoin_38 10.00 root inner join, inner:UnionScan_37, outer key:test.city.id, inner key:test.city.id
│ ├─UnionScan_37 0.97 root gt(test.city.province_id, 1), lt(test.city.province_id, 100)
│ │ └─IndexLookUp_36 0.97 root
│ │ ├─IndexScan_33 1.00 cop[tikv] table:t1, index:id, range: decided by [eq(test.city.id, test.city.id)], keep order:false
│ │ └─Selection_35 0.97 cop[tikv] gt(test.city.province_id, 1), lt(test.city.province_id, 100)
│ │ └─TableScan_34 1.00 cop[tikv] table:t1, keep order:false
│ └─UnionScan_47 10.33 root
│ └─TableReader_49 10.33 root data:TableScan_48
│ └─TableScan_48 10.33 cop[tikv] table:t2, range:[-inf,+inf], keep order:false
└─UnionScan_57 519304.44 root gt(test.city.province_id, 1), lt(test.city.province_id, 100), not(isnull(test.city.province_id))
└─TableReader_60 519304.44 root data:Selection_59
└─Selection_59 519304.44 cop[tikv] gt(test.city.province_id, 1), lt(test.city.province_id, 100), not(isnull(test.city.province_id))
└─TableScan_58 536284.00 cop[tikv] table:t3, range:[-inf,+inf], keep order:false
commit;
Loading

0 comments on commit 5c5e4f9

Please sign in to comment.