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 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
@@ -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 'flags' field that may appear in binlogs. There are
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 'flags' bitmap with the EVENT_HEADER_FLAGS hash to return
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 'flags' with the array of names.
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 column metadata within a table map event.
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 |c|
224
- parser.read_mysql_type_metadata(c)
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
- def read_mysql_type_metadata(column_type)
205
- case column_type
206
- when :float, :double
207
- { :size => read_uint8 }
208
- when :varchar
209
- { :max_length => read_uint16 }
210
- when :bit
211
- { :size => read_uint8 }
212
- when :decimal
213
- {
214
- :precision => read_uint8,
215
- :decimals => read_uint8,
216
- }
217
- when :blob
218
- { :length_size => read_uint8 }
219
- when :string, :var_string
220
- {
221
- :real_type => read_uint8,
222
- :max_length => read_uint8,
223
- }
224
- when :geometry
225
- { :length_size => read_uint8 }
226
- end
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
- #when :date
258
- #when :time
259
- #when :datetime
260
- #when :newdate
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,5 @@
1
1
  module MysqlBinlog
2
+ # Read a binary log from a file on disk.
2
3
  class BinlogFileReader
3
4
  def initialize(filename)
4
5
  @filename = filename
@@ -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: 17
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 5
10
- version: 0.1.5
9
+ - 6
10
+ version: 0.1.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jeremy Cole