Skip to content

Commit 36e8af6

Browse files
tangentati-chi-bot
authored andcommitted
This is an automated cherry-pick of #62979
Signed-off-by: ti-chi-bot <[email protected]>
1 parent f4cf940 commit 36e8af6

File tree

7 files changed

+305
-4
lines changed

7 files changed

+305
-4
lines changed

pkg/ddl/column.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,89 @@ func updateChangingCol(col *model.ColumnInfo, newName pmodel.CIStr, newOffset in
415415
// so that there is no error in getting the default value from OriginDefaultValue.
416416
// Besides, nil data that was not backfilled in the "add column" is backfilled after the column is changed.
417417
// So it can set OriginDefaultValue to nil.
418+
<<<<<<< HEAD
418419
col.OriginDefaultValue = nil
419420
return col
421+
=======
422+
changingCol.OriginDefaultValue = nil
423+
oldCol.ChangeStateInfo = &model.ChangeStateInfo{DependencyColumnOffset: changingCol.Offset}
424+
}
425+
426+
func moveChangingColumnInfoToDest(tblInfo *model.TableInfo, oldCol, changingCol *model.ColumnInfo, pos *ast.ColumnPosition) {
427+
// Swap the old column with new column position.
428+
oldOffset := oldCol.Offset
429+
changingOffset := changingCol.Offset
430+
tblInfo.MoveColumnInfo(oldOffset, changingOffset)
431+
tblInfo.MoveColumnInfo(changingCol.Offset, oldOffset)
432+
// Move the new column to a correct offset.
433+
// The validation of the position is done in `validatePosition`.
434+
destOffset, err := LocateOffsetToMove(changingCol.Offset, pos, tblInfo)
435+
intest.AssertNoError(err)
436+
tblInfo.MoveColumnInfo(changingCol.Offset, destOffset)
437+
}
438+
439+
// moveOldColumnInfo is used to make sure the columns in TableInfo
440+
// are in correct order after the old column is changed to non-public state.
441+
func moveOldColumnInfo(tblInfo *model.TableInfo, oldCol *model.ColumnInfo) {
442+
order := []model.SchemaState{
443+
model.StatePublic,
444+
model.StateWriteReorganization,
445+
model.StateWriteOnly,
446+
model.StateDeleteReorganization,
447+
model.StateDeleteOnly,
448+
model.StateNone,
449+
}
450+
for len(order) > 0 && order[len(order)-1] != oldCol.State {
451+
order = order[:len(order)-1]
452+
}
453+
dest := len(tblInfo.Columns) - 1
454+
for i, col := range tblInfo.Columns {
455+
if col.ID == oldCol.ID {
456+
continue
457+
}
458+
if !slices.Contains(order, col.State) {
459+
dest = i
460+
break
461+
}
462+
}
463+
tblInfo.MoveColumnInfo(oldCol.Offset, dest)
464+
}
465+
466+
func moveIndexInfoToDest(tblInfo *model.TableInfo, changingCol *model.ColumnInfo,
467+
oldIdxInfos, changingIdxInfos []*model.IndexInfo) {
468+
for i, cIdx := range changingIdxInfos {
469+
hasOtherChangingCol := false
470+
for _, ic := range cIdx.Columns {
471+
idxCol := tblInfo.Columns[ic.Offset]
472+
if idxCol.ID == changingCol.ID {
473+
continue // ignore current modifying column.
474+
}
475+
if idxCol.ChangeStateInfo != nil {
476+
hasOtherChangingCol = true
477+
break
478+
}
479+
}
480+
// For the indexes that still contains other changing column, skip swaping it now.
481+
// We leave the swaping work to the last modify column job.
482+
if !hasOtherChangingCol {
483+
swapIndexInfoByID(tblInfo, oldIdxInfos[i].ID, changingIdxInfos[i].ID)
484+
}
485+
}
486+
}
487+
488+
func swapIndexInfoByID(tblInfo *model.TableInfo, idxIDA, idxIDB int64) {
489+
offsetA := 0
490+
offsetB := 0
491+
for i, idx := range tblInfo.Indices {
492+
switch idx.ID {
493+
case idxIDA:
494+
offsetA = i
495+
case idxIDB:
496+
offsetB = i
497+
}
498+
}
499+
tblInfo.Indices[offsetA], tblInfo.Indices[offsetB] = tblInfo.Indices[offsetB], tblInfo.Indices[offsetA]
500+
>>>>>>> deb4d6563df (table: keep `table.Columns` order by states during modifying column (#62979))
420501
}
421502

422503
func buildRelatedIndexInfos(tblInfo *model.TableInfo, colID int64) []*model.IndexInfo {
@@ -894,10 +975,70 @@ func (w *updateColumnWorker) BackfillData(_ context.Context, handleRange reorgBa
894975
return
895976
}
896977

978+
<<<<<<< HEAD
897979
func updateChangingObjState(changingCol *model.ColumnInfo, changingIdxs []*model.IndexInfo, schemaState model.SchemaState) {
898980
changingCol.State = schemaState
899981
for _, idx := range changingIdxs {
900982
idx.State = schemaState
983+
=======
984+
func validatePosition(tblInfo *model.TableInfo, oldCol *model.ColumnInfo, pos *ast.ColumnPosition) error {
985+
if pos != nil && pos.RelativeColumn != nil && oldCol.Name.L == pos.RelativeColumn.Name.L {
986+
// For cases like `modify column b after b`, it should report this error.
987+
return errors.Trace(infoschema.ErrColumnNotExists.GenWithStackByArgs(oldCol.Name, tblInfo.Name))
988+
}
989+
_, err := LocateOffsetToMove(oldCol.Offset, pos, tblInfo)
990+
if err != nil {
991+
return errors.Trace(err)
992+
}
993+
return nil
994+
}
995+
996+
func markOldObjectRemoving(oldCol, changingCol *model.ColumnInfo, oldIdxs, changingIdxs []*model.IndexInfo, newColName ast.CIStr) {
997+
publicName := newColName
998+
removingName := ast.NewCIStr(getRemovingObjName(oldCol.Name.O))
999+
1000+
renameColumnTo(oldCol, oldIdxs, removingName)
1001+
renameColumnTo(changingCol, changingIdxs, publicName)
1002+
for i := range oldIdxs {
1003+
oldIdxName := oldIdxs[i].Name.O
1004+
publicName := ast.NewCIStr(getRemovingObjOriginName(oldIdxName))
1005+
removingName := ast.NewCIStr(getRemovingObjName(oldIdxName))
1006+
1007+
changingIdxs[i].Name = publicName
1008+
oldIdxs[i].Name = removingName
1009+
}
1010+
}
1011+
1012+
func removeOldObjects(tblInfo *model.TableInfo, oldCol *model.ColumnInfo, oldIdxs []*model.IndexInfo) []int64 {
1013+
tblInfo.MoveColumnInfo(oldCol.Offset, len(tblInfo.Columns)-1)
1014+
tblInfo.Columns = tblInfo.Columns[:len(tblInfo.Columns)-1]
1015+
var removedIdxIDs []int64
1016+
if len(oldIdxs) > 0 {
1017+
removedIdxIDs = make([]int64, 0, len(oldIdxs))
1018+
for _, idx := range oldIdxs {
1019+
removedIdxIDs = append(removedIdxIDs, idx.ID)
1020+
}
1021+
removeOldIndexes(tblInfo, oldIdxs)
1022+
}
1023+
return removedIdxIDs
1024+
}
1025+
1026+
func renameColumnTo(col *model.ColumnInfo, idxInfos []*model.IndexInfo, newName ast.CIStr) {
1027+
for _, idx := range idxInfos {
1028+
for _, idxCol := range idx.Columns {
1029+
if idxCol.Name.L == col.Name.L {
1030+
idxCol.Name = newName
1031+
}
1032+
}
1033+
}
1034+
col.Name = newName
1035+
}
1036+
1037+
func updateObjectState(col *model.ColumnInfo, idxs []*model.IndexInfo, state model.SchemaState) {
1038+
col.State = state
1039+
for _, idx := range idxs {
1040+
idx.State = state
1041+
>>>>>>> deb4d6563df (table: keep `table.Columns` order by states during modifying column (#62979))
9011042
}
9021043
}
9031044

pkg/ddl/executor.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ import (
7777
const (
7878
expressionIndexPrefix = "_V$"
7979
changingColumnPrefix = "_Col$_"
80+
<<<<<<< HEAD
81+
=======
82+
removingObjPrefix = "_Tombstone$_"
83+
>>>>>>> deb4d6563df (table: keep `table.Columns` order by states during modifying column (#62979))
8084
changingIndexPrefix = "_Idx$_"
8185
tableNotExist = -1
8286
tinyBlobMaxLength = 255

pkg/ddl/modify_column.go

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ func (w *worker) onModifyColumn(jobCtx *jobContext, job *model.Job) (ver int64,
168168
}
169169
}
170170

171+
defer checkColumnOrderByStates(tblInfo)
171172
return w.doModifyColumnTypeWithData(
172173
jobCtx, job, dbInfo, tblInfo, changingCol, oldCol, args)
173174
}
@@ -436,7 +437,7 @@ func (w *worker) doModifyColumnTypeWithData(
436437
}
437438
}
438439
// none -> delete only
439-
updateChangingObjState(changingCol, changingIdxs, model.StateDeleteOnly)
440+
updateObjectState(changingCol, changingIdxs, model.StateDeleteOnly)
440441
failpoint.Inject("mockInsertValueAfterCheckNull", func(val failpoint.Value) {
441442
if valStr, ok := val.(string); ok {
442443
var sctx sessionctx.Context
@@ -488,7 +489,7 @@ func (w *worker) doModifyColumnTypeWithData(
488489
}
489490
}
490491
// delete only -> write only
491-
updateChangingObjState(changingCol, changingIdxs, model.StateWriteOnly)
492+
updateObjectState(changingCol, changingIdxs, model.StateWriteOnly)
492493
ver, err = updateVersionAndTableInfo(jobCtx, job, tblInfo, originalState != changingCol.State)
493494
if err != nil {
494495
return ver, errors.Trace(err)
@@ -497,7 +498,7 @@ func (w *worker) doModifyColumnTypeWithData(
497498
failpoint.InjectCall("afterModifyColumnStateDeleteOnly", job.ID)
498499
case model.StateWriteOnly:
499500
// write only -> reorganization
500-
updateChangingObjState(changingCol, changingIdxs, model.StateWriteReorganization)
501+
updateObjectState(changingCol, changingIdxs, model.StateWriteReorganization)
501502
ver, err = updateVersionAndTableInfo(jobCtx, job, tblInfo, originalState != changingCol.State)
502503
if err != nil {
503504
return ver, errors.Trace(err)
@@ -528,16 +529,65 @@ func (w *worker) doModifyColumnTypeWithData(
528529
job.State = model.JobStateRollingback
529530
return ver, errors.Trace(err)
530531
}
532+
<<<<<<< HEAD
533+
=======
534+
changingIdxInfos := buildRelatedIndexInfos(tblInfo, changingCol.ID)
535+
intest.Assert(len(oldIdxInfos) == len(changingIdxInfos))
536+
537+
updateObjectState(oldCol, oldIdxInfos, model.StateWriteOnly)
538+
updateObjectState(changingCol, changingIdxInfos, model.StatePublic)
539+
markOldObjectRemoving(oldCol, changingCol, oldIdxInfos, changingIdxInfos, colName)
540+
moveChangingColumnInfoToDest(tblInfo, oldCol, changingCol, pos)
541+
moveOldColumnInfo(tblInfo, oldCol)
542+
moveIndexInfoToDest(tblInfo, changingCol, oldIdxInfos, changingIdxInfos)
543+
updateModifyingCols(oldCol, changingCol)
544+
>>>>>>> deb4d6563df (table: keep `table.Columns` order by states during modifying column (#62979))
531545

532546
updateChangingObjState(changingCol, changingIdxs, model.StatePublic)
533547
ver, err = updateVersionAndTableInfo(jobCtx, job, tblInfo, originalState != changingCol.State)
534548
if err != nil {
535549
return ver, errors.Trace(err)
536550
}
551+
<<<<<<< HEAD
537552
modifyColumnEvent := notifier.NewModifyColumnEvent(tblInfo, []*model.ColumnInfo{changingCol})
538553
err = asyncNotifyEvent(jobCtx, modifyColumnEvent, job, noSubJob, w.sess)
539554
if err != nil {
540555
return ver, errors.Trace(err)
556+
=======
557+
case model.StatePublic:
558+
oldIdxInfos := buildRelatedIndexInfos(tblInfo, oldCol.ID)
559+
switch oldCol.State {
560+
case model.StateWriteOnly:
561+
updateObjectState(oldCol, oldIdxInfos, model.StateDeleteOnly)
562+
moveOldColumnInfo(tblInfo, oldCol)
563+
ver, err = updateVersionAndTableInfo(jobCtx, job, tblInfo, true)
564+
if err != nil {
565+
return ver, errors.Trace(err)
566+
}
567+
case model.StateDeleteOnly:
568+
removedIdxIDs := removeOldObjects(tblInfo, oldCol, oldIdxInfos)
569+
modifyColumnEvent := notifier.NewModifyColumnEvent(tblInfo, []*model.ColumnInfo{changingCol})
570+
err = asyncNotifyEvent(jobCtx, modifyColumnEvent, job, noSubJob, w.sess)
571+
if err != nil {
572+
return ver, errors.Trace(err)
573+
}
574+
575+
ver, err = updateVersionAndTableInfo(jobCtx, job, tblInfo, true)
576+
if err != nil {
577+
return ver, errors.Trace(err)
578+
}
579+
// Finish this job.
580+
job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo)
581+
// Refactor the job args to add the old index ids into delete range table.
582+
rmIdxs := append(removedIdxIDs, args.RedundantIdxs...)
583+
args.IndexIDs = rmIdxs
584+
args.PartitionIDs = getPartitionIDs(tblInfo)
585+
job.FillFinishedArgs(args)
586+
default:
587+
errMsg := fmt.Sprintf("unexpected column state %s in modify column job", oldCol.State)
588+
intest.Assert(false, errMsg)
589+
return ver, errors.Errorf(errMsg)
590+
>>>>>>> deb4d6563df (table: keep `table.Columns` order by states during modifying column (#62979))
541591
}
542592

543593
// Finish this job.
@@ -549,7 +599,6 @@ func (w *worker) doModifyColumnTypeWithData(
549599
default:
550600
err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("column", changingCol.State)
551601
}
552-
553602
return ver, errors.Trace(err)
554603
}
555604

@@ -671,6 +720,41 @@ func checkModifyColumnWithGeneratedColumnsConstraint(allCols []*table.Column, ol
671720
return nil
672721
}
673722

723+
var colStateOrd = map[model.SchemaState]int{
724+
model.StateNone: 0,
725+
model.StateDeleteOnly: 1,
726+
model.StateDeleteReorganization: 2,
727+
model.StateWriteOnly: 3,
728+
model.StateWriteReorganization: 4,
729+
model.StatePublic: 5,
730+
}
731+
732+
func checkColumnOrderByStates(tblInfo *model.TableInfo) {
733+
if intest.InTest {
734+
minState := model.StatePublic
735+
for _, col := range tblInfo.Columns {
736+
if colStateOrd[col.State] < colStateOrd[minState] {
737+
minState = col.State
738+
} else if colStateOrd[col.State] > colStateOrd[minState] {
739+
intest.Assert(false, fmt.Sprintf("column %s state %s is not in order, expect at least %s", col.Name, col.State, minState))
740+
}
741+
if col.ChangeStateInfo != nil {
742+
offset := col.ChangeStateInfo.DependencyColumnOffset
743+
intest.Assert(offset >= 0 && offset < len(tblInfo.Columns))
744+
depCol := tblInfo.Columns[offset]
745+
switch {
746+
case strings.HasPrefix(col.Name.O, changingColumnPrefix):
747+
name := getChangingColumnOriginName(col)
748+
intest.Assert(name == depCol.Name.O, "%s != %s", name, depCol.Name.O)
749+
case strings.HasPrefix(depCol.Name.O, removingObjPrefix):
750+
name := getRemovingObjOriginName(depCol.Name.O)
751+
intest.Assert(name == col.Name.O, "%s != %s", name, col.Name.O)
752+
}
753+
}
754+
}
755+
}
756+
}
757+
674758
// GetModifiableColumnJob returns a DDL job of model.ActionModifyColumn.
675759
func GetModifiableColumnJob(
676760
ctx context.Context,

pkg/ddl/multi_schema_change_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,54 @@ func TestMultiSchemaChangeMixedWithUpdate(t *testing.T) {
776776
require.NoError(t, checkErr)
777777
}
778778

779+
func TestMultiSchemaChangeModifyColumnOrderByStates(t *testing.T) {
780+
store := testkit.CreateMockStore(t)
781+
tk := testkit.NewTestKit(t, store)
782+
tk.MustExec("use test;")
783+
784+
tk.MustExec("create table t (a int, b int);")
785+
tk.MustExec("insert into t values (1, 1);")
786+
tk.MustExec("alter table t modify column b smallint, add column d int;")
787+
788+
tk.MustExec("drop table t;")
789+
tk.MustExec("create table t (a int, b int);")
790+
tk.MustExec("insert into t values (1, 1);")
791+
tk.MustExec("alter table t modify column a smallint, add column c int, modify column b smallint;")
792+
793+
tk.MustExec("drop table t;")
794+
tk.MustExec("create table t (a int, b int, c char(10));")
795+
tk.MustExec("insert into t values (1, 1, '1');")
796+
tk.MustExec("alter table t modify column c int after a, add column d int, add column e int, modify column b smallint;")
797+
798+
tk.MustExec("drop table t;")
799+
tk.MustExec("create table t (id bigint, c1 bigint, c2 bigint);")
800+
tk.MustExec("alter table t modify column c2 int after id, modify column id int after c2;")
801+
802+
tk.MustExec("drop table t;")
803+
tk.MustExec("create table t1(id bigint, c1 bigint, c2 bigint);")
804+
tk.MustExec("alter table t1 modify column c2 int, drop column id;")
805+
}
806+
807+
func TestMultiSchemaChangeDMLUpdate(t *testing.T) {
808+
store := testkit.CreateMockStore(t)
809+
tk := testkit.NewTestKit(t, store)
810+
811+
tk.MustExec("use test")
812+
tk.MustExec("create table t(a int, b int, c int, d int)")
813+
814+
testfailpoint.EnableCall(t, "github.com/pingcap/tidb/pkg/ddl/afterWaitSchemaSynced", func(job *model.Job) {
815+
tk := testkit.NewTestKit(t, store)
816+
tk.MustExec("use test")
817+
tk.MustExec("insert into t(a, c) values (1, 2), (2, 3), (3, 4), (4, 5)")
818+
tk.MustExec("update t set c = 5 where a = 1")
819+
tk.MustExec("delete from t")
820+
})
821+
tk.MustExec("alter table t change column b e int unsigned, change column d f int unsigned")
822+
testfailpoint.Disable(t, "github.com/pingcap/tidb/pkg/ddl/afterWaitSchemaSynced")
823+
824+
tk.MustExec("drop table t")
825+
}
826+
779827
func TestMultiSchemaChangeBlockedByRowLevelChecksum(t *testing.T) {
780828
store := testkit.CreateMockStore(t)
781829

0 commit comments

Comments
 (0)