Skip to content

Commit 6bcd156

Browse files
authored
ddl: Corrected index management during REORGANIZE PARTITION (#56786) (#57426)
close #56634
1 parent df4b5d5 commit 6bcd156

File tree

9 files changed

+503
-157
lines changed

9 files changed

+503
-157
lines changed

pkg/ddl/partition.go

Lines changed: 47 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,9 +2185,7 @@ func (w *worker) rollbackLikeDropPartition(jobCtx *jobContext, job *model.Job) (
21852185

21862186
var dropIndices []*model.IndexInfo
21872187
for _, indexInfo := range tblInfo.Indices {
2188-
if indexInfo.Unique &&
2189-
indexInfo.State == model.StateDeleteReorganization &&
2190-
tblInfo.Partition.DDLState == model.StateDeleteReorganization {
2188+
if indexInfo.State == model.StateWriteOnly {
21912189
dropIndices = append(dropIndices, indexInfo)
21922190
}
21932191
}
@@ -3043,9 +3041,6 @@ func (w *worker) onExchangeTablePartition(jobCtx *jobContext, job *model.Job) (v
30433041
}
30443042

30453043
func getNewGlobal(partInfo *model.PartitionInfo, idx *model.IndexInfo) bool {
3046-
if len(partInfo.DDLUpdateIndexes) == 0 {
3047-
return idx.Global
3048-
}
30493044
for _, newIdx := range partInfo.DDLUpdateIndexes {
30503045
if strings.EqualFold(idx.Name.L, newIdx.IndexName) {
30513046
return newIdx.Global
@@ -3151,6 +3146,9 @@ func getReorgPartitionInfo(t *meta.Mutator, job *model.Job, args *model.TablePar
31513146
//
31523147
// Everything now looks as it should, no memory of old partitions/indexes,
31533148
// and no more double writing, since the previous state is only reading the new partitions/indexes.
3149+
//
3150+
// Note: Special handling is also required in tables.newPartitionedTable(),
3151+
// to get per partition indexes in the right state.
31543152
func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver int64, _ error) {
31553153
args, err := model.GetTablePartitionArgs(job)
31563154
if err != nil {
@@ -3262,39 +3260,33 @@ func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver
32623260
if err != nil {
32633261
return ver, errors.Trace(err)
32643262
}
3265-
if !inAllPartitionColumns {
3266-
// Currently only support Explicit Global indexes.
3267-
if !newGlobal {
3268-
job.State = model.JobStateCancelled
3269-
return ver, dbterror.ErrGlobalIndexNotExplicitlySet.GenWithStackByArgs(index.Name.O)
3270-
}
3271-
// Duplicate the unique indexes with new index ids.
3272-
// If previously was Global or will be Global:
3273-
// it must be recreated with new index ID
3274-
// TODO: Could we allow that session in StateWriteReorganization, when StateDeleteReorganization
3275-
// has started, may not find changes through the global index that sessions in StateDeleteReorganization made?
3276-
// If so, then we could avoid copying the full Global Index if it has not changed from LOCAL!
3277-
// It might be possible to use the new, not yet public partitions to access those rows?!
3278-
// Just that it would not work with explicit partition select SELECT FROM t PARTITION (p,...)
3279-
newIndex := index.Clone()
3280-
newIndex.State = model.StateDeleteOnly
3281-
newIndex.ID = AllocateIndexID(tblInfo)
3282-
newIndex.Global = true
3283-
tblInfo.Indices = append(tblInfo.Indices, newIndex)
3284-
} else {
3285-
if newGlobal {
3286-
// TODO: For the future loosen this restriction and allow global indexes for unique keys also including all partitioning columns
3287-
return ver, dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("PARTITION BY, index '%v' is unique and contains all partitioning columns, but has Global Index set", index.Name.O))
3288-
}
3289-
if index.Global {
3290-
// Index was previously Global, now it needs to be duplicated and become a local index.
3291-
newIndex := index.Clone()
3292-
newIndex.State = model.StateDeleteOnly
3293-
newIndex.ID = AllocateIndexID(tblInfo)
3294-
newIndex.Global = false
3295-
tblInfo.Indices = append(tblInfo.Indices, newIndex)
3296-
}
3263+
// Currently only support Explicit Global indexes.
3264+
if !inAllPartitionColumns && !newGlobal {
3265+
job.State = model.JobStateCancelled
3266+
return ver, dbterror.ErrGlobalIndexNotExplicitlySet.GenWithStackByArgs(index.Name.O)
3267+
}
3268+
if !index.Global && !newGlobal {
3269+
// still local index, no need to duplicate index.
3270+
continue
32973271
}
3272+
if tblInfo.Partition.DDLChangedIndex == nil {
3273+
tblInfo.Partition.DDLChangedIndex = make(map[int64]bool)
3274+
}
3275+
// Duplicate the unique indexes with new index ids.
3276+
// If previously was Global or will be Global:
3277+
// it must be recreated with new index ID
3278+
// TODO: Could we allow that session in StateWriteReorganization, when StateDeleteReorganization
3279+
// has started, may not find changes through the global index that sessions in StateDeleteReorganization made?
3280+
// If so, then we could avoid copying the full Global Index if it has not changed from LOCAL!
3281+
// It might be possible to use the new, not yet public partitions to access those rows?!
3282+
// Just that it would not work with explicit partition select SELECT FROM t PARTITION (p,...)
3283+
newIndex := index.Clone()
3284+
newIndex.State = model.StateDeleteOnly
3285+
newIndex.ID = AllocateIndexID(tblInfo)
3286+
tblInfo.Partition.DDLChangedIndex[index.ID] = false
3287+
tblInfo.Partition.DDLChangedIndex[newIndex.ID] = true
3288+
newIndex.Global = newGlobal
3289+
tblInfo.Indices = append(tblInfo.Indices, newIndex)
32983290
}
32993291
failpoint.Inject("reorgPartCancel1", func(val failpoint.Value) {
33003292
if val.(bool) {
@@ -3487,26 +3479,18 @@ func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver
34873479
if !index.Unique {
34883480
continue
34893481
}
3490-
switch index.State {
3491-
case model.StateWriteReorganization:
3482+
isNew, ok := tblInfo.Partition.DDLChangedIndex[index.ID]
3483+
if !ok {
3484+
continue
3485+
}
3486+
if isNew {
34923487
// Newly created index, replacing old unique/global index
34933488
index.State = model.StatePublic
3494-
case model.StatePublic:
3495-
if index.Global {
3496-
// Mark the old global index as non-readable, and to be dropped
3497-
index.State = model.StateDeleteReorganization
3498-
} else {
3499-
inAllPartitionColumns, err := checkPartitionKeysConstraint(partInfo, index.Columns, tblInfo)
3500-
if err != nil {
3501-
return rollbackReorganizePartitionWithErr(jobCtx, job, err)
3502-
}
3503-
if !inAllPartitionColumns {
3504-
// Mark the old unique index as non-readable, and to be dropped,
3505-
// since it is replaced by a global index
3506-
index.State = model.StateDeleteReorganization
3507-
}
3508-
}
3489+
continue
35093490
}
3491+
// Old index, should not be visible any longer,
3492+
// but needs to be kept up-to-date in case rollback happens.
3493+
index.State = model.StateWriteOnly
35103494
}
35113495
firstPartIdx, lastPartIdx, idMap, err2 := getReplacedPartitionIDs(partNames, tblInfo.Partition)
35123496
if err2 != nil {
@@ -3563,14 +3547,18 @@ func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver
35633547

35643548
var dropIndices []*model.IndexInfo
35653549
for _, indexInfo := range tblInfo.Indices {
3566-
if indexInfo.Unique && indexInfo.State == model.StateDeleteReorganization {
3550+
if indexInfo.Unique && indexInfo.State == model.StateWriteOnly {
35673551
// Drop the old unique (possible global) index, see onDropIndex
35683552
indexInfo.State = model.StateNone
35693553
DropIndexColumnFlag(tblInfo, indexInfo)
35703554
RemoveDependentHiddenColumns(tblInfo, indexInfo)
35713555
dropIndices = append(dropIndices, indexInfo)
35723556
}
35733557
}
3558+
// TODO: verify that the indexes are dropped,
3559+
// and that StateDeleteOnly+StateDeleteReorganization is not needed.
3560+
// local indexes is not an issue, since they will be gone with the dropped
3561+
// partitions, but replaced global indexes should be checked!
35743562
for _, indexInfo := range dropIndices {
35753563
removeIndexInfo(tblInfo, indexInfo)
35763564
}
@@ -3632,6 +3620,9 @@ func (w *worker) onReorganizePartition(jobCtx *jobContext, job *model.Job) (ver
36323620
failpoint.Return(ver, errors.New("Injected error by reorgPartFail5"))
36333621
}
36343622
})
3623+
failpoint.Inject("updateVersionAndTableInfoErrInStateDeleteReorganization", func() {
3624+
failpoint.Return(ver, errors.New("Injected error in StateDeleteReorganization"))
3625+
})
36353626
args.OldPhysicalTblIDs = physicalTableIDs
36363627
args.NewPartitionIDs = newIDs
36373628
ver, err = updateVersionAndTableInfo(jobCtx, job, tblInfo, true)

pkg/ddl/rollingback.go

Lines changed: 20 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,6 @@ func convertAddTablePartitionJob2RollbackJob(jobCtx *jobContext, job *model.Job,
331331
if err != nil {
332332
return ver, errors.Trace(err)
333333
}
334-
tblInfo.Partition.DDLState = model.StateNone
335-
tblInfo.Partition.DDLAction = model.ActionNone
336334
job.State = model.JobStateRollingback
337335
return ver, errors.Trace(otherwiseErr)
338336
}
@@ -371,51 +369,25 @@ func convertReorgPartitionJob2RollbackJob(jobCtx *jobContext, job *model.Job, ot
371369
partNames = append(partNames, pd.Name.L)
372370
}
373371
var dropIndices []*model.IndexInfo
374-
// When Global Index is duplicated to a non Global, we later need
375-
// to know if if it was Global before (marked to be dropped) or not.
376-
globalToUniqueDupMap := make(map[string]int64)
377372
for _, indexInfo := range tblInfo.Indices {
378373
if !indexInfo.Unique {
379374
continue
380375
}
381-
switch indexInfo.State {
382-
case model.StateWriteReorganization, model.StateDeleteOnly,
383-
model.StateWriteOnly:
384-
dropIndices = append(dropIndices, indexInfo)
385-
case model.StateDeleteReorganization:
386-
if pi.DDLState != model.StateDeleteReorganization {
387-
continue
388-
}
389-
// Old index marked to be dropped, rollback by making it public again
390-
indexInfo.State = model.StatePublic
391-
if indexInfo.Global {
392-
if id, ok := globalToUniqueDupMap[indexInfo.Name.L]; ok {
393-
return ver, errors.NewNoStackErrorf("Duplicate global index names '%s', %d != %d", indexInfo.Name.O, indexInfo.ID, id)
394-
}
395-
globalToUniqueDupMap[indexInfo.Name.L] = indexInfo.ID
396-
}
397-
case model.StatePublic:
398-
if pi.DDLState != model.StateDeleteReorganization {
399-
continue
376+
isNew, ok := pi.DDLChangedIndex[indexInfo.ID]
377+
if !ok {
378+
// non-changed index
379+
continue
380+
}
381+
if !isNew {
382+
if pi.DDLState == model.StateDeleteReorganization {
383+
// Revert the non-public state
384+
indexInfo.State = model.StatePublic
400385
}
401-
// We cannot drop the index here, we need to wait until
402-
// the next schema version
403-
// i.e. rollback in rollbackLikeDropPartition
404-
// New index that became public in this state,
405-
// mark it to be dropped in next schema version
406-
if indexInfo.Global {
407-
indexInfo.State = model.StateDeleteReorganization
386+
} else {
387+
if pi.DDLState == model.StateDeleteReorganization {
388+
indexInfo.State = model.StateWriteOnly
408389
} else {
409-
// How to know if this index was created as a duplicate or not?
410-
if id, ok := globalToUniqueDupMap[indexInfo.Name.L]; ok {
411-
// The original index
412-
if id >= indexInfo.ID {
413-
return ver, errors.NewNoStackErrorf("Indexes in wrong order during rollback, '%s', %d >= %d", indexInfo.Name.O, id, indexInfo.ID)
414-
}
415-
indexInfo.State = model.StateDeleteReorganization
416-
} else {
417-
globalToUniqueDupMap[indexInfo.Name.L] = indexInfo.ID
418-
}
390+
dropIndices = append(dropIndices, indexInfo)
419391
}
420392
}
421393
}
@@ -466,13 +438,19 @@ func convertReorgPartitionJob2RollbackJob(jobCtx *jobContext, job *model.Job, ot
466438
return ver, errors.Trace(errors.New("Internal error, failed to find original partition definitions"))
467439
}
468440
pi.Definitions = newDefs
469-
pi.Num = uint64(len(pi.Definitions))
470441
} else {
442+
// Move back to StateWriteReorganization, i.e. use the original table
443+
// (non-partitioned or differently partitioned) as the main table to use.
444+
// Otherwise, the Type does not match the expression.
471445
pi.Type, pi.DDLType = pi.DDLType, pi.Type
472446
pi.Expr, pi.DDLExpr = pi.DDLExpr, pi.Expr
473447
pi.Columns, pi.DDLColumns = pi.DDLColumns, pi.Columns
474448
pi.Definitions = pi.DroppingDefinitions
475449
}
450+
pi.Num = uint64(len(pi.Definitions))
451+
// We should move back one state, since there might be other sessions seeing the new partitions.
452+
job.SchemaState = model.StateWriteReorganization
453+
pi.DDLState = job.SchemaState
476454
}
477455

478456
args := jobCtx.jobArgs.(*model.TablePartitionArgs)

0 commit comments

Comments
 (0)