@@ -19,17 +19,18 @@ import (
19
19
"regexp"
20
20
"sync"
21
21
22
- "github.com/pingcap/failpoint"
23
-
24
22
"github.com/pingcap/dm/dm/config"
25
23
"github.com/pingcap/dm/pkg/conn"
26
24
tcontext "github.com/pingcap/dm/pkg/context"
27
25
"github.com/pingcap/dm/pkg/cputil"
28
26
parserpkg "github.com/pingcap/dm/pkg/parser"
29
27
"github.com/pingcap/dm/pkg/terror"
28
+ "github.com/pingcap/dm/pkg/utils"
30
29
"github.com/pingcap/dm/syncer/dbconn"
31
30
31
+ "github.com/pingcap/failpoint"
32
32
"github.com/pingcap/parser/ast"
33
+ "github.com/pingcap/parser/model"
33
34
"github.com/pingcap/tidb-tools/pkg/dbutil"
34
35
"github.com/pingcap/tidb-tools/pkg/filter"
35
36
"go.uber.org/zap"
@@ -50,7 +51,7 @@ type OnlinePlugin interface {
50
51
Apply (tctx * tcontext.Context , tables []* filter.Table , statement string , stmt ast.StmtNode ) ([]string , string , string , error )
51
52
// Finish would delete online ddl from memory and storage
52
53
Finish (tctx * tcontext.Context , schema , table string ) error
53
- // TableType returns ghhost /real table
54
+ // TableType returns ghost /real table
54
55
TableType (table string ) TableType
55
56
// RealName returns real table name that removed ghost suffix and handled by table router
56
57
RealName (table string ) string
@@ -63,6 +64,8 @@ type OnlinePlugin interface {
63
64
Close ()
64
65
// CheckAndUpdate try to check and fix the schema/table case-sensitive issue
65
66
CheckAndUpdate (tctx * tcontext.Context , schemas map [string ]string , tables map [string ]map [string ]string ) error
67
+ // CheckRegex checks the regex of shadow/trash table rules and reports an error if a ddl event matches only either of the rules
68
+ CheckRegex (stmt ast.StmtNode , schema string , flavor utils.LowerCaseTableNamesFlavor ) error
66
69
}
67
70
68
71
// TableType is type of table.
@@ -75,6 +78,12 @@ const (
75
78
TrashTable TableType = "trash table" // means we should ignore these tables
76
79
)
77
80
81
+ const (
82
+ shadowTable int = iota
83
+ trashTable
84
+ allTable
85
+ )
86
+
78
87
// GhostDDLInfo stores ghost information and ddls.
79
88
type GhostDDLInfo struct {
80
89
Schema string `json:"schema"`
@@ -391,14 +400,14 @@ func NewRealOnlinePlugin(tctx *tcontext.Context, cfg *config.SubTaskConfig) (Onl
391
400
for _ , sg := range cfg .ShadowTableRules {
392
401
shadowReg , err := regexp .Compile (sg )
393
402
if err != nil {
394
- return nil , terror .ErrConfigOnlineDDLInvalidRegex .Generate ("shadow-table-rules" , sg , "fail to compile: " + err .Error ())
403
+ return nil , terror .ErrConfigOnlineDDLInvalidRegex .Generate (config . ShadowTableRules , sg , "fail to compile: " + err .Error ())
395
404
}
396
405
shadowRegs = append (shadowRegs , shadowReg )
397
406
}
398
407
for _ , tg := range cfg .TrashTableRules {
399
408
trashReg , err := regexp .Compile (tg )
400
409
if err != nil {
401
- return nil , terror .ErrConfigOnlineDDLInvalidRegex .Generate ("trash-table-rules" , tg , "fail to compile: " + err .Error ())
410
+ return nil , terror .ErrConfigOnlineDDLInvalidRegex .Generate (config . TrashTableRules , tg , "fail to compile: " + err .Error ())
402
411
}
403
412
trashRegs = append (trashRegs , trashReg )
404
413
}
@@ -558,3 +567,83 @@ func (r *RealOnlinePlugin) ResetConn(tctx *tcontext.Context) error {
558
567
func (r * RealOnlinePlugin ) CheckAndUpdate (tctx * tcontext.Context , schemas map [string ]string , tables map [string ]map [string ]string ) error {
559
568
return r .storage .CheckAndUpdate (tctx , schemas , tables , r .RealName )
560
569
}
570
+
571
+ // CheckRegex checks the regex of shadow/trash table rules and reports an error if a ddl event matches only either of the rules.
572
+ func (r * RealOnlinePlugin ) CheckRegex (stmt ast.StmtNode , schema string , flavor utils.LowerCaseTableNamesFlavor ) error {
573
+ var (
574
+ v * ast.RenameTableStmt
575
+ ok bool
576
+ )
577
+ if v , ok = stmt .(* ast.RenameTableStmt ); ! ok {
578
+ return nil
579
+ }
580
+ t2ts := v .TableToTables
581
+ if len (t2ts ) != 2 {
582
+ return nil
583
+ }
584
+ onlineDDLMatched := allTable
585
+ tableRecords := make ([]* filter.Table , 2 )
586
+ schemaName := model .NewCIStr (schema ) // fill schema name
587
+
588
+ // Online DDL sql example: RENAME TABLE `test`.`t1` TO `test`.`_t1_old`, `test`.`_t1_new` TO `test`.`t1`
589
+ // We should parse two rename DDL from this DDL:
590
+ // tables[0] tables[1]
591
+ // DDL 0 real table ───► trash table
592
+ // DDL 1 shadow table ───► real table
593
+ // If we only have one of them, that means users may configure a wrong trash/shadow table regex
594
+ for i , t2t := range t2ts {
595
+ if t2t .OldTable .Schema .O == "" {
596
+ t2t .OldTable .Schema = schemaName
597
+ }
598
+ if t2t .NewTable .Schema .O == "" {
599
+ t2t .NewTable .Schema = schemaName
600
+ }
601
+
602
+ v .TableToTables = []* ast.TableToTable {t2t }
603
+
604
+ if i == 0 {
605
+ tableRecords [trashTable ] = fetchTable (t2t .NewTable , flavor )
606
+ if r .TableType (t2t .OldTable .Name .String ()) == RealTable &&
607
+ r .TableType (t2t .NewTable .Name .String ()) == TrashTable {
608
+ onlineDDLMatched = trashTable
609
+ }
610
+ } else {
611
+ tableRecords [shadowTable ] = fetchTable (t2t .OldTable , flavor )
612
+ if r .TableType (t2t .OldTable .Name .String ()) == GhostTable &&
613
+ r .TableType (t2t .NewTable .Name .String ()) == RealTable {
614
+ // if no trash table is not matched before, we should record that shadow table is matched here
615
+ // if shadow table is matched before, we just return all tables are matched and a nil error
616
+ if onlineDDLMatched != trashTable {
617
+ onlineDDLMatched = shadowTable
618
+ } else {
619
+ onlineDDLMatched = allTable
620
+ }
621
+ }
622
+ }
623
+ }
624
+ if onlineDDLMatched != allTable {
625
+ return terror .ErrConfigOnlineDDLMistakeRegex .Generate (stmt .Text (), tableRecords [onlineDDLMatched ^ 1 ], unmatchedOnlineDDLRules (onlineDDLMatched ))
626
+ }
627
+ return nil
628
+ }
629
+
630
+ func unmatchedOnlineDDLRules (match int ) string {
631
+ switch match {
632
+ case shadowTable :
633
+ return config .TrashTableRules
634
+ case trashTable :
635
+ return config .ShadowTableRules
636
+ default :
637
+ return ""
638
+ }
639
+ }
640
+
641
+ func fetchTable (t * ast.TableName , flavor utils.LowerCaseTableNamesFlavor ) * filter.Table {
642
+ var tb * filter.Table
643
+ if flavor == utils .LCTableNamesSensitive {
644
+ tb = & filter.Table {Schema : t .Schema .O , Name : t .Name .O }
645
+ } else {
646
+ tb = & filter.Table {Schema : t .Schema .L , Name : t .Name .L }
647
+ }
648
+ return tb
649
+ }
0 commit comments