@@ -419,7 +419,7 @@ func (a *ExecStmt) PointGet(ctx context.Context) (*recordSet, error) {
419419 }
420420
421421 if executor == nil {
422- b := newExecutorBuilder (a .Ctx , a .InfoSchema , a .Ti )
422+ b := newExecutorBuilder (ctx , a .Ctx , a .InfoSchema , a .Ti )
423423 executor = b .build (a .Plan )
424424 if b .err != nil {
425425 return nil , b .err
@@ -678,7 +678,7 @@ func (a *ExecStmt) Exec(ctx context.Context) (_ sqlexec.RecordSet, err error) {
678678 execStartTime = time .Now ()
679679 }
680680
681- e , err := a .buildExecutor ()
681+ e , err := a .buildExecutor (ctx )
682682 if err != nil {
683683 return nil , err
684684 }
@@ -1281,6 +1281,11 @@ func (a *ExecStmt) handlePessimisticDML(ctx context.Context, e exec.Executor) (e
12811281
12821282 // acquire xlocks
12831283 keys = txnCtx .CollectUnchangedKeysForXLock (keys )
1284+ sharedKeys := txnCtx .CollectUnchangedKeysForSLock (nil )
1285+ keys , sharedKeys , err = moveWrittenSharedLockKeysToExclusive (ctx , txn , keys , sharedKeys )
1286+ if err != nil {
1287+ return err
1288+ }
12841289 if ex , err := tryLockKeys (e , keys , false ); err != nil {
12851290 return err
12861291 } else if ex != nil {
@@ -1289,9 +1294,8 @@ func (a *ExecStmt) handlePessimisticDML(ctx context.Context, e exec.Executor) (e
12891294 }
12901295
12911296 // acquire slocks
1292- keys = txnCtx .CollectUnchangedKeysForSLock (keys [:0 ])
12931297 startLock := time .Now ()
1294- if ex , err := tryLockKeys (e , keys , true ); err != nil {
1298+ if ex , err := tryLockKeys (e , sharedKeys , true ); err != nil {
12951299 return err
12961300 } else if ex != nil {
12971301 e = ex
@@ -1303,6 +1307,52 @@ func (a *ExecStmt) handlePessimisticDML(ctx context.Context, e exec.Executor) (e
13031307 }
13041308}
13051309
1310+ func moveWrittenSharedLockKeysToExclusive (
1311+ ctx context.Context ,
1312+ txn kv.Transaction ,
1313+ exclusiveKeys []kv.Key ,
1314+ sharedKeys []kv.Key ,
1315+ ) (finalExclusiveKeys []kv.Key , finalSharedKeys []kv.Key , err error ) {
1316+ if len (sharedKeys ) == 0 {
1317+ return exclusiveKeys , sharedKeys , nil
1318+ }
1319+
1320+ exclusiveKeySet := make (map [string ]struct {}, len (exclusiveKeys ))
1321+ for _ , key := range exclusiveKeys {
1322+ exclusiveKeySet [string (key )] = struct {}{}
1323+ }
1324+
1325+ memBuffer := txn .GetMemBuffer ()
1326+ memBuffer .RLock ()
1327+ defer memBuffer .RUnlock ()
1328+
1329+ // sharedKeys is collected locally for this lock phase, so reuse its buffer
1330+ // for the filtered shared-only result.
1331+ sharedOnlyKeys := sharedKeys [:0 ]
1332+ for _ , key := range sharedKeys {
1333+ if _ , ok := exclusiveKeySet [string (key )]; ok {
1334+ continue
1335+ }
1336+
1337+ // A key written by this transaction must not be protected only by a
1338+ // shared pessimistic lock, otherwise commit prewrite would put over
1339+ // its own shared pessimistic lock.
1340+ _ , err := memBuffer .GetLocal (ctx , key )
1341+ if err == nil {
1342+ exclusiveKeys = append (exclusiveKeys , key )
1343+ exclusiveKeySet [string (key )] = struct {}{}
1344+ continue
1345+ }
1346+ if ! kv .ErrNotExist .Equal (err ) {
1347+ return nil , nil , err
1348+ }
1349+
1350+ sharedOnlyKeys = append (sharedOnlyKeys , key )
1351+ }
1352+
1353+ return exclusiveKeys , sharedOnlyKeys , nil
1354+ }
1355+
13061356// updateFKCheckLockStats updates the Lock stats of FK check executors after the deferred
13071357// pessimistic lock phase completes. In pessimistic mode, FK check keys are not locked
13081358// inline during doCheck but deferred to handlePessimisticDML. This function attributes
@@ -1371,7 +1421,7 @@ func (a *ExecStmt) handlePessimisticLockError(ctx context.Context, lockErr error
13711421 a .resetPhaseDurations ()
13721422
13731423 a .inheritContextFromExecuteStmt ()
1374- e , err := a .buildExecutor ()
1424+ e , err := a .buildExecutor (ctx )
13751425 if err != nil {
13761426 return nil , err
13771427 }
@@ -1397,28 +1447,28 @@ type pessimisticTxn interface {
13971447}
13981448
13991449// buildExecutor build an executor from plan, prepared statement may need additional procedure.
1400- func (a * ExecStmt ) buildExecutor () (exec.Executor , error ) {
1450+ func (a * ExecStmt ) buildExecutor (ctx context. Context ) (exec.Executor , error ) {
14011451 defer func (start time.Time ) { a .phaseBuildDurations [0 ] += time .Since (start ) }(time .Now ())
1402- ctx := a .Ctx
1403- stmtCtx := ctx .GetSessionVars ().StmtCtx
1452+ sctx := a .Ctx
1453+ stmtCtx := sctx .GetSessionVars ().StmtCtx
14041454 if _ , ok := a .Plan .(* plannercore.Execute ); ! ok {
14051455 if stmtCtx .Priority == mysql .NoPriority && a .LowerPriority {
14061456 stmtCtx .Priority = kv .PriorityLow
14071457 }
14081458 }
1409- if _ , ok := a .Plan .(* plannercore.Analyze ); ok && ctx .GetSessionVars ().InRestrictedSQL {
1410- ctx .GetSessionVars ().StmtCtx .Priority = kv .PriorityLow
1459+ if _ , ok := a .Plan .(* plannercore.Analyze ); ok && sctx .GetSessionVars ().InRestrictedSQL {
1460+ sctx .GetSessionVars ().StmtCtx .Priority = kv .PriorityLow
14111461 }
14121462
1413- b := newExecutorBuilder (ctx , a .InfoSchema , a .Ti )
1463+ b := newExecutorBuilder (ctx , sctx , a .InfoSchema , a .Ti )
14141464 e := b .build (a .Plan )
14151465 if b .err != nil {
14161466 return nil , errors .Trace (b .err )
14171467 }
14181468
14191469 failpoint .Inject ("assertTxnManagerAfterBuildExecutor" , func () {
14201470 sessiontxn .RecordAssert (a .Ctx , "assertTxnManagerAfterBuildExecutor" , true )
1421- sessiontxn .AssertTxnManagerInfoSchema (b .ctx , b .is )
1471+ sessiontxn .AssertTxnManagerInfoSchema (b .sctx , b .is )
14221472 })
14231473
14241474 // ExecuteExec is not a real Executor, we only use it to build another Executor from a prepared statement.
@@ -1428,7 +1478,7 @@ func (a *ExecStmt) buildExecutor() (exec.Executor, error) {
14281478 return nil , err
14291479 }
14301480 if executorExec .lowerPriority {
1431- ctx .GetSessionVars ().StmtCtx .Priority = kv .PriorityLow
1481+ sctx .GetSessionVars ().StmtCtx .Priority = kv .PriorityLow
14321482 }
14331483 e = executorExec .stmtExec
14341484 }
0 commit comments