github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/syncer/handle_error.go (about) 1 // Copyright 2020 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package syncer 15 16 import ( 17 "context" 18 "encoding/json" 19 "fmt" 20 21 "github.com/go-mysql-org/go-mysql/replication" 22 "github.com/pingcap/tidb/pkg/parser" 23 "github.com/pingcap/tidb/pkg/parser/ast" 24 "github.com/pingcap/tiflow/dm/pb" 25 "github.com/pingcap/tiflow/dm/pkg/binlog" 26 parserpkg "github.com/pingcap/tiflow/dm/pkg/parser" 27 "github.com/pingcap/tiflow/dm/pkg/terror" 28 ) 29 30 // HandleError handle error for syncer. 31 func (s *Syncer) HandleError(ctx context.Context, req *pb.HandleWorkerErrorRequest) (string, error) { 32 pos := req.BinlogPos 33 34 if len(pos) == 0 { 35 startLocation, isQueryEvent := s.getErrLocation() 36 if startLocation == nil { 37 return "", fmt.Errorf("source '%s' has no error", s.cfg.SourceID) 38 } 39 40 if !isQueryEvent && req.Op != pb.ErrorOp_Inject { 41 return "", fmt.Errorf("only support to handle ddl error currently, see https://docs.pingcap.com/tidb-data-migration/stable/error-handling for other errors") 42 } 43 pos = startLocation.Position.String() 44 } else { 45 startLocation, err := binlog.VerifyBinlogPos(pos) 46 if err != nil { 47 return "", err 48 } 49 pos = startLocation.String() 50 } 51 52 var err error 53 // remove outdated operators when add operator 54 s.streamerController.RemoveOutdated(s.checkpoint.FlushedGlobalPoint().Position) 55 if err != nil { 56 return "", err 57 } 58 59 if req.Op == pb.ErrorOp_List { 60 commands := s.streamerController.ListEqualAndAfter(pos) 61 commandsJSON, err1 := json.Marshal(commands) 62 if err1 != nil { 63 return "", err1 64 } 65 return string(commandsJSON), err1 66 } 67 if req.Op == pb.ErrorOp_Revert { 68 return "", s.streamerController.Delete(pos) 69 } 70 71 events := make([]*replication.BinlogEvent, 0) 72 73 if req.Op == pb.ErrorOp_Replace || req.Op == pb.ErrorOp_Inject { 74 events, err = s.genEvents(ctx, req.Sqls) 75 if err != nil { 76 return "", err 77 } 78 } 79 80 req.BinlogPos = pos 81 82 return "", s.streamerController.Set(req, events) 83 } 84 85 func (s *Syncer) genEvents(ctx context.Context, sqls []string) ([]*replication.BinlogEvent, error) { 86 events := make([]*replication.BinlogEvent, 0) 87 88 parser2, err := s.fromDB.GetParser(ctx) 89 if err != nil { 90 s.tctx.L().Error("failed to get SQL mode specified parser from upstream, using default SQL mode instead") 91 parser2 = parser.New() 92 } 93 94 for _, sql := range sqls { 95 node, err := parser2.ParseOneStmt(sql, "", "") 96 if err != nil { 97 return nil, terror.Annotatef(terror.ErrSyncerUnitParseStmt.New(err.Error()), "sql %s", sql) 98 } 99 100 switch node.(type) { 101 case ast.DDLNode: 102 tables, err := parserpkg.FetchDDLTables("", node, s.SourceTableNamesFlavor) 103 if err != nil { 104 return nil, err 105 } 106 107 schema := tables[0].Schema 108 if len(schema) == 0 { 109 return nil, terror.ErrSyncerUnitInjectDDLWithoutSchema.Generate(sql) 110 } 111 events = append(events, genQueryEvent([]byte(schema), []byte(sql))) 112 default: 113 // TODO: support DML 114 return nil, terror.ErrSyncerEvent.New("only support replace or inject with DDL currently") 115 } 116 } 117 return events, nil 118 } 119 120 // genQueryEvent generate QueryEvent with empty EventSize and LogPos. 121 func genQueryEvent(schema, query []byte) *replication.BinlogEvent { 122 header := &replication.EventHeader{ 123 EventType: replication.QUERY_EVENT, 124 } 125 queryEvent := &replication.QueryEvent{ 126 Schema: schema, 127 Query: query, 128 } 129 e := &replication.BinlogEvent{ 130 Header: header, 131 Event: queryEvent, 132 } 133 return e 134 }