Skip to content

Commit

Permalink
session: fix two cases when updating bind info (pingcap#22338) (pingc…
Browse files Browse the repository at this point in the history
…ap#22341)

* cherry pick pingcap#22338 to release-4.0

Signed-off-by: ti-srebot <[email protected]>

* resolve conflict

Co-authored-by: rebelice <[email protected]>
  • Loading branch information
ti-srebot and rebelice authored Jan 11, 2021
1 parent 1edefab commit 98820cf
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 21 deletions.
29 changes: 20 additions & 9 deletions session/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,7 @@ func upgradeToVer51(s Session, ver int64) {
if ver >= version51 {
return
}
bindMap := make(map[string]types.Time)
h := &bindinfo.BindHandle{}
var err error
mustExecute(s, "BEGIN PESSIMISTIC")
Expand All @@ -1151,7 +1152,7 @@ func upgradeToVer51(s Session, ver int64) {
}()
mustExecute(s, h.LockBindInfoSQL())
var recordSets []sqlexec.RecordSet
recordSets, err = s.ExecuteInternal(context.Background(), "SELECT original_sql, bind_sql, default_db, charset, collation from mysql.bind_info where source != 'builtin'")
recordSets, err = s.ExecuteInternal(context.Background(), "SELECT original_sql, bind_sql, default_db, charset, collation, update_time from mysql.bind_info where source != 'builtin'")
if err != nil {
logutil.BgLogger().Fatal("upgradeToVer61 error", zap.Error(err))
}
Expand All @@ -1170,27 +1171,37 @@ func upgradeToVer51(s Session, ver int64) {
if req.NumRows() == 0 {
break
}
updateBindInfo(s, iter, p, now.String())
updateBindInfo(s, iter, p, now.String(), bindMap)
}
}

func updateBindInfo(s Session, iter *chunk.Iterator4Chunk, p *parser.Parser, now string) {
func updateBindInfo(s Session, iter *chunk.Iterator4Chunk, p *parser.Parser, now string, bindMap map[string]types.Time) {
for row := iter.Begin(); row != iter.End(); row = iter.Next() {
original := row.GetString(0)
bind := row.GetString(1)
db := row.GetString(2)
charset := row.GetString(3)
collation := row.GetString(4)
originStmt, err := p.ParseOneStmt(original, charset, collation)
updateTime := row.GetTime(5)
stmt, err := p.ParseOneStmt(bind, charset, collation)
if err != nil {
logutil.BgLogger().Fatal("updateBindInfo error", zap.Error(err))
}
bindStmt, err := p.ParseOneStmt(bind, charset, collation)
if err != nil {
logutil.BgLogger().Fatal("updateBindInfo error", zap.Error(err))
originWithDB := parser.Normalize(utilparser.RestoreWithDefaultDB(stmt, db))
if latest, ok := bindMap[originWithDB]; ok {
// In the following cases, duplicate originWithDB may occur
// originalText |bindText |DB
// `select * from t` |`select /*+ use_index(t, idx) */ * from t` |`test`
// `select * from test.t` |`select /*+ use_index(t, idx) */ * from test.t`|``
// If repeated, we will keep the latest binding.
if updateTime.Compare(latest) <= 0 {
continue
}
mustExecute(s, fmt.Sprintf(`DELETE FROM mysql.bind_info WHERE original_sql=%s`, expression.Quote(original)))
original = originWithDB
}
originWithDB := parser.Normalize(utilparser.RestoreWithDefaultDB(originStmt, db))
bindWithDB := utilparser.RestoreWithDefaultDB(bindStmt, db)
bindMap[originWithDB] = updateTime
bindWithDB := utilparser.RestoreWithDefaultDB(stmt, db)
mustExecute(s, fmt.Sprintf(`UPDATE mysql.bind_info SET original_sql=%s, bind_sql=%s, default_db='', update_time=%s where original_sql=%s`,
expression.Quote(originWithDB),
expression.Quote(bindWithDB),
Expand Down
86 changes: 74 additions & 12 deletions session/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,32 +431,94 @@ func (s *testBootstrapSuite) TestStmtSummary(c *C) {
c.Assert(r.Close(), IsNil)
}

type bindTestStruct struct {
originText string
bindText string
db string
originWithDB string
bindWithDB string
deleteText string
}

func (s *testBootstrapSuite) TestUpdateBindInfo(c *C) {
bindCases := []bindTestStruct{
{
originText: "select * from t where a > ?",
bindText: "select /*+ use_index(t, idxb) */ * from t where a > 1",
db: "test",
originWithDB: "select * from test . t where a > ?",
bindWithDB: "SELECT /*+ use_index(t idxb)*/ * FROM test.t WHERE a > 1",
deleteText: "select * from test.t where a > 1",
},
{
originText: "select count ( ? ), max ( a ) from t group by b",
bindText: "select /*+ use_index(t, idx) */ count(1), max(a) from t group by b",
db: "test",
originWithDB: "select count ( ? ) , max ( a ) from test . t group by b",
bindWithDB: "SELECT /*+ use_index(t idx)*/ count(1),max(a) FROM test.t GROUP BY b",
deleteText: "select count(1), max(a) from test.t group by b",
},
}
defer testleak.AfterTest(c)()
ctx := context.Background()
store, dom := newStoreWithBootstrap(c, s.dbName)
defer store.Close()
defer dom.Close()
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, `insert into mysql.bind_info values('select * from t where a > ?','select /*+ use_index(t, idxb) */ * from t where a > 1', 'test','using', '2021-01-04 14:50:58.257','2021-01-04 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`)
for _, bindCase := range bindCases {
sql := fmt.Sprintf("insert into mysql.bind_info values('%s', '%s', '%s', 'using', '2021-01-04 14:50:58.257', '2021-01-04 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')",
bindCase.originText,
bindCase.bindText,
bindCase.db,
)
mustExecSQL(c, se, sql)

upgradeToVer51(se, version50)
r := mustExecSQL(c, se, `select original_sql, bind_sql, default_db, status from mysql.bind_info where source != 'builtin'`)
req := r.NewChunk()
c.Assert(r.Next(ctx, req), IsNil)
row := req.GetRow(0)
c.Assert(row.GetString(0), Equals, bindCase.originWithDB)
c.Assert(row.GetString(1), Equals, bindCase.bindWithDB)
c.Assert(row.GetString(2), Equals, "")
c.Assert(row.GetString(3), Equals, "using")
c.Assert(r.Close(), IsNil)
sql = fmt.Sprintf("drop global binding for %s", bindCase.deleteText)
mustExecSQL(c, se, sql)
r = mustExecSQL(c, se, `select original_sql, bind_sql, status from mysql.bind_info where source != 'builtin'`)
c.Assert(r.Next(ctx, req), IsNil)
row = req.GetRow(0)
c.Assert(row.GetString(0), Equals, bindCase.originWithDB)
c.Assert(row.GetString(1), Equals, bindCase.bindWithDB)
c.Assert(row.GetString(2), Equals, "deleted")
c.Assert(r.Close(), IsNil)
sql = fmt.Sprintf("delete from mysql.bind_info where original_sql = '%s'", bindCase.originWithDB)
mustExecSQL(c, se, sql)
}
}

func (s *testBootstrapSuite) TestUpdateDuplicateBindInfo(c *C) {
defer testleak.AfterTest(c)()
ctx := context.Background()
store, dom := newStoreWithBootstrap(c, s.dbName)
defer store.Close()
defer dom.Close()
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, `insert into mysql.bind_info values('select * from t', 'select /*+ use_index(t, idx_a)*/ * from t', 'test', 'using', '2021-01-04 14:50:58.257', '2021-01-04 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`)
// The latest one.
mustExecSQL(c, se, `insert into mysql.bind_info values('select * from test.t', 'select /*+ use_index(t, idx_b)*/ * from test.t', 'test', 'using', '2021-01-04 14:50:58.257', '2021-01-09 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`)

upgradeToVer51(se, version50)

r := mustExecSQL(c, se, `select original_sql, bind_sql, default_db, status from mysql.bind_info where source != 'builtin'`)
req := r.NewChunk()
c.Assert(r.Next(ctx, req), IsNil)
c.Assert(req.NumRows(), Equals, 1)
row := req.GetRow(0)
c.Assert(row.GetString(0), Equals, "select * from test . t where a > ?")
c.Assert(row.GetString(1), Equals, "SELECT /*+ use_index(t idxb)*/ * FROM test.t WHERE a > 1")
c.Assert(row.GetString(0), Equals, "select * from test . t")
c.Assert(row.GetString(1), Equals, "SELECT /*+ use_index(t idx_b)*/ * FROM test.t")
c.Assert(row.GetString(2), Equals, "")
c.Assert(row.GetString(3), Equals, "using")
c.Assert(r.Close(), IsNil)
mustExecSQL(c, se, `drop global binding for select * from test.t where a > 1`)
r = mustExecSQL(c, se, `select original_sql, bind_sql, status from mysql.bind_info where source != 'builtin'`)
c.Assert(r.Next(ctx, req), IsNil)
row = req.GetRow(0)
c.Assert(row.GetString(0), Equals, "select * from test . t where a > ?")
c.Assert(row.GetString(1), Equals, "SELECT /*+ use_index(t idxb)*/ * FROM test.t WHERE a > 1")
c.Assert(row.GetString(2), Equals, "deleted")
c.Assert(r.Close(), IsNil)
mustExecSQL(c, se, `delete from mysql.bind_info where original_sql = 'select * from test . t where a > ?'`)
mustExecSQL(c, se, "delete from mysql.bind_info where original_sql = 'select * from test . t'")
}

0 comments on commit 98820cf

Please sign in to comment.