Skip to content

Commit 7236665

Browse files
zyguanti-chi-bot
authored andcommitted
This is an automated cherry-pick of pingcap#42210
Signed-off-by: ti-chi-bot <ti-community-prow-bot@tidb.io>
1 parent 36a9810 commit 7236665

File tree

4 files changed

+156
-17
lines changed

4 files changed

+156
-17
lines changed

executor/insert_common.go

+48
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,10 @@ func (e *InsertValues) batchCheckAndInsert(ctx context.Context, rows [][]types.D
10991099
}
11001100
} else {
11011101
e.ctx.GetSessionVars().StmtCtx.AppendWarning(r.handleKey.dupErr)
1102+
if txnCtx := e.ctx.GetSessionVars().TxnCtx; txnCtx.IsPessimistic {
1103+
// lock duplicated row key on insert-ignore
1104+
txnCtx.AddUnchangedRowKey(r.handleKey.newKey)
1105+
}
11021106
continue
11031107
}
11041108
} else if !kv.IsErrNotFound(err) {
@@ -1108,12 +1112,45 @@ func (e *InsertValues) batchCheckAndInsert(ctx context.Context, rows [][]types.D
11081112
for _, uk := range r.uniqueKeys {
11091113
_, err := txn.Get(ctx, uk.newKey)
11101114
if err == nil {
1115+
<<<<<<< HEAD
11111116
// If duplicate keys were found in BatchGet, mark row = nil.
11121117
e.ctx.GetSessionVars().StmtCtx.AppendWarning(uk.dupErr)
11131118
skip = true
11141119
break
11151120
}
11161121
if !kv.IsErrNotFound(err) {
1122+
=======
1123+
if replace {
1124+
_, handle, err := tables.FetchDuplicatedHandle(
1125+
ctx,
1126+
uk.newKey,
1127+
true,
1128+
txn,
1129+
e.Table.Meta().ID,
1130+
uk.commonHandle,
1131+
)
1132+
if err != nil {
1133+
return err
1134+
}
1135+
if handle == nil {
1136+
continue
1137+
}
1138+
_, err = e.removeRow(ctx, txn, handle, r, true)
1139+
if err != nil {
1140+
return err
1141+
}
1142+
} else {
1143+
// If duplicate keys were found in BatchGet, mark row = nil.
1144+
e.ctx.GetSessionVars().StmtCtx.AppendWarning(uk.dupErr)
1145+
if txnCtx := e.ctx.GetSessionVars().TxnCtx; txnCtx.IsPessimistic {
1146+
// lock duplicated unique key on insert-ignore
1147+
txnCtx.AddUnchangedRowKey(uk.newKey)
1148+
}
1149+
skip = true
1150+
break
1151+
}
1152+
} else if !kv.IsErrNotFound(err) {
1153+
>>>>>>> 24cd54c7462 (executor: lock duplicated keys on insert-ignore & replace-nothing (#42210))
11171154
return err
11181155
}
11191156
}
@@ -1158,7 +1195,18 @@ func (e *InsertValues) removeRow(ctx context.Context, txn kv.Transaction, r toBe
11581195
return err
11591196
}
11601197
if identical {
1198+
<<<<<<< HEAD
11611199
return nil
1200+
=======
1201+
if inReplace {
1202+
e.ctx.GetSessionVars().StmtCtx.AddAffectedRows(1)
1203+
}
1204+
_, err := appendUnchangedRowForLock(e.ctx, r.t, handle, oldRow)
1205+
if err != nil {
1206+
return false, err
1207+
}
1208+
return true, nil
1209+
>>>>>>> 24cd54c7462 (executor: lock duplicated keys on insert-ignore & replace-nothing (#42210))
11621210
}
11631211

11641212
err = r.t.RemoveRecord(e.ctx, handle, oldRow)

executor/insert_test.go

+87
Original file line numberDiff line numberDiff line change
@@ -1940,3 +1940,90 @@ func TestInsertIntoSelectError(t *testing.T) {
19401940
tk.MustQuery("SELECT * FROM t1;").Check(testkit.Rows("0", "0", "0"))
19411941
tk.MustExec("DROP TABLE t1;")
19421942
}
1943+
<<<<<<< HEAD
1944+
=======
1945+
1946+
// https://github.com/pingcap/tidb/issues/32213.
1947+
func TestIssue32213(t *testing.T) {
1948+
store := testkit.CreateMockStore(t)
1949+
tk := testkit.NewTestKit(t, store)
1950+
tk.MustExec(`use test`)
1951+
1952+
tk.MustExec("create table test.t1(c1 float)")
1953+
tk.MustExec("insert into test.t1 values(999.99)")
1954+
tk.MustQuery("select cast(test.t1.c1 as decimal(4, 1)) from test.t1").Check(testkit.Rows("999.9"))
1955+
tk.MustQuery("select cast(test.t1.c1 as decimal(5, 1)) from test.t1").Check(testkit.Rows("1000.0"))
1956+
1957+
tk.MustExec("drop table if exists test.t1")
1958+
tk.MustExec("create table test.t1(c1 decimal(6, 4))")
1959+
tk.MustExec("insert into test.t1 values(99.9999)")
1960+
tk.MustQuery("select cast(test.t1.c1 as decimal(5, 3)) from test.t1").Check(testkit.Rows("99.999"))
1961+
tk.MustQuery("select cast(test.t1.c1 as decimal(6, 3)) from test.t1").Check(testkit.Rows("100.000"))
1962+
}
1963+
1964+
func TestInsertLock(t *testing.T) {
1965+
store := testkit.CreateMockStore(t)
1966+
tk1 := testkit.NewTestKit(t, store)
1967+
tk2 := testkit.NewTestKit(t, store)
1968+
tk1.MustExec("use test")
1969+
tk2.MustExec("use test")
1970+
1971+
for _, tt := range []struct {
1972+
name string
1973+
ddl string
1974+
dml string
1975+
}{
1976+
{
1977+
"replace-pk",
1978+
"create table t (c int primary key clustered)",
1979+
"replace into t values (1)",
1980+
},
1981+
{
1982+
"replace-uk",
1983+
"create table t (c int unique key)",
1984+
"replace into t values (1)",
1985+
},
1986+
{
1987+
"insert-ingore-pk",
1988+
"create table t (c int primary key clustered)",
1989+
"insert ignore into t values (1)",
1990+
},
1991+
{
1992+
"insert-ingore-uk",
1993+
"create table t (c int unique key)",
1994+
"insert ignore into t values (1)",
1995+
},
1996+
{
1997+
"insert-update-pk",
1998+
"create table t (c int primary key clustered)",
1999+
"insert into t values (1) on duplicate key update c = values(c)",
2000+
},
2001+
{
2002+
"insert-update-uk",
2003+
"create table t (c int unique key)",
2004+
"insert into t values (1) on duplicate key update c = values(c)",
2005+
},
2006+
} {
2007+
t.Run(tt.name, func(t *testing.T) {
2008+
tk1.MustExec("drop table if exists t")
2009+
tk1.MustExec(tt.ddl)
2010+
tk1.MustExec("insert into t values (1)")
2011+
tk1.MustExec("begin")
2012+
tk1.MustExec(tt.dml)
2013+
done := make(chan struct{})
2014+
go func() {
2015+
tk2.MustExec("delete from t")
2016+
done <- struct{}{}
2017+
}()
2018+
select {
2019+
case <-done:
2020+
require.Failf(t, "txn2 is not blocked by %q", tt.dml)
2021+
case <-time.After(100 * time.Millisecond):
2022+
}
2023+
tk1.MustExec("commit")
2024+
<-done
2025+
tk1.MustQuery("select * from t").Check([][]interface{}{})
2026+
})
2027+
}
2028+
}
2029+
>>>>>>> 24cd54c7462 (executor: lock duplicated keys on insert-ignore & replace-nothing (#42210))

executor/write.go

+20-16
Original file line numberDiff line numberDiff line change
@@ -138,22 +138,8 @@ func updateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, old
138138
if sctx.GetSessionVars().ClientCapability&mysql.ClientFoundRows > 0 {
139139
sc.AddAffectedRows(1)
140140
}
141-
142-
physicalID := t.Meta().ID
143-
if pt, ok := t.(table.PartitionedTable); ok {
144-
p, err := pt.GetPartitionByRow(sctx, oldData)
145-
if err != nil {
146-
return false, err
147-
}
148-
physicalID = p.GetPhysicalID()
149-
}
150-
151-
unchangedRowKey := tablecodec.EncodeRowKeyWithHandle(physicalID, h)
152-
txnCtx := sctx.GetSessionVars().TxnCtx
153-
if txnCtx.IsPessimistic {
154-
txnCtx.AddUnchangedRowKey(unchangedRowKey)
155-
}
156-
return false, nil
141+
_, err := appendUnchangedRowForLock(sctx, t, h, oldData)
142+
return false, err
157143
}
158144

159145
// 4. Fill values into on-update-now fields, only if they are really changed.
@@ -219,6 +205,24 @@ func updateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, old
219205
return true, nil
220206
}
221207

208+
func appendUnchangedRowForLock(sctx sessionctx.Context, t table.Table, h kv.Handle, row []types.Datum) (bool, error) {
209+
txnCtx := sctx.GetSessionVars().TxnCtx
210+
if !txnCtx.IsPessimistic {
211+
return false, nil
212+
}
213+
physicalID := t.Meta().ID
214+
if pt, ok := t.(table.PartitionedTable); ok {
215+
p, err := pt.GetPartitionByRow(sctx, row)
216+
if err != nil {
217+
return false, err
218+
}
219+
physicalID = p.GetPhysicalID()
220+
}
221+
unchangedRowKey := tablecodec.EncodeRowKeyWithHandle(physicalID, h)
222+
txnCtx.AddUnchangedRowKey(unchangedRowKey)
223+
return true, nil
224+
}
225+
222226
func rebaseAutoRandomValue(ctx context.Context, sctx sessionctx.Context, t table.Table, newData *types.Datum, col *table.Column) error {
223227
tableInfo := t.Meta()
224228
if !tableInfo.ContainsAutoRandomBits() {

session/pessimistic_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ func (s *testPessimisticSuite) TestOptimisticConflicts(c *C) {
476476
tk.MustExec("begin pessimistic")
477477
// This SQL use BatchGet and cache data in the txn snapshot.
478478
// It can be changed to other SQLs that use BatchGet.
479-
tk.MustExec("insert ignore into conflict values (1, 2)")
479+
tk.MustExec("select * from conflict where id in (1, 2, 3)")
480480

481481
tk2.MustExec("update conflict set c = c - 1")
482482

0 commit comments

Comments
 (0)