mysql_binlog 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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