diff --git a/ddl/db_partition_test.go b/ddl/db_partition_test.go index d193819d05abb..67f5a53574e88 100644 --- a/ddl/db_partition_test.go +++ b/ddl/db_partition_test.go @@ -2103,6 +2103,18 @@ func TestAlterTableExchangePartition(t *testing.T) { require.NoError(t, err) tk.MustExec("alter table e15 exchange partition p0 with table e16") + + tk.MustExec("create table e17 (a int)") + tk.MustExec("alter table e17 set tiflash replica 1") + tk.MustExec("insert into e17 values (1)") + + tk.MustExec("create table e18 (a int) partition by range (a) (partition p0 values less than (4), partition p1 values less than (10))") + tk.MustExec("alter table e18 set tiflash replica 1") + tk.MustExec("insert into e18 values (2)") + + tk.MustExec("alter table e18 exchange partition p0 with table e17") + tk.MustQuery("select * /*+ read_from_storage(tiflash[e18]) */ from e18").Check(testkit.Rows("1")) + tk.MustQuery("select * /*+ read_from_storage(tiflash[e17]) */ from e17").Check(testkit.Rows("2")) } func TestExchangePartitionTableCompatiable(t *testing.T) { @@ -2377,6 +2389,35 @@ func TestExchangePartitionHook(t *testing.T) { tk.MustQuery("select * from pt partition(p0)").Check(testkit.Rows("1")) } +func TestExchangePartitionAutoID(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("set @@tidb_enable_exchange_partition=1") + defer tk.MustExec("set @@tidb_enable_exchange_partition=0") + + tk.MustExec("use test") + tk.MustExec(`create table pt (a int primary key auto_increment) partition by range(a) ( + partition p0 values less than (3), + partition p1 values less than (6), + PARTITION p2 values less than (9), + PARTITION p3 values less than (50000000) + );`) + tk.MustExec(`create table nt(a int primary key auto_increment);`) + tk.MustExec(`insert into pt values (0), (4)`) + tk.MustExec("insert into nt values (1)") + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/exchangePartitionAutoID", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/exchangePartitionAutoID")) + }() + + tk.MustExec("alter table pt exchange partition p0 with table nt") + tk.MustExec("insert into nt values (NULL)") + tk.MustQuery("select count(*) from nt where a >= 4000000").Check(testkit.Rows("1")) + tk.MustQuery("select count(*) from pt where a >= 4000000").Check(testkit.Rows("1")) +} + func TestExchangePartitionExpressIndex(t *testing.T) { restore := config.RestoreFunc() defer restore() diff --git a/ddl/partition.go b/ddl/partition.go index 6463c9c243b40..00d8ae62e4ea1 100644 --- a/ddl/partition.go +++ b/ddl/partition.go @@ -2040,6 +2040,21 @@ func (w *worker) onExchangeTablePartition(d *ddlCtx, t *meta.Meta, job *model.Jo return ver, errors.Trace(err) } + failpoint.Inject("exchangePartitionAutoID", func(val failpoint.Value) { + if val.(bool) { + se, err := w.sessPool.get() + defer w.sessPool.put(se) + if err != nil { + failpoint.Return(ver, err) + } + sess := newSession(se) + _, err = sess.execute(context.Background(), "insert into test.pt values (40000000)", "exchange_partition_test") + if err != nil { + failpoint.Return(ver, err) + } + } + }) + err = checkExchangePartitionPlacementPolicy(t, partDef.PlacementPolicyRef, nt.PlacementPolicyRef) if err != nil { job.State = model.JobStateCancelled diff --git a/infoschema/builder.go b/infoschema/builder.go index 4e7b767aecdb0..9ac3a203151a4 100644 --- a/infoschema/builder.go +++ b/infoschema/builder.go @@ -15,6 +15,7 @@ package infoschema import ( + "context" "fmt" "strings" @@ -32,6 +33,7 @@ import ( "github.com/pingcap/tidb/table/tables" "github.com/pingcap/tidb/util/domainutil" "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/util/mathutil" "github.com/pingcap/tidb/util/sqlexec" "go.uber.org/zap" "golang.org/x/exp/slices" @@ -287,6 +289,40 @@ func (b *Builder) applyRecoverTable(m *meta.Meta, diff *model.SchemaDiff) ([]int return tblIDs, nil } +func updateAutoIDForExchangePartition(store kv.Storage, ptSchemaID, ptID, ntSchemaID, ntID int64) error { + err := kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + ptAutoIDs, err := t.GetAutoIDAccessors(ptSchemaID, ptID).Get() + if err != nil { + return err + } + + // non-partition table auto IDs. + ntAutoIDs, err := t.GetAutoIDAccessors(ntSchemaID, ntID).Get() + if err != nil { + return err + } + + // Set both tables to the maximum auto IDs between normal table and partitioned table. + newAutoIDs := meta.AutoIDGroup{ + RowID: mathutil.Max(ptAutoIDs.RowID, ntAutoIDs.RowID), + IncrementID: mathutil.Max(ptAutoIDs.IncrementID, ntAutoIDs.IncrementID), + RandomID: mathutil.Max(ptAutoIDs.RandomID, ntAutoIDs.RandomID), + } + err = t.GetAutoIDAccessors(ptSchemaID, ptID).Put(newAutoIDs) + if err != nil { + return err + } + err = t.GetAutoIDAccessors(ntSchemaID, ntID).Put(newAutoIDs) + if err != nil { + return err + } + return nil + }) + + return err +} + func (b *Builder) applyDefaultAction(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { tblIDs, err := b.applyTableUpdate(m, diff) if err != nil { @@ -308,6 +344,14 @@ func (b *Builder) applyDefaultAction(m *meta.Meta, diff *model.SchemaDiff) ([]in return nil, errors.Trace(err) } tblIDs = append(tblIDs, affectedIDs...) + + if diff.Type == model.ActionExchangeTablePartition { + // handle partition table and table AutoID + err = updateAutoIDForExchangePartition(b.store, affectedDiff.SchemaID, affectedDiff.TableID, diff.SchemaID, diff.TableID) + if err != nil { + return nil, errors.Trace(err) + } + } } return tblIDs, nil