diff --git a/executor/builder.go b/executor/builder.go index e2be3cadbbc82..ac491fa44e080 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -89,6 +89,9 @@ type executorBuilder struct { // ExplicitStaleness means whether the 'SELECT' clause are using 'AS OF TIMESTAMP' to perform stale read explicitly. explicitStaleness bool txnScope string + inUpdateStmt bool + inDeleteStmt bool + inInsertStmt bool } // CTEStorages stores resTbl and iterInTbl for CTEExec. @@ -781,6 +784,7 @@ func (b *executorBuilder) buildSetConfig(v *plannercore.SetConfig) Executor { } func (b *executorBuilder) buildInsert(v *plannercore.Insert) Executor { + b.inInsertStmt = true if v.SelectPlan != nil { // Try to update the forUpdateTS for insert/replace into select statements. // Set the selectPlan parameter to nil to make it always update the forUpdateTS. @@ -1387,6 +1391,12 @@ func (b *executorBuilder) buildProjection(v *plannercore.PhysicalProjection) Exe if int64(v.StatsCount()) < int64(b.ctx.GetSessionVars().MaxChunkSize) { e.numWorkers = 0 } + + // Use un-parallel projection for query that write on memdb to avoid data race. + // See also https://github.com/pingcap/tidb/issues/26832 + if b.inUpdateStmt || b.inDeleteStmt || b.inInsertStmt || b.hasLock { + e.numWorkers = 0 + } return e } @@ -1860,6 +1870,7 @@ func (b *executorBuilder) buildSplitRegion(v *plannercore.SplitRegion) Executor } func (b *executorBuilder) buildUpdate(v *plannercore.Update) Executor { + b.inUpdateStmt = true tblID2table := make(map[int64]table.Table, len(v.TblColPosInfos)) multiUpdateOnSameTable := make(map[int64]bool) for _, info := range v.TblColPosInfos { @@ -1931,6 +1942,7 @@ func getAssignFlag(ctx sessionctx.Context, v *plannercore.Update, schemaLen int) } func (b *executorBuilder) buildDelete(v *plannercore.Delete) Executor { + b.inDeleteStmt = true tblID2table := make(map[int64]table.Table, len(v.TblColPosInfos)) for _, info := range v.TblColPosInfos { tblID2table[info.TblID], _ = b.is.TableByID(info.TblID)