Skip to content

Commit 7f632be

Browse files
authored
ddl: add ttl related jobs / execution (#39298)
close #39269, close #39270, close #39271
1 parent 68178d6 commit 7f632be

19 files changed

+710
-1
lines changed

build/nogo_config.json

+2
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,8 @@
365365
"ddl/backfilling.go": "ddl/backfilling.go",
366366
"ddl/column.go": "ddl/column.go",
367367
"ddl/index.go": "ddl/index.go",
368+
"ddl/ttl.go": "ddl/ttl.go",
369+
"ddl/ttl_test.go": "ddl/ttl_test.go",
368370
"ddl/ingest/": "ddl/ingest/",
369371
"expression/builtin_cast.go": "expression/builtin_cast code",
370372
"server/conn.go": "server/conn.go",

ddl/BUILD.bazel

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ go_library(
4545
"stat.go",
4646
"table.go",
4747
"table_lock.go",
48+
"ttl.go",
4849
],
4950
importpath = "github.com/pingcap/tidb/ddl",
5051
visibility = [
@@ -195,6 +196,7 @@ go_test(
195196
"table_split_test.go",
196197
"table_test.go",
197198
"tiflash_replica_test.go",
199+
"ttl_test.go",
198200
],
199201
embed = [":ddl"],
200202
flaky = True,

ddl/column.go

+18
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@ func checkDropColumn(d *ddlCtx, t *meta.Meta, job *model.Job) (*model.TableInfo,
342342
if err = checkDropColumnWithForeignKeyConstraintInOwner(d, t, job, tblInfo, colName.L); err != nil {
343343
return nil, nil, nil, false, errors.Trace(err)
344344
}
345+
if err = checkDropColumnWithTTLConfig(tblInfo, colName.L); err != nil {
346+
return nil, nil, nil, false, errors.Trace(err)
347+
}
345348
idxInfos := listIndicesWithColumn(colName.L, tblInfo.Indices)
346349
return tblInfo, colInfo, idxInfos, false, nil
347350
}
@@ -858,6 +861,9 @@ func adjustTableInfoAfterModifyColumnWithData(tblInfo *model.TableInfo, pos *ast
858861
indexesToRemove := filterIndexesToRemove(changingIdxs, newName, tblInfo)
859862
replaceOldIndexes(tblInfo, indexesToRemove)
860863
}
864+
if tblInfo.TTLInfo != nil {
865+
updateTTLInfoWhenModifyColumn(tblInfo, oldCol.Name, changingCol.Name)
866+
}
861867
// Move the new column to a correct offset.
862868
destOffset, err := LocateOffsetToMove(changingCol.Offset, pos, tblInfo)
863869
if err != nil {
@@ -932,6 +938,17 @@ func updateFKInfoWhenModifyColumn(tblInfo *model.TableInfo, oldCol, newCol model
932938
}
933939
}
934940

941+
func updateTTLInfoWhenModifyColumn(tblInfo *model.TableInfo, oldCol, newCol model.CIStr) {
942+
if oldCol.L == newCol.L {
943+
return
944+
}
945+
if tblInfo.TTLInfo != nil {
946+
if tblInfo.TTLInfo.ColumnName.L == oldCol.L {
947+
tblInfo.TTLInfo.ColumnName = newCol
948+
}
949+
}
950+
}
951+
935952
// filterIndexesToRemove filters out the indexes that can be removed.
936953
func filterIndexesToRemove(changingIdxs []*model.IndexInfo, colName model.CIStr, tblInfo *model.TableInfo) []*model.IndexInfo {
937954
indexesToRemove := make([]*model.IndexInfo, 0, len(changingIdxs))
@@ -1474,6 +1491,7 @@ func adjustTableInfoAfterModifyColumn(
14741491
tblInfo.MoveColumnInfo(oldCol.Offset, destOffset)
14751492
updateNewIdxColsNameOffset(tblInfo.Indices, oldCol.Name, newCol)
14761493
updateFKInfoWhenModifyColumn(tblInfo, oldCol.Name, newCol.Name)
1494+
updateTTLInfoWhenModifyColumn(tblInfo, oldCol.Name, newCol.Name)
14771495
return nil
14781496
}
14791497

ddl/ddl_api.go

+150
Original file line numberDiff line numberDiff line change
@@ -2115,6 +2115,11 @@ func checkTableInfoValidWithStmt(ctx sessionctx.Context, tbInfo *model.TableInfo
21152115
}
21162116
}
21172117
}
2118+
if tbInfo.TTLInfo != nil {
2119+
if err := checkTTLInfoValid(ctx, tbInfo); err != nil {
2120+
return errors.Trace(err)
2121+
}
2122+
}
21182123

21192124
return nil
21202125
}
@@ -2193,6 +2198,10 @@ func BuildTableInfoWithLike(ctx sessionctx.Context, ident ast.Ident, referTblInf
21932198
copy(pi.Definitions, referTblInfo.Partition.Definitions)
21942199
tblInfo.Partition = &pi
21952200
}
2201+
2202+
if referTblInfo.TTLInfo != nil {
2203+
tblInfo.TTLInfo = referTblInfo.TTLInfo.Clone()
2204+
}
21962205
return &tblInfo, nil
21972206
}
21982207

