Skip to content

Commit

Permalink
executor: handle the corner case that temp index is not exist but the…
Browse files Browse the repository at this point in the history
… normal index is exist (#51862) (#56092)

close #51784
  • Loading branch information
ti-chi-bot authored Sep 17, 2024
1 parent af6aa91 commit 349f534
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 33 deletions.
40 changes: 40 additions & 0 deletions ddl/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import (
"github.com/pingcap/tidb/util/dbterror"
"github.com/pingcap/tidb/util/mock"
"github.com/pingcap/tidb/util/sqlexec"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tikv/client-go/v2/oracle"
"github.com/tikv/client-go/v2/tikv"
Expand Down Expand Up @@ -1817,3 +1818,42 @@ func TestMDLTruncateTable(t *testing.T) {
require.True(t, timetk2.After(timeMain))
require.True(t, timetk3.After(timeMain))
}

func TestInsertIgnore(t *testing.T) {
store, dom := testkit.CreateMockStoreAndDomain(t)

tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t(a smallint(6) DEFAULT '-13202', b varchar(221) NOT NULL DEFAULT 'duplicatevalue', " +
"c tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (c, b));")

tk1 := testkit.NewTestKit(t, store)
tk1.MustExec("use test")

d := dom.DDL()
originalCallback := d.GetHook()
defer d.SetHook(originalCallback)
callback := &ddl.TestDDLCallback{}

onJobUpdatedExportedFunc := func(job *model.Job) {
switch job.SchemaState {
case model.StateDeleteOnly:
_, err := tk1.Exec("INSERT INTO t VALUES (-18585,'aaa',1), (-18585,'0',1), (-18585,'1',1), (-18585,'duplicatevalue',1);")
assert.NoError(t, err)
case model.StateWriteReorganization:
tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t"))
assert.NoError(t, err)
idx := tbl.Meta().FindIndexByName("idx")
if idx.BackfillState == model.BackfillStateReadyToMerge {
_, err := tk1.Exec("insert ignore into `t` values ( 234,'duplicatevalue',-2028 );")
assert.NoError(t, err)
return
}
}
}
callback.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc)
d.SetHook(callback)

tk.MustExec("alter table t add unique index idx(b);")
tk.MustExec("admin check table t;")
}
87 changes: 54 additions & 33 deletions executor/insert_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,29 @@ func (e *InsertValues) collectRuntimeStatsEnabled() bool {
return false
}

func (e *InsertValues) handleDuplicateKey(ctx context.Context, txn kv.Transaction, uk *keyValueWithDupInfo, replace bool, r toBeCheckedRow) (bool, error) {
if !replace {
e.ctx.GetSessionVars().StmtCtx.AppendWarning(uk.dupErr)
if txnCtx := e.ctx.GetSessionVars().TxnCtx; txnCtx.IsPessimistic {
// lock duplicated row key on insert-ignore
txnCtx.AddUnchangedRowKey(uk.newKey)
}
return true, nil
}
_, handle, err := tables.FetchDuplicatedHandle(ctx, uk.newKey, true, txn, e.Table.Meta().ID, uk.commonHandle)
if err != nil {
return false, err
}
if handle == nil {
return false, nil
}
_, err = e.removeRow(ctx, txn, handle, r, true)
if err != nil {
return false, err
}
return false, nil
}

// batchCheckAndInsert checks rows with duplicate errors.
// All duplicate rows will be ignored and appended as duplicate warnings.
func (e *InsertValues) batchCheckAndInsert(ctx context.Context, rows [][]types.Datum,
Expand Down Expand Up @@ -1177,53 +1200,51 @@ func (e *InsertValues) batchCheckAndInsert(ctx context.Context, rows [][]types.D
return err
}
}
skip := false
rowInserted := false
for _, uk := range r.uniqueKeys {
_, err := txn.Get(ctx, uk.newKey)
if err != nil && !kv.IsErrNotFound(err) {
return err
}
if err == nil {
if replace {
_, handle, err := tables.FetchDuplicatedHandle(
ctx,
uk.newKey,
true,
txn,
e.Table.Meta().ID,
uk.commonHandle,
)
if err != nil {
return err
}
if handle == nil {
continue
}
_, err = e.removeRow(ctx, txn, handle, r, true)
rowInserted, err = e.handleDuplicateKey(ctx, txn, uk, replace, r)
if err != nil {
return err
}
if rowInserted {
break
}
continue
}
if tablecodec.IsTempIndexKey(uk.newKey) {
tablecodec.TempIndexKey2IndexKey(uk.newKey)
_, err = txn.Get(ctx, uk.newKey)
if err != nil && !kv.IsErrNotFound(err) {
return err
}
if err == nil {
rowInserted, err = e.handleDuplicateKey(ctx, txn, uk, replace, r)
if err != nil {
return err
}
} else {
// If duplicate keys were found in BatchGet, mark row = nil.
e.ctx.GetSessionVars().StmtCtx.AppendWarning(uk.dupErr)
if txnCtx := e.ctx.GetSessionVars().TxnCtx; txnCtx.IsPessimistic {
// lock duplicated unique key on insert-ignore
txnCtx.AddUnchangedRowKey(uk.newKey)
if rowInserted {
break
}
skip = true
break
}
} else if !kv.IsErrNotFound(err) {
return err
}
}

if rowInserted {
continue
}

// If row was checked with no duplicate keys,
// it should be add to values map for the further row check.
// There may be duplicate keys inside the insert statement.
if !skip {
e.ctx.GetSessionVars().StmtCtx.AddCopiedRows(1)
err = addRecord(ctx, rows[i])
if err != nil {
return err
}
e.ctx.GetSessionVars().StmtCtx.AddCopiedRows(1)
err = addRecord(ctx, rows[i])
if err != nil {
return err
}
}
if e.stats != nil {
Expand Down

0 comments on commit 349f534

Please sign in to comment.