mysql_binlog 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/mysql_binlog.rb +5 -0
- data/lib/mysql_binlog/binlog.rb +31 -0
- data/lib/mysql_binlog/binlog_event_parser.rb +54 -9
- data/lib/mysql_binlog/binlog_field_parser.rb +49 -33
- data/lib/mysql_binlog/reader/binlog_file_reader.rb +1 -0
- data/lib/mysql_binlog/reader/binlog_stream_reader.rb +3 -1
- metadata +3 -3
data/lib/mysql_binlog.rb
CHANGED
@@ -4,3 +4,8 @@ require 'mysql_binlog/binlog_event_parser'
|
|
4
4
|
require 'mysql_binlog/reader/debugging_reader'
|
5
5
|
require 'mysql_binlog/reader/binlog_file_reader'
|
6
6
|
require 'mysql_binlog/reader/binlog_stream_reader'
|
7
|
+
|
8
|
+
# The MysqlBinlog module contains a series of classes for reading and
|
9
|
+
# parsing binary log events from MySQL binary logs.
|
10
|
+
module MysqlBinlog
|
11
|
+
end
|
data/lib/mysql_binlog/binlog.rb
CHANGED
@@ -1,9 +1,37 @@
|
|
1
1
|
module MysqlBinlog
|
2
|
+
# This version of the binary log format is not supported by this library.
|
2
3
|
class UnsupportedVersionException < Exception; end
|
4
|
+
|
5
|
+
# An error was encountered when trying to read the log, which was likely
|
6
|
+
# due to garbage data in the log. Continuing is likely impossible.
|
3
7
|
class MalformedBinlogException < Exception; end
|
8
|
+
|
9
|
+
# When attempting a read, no data was returned.
|
4
10
|
class ZeroReadException < Exception; end
|
11
|
+
|
12
|
+
# When attempting a read, fewer bytes of data were returned than were
|
13
|
+
# requested by the reader, likely indicating a truncated file or corrupted
|
14
|
+
# event.
|
5
15
|
class ShortReadException < Exception; end
|
6
16
|
|
17
|
+
# Read a binary log, parsing and returning events.
|
18
|
+
#
|
19
|
+
# == Examples
|
20
|
+
#
|
21
|
+
# A basic example of using the Binlog class:
|
22
|
+
#
|
23
|
+
# require 'mysql_binlog'
|
24
|
+
# include MysqlBinlog
|
25
|
+
#
|
26
|
+
# # Open a binary log from a file on disk.
|
27
|
+
# binlog = Binlog.new(BinlogFileReader.new("mysql-bin.000001"))
|
28
|
+
#
|
29
|
+
# # Iterate over all events from the log, printing the event type (such
|
30
|
+
# # as :query_event, :write_rows_event, etc.)
|
31
|
+
# binlog.each_event do |event|
|
32
|
+
# puts event[:type]
|
33
|
+
# end
|
34
|
+
#
|
7
35
|
class Binlog
|
8
36
|
attr_reader :fde
|
9
37
|
attr_accessor :reader
|
@@ -35,6 +63,7 @@ module MysqlBinlog
|
|
35
63
|
def skip_event(header)
|
36
64
|
reader.skip(header)
|
37
65
|
end
|
66
|
+
private :skip_event
|
38
67
|
|
39
68
|
# Read the content of the event, which follows the header.
|
40
69
|
def read_event_fields(header)
|
@@ -53,6 +82,7 @@ module MysqlBinlog
|
|
53
82
|
|
54
83
|
fields
|
55
84
|
end
|
85
|
+
private :read_event_fields
|
56
86
|
|
57
87
|
# Scan events until finding one that isn't rejected by the filter rules.
|
58
88
|
# If there are no filter rules, this will return the next event provided
|
@@ -130,6 +160,7 @@ module MysqlBinlog
|
|
130
160
|
:server_version => fde[:server_version],
|
131
161
|
}
|
132
162
|
end
|
163
|
+
private :process_fde
|
133
164
|
|
134
165
|
# Iterate through all events.
|
135
166
|
def each_event
|
@@ -9,7 +9,7 @@ module MysqlBinlog
|
|
9
9
|
{ :name => :flags, :length => 2, :format => "v" },
|
10
10
|
]
|
11
11
|
|
12
|
-
# Values for the
|
12
|
+
# Values for the +flags+ field that may appear in binary logs. There are
|
13
13
|
# several other values that never appear in a file but may be used
|
14
14
|
# in events in memory.
|
15
15
|
EVENT_HEADER_FLAGS = {
|
@@ -76,15 +76,26 @@ module MysqlBinlog
|
|
76
76
|
:relaxed_unique_checks => 1 << 27,
|
77
77
|
}
|
78
78
|
|
79
|
+
# A mapping array for all values that may appear in the +Intvar_type+ field
|
80
|
+
# of an intvar_event.
|
79
81
|
INTVAR_EVENT_INTVAR_TYPES = [
|
80
82
|
nil,
|
81
83
|
:last_insert_id,
|
82
84
|
:insert_id,
|
83
85
|
]
|
84
86
|
|
87
|
+
# Parse binary log events from a provided binary log. Must be driven
|
88
|
+
# externally, but handles all the details of parsing an event header
|
89
|
+
# and the content of the various event types.
|
85
90
|
class BinlogEventParser
|
91
|
+
# The binary log object this event parser will parse events from.
|
86
92
|
attr_accessor :binlog
|
93
|
+
|
94
|
+
# The binary log reader extracted from the binlog object for convenience.
|
87
95
|
attr_accessor :reader
|
96
|
+
|
97
|
+
# The binary log field parser extracted from the binlog object for
|
98
|
+
# convenience.
|
88
99
|
attr_accessor :parser
|
89
100
|
|
90
101
|
def initialize(binlog_instance)
|
@@ -94,12 +105,12 @@ module MysqlBinlog
|
|
94
105
|
@table_map = {}
|
95
106
|
end
|
96
107
|
|
97
|
-
# Parse an event header, described by the EVENT_HEADER structure above.
|
108
|
+
# Parse an event header, described by the +EVENT_HEADER+ structure above.
|
98
109
|
def event_header
|
99
110
|
header = parser.read_and_unpack(EVENT_HEADER)
|
100
111
|
|
101
|
-
# Merge the read
|
102
|
-
# the flags by name instead of returning the bitmap as an integer.
|
112
|
+
# Merge the read +flags+ bitmap with the +EVENT_HEADER_FLAGS+ hash to
|
113
|
+
# return the flags by name instead of returning the bitmap as an integer.
|
103
114
|
flags = EVENT_HEADER_FLAGS.inject([]) do |result, (flag_name, flag_bit_value)|
|
104
115
|
if (header[:flags] & flag_bit_value) != 0
|
105
116
|
result << flag_name
|
@@ -107,7 +118,7 @@ module MysqlBinlog
|
|
107
118
|
result
|
108
119
|
end
|
109
120
|
|
110
|
-
# Overwrite the integer version of
|
121
|
+
# Overwrite the integer version of +flags+ with the array of names.
|
111
122
|
header[:flags] = flags
|
112
123
|
|
113
124
|
header
|
@@ -135,7 +146,7 @@ module MysqlBinlog
|
|
135
146
|
# Parse a dynamic +status+ structure within a query_event, which consists
|
136
147
|
# of a status_length (uint16) followed by a number of status variables
|
137
148
|
# (determined by the +status_length+) each of which consist of:
|
138
|
-
# * A type code (uint8), one of QUERY_EVENT_STATUS_TYPES
|
149
|
+
# * A type code (+uint8+), one of +QUERY_EVENT_STATUS_TYPES+.
|
139
150
|
# * The content itself, determined by the type. Additional processing is
|
140
151
|
# required based on the type.
|
141
152
|
def _query_event_status(header, fields)
|
@@ -176,6 +187,7 @@ module MysqlBinlog
|
|
176
187
|
end
|
177
188
|
status
|
178
189
|
end
|
190
|
+
private :_query_event_status
|
179
191
|
|
180
192
|
# Parse fields for a +Query+ event.
|
181
193
|
def query_event(header)
|
@@ -217,13 +229,44 @@ module MysqlBinlog
|
|
217
229
|
fields
|
218
230
|
end
|
219
231
|
|
220
|
-
# Parse
|
232
|
+
# Parse a number of bytes from the metadata section of a +Table_map+ event
|
233
|
+
# representing various fields based on the column type of the column
|
234
|
+
# being processed.
|
235
|
+
def _table_map_event_column_metadata_read(column_type)
|
236
|
+
case column_type
|
237
|
+
when :float, :double
|
238
|
+
{ :size => parser.read_uint8 }
|
239
|
+
when :varchar
|
240
|
+
{ :max_length => parser.read_uint16 }
|
241
|
+
when :bit
|
242
|
+
{
|
243
|
+
:size_bits => parser.read_uint8,
|
244
|
+
:size_bytes => parser.read_uint8,
|
245
|
+
}
|
246
|
+
when :newdecimal
|
247
|
+
{
|
248
|
+
:precision => parser.read_uint8,
|
249
|
+
:decimals => parser.read_uint8,
|
250
|
+
}
|
251
|
+
when :blob, :geometry
|
252
|
+
{ :length_size => parser.read_uint8 }
|
253
|
+
when :string, :var_string
|
254
|
+
{
|
255
|
+
:real_type => MYSQL_TYPES[parser.read_uint8],
|
256
|
+
:max_length => parser.read_uint8,
|
257
|
+
}
|
258
|
+
end
|
259
|
+
end
|
260
|
+
private :_table_map_event_column_metadata_read
|
261
|
+
|
262
|
+
# Parse column metadata within a +Table_map+ event.
|
221
263
|
def _table_map_event_column_metadata(columns_type)
|
222
264
|
length = parser.read_varint
|
223
|
-
columns_type.map do |
|
224
|
-
|
265
|
+
columns_type.map do |column|
|
266
|
+
_table_map_event_column_metadata_read(column)
|
225
267
|
end
|
226
268
|
end
|
269
|
+
private :_table_map_event_column_metadata
|
227
270
|
|
228
271
|
# Parse fields for a +Table_map+ event.
|
229
272
|
def table_map_event(header)
|
@@ -269,6 +312,7 @@ module MysqlBinlog
|
|
269
312
|
end
|
270
313
|
row_image
|
271
314
|
end
|
315
|
+
private :_generic_rows_event_row_image
|
272
316
|
|
273
317
|
# Parse the row images present in a row-based replication row event. This
|
274
318
|
# is rather incomplete right now due missing support for many MySQL types,
|
@@ -291,6 +335,7 @@ module MysqlBinlog
|
|
291
335
|
end
|
292
336
|
row_images
|
293
337
|
end
|
338
|
+
private :_generic_rows_event_row_images
|
294
339
|
|
295
340
|
# Parse fields for any of the row-based replication row events:
|
296
341
|
# * +Write_rows+ which is used for +INSERT+.
|
@@ -36,6 +36,8 @@ module MysqlBinlog
|
|
36
36
|
type_array
|
37
37
|
end
|
38
38
|
|
39
|
+
# Parse various types of standard and non-standard data types from a
|
40
|
+
# provided binary log using its reader to read data.
|
39
41
|
class BinlogFieldParser
|
40
42
|
attr_accessor :binlog
|
41
43
|
attr_accessor :reader
|
@@ -201,29 +203,46 @@ module MysqlBinlog
|
|
201
203
|
fields
|
202
204
|
end
|
203
205
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
206
|
+
# Extract a number of sequential bits at a given offset within an integer.
|
207
|
+
# This is used to unpack bit-packed fields.
|
208
|
+
def extract_bits(value, bits, offset)
|
209
|
+
(value & ((1 << bits) - 1) << offset) >> offset
|
210
|
+
end
|
211
|
+
|
212
|
+
# Convert a packed +DATE+ from a uint24 into a string representing
|
213
|
+
# the date.
|
214
|
+
def convert_mysql_type_date(value)
|
215
|
+
"%04i-%02i-%02i" % [
|
216
|
+
extract_bits(value, 15, 9),
|
217
|
+
extract_bits(value, 4, 5),
|
218
|
+
extract_bits(value, 5, 0),
|
219
|
+
]
|
220
|
+
end
|
221
|
+
|
222
|
+
# Convert a packed +TIME+ from a uint24 into a string representing
|
223
|
+
# the time.
|
224
|
+
def convert_mysql_type_time(value)
|
225
|
+
"%02i:%02i:%02i" % [
|
226
|
+
value / 10000,
|
227
|
+
(value % 10000) / 100,
|
228
|
+
value % 100,
|
229
|
+
]
|
230
|
+
end
|
231
|
+
|
232
|
+
# Convert a packed +DATETIME+ from a uint64 into a string representing
|
233
|
+
# the date and time.
|
234
|
+
def convert_mysql_type_datetime(value)
|
235
|
+
date = value / 1000000
|
236
|
+
time = value % 1000000
|
237
|
+
|
238
|
+
"%04i-%02i-%02i %02i:%02i:%02i" % [
|
239
|
+
date / 10000,
|
240
|
+
(date % 10000) / 100,
|
241
|
+
date % 100,
|
242
|
+
time / 10000,
|
243
|
+
(time % 10000) / 100,
|
244
|
+
time % 100,
|
245
|
+
]
|
227
246
|
end
|
228
247
|
|
229
248
|
# Read a single field, provided the MySQL column type as a symbol. Not all
|
@@ -248,24 +267,21 @@ module MysqlBinlog
|
|
248
267
|
read_varstring
|
249
268
|
when :varchar
|
250
269
|
read_lpstring(2)
|
251
|
-
when :blob
|
270
|
+
when :blob, :geometry
|
252
271
|
read_lpstring(metadata[:length_size])
|
253
272
|
when :timestamp
|
254
273
|
read_uint32
|
255
274
|
when :year
|
256
275
|
read_uint8 + 1900
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
276
|
+
when :date
|
277
|
+
convert_mysql_type_date(read_uint24)
|
278
|
+
when :time
|
279
|
+
convert_mysql_type_time(read_uint24)
|
280
|
+
when :datetime
|
281
|
+
convert_mysql_type_datetime(read_uint64)
|
261
282
|
#when :bit
|
262
|
-
#when :decimal
|
263
283
|
#when :newdecimal
|
264
|
-
#when :enum
|
265
|
-
#when :set
|
266
|
-
#when :geometry
|
267
284
|
end
|
268
285
|
end
|
269
|
-
|
270
286
|
end
|
271
287
|
end
|
@@ -1,4 +1,7 @@
|
|
1
1
|
module MysqlBinlog
|
2
|
+
# Read a binary log from a stream dumped using the +MysqlBinlogDump+
|
3
|
+
# library to request a +COM_BINLOG_DUMP+ from a MySQL server via the
|
4
|
+
# +Mysql+ library.
|
2
5
|
class BinlogStreamReader
|
3
6
|
def initialize(connection, filename, position)
|
4
7
|
require 'mysql_binlog_dump'
|
@@ -11,7 +14,6 @@ module MysqlBinlog
|
|
11
14
|
end
|
12
15
|
|
13
16
|
def rotate(filename, position)
|
14
|
-
puts "rotate called with #{filename}:#{position}"
|
15
17
|
@filename = filename
|
16
18
|
@position = position
|
17
19
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysql_binlog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 6
|
10
|
+
version: 0.1.6
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jeremy Cole
|