Skip to content

Commit

Permalink
executor: fix IsPointGet judgment condition (#10278) (#10299)
Browse files Browse the repository at this point in the history
  • Loading branch information
tiancaiamao authored and jackysp committed Apr 30, 2019
1 parent 2dcf873 commit 1f69481
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 10 deletions.
16 changes: 8 additions & 8 deletions executor/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,11 +309,11 @@ func (a *ExecStmt) buildExecutor(ctx sessionctx.Context) (Executor, error) {
if _, ok := a.Plan.(*plannercore.Execute); !ok {
// Do not sync transaction for Execute statement, because the real optimization work is done in
// "ExecuteExec.Build".
isPointGet, err := IsPointGetWithPKOrUniqueKeyByAutoCommit(ctx, a.Plan)
useMaxTS, err := IsPointGetWithPKOrUniqueKeyByAutoCommit(ctx, a.Plan)
if err != nil {
return nil, errors.Trace(err)
}
if isPointGet {
if useMaxTS {
logutil.Logger(context.Background()).Debug("init txnStartTS with MaxUint64", zap.Uint64("conn", ctx.GetSessionVars().ConnectionID), zap.String("text", a.Text))
err = ctx.InitTxnWithStartTS(math.MaxUint64)
}
Expand All @@ -324,7 +324,7 @@ func (a *ExecStmt) buildExecutor(ctx sessionctx.Context) (Executor, error) {
stmtCtx := ctx.GetSessionVars().StmtCtx
if stmtPri := stmtCtx.Priority; stmtPri == mysql.NoPriority {
switch {
case isPointGet:
case useMaxTS:
stmtCtx.Priority = kv.PriorityHigh
case a.Expensive:
stmtCtx.Priority = kv.PriorityLow
Expand Down Expand Up @@ -461,7 +461,7 @@ func (a *ExecStmt) getStatsInfo() map[string]uint64 {
// IsPointGetWithPKOrUniqueKeyByAutoCommit returns true when meets following conditions:
// 1. ctx is auto commit tagged
// 2. txn is not valid
// 2. plan is point get by pk or unique key
// 2. plan is point get by pk, or point get by unique index (no double read)
func IsPointGetWithPKOrUniqueKeyByAutoCommit(ctx sessionctx.Context, p plannercore.Plan) (bool, error) {
// check auto commit
if !ctx.GetSessionVars().IsAutocommit() {
Expand Down Expand Up @@ -489,14 +489,14 @@ func IsPointGetWithPKOrUniqueKeyByAutoCommit(ctx sessionctx.Context, p plannerco
case *plannercore.PhysicalIndexReader:
indexScan := v.IndexPlans[0].(*plannercore.PhysicalIndexScan)
return indexScan.IsPointGetByUniqueKey(ctx.GetSessionVars().StmtCtx), nil
case *plannercore.PhysicalIndexLookUpReader:
indexScan := v.IndexPlans[0].(*plannercore.PhysicalIndexScan)
return indexScan.IsPointGetByUniqueKey(ctx.GetSessionVars().StmtCtx), nil
case *plannercore.PhysicalTableReader:
tableScan := v.TablePlans[0].(*plannercore.PhysicalTableScan)
return len(tableScan.Ranges) == 1 && tableScan.Ranges[0].IsPoint(ctx.GetSessionVars().StmtCtx), nil
case *plannercore.PointGetPlan:
return true, nil
// If the PointGetPlan needs to read data using unique index (double read), we
// can't use max uint64, because using math.MaxUint64 can't guarantee repeatable-read
// and the data and index would be inconsistent!
return v.IndexInfo == nil, nil
default:
return false, nil
}
Expand Down
34 changes: 33 additions & 1 deletion executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import (
"github.com/pingcap/tidb/util/testutil"
"github.com/pingcap/tidb/util/timeutil"
"github.com/pingcap/tipb/go-tipb"
"github.com/tiancaiamao/debugger"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"golang.org/x/net/context"
Expand Down Expand Up @@ -1850,7 +1851,8 @@ func (s *testSuite) TestIsPointGet(c *C) {
tk.MustExec("use mysql")
ctx := tk.Se.(sessionctx.Context)
tests := map[string]bool{
"select * from help_topic where name='aaa'": true,
"select * from help_topic where name='aaa'": false,
"select 1 from help_topic where name='aaa'": true,
"select * from help_topic where help_topic_id=1": true,
"select * from help_topic where help_category_id=1": false,
}
Expand All @@ -1869,6 +1871,36 @@ func (s *testSuite) TestIsPointGet(c *C) {
}
}

func (s *testSuite) TestPointGetRepeatableRead(c *C) {
gofail.Enable("github.com/pingcap/tidb/executor/pointGetRepeatableReadTest1", `return(true)`)
gofail.Enable("github.com/pingcap/tidb/executor/pointGetRepeatableReadTest2", `return(true)`)
defer gofail.Disable("github.com/pingcap/tidb/executor/pointGetRepeatableReadTest1")
defer gofail.Disable("github.com/pingcap/tidb/executor/pointGetRepeatableReadTest2")

tk1 := testkit.NewTestKit(c, s.store)
tk1.MustExec("use test")
tk1.MustExec(`create table point_get (a int, b int, c int,
primary key k_a(a),
unique key k_b(b))`)
tk1.MustExec("insert into point_get values (1, 1, 1)")
tk2 := testkit.NewTestKit(c, s.store)
tk2.MustExec("use test")

go func() {
ctx := context.WithValue(context.Background(), "pointGetRepeatableReadTest", true)
rs, err := tk1.Se.Execute(ctx, "select c from point_get where b = 1")
c.Assert(err, IsNil)
result := tk1.ResultSetToResultWithCtx(ctx, rs[0], Commentf("execute sql fail"))
result.Check(testkit.Rows("1"))
}()

label := debugger.Bind("point-get-g2")
debugger.Continue("point-get-g1")
debugger.Breakpoint(label)
tk2.MustExec("update point_get set b = 2, c = 2 where a = 1")
debugger.Continue("point-get-g1")
}

func (s *testSuite) TestRow(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")
Expand Down
23 changes: 23 additions & 0 deletions executor/point_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/chunk"
"github.com/pingcap/tidb/util/codec"
"github.com/tiancaiamao/debugger"
"golang.org/x/net/context"
)

Expand Down Expand Up @@ -71,6 +72,13 @@ func (e *PointGetExecutor) Close() error {
return nil
}

func dummyUseDebuggerPackageToMakeGoLintHappy() {
// debugger is import and used in gofail only
// We need to **use** it, otherwise 'make check' would complain:
// imported and not used: "github.com/tiancaiamao/debugger"
debugger.Bind("xx")
}

// Next implements the Executor interface.
func (e *PointGetExecutor) Next(ctx context.Context, chk *chunk.Chunk) error {
chk.Reset()
Expand All @@ -88,6 +96,13 @@ func (e *PointGetExecutor) Next(ctx context.Context, chk *chunk.Chunk) error {
if err1 != nil {
return errors.Trace(err1)
}

// gofail: var pointGetRepeatableReadTest1 bool
// if pointGetRepeatableReadTest1 && ctx.Value("pointGetRepeatableReadTest") != nil {
// label := debugger.Bind("point-get-g1")
// debugger.Breakpoint(label)
// }

handleVal, err1 := e.get(idxKey)
if err1 != nil && !kv.ErrNotExist.Equal(err1) {
return errors.Trace(err1)
Expand All @@ -99,7 +114,15 @@ func (e *PointGetExecutor) Next(ctx context.Context, chk *chunk.Chunk) error {
if err1 != nil {
return errors.Trace(err1)
}

// gofail: var pointGetRepeatableReadTest2 bool
// if pointGetRepeatableReadTest2 && ctx.Value("pointGetRepeatableReadTest") != nil {
// label := debugger.Bind("point-get-g1")
// debugger.Continue("point-get-g2")
// debugger.Breakpoint(label)
// }
}

key := tablecodec.EncodeRowKeyWithHandle(e.tblInfo.ID, e.handle)
val, err := e.get(key)
if err != nil && !kv.ErrNotExist.Equal(err) {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ require (
github.com/prometheus/procfs v0.0.0-20180408092902-8b1c2da0d56d // indirect
github.com/sirupsen/logrus v0.0.0-20170323161349-3bcb09397d6d
github.com/spaolacci/murmur3 v0.0.0-20150829172844-0d12bf811670
github.com/tiancaiamao/debugger v0.0.0-20190428065433-3a10ffa41d22
github.com/twinj/uuid v0.0.0-20150629100731-70cac2bcd273
github.com/uber/jaeger-client-go v2.8.0+incompatible
github.com/uber/jaeger-lib v1.1.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ github.com/spaolacci/murmur3 v0.0.0-20150829172844-0d12bf811670/go.mod h1:JwIasO
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/tiancaiamao/debugger v0.0.0-20190428065433-3a10ffa41d22 h1:P4sgavMKEdqNOws2VfR2c/Bye9nYFgV8gHyiW1wpQhE=
github.com/tiancaiamao/debugger v0.0.0-20190428065433-3a10ffa41d22/go.mod h1:qaShs3uDBYnvaQZJAJ6PjPg8kuAHR9zUJ8ilSLK1y/w=
github.com/twinj/uuid v0.0.0-20150629100731-70cac2bcd273 h1:YqFyfcgqxQqjpRr0SEG0Z555J/3kPqDL/xmRyeAaX/0=
github.com/twinj/uuid v0.0.0-20150629100731-70cac2bcd273/go.mod h1:mMgcE1RHFUFqe5AfiwlINXisXfDGro23fWdPUfOMjRY=
github.com/uber/jaeger-client-go v2.8.0+incompatible h1:7DGH8Hqk6PirD+GE+bvCf0cLnspLuae7N1NcwMeQcyg=
Expand Down
7 changes: 6 additions & 1 deletion util/testkit/testkit.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,12 @@ func (tk *TestKit) MustQuery(sql string, args ...interface{}) *Result {
// ResultSetToResult converts sqlexec.RecordSet to testkit.Result.
// It is used to check results of execute statement in binary mode.
func (tk *TestKit) ResultSetToResult(rs sqlexec.RecordSet, comment check.CommentInterface) *Result {
rows, err := session.GetRows4Test(context.Background(), tk.Se, rs)
return tk.ResultSetToResultWithCtx(context.Background(), rs, comment)
}

// ResultSetToResultWithCtx converts sqlexec.RecordSet to testkit.Result.
func (tk *TestKit) ResultSetToResultWithCtx(ctx context.Context, rs sqlexec.RecordSet, comment check.CommentInterface) *Result {
rows, err := session.GetRows4Test(ctx, tk.Se, rs)
tk.c.Assert(errors.ErrorStack(err), check.Equals, "", comment)
err = rs.Close()
tk.c.Assert(errors.ErrorStack(err), check.Equals, "", comment)
Expand Down

0 comments on commit 1f69481

Please sign in to comment.