forked from GoDannyLai/binlog_rollback
-
Notifications
You must be signed in to change notification settings - Fork 0
/
binlog_com.go
303 lines (252 loc) · 8.15 KB
/
binlog_com.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
package main
import (
"dannytools/constvar"
"dannytools/dsql"
"dannytools/ehand"
"dannytools/logging"
//"os"
"path/filepath"
"strings"
"time"
//"github.com/davecgh/go-spew/spew"
"fmt"
"sync"
"github.com/siddontang/go-mysql/mysql"
"github.com/siddontang/go-mysql/replication"
)
type BinEventHandlingIndx struct {
EventIdx uint64
lock sync.RWMutex
finished bool
}
var G_HandlingBinEventIndex *BinEventHandlingIndx
type MaxBinEventIdx struct {
MaxEventIdx uint64
lock sync.RWMutex
}
func (this *MaxBinEventIdx) SetMaxBinEventIdx(idx uint64) {
this.lock.Lock()
defer this.lock.Unlock()
this.MaxEventIdx = idx
}
var g_MaxBin_Event_Idx *MaxBinEventIdx
type BinEventDbTableQuery struct {
database string
table string
sql string
}
type MyBinEvent struct {
MyPos mysql.Position //this is the end position
EventIdx uint64
BinEvent *replication.RowsEvent
StartPos uint32 // this is the start position
IfRowsEvent bool
SqlType string // insert, update, delete
Timestamp uint32
TrxIndex uint64
TrxStatus int // 0:begin, 1: commit, 2: rollback, -1: in_progress
QuerySql *dsql.SqlInfo // for ddl and binlog which is not row format
OrgSql string // for ddl and binlog which is not row format
}
func CheckBinHeaderCondition(cfg *ConfCmd, header *replication.EventHeader, currentBinlog string) int {
// process: 0, continue: 1, break: 2
myPos := mysql.Position{Name: currentBinlog, Pos: header.LogPos}
//fmt.Println(cfg.StartFilePos, cfg.IfSetStopFilePos, myPos)
if cfg.IfSetStartFilePos {
cmpRe := myPos.Compare(cfg.StartFilePos)
if cmpRe == -1 {
return C_reContinue
}
}
if cfg.IfSetStopFilePos {
cmpRe := myPos.Compare(cfg.StopFilePos)
if cmpRe >= 0 {
return C_reBreak
}
}
//fmt.Println(cfg.StartDatetime, cfg.StopDatetime, header.Timestamp)
if cfg.IfSetStartDateTime {
if header.Timestamp < cfg.StartDatetime {
return C_reContinue
}
}
if cfg.IfSetStopDateTime {
if header.Timestamp >= cfg.StopDatetime {
return C_reBreak
}
}
if cfg.FilterSqlLen == 0 {
return C_reProcess
}
if header.EventType == replication.WRITE_ROWS_EVENTv1 || header.EventType == replication.WRITE_ROWS_EVENTv2 {
if cfg.IsTargetDml("insert") {
return C_reProcess
} else {
return C_reContinue
}
}
if header.EventType == replication.UPDATE_ROWS_EVENTv1 || header.EventType == replication.UPDATE_ROWS_EVENTv2 {
if cfg.IsTargetDml("update") {
return C_reProcess
} else {
return C_reContinue
}
}
if header.EventType == replication.DELETE_ROWS_EVENTv1 || header.EventType == replication.DELETE_ROWS_EVENTv2 {
if cfg.IsTargetDml("delete") {
return C_reProcess
} else {
return C_reContinue
}
}
return C_reProcess
}
func (this *MyBinEvent) CheckBinEvent(cfg *ConfCmd, ev *replication.BinlogEvent, currentBinlog *string) int {
myPos := mysql.Position{Name: *currentBinlog, Pos: ev.Header.LogPos}
switch ev.Header.EventType {
case replication.ROTATE_EVENT:
rotatEvent := ev.Event.(*replication.RotateEvent)
*currentBinlog = string(rotatEvent.NextLogName)
if cfg.ToLastLog && cfg.Mode == "repl" && cfg.WorkType == "stats" {
return C_reContinue
}
myPos.Name = string(rotatEvent.NextLogName)
myPos.Pos = uint32(rotatEvent.Position)
gLogger.WriteToLogByFieldsNormalOnlyMsg(fmt.Sprintf("log rotate %s", myPos.String()), logging.INFO)
if cfg.IfSetStartFilePos {
cmpRe := myPos.Compare(cfg.StartFilePos)
if cmpRe == -1 {
return C_reContinue
}
}
if cfg.IfSetStopFilePos {
cmpRe := myPos.Compare(cfg.StopFilePos)
if cmpRe >= 0 {
return C_reBreak
}
}
this.IfRowsEvent = false
return C_reContinue
case replication.WRITE_ROWS_EVENTv1,
replication.UPDATE_ROWS_EVENTv1,
replication.DELETE_ROWS_EVENTv1,
replication.WRITE_ROWS_EVENTv2,
replication.UPDATE_ROWS_EVENTv2,
replication.DELETE_ROWS_EVENTv2:
//replication.XID_EVENT,
//replication.TABLE_MAP_EVENT:
wrEvent := ev.Event.(*replication.RowsEvent)
db := string(wrEvent.Table.Schema)
tb := string(wrEvent.Table.Table)
if !cfg.IsTargetTable(db, tb) {
return C_reContinue
}
/*
if len(cfg.Databases) > 0 {
if !sliceKits.ContainsString(cfg.Databases, db) {
return C_reContinue
}
}
if len(cfg.Tables) > 0 {
if !sliceKits.ContainsString(cfg.Tables, tb) {
return C_reContinue
}
}
*/
this.BinEvent = wrEvent
this.IfRowsEvent = true
case replication.QUERY_EVENT:
this.IfRowsEvent = false
queryEvent := ev.Event.(*replication.QueryEvent)
db := string(queryEvent.Schema) // for DML/DDL, schema is not empty if connection has default database, ie use db
sqlStr := string(queryEvent.Query)
this.OrgSql = sqlStr
lowerSqlStr := strings.TrimSpace(strings.ToLower(sqlStr))
// for mysql, begin of transaction, it write a query event with sql 'BEGIN'
// for DML, row or statement, a trx is composed of gtid event, query event(BEGIN), query event/Rows_query event, xid event
// for DDL, a trx is composed of gtid event, query event
// query event may be composed of use database, DML/DDL sql
if lowerSqlStr != "begin" && lowerSqlStr != "commit" {
if db == "" && gUseDatabase != "" {
db = gUseDatabase
}
//ev.Dump(os.Stdout)
//tidb.parser not support create trigger statement
parsedResult, lastUseDb, err := dsql.ParseSqlsForSqlInfo(gSqlParser, sqlStr, db)
//spew.Dump(parsedResult)
if err != nil {
if cfg.IgnoreParsedErrRegexp != nil && cfg.IgnoreParsedErrRegexp.MatchString(lowerSqlStr) {
gLogger.WriteToLogByFieldsErrorExtramsgExitCode(err, fmt.Sprintf("\nerror to parse sql from query event, binlog=%s, time=%s, sql=%s",
myPos.String(), time.Unix(int64(ev.Header.Timestamp), 0).Format(constvar.DATETIME_FORMAT_NOSPACE), sqlStr),
logging.ERROR, ehand.ERR_ERROR)
return C_reContinue
} else {
//exit program here
gLogger.WriteToLogByFieldsErrorExtramsgExit(err, fmt.Sprintf("\nerror to parse sql from query event, binlog=%s, time=%s, sql=%s",
myPos.String(), time.Unix(int64(ev.Header.Timestamp), 0).Format(constvar.DATETIME_FORMAT_NOSPACE), sqlStr),
logging.ERROR, ehand.ERR_ERROR)
return C_reBreak
}
}
// have new use database
if lastUseDb != "" && gUseDatabase != lastUseDb {
gUseDatabase = lastUseDb
}
// skip other query event, we only care Table DDL and DML
if len(parsedResult) != 1 {
gLogger.WriteToLogByFieldsNormalOnlyMsg(fmt.Sprintf("skip it, parsed result for query event is nil or more than one(len(parsedResult)=%d), binlog=%s, time=%s, sql=%s",
len(parsedResult), myPos.String(), time.Unix(int64(ev.Header.Timestamp), 0).Format(constvar.DATETIME_FORMAT_NOSPACE), sqlStr), logging.WARNING)
return C_reContinue
}
// should only one result
if parsedResult[0].IsDml() {
if !cfg.ParseStatementSql {
//gLogger.WriteToLogByFieldsNormalOnlyMsg("not parse dml", logging.INFO)
return C_reContinue
}
if !cfg.IsTargetDml(parsedResult[0].GetDmlName()) {
//gLogger.WriteToLogByFieldsNormalOnlyMsg(fmt.Sprintf("dml %s not target", parsedResult[0].GetDmlName()), logging.INFO)
return C_reContinue
}
ifAnyTargetTable := false
for j := range parsedResult[0].Tables {
if parsedResult[0].Tables[j].Database == "" {
parsedResult[0].Tables[j].Database = db
}
if cfg.IsTargetTable(parsedResult[0].Tables[j].Database, parsedResult[0].Tables[j].Table) {
ifAnyTargetTable = true
}
}
if !ifAnyTargetTable {
//gLogger.WriteToLogByFieldsNormalOnlyMsg("not target table", logging.INFO)
return C_reContinue
}
}
this.QuerySql = parsedResult[0].Copy()
//gLogger.WriteToLogByFieldsNormalOnlyMsg("should be processed", logging.INFO)
}
case replication.XID_EVENT:
this.IfRowsEvent = false
case replication.MARIADB_GTID_EVENT:
this.IfRowsEvent = false
default:
this.IfRowsEvent = false
return C_reContinue
}
return C_reProcess
}
func GetFirstBinlogPosToParse(cfg *ConfCmd) (string, int64) {
var binlog string
var pos int64
if cfg.StartFile != "" {
binlog = filepath.Join(cfg.BinlogDir, cfg.StartFile)
} else {
binlog = cfg.GivenBinlogFile
}
if cfg.StartPos != 0 {
pos = int64(cfg.StartPos)
} else {
pos = 4
}
return binlog, pos
}