@@ -3000,6 +3009,8 @@ func SetDirectPlacementOpt(placementSettings *model.PlacementSettings, placement
30003009

30013010
// handleTableOptions updates tableInfo according to table options.
30023011
func handleTableOptions(options []*ast.TableOption, tbInfo *model.TableInfo) error {
3012+
var handledTTLOrTTLEnable bool
3013+
30033014
for _, op := range options {
30043015
switch op.Tp {
30053016
case ast.TableOptionAutoIncrement:
@@ -3036,6 +3047,23 @@ func handleTableOptions(options []*ast.TableOption, tbInfo *model.TableInfo) err
30363047
tbInfo.PlacementPolicyRef = &model.PolicyRefInfo{
30373048
Name: model.NewCIStr(op.StrValue),
30383049
}
3050+
case ast.TableOptionTTL, ast.TableOptionTTLEnable:
3051+
if handledTTLOrTTLEnable {
3052+
continue
3053+
}
3054+
3055+
ttlInfo, ttlEnable, err := getTTLInfoInOptions(options)
3056+
if err != nil {
3057+
return err
3058+
}
3059+
// It's impossible that `ttlInfo` and `ttlEnable` are all nil, because we have met this option.
3060+
// After exclude the situation `ttlInfo == nil && ttlEnable != nil`, we could say `ttlInfo != nil`
3061+
if ttlInfo == nil && ttlEnable != nil {
3062+
return errors.Trace(dbterror.ErrSetTTLEnableForNonTTLTable)
3063+
}
3064+
3065+
tbInfo.TTLInfo = ttlInfo
3066+
handledTTLOrTTLEnable = true
30393067
}
30403068
}
30413069
shardingBits := shardingBits(tbInfo)
@@ -3227,6 +3255,7 @@ func (d *ddl) AlterTable(ctx context.Context, sctx sessionctx.Context, stmt *ast
32273255
}
32283256
for _, spec := range validSpecs {
32293257
var handledCharsetOrCollate bool
3258+
var handledTTLOrTTLEnable bool
32303259
switch spec.Tp {
32313260
case ast.AlterTableAddColumns:
32323261
err = d.AddColumn(sctx, ident, spec)
@@ -3363,6 +3392,20 @@ func (d *ddl) AlterTable(ctx context.Context, sctx sessionctx.Context, stmt *ast
33633392
Name: model.NewCIStr(opt.StrValue),
33643393
}
33653394
case ast.TableOptionEngine:
3395+
case ast.TableOptionTTL, ast.TableOptionTTLEnable:
3396+
var ttlInfo *model.TTLInfo
3397+
var ttlEnable *bool
3398+
3399+
if handledTTLOrTTLEnable {
3400+
continue
3401+
}
3402+
ttlInfo, ttlEnable, err = getTTLInfoInOptions(spec.Options)
3403+
if err != nil {
3404+
return err
3405+
}
3406+
err = d.AlterTableTTLInfoOrEnable(sctx, ident, ttlInfo, ttlEnable)
3407+
3408+
handledTTLOrTTLEnable = true
33663409
default:
33673410
err = dbterror.ErrUnsupportedAlterTableOption
33683411
}
@@ -3406,6 +3449,9 @@ func (d *ddl) AlterTable(ctx context.Context, sctx sessionctx.Context, stmt *ast
34063449
case ast.AlterTableDisableKeys, ast.AlterTableEnableKeys:
34073450
// Nothing to do now, see https://github.com/pingcap/tidb/issues/1051
34083451
// MyISAM specific
3452+
case ast.AlterTableRemoveTTL:
3453+
// the parser makes sure we have only one `ast.AlterTableRemoveTTL` in an alter statement
3454+
err = d.AlterTableRemoveTTL(sctx, ident)
34093455
default:
34103456
err = errors.Trace(dbterror.ErrUnsupportedAlterTableSpec)
34113457
}
@@ -4238,6 +4284,11 @@ func checkIsDroppableColumn(ctx sessionctx.Context, is infoschema.InfoSchema, sc
42384284
if err != nil {
42394285
return false, errors.Trace(err)
42404286
}
4287+
// Check the column with TTL config
4288+
err = checkDropColumnWithTTLConfig(tblInfo, colName.L)
4289+
if err != nil {
4290+
return false, errors.Trace(err)
4291+
}
42414292
// We don't support dropping column with PK handle covered now.
42424293
if col.IsPKHandleColumn(tblInfo) {
42434294
return false, dbterror.ErrUnsupportedPKHandle
@@ -4724,6 +4775,13 @@ func GetModifiableColumnJob(
47244775
return nil, errors.Trace(err)
47254776
}
47264777

4778+
if t.Meta().TTLInfo != nil {
4779+
// the column referenced by TTL should be a time type
4780+
if t.Meta().TTLInfo.ColumnName.L == originalColName.L && !types.IsTypeTime(newCol.ColumnInfo.FieldType.GetType()) {
4781+
return nil, errors.Trace(dbterror.ErrUnsupportedColumnInTTLConfig.GenWithStackByArgs(newCol.ColumnInfo.Name.O))
4782+
}
4783+
}
4784+
47274785
var newAutoRandBits uint64
47284786
if newAutoRandBits, err = checkAutoRandom(t.Meta(), col, specNewColumn); err != nil {
47294787
return nil, errors.Trace(err)
@@ -5262,6 +5320,98 @@ func (d *ddl) AlterTableSetTiFlashReplica(ctx sessionctx.Context, ident ast.Iden
52625320
return errors.Trace(err)
52635321
}
52645322

5323+
// AlterTableTTLInfoOrEnable submit ddl job to change table info according to the ttlInfo, or ttlEnable
5324+
// at least one of the `ttlInfo` or `ttlEnable` should be not nil.
5325+
// When `ttlInfo` is nil, and `ttlEnable` is not, it will use the original `.TTLInfo` in the table info and modify the
5326+
// `.Enable`. If the `.TTLInfo` in the table info is empty, this function will return an error.
5327+
// When `ttlInfo` is not nil, it simply submits the job with the `ttlInfo` and ignore the `ttlEnable`.
5328+
func (d *ddl) AlterTableTTLInfoOrEnable(ctx sessionctx.Context, ident ast.Ident, ttlInfo *model.TTLInfo, ttlEnable *bool) error {
5329+
is := d.infoCache.GetLatest()
5330+
schema, ok := is.SchemaByName(ident.Schema)
5331+
if !ok {
5332+
return infoschema.ErrDatabaseNotExists.GenWithStackByArgs(ident.Schema)
5333+
}
5334+
5335+
tb, err := is.TableByName(ident.Schema, ident.Name)
5336+
if err != nil {
5337+
return errors.Trace(infoschema.ErrTableNotExists.GenWithStackByArgs(ident.Schema, ident.Name))
5338+
}
5339+
5340+
tblInfo := tb.Meta().Clone()
5341+
tableID := tblInfo.ID
5342+
tableName := tblInfo.Name.L
5343+
5344+
var job *model.Job
5345+
if ttlInfo != nil {
5346+
tblInfo.TTLInfo = ttlInfo
5347+
err = checkTTLInfoValid(ctx, tblInfo)
5348+
if err != nil {
5349+
return err
5350+
}
5351+
job = &model.Job{
5352+
SchemaID: schema.ID,
5353+
TableID: tableID,
5354+
SchemaName: schema.Name.L,
5355+
TableName: tableName,
5356+
Type: model.ActionAlterTTLInfo,
5357+
BinlogInfo: &model.HistoryInfo{},
5358+
Args: []interface{}{ttlInfo, ttlEnable},
5359+
}
5360+
} else {
5361+
if tblInfo.TTLInfo == nil {
5362+
return errors.Trace(dbterror.ErrSetTTLEnableForNonTTLTable)
5363+
}
5364+
5365+
job = &model.Job{
5366+
SchemaID: schema.ID,
5367+
TableID: tableID,
5368+
SchemaName: schema.Name.L,
5369+
TableName: tableName,
5370+
Type: model.ActionAlterTTLInfo,
5371+
BinlogInfo: &model.HistoryInfo{},
5372+
Args: []interface{}{ttlInfo, ttlEnable},
5373+
}
5374+
}
5375+
5376+
err = d.DoDDLJob(ctx, job)
5377+
err = d.callHookOnChanged(job, err)
5378+
return errors.Trace(err)
5379+
}
5380+
5381+
func (d *ddl) AlterTableRemoveTTL(ctx sessionctx.Context, ident ast.Ident) error {
5382+
is := d.infoCache.GetLatest()
5383+
5384+
schema, ok := is.SchemaByName(ident.Schema)
5385+
if !ok {
5386+
return infoschema.ErrDatabaseNotExists.GenWithStackByArgs(ident.Schema)
5387+
}
5388+
5389+
tb, err := is.TableByName(ident.Schema, ident.Name)
5390+
if err != nil {
5391+
return errors.Trace(infoschema.ErrTableNotExists.GenWithStackByArgs(ident.Schema, ident.Name))
5392+
}
5393+
5394+
tblInfo := tb.Meta().Clone()
5395+
tableID := tblInfo.ID
5396+
tableName := tblInfo.Name.L
5397+
5398+
if tblInfo.TTLInfo != nil {
5399+
job := &model.Job{
5400+
SchemaID: schema.ID,
5401+
TableID: tableID,
5402+
SchemaName: schema.Name.L,
5403+
TableName: tableName,
5404+
Type: model.ActionAlterTTLRemove,
5405+
BinlogInfo: &model.HistoryInfo{},
5406+
}
5407+
err = d.DoDDLJob(ctx, job)
5408+
err = d.callHookOnChanged(job, err)
5409+
return errors.Trace(err)
5410+
}
5411+
5412+
return nil
5413+
}
5414+
52655415
func isTableTiFlashSupported(schema *model.DBInfo, tb table.Table) error {
52665416
// Memory tables and system tables are not supported by TiFlash
52675417
if util.IsMemOrSysDB(schema.Name.L) {

ddl/ddl_worker.go

+4
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,10 @@ func (w *worker) runDDLJob(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64,
12871287
ver, err = w.onFlashbackCluster(d, t, job)
12881288
case model.ActionMultiSchemaChange:
12891289
ver, err = onMultiSchemaChange(w, d, t, job)
1290+
case model.ActionAlterTTLInfo:
1291+
ver, err = onTTLInfoChange(d, t, job)
1292+
case model.ActionAlterTTLRemove:
1293+
ver, err = onTTLInfoRemove(d, t, job)
12901294
default:
12911295
// Invalid job, cancel it.
12921296
job.State = model.JobStateCancelled

ddl/table_test.go

+81
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ import (
2424
"github.com/pingcap/tidb/kv"
2525
"github.com/pingcap/tidb/meta"
2626
"github.com/pingcap/tidb/meta/autoid"
27+
"github.com/pingcap/tidb/parser/ast"
2728
"github.com/pingcap/tidb/parser/model"
29+
"github.com/pingcap/tidb/parser/mysql"
2830
"github.com/pingcap/tidb/sessionctx"
2931
"github.com/pingcap/tidb/sessiontxn"
3032
"github.com/pingcap/tidb/table"
@@ -371,3 +373,82 @@ func TestCreateTables(t *testing.T) {
371373
testGetTable(t, domain, genIDs[1])
372374
testGetTable(t, domain, genIDs[2])
373375
}
376+
377+
func TestAlterTTL(t *testing.T) {
378+
store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease)
379+
380+
d := domain.DDL()
381+
382+
dbInfo, err := testSchemaInfo(store, "test_table")
383+
require.NoError(t, err)
384+
testCreateSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo)
385+
386+
ctx := testkit.NewTestKit(t, store).Session()
387+
388+
// initialize a table with ttlInfo
389+
tableName := "t"
390+
tblInfo, err := testTableInfo(store, tableName, 2)
391+
require.NoError(t, err)
392+
tblInfo.Columns[0].FieldType = *types.NewFieldType(mysql.TypeDatetime)
393+
tblInfo.Columns[1].FieldType = *types.NewFieldType(mysql.TypeDatetime)
394+
tblInfo.TTLInfo = &model.TTLInfo{
395+
ColumnName: tblInfo.Columns[0].Name,
396+
IntervalExprStr: "5",
397+
IntervalTimeUnit: int(ast.TimeUnitDay),
398+
}
399+
400+
// create table
401+
job := testCreateTable(t, ctx, d, dbInfo, tblInfo)
402+
testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic)
403+
testCheckJobDone(t, store, job.ID, true)
404+
405+
// submit ddl job to modify ttlInfo
406+
tableInfoAfterAlterTTLInfo := tblInfo.Clone()
407+
require.NoError(t, err)
408+
tableInfoAfterAlterTTLInfo.TTLInfo = &model.TTLInfo{
409+
ColumnName: tblInfo.Columns[1].Name,
410+
IntervalExprStr: "1",
411+
IntervalTimeUnit: int(ast.TimeUnitYear),
412+
}
413+
414+
job = &model.Job{
415+
SchemaID: dbInfo.ID,
416+
TableID: tblInfo.ID,
417+
Type: model.ActionAlterTTLInfo,
418+
BinlogInfo: &model.HistoryInfo{},
419+
Args: []interface{}{&model.TTLInfo{
420+
ColumnName: tblInfo.Columns[1].Name,
421+
IntervalExprStr: "1",
422+
IntervalTimeUnit: int(ast.TimeUnitYear),
423+
}},
424+
}
425+
ctx.SetValue(sessionctx.QueryString, "skip")
426+
require.NoError(t, d.DoDDLJob(ctx, job))
427+
428+
v := getSchemaVer(t, ctx)
429+
checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: nil})
430+
431+
// assert the ddlInfo as expected
432+
historyJob, err := ddl.GetHistoryJobByID(testkit.NewTestKit(t, store).Session(), job.ID)
433+
require.NoError(t, err)
434+
require.Equal(t, tableInfoAfterAlterTTLInfo.TTLInfo, historyJob.BinlogInfo.TableInfo.TTLInfo)
435+
436+
// submit a ddl job to modify ttlEnabled
437+
job = &model.Job{
438+
SchemaID: dbInfo.ID,
439+
TableID: tblInfo.ID,
440+
Type: model.ActionAlterTTLRemove,
441+
BinlogInfo: &model.HistoryInfo{},
442+
Args: []interface{}{true},
443+
}
444+
ctx.SetValue(sessionctx.QueryString, "skip")
445+
require.NoError(t, d.DoDDLJob(ctx, job))
446+
447+
v = getSchemaVer(t, ctx)
448+
checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: nil})
449+
450+
// assert the ddlInfo as expected
451+
historyJob, err = ddl.GetHistoryJobByID(testkit.NewTestKit(t, store).Session(), job.ID)
452+
require.NoError(t, err)
453+
require.Empty(t, historyJob.BinlogInfo.TableInfo.TTLInfo)
454+
}

0 commit comments

Comments
 (0)