innodb_ruby 0.9.13 → 0.9.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -74,7 +74,7 @@ def usage(exit_code, message = nil)
74
74
 
75
75
  print <<'END_OF_USAGE'
76
76
 
77
- Usage: innodb_log [-d] [-s] -f <log file> <mode>
77
+ Usage: innodb_log [-d] [-l <lsn>] -f <log file> <mode>
78
78
 
79
79
  --help, -?
80
80
  Print this usage text.
@@ -492,15 +492,15 @@ end
492
492
  # filled block colored based on the index the page is part of. Print a legend
493
493
  # for the colors used afterwards.
494
494
  def space_extents_illustrate(space)
495
- width = space.pages_per_extent
495
+ line_width = space.pages_per_extent
496
496
  puts
497
- puts "%12s ╭%-#{width}s╮" % [ "Start Page", "─" * width ]
497
+ puts "%12s ╭%-#{line_width}s╮" % [ "Start Page", "─" * line_width ]
498
498
 
499
499
  identifiers = {}
500
500
  count_by_identifier = Hash.new(0)
501
501
 
502
502
  space.each_xdes do |entry|
503
- puts "%12i │%-#{width}s│" % [
503
+ puts "%12i │%-#{line_width}s│" % [
504
504
  entry.xdes[:start_page],
505
505
  entry.each_page_status.inject("") { |bitmap, (page_number, page_status)|
506
506
  if page_number < space.pages
@@ -543,7 +543,7 @@ def space_extents_illustrate(space)
543
543
  end
544
544
  total_pages = count_by_identifier.values.reduce(:+)
545
545
 
546
- puts "%12s ╰%-#{width}s╯" % [ "", "─" * width ]
546
+ puts "%12s ╰%-#{line_width}s╯" % [ "", "─" * line_width ]
547
547
 
548
548
  puts
549
549
  puts "Legend (%s = 1 page):" % [filled_block(1.0, nil)]
@@ -614,7 +614,8 @@ end
614
614
  # filled block colored based on the index the page is part of. Print a legend
615
615
  # for the colors used afterwards.
616
616
  def space_extents_illustrate_svg(space)
617
- width = space.pages_per_extent
617
+ line_width = space.pages_per_extent
618
+ block_size = @options.illustration_block_size
618
619
 
619
620
  puts "<?xml version=\"1.0\"?>"
620
621
  puts "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
@@ -622,7 +623,6 @@ def space_extents_illustrate_svg(space)
622
623
  identifiers = {}
623
624
  count_by_identifier = Hash.new(0)
624
625
 
625
- block_size = 16
626
626
  graphic_x = 48
627
627
  graphic_y = 16
628
628
 
@@ -716,7 +716,7 @@ def space_extents_illustrate_svg(space)
716
716
  puts svg_extent_legend(
717
717
  graphic_x + block_x,
718
718
  graphic_y + block_y,
719
- block_size,
719
+ block_size
720
720
  )
721
721
  block_y += block_size + 2
722
722
 
@@ -760,6 +760,7 @@ end
760
760
 
761
761
  def space_lsn_age_illustrate(space)
762
762
  colors = ANSI_COLORS_HEATMAP
763
+ line_width = @options.illustration_line_width
763
764
 
764
765
  # Calculate the minimum and maximum LSN in the space. This is pretty
765
766
  # inefficient as we end up scanning all pages twice.
@@ -776,11 +777,11 @@ def space_lsn_age_illustrate(space)
776
777
  lsn_delta = lsn_max - lsn_min
777
778
 
778
779
  puts
779
- puts "%12s ╭%-64s╮" % [ "Start Page", "─" * 64 ]
780
+ puts "%12s ╭%-#{line_width}s╮" % [ "Start Page", "─" * line_width ]
780
781
 
781
782
  start_page = 0
782
- page_lsn.each_slice(64) do |slice|
783
- puts "%12i │%-64s│" % [
783
+ page_lsn.each_slice(line_width) do |slice|
784
+ puts "%12i │%-#{line_width}s│" % [
784
785
  start_page,
785
786
  slice.inject("") { |line, lsn|
786
787
  if lsn
@@ -793,10 +794,10 @@ def space_lsn_age_illustrate(space)
793
794
  line
794
795
  },
795
796
  ]
796
- start_page += 64
797
+ start_page += line_width
797
798
  end
798
799
 
799
- puts "%12s ╰%-64s╯" % [ "", "─" * 64 ]
800
+ puts "%12s ╰%-#{line_width}s╯" % [ "", "─" * line_width ]
800
801
 
801
802
  lsn_legend = "<" + ("─" * (colors.size - 2)) + ">"
802
803
 
@@ -840,6 +841,8 @@ end
840
841
 
841
842
  def space_lsn_age_illustrate_svg(space)
842
843
  colors = RGBHEX_COLORS_HEATMAP
844
+ line_width = @options.illustration_line_width
845
+ block_size = @options.illustration_block_size
843
846
 
844
847
  puts "<?xml version=\"1.0\"?>"
845
848
  puts "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
@@ -858,7 +861,6 @@ def space_lsn_age_illustrate_svg(space)
858
861
  end
859
862
  lsn_delta = lsn_max - lsn_min
860
863
 
861
- block_size = 16
862
864
  graphic_x = 48
863
865
  graphic_y = 16
864
866
 
@@ -875,7 +877,7 @@ def space_lsn_age_illustrate_svg(space)
875
877
  }, "Page")
876
878
 
877
879
  start_page = 0
878
- page_lsn.each_slice(64) do |slice|
880
+ page_lsn.each_slice(line_width) do |slice|
879
881
  block_x = 0
880
882
  slice.each do |lsn|
881
883
  rgbhex = ""
@@ -900,7 +902,7 @@ def space_lsn_age_illustrate_svg(space)
900
902
  "text-anchor" => "end",
901
903
  }, start_page)
902
904
  block_y += block_size
903
- start_page += 64
905
+ start_page += line_width
904
906
  end
905
907
 
906
908
  puts svg("path", {
@@ -910,7 +912,7 @@ def space_lsn_age_illustrate_svg(space)
910
912
  "d" => svg_path_rounded_rect(
911
913
  graphic_x,
912
914
  graphic_y,
913
- block_x,
915
+ line_width * block_size,
914
916
  block_y,
915
917
  4
916
918
  ),
@@ -1013,6 +1015,42 @@ def page_account(innodb_system, space, page_number)
1013
1015
  page_type[:usage],
1014
1016
  ]
1015
1017
 
1018
+ if page.corrupt?
1019
+ puts " Page appears to be corrupt."
1020
+ puts " Stored checksum is %d, type %s." % [
1021
+ page.checksum,
1022
+ page.checksum_type ? page.checksum_type : "unknown",
1023
+ ]
1024
+ puts " Calculated checksums:"
1025
+ puts " crc32 %d" % [page.checksum_crc32]
1026
+ puts " innodb %d" % [page.checksum_innodb]
1027
+ end
1028
+
1029
+ if page.torn?
1030
+ puts " Page appears to be torn."
1031
+ puts " Full LSN from header is %d." % [page.lsn]
1032
+ puts " Low 32 bits of LSN from header is %d, trailer is %d." % [
1033
+ page.lsn_low32_header,
1034
+ page.lsn_low32_trailer,
1035
+ ]
1036
+ end
1037
+
1038
+ if page.misplaced?
1039
+ puts " Page appears to be misplaced."
1040
+ if page.misplaced_offset?
1041
+ puts " Requested page %d but offset stored in page is %d." % [
1042
+ page_number,
1043
+ page.offset,
1044
+ ]
1045
+ end
1046
+ if page.misplaced_space?
1047
+ puts " Space's ID %d does not match page's stored space ID %d." % [
1048
+ page.space.space_id,
1049
+ page.space_id,
1050
+ ]
1051
+ end
1052
+ end
1053
+
1016
1054
  xdes = space.xdes_for_page(page_number)
1017
1055
  puts " Extent descriptor for pages %d-%d is at page %d, offset %d." % [
1018
1056
  xdes.start_page,
@@ -1630,30 +1668,34 @@ Signal.trap("INT") { exit }
1630
1668
  Signal.trap("PIPE") { exit }
1631
1669
 
1632
1670
  @options = OpenStruct.new
1633
- @options.trace = 0
1634
- @options.system_space_file = nil
1635
- @options.space_file = nil
1636
- @options.table_name = nil
1637
- @options.index_name = nil
1638
- @options.page = nil
1639
- @options.record = nil
1640
- @options.level = nil
1641
- @options.list = nil
1642
- @options.describer = nil
1671
+ @options.trace = 0
1672
+ @options.system_space_file = nil
1673
+ @options.space_file = nil
1674
+ @options.table_name = nil
1675
+ @options.index_name = nil
1676
+ @options.page = nil
1677
+ @options.record = nil
1678
+ @options.level = nil
1679
+ @options.list = nil
1680
+ @options.describer = nil
1681
+ @options.illustration_line_width = 64
1682
+ @options.illustration_block_size = 8
1643
1683
 
1644
1684
  getopt_options = [
1645
- [ "--help", "-?", GetoptLong::NO_ARGUMENT ],
1646
- [ "--trace", "-t", GetoptLong::NO_ARGUMENT ],
1647
- [ "--system-space-file", "-s", GetoptLong::REQUIRED_ARGUMENT ],
1648
- [ "--space-file", "-f", GetoptLong::REQUIRED_ARGUMENT ],
1649
- [ "--table-name", "-T", GetoptLong::REQUIRED_ARGUMENT ],
1650
- [ "--index-name", "-I", GetoptLong::REQUIRED_ARGUMENT ],
1651
- [ "--page", "-p", GetoptLong::REQUIRED_ARGUMENT ],
1652
- [ "--record", "-R", GetoptLong::REQUIRED_ARGUMENT ],
1653
- [ "--level", "-l", GetoptLong::REQUIRED_ARGUMENT ],
1654
- [ "--list", "-L", GetoptLong::REQUIRED_ARGUMENT ],
1655
- [ "--require", "-r", GetoptLong::REQUIRED_ARGUMENT ],
1656
- [ "--describer", "-d", GetoptLong::REQUIRED_ARGUMENT ],
1685
+ [ "--help", "-?", GetoptLong::NO_ARGUMENT ],
1686
+ [ "--trace", "-t", GetoptLong::NO_ARGUMENT ],
1687
+ [ "--system-space-file", "-s", GetoptLong::REQUIRED_ARGUMENT ],
1688
+ [ "--space-file", "-f", GetoptLong::REQUIRED_ARGUMENT ],
1689
+ [ "--table-name", "-T", GetoptLong::REQUIRED_ARGUMENT ],
1690
+ [ "--index-name", "-I", GetoptLong::REQUIRED_ARGUMENT ],
1691
+ [ "--page", "-p", GetoptLong::REQUIRED_ARGUMENT ],
1692
+ [ "--record", "-R", GetoptLong::REQUIRED_ARGUMENT ],
1693
+ [ "--level", "-l", GetoptLong::REQUIRED_ARGUMENT ],
1694
+ [ "--list", "-L", GetoptLong::REQUIRED_ARGUMENT ],
1695
+ [ "--require", "-r", GetoptLong::REQUIRED_ARGUMENT ],
1696
+ [ "--describer", "-d", GetoptLong::REQUIRED_ARGUMENT ],
1697
+ [ "--illustration-line-width", GetoptLong::REQUIRED_ARGUMENT ],
1698
+ [ "--illustration-block-size", GetoptLong::REQUIRED_ARGUMENT ],
1657
1699
  ]
1658
1700
 
1659
1701
  getopt = GetoptLong.new(*getopt_options)
@@ -1684,6 +1726,10 @@ getopt.each do |opt, arg|
1684
1726
  require File.expand_path(arg)
1685
1727
  when "--describer"
1686
1728
  @options.describer = arg
1729
+ when "--illustration-line-width"
1730
+ @options.illustration_line_width = arg.to_i
1731
+ when "--illustration-block-size"
1732
+ @options.illustration_block_size = arg.to_i
1687
1733
  end
1688
1734
  end
1689
1735
 
@@ -1804,8 +1850,8 @@ when "space-summary"
1804
1850
  when "space-index-pages-summary"
1805
1851
  space_index_pages_summary(space, @options.page || 0)
1806
1852
  when "space-index-pages-free-plot"
1807
- name = File.basename(@options.space_file).sub(".ibd", "")
1808
- space_index_pages_free_plot(space, name, @options.page || 0)
1853
+ file_name = space.name.sub(".ibd", "").sub(/[^a-zA-Z0-9_]/, "_")
1854
+ space_index_pages_free_plot(space, file_name, @options.page || 0)
1809
1855
  when "space-page-type-regions"
1810
1856
  space_page_type_regions(space, @options.page || 0)
1811
1857
  when "space-page-type-summary"
@@ -16,6 +16,7 @@ end
16
16
 
17
17
  require "pp"
18
18
  require "enumerator"
19
+ require "digest/crc32c"
19
20
  require "innodb/util/buffer_cursor"
20
21
  require "innodb/util/read_bits_at_offset"
21
22
 
@@ -28,7 +28,8 @@ class Innodb::Index
28
28
  end
29
29
 
30
30
  def page(page_number)
31
- page = @space.page(page_number)
31
+ page = @space.page(page_number) or
32
+ raise "Page #{page_number} couldn't be read"
32
33
  page.record_describer = @record_describer
33
34
  page
34
35
  end
@@ -19,27 +19,27 @@ class Innodb::Page
19
19
  # extract the page type in order to avoid throwing away a generic
20
20
  # Innodb::Page object when parsing every specialized page, but this is
21
21
  # a bit cleaner, and we're not particularly performance sensitive.
22
- def self.parse(space, buffer)
22
+ def self.parse(space, buffer, page_number=nil)
23
23
  # Create a page object as a generic page.
24
- page = Innodb::Page.new(space, buffer)
24
+ page = Innodb::Page.new(space, buffer, page_number)
25
25
 
26
26
  # If there is a specialized class available for this page type, re-create
27
27
  # the page object using that specialized class.
28
28
  if specialized_class = SPECIALIZED_CLASSES[page.type]
29
- page = specialized_class.handle(page, space, buffer)
29
+ page = specialized_class.handle(page, space, buffer, page_number)
30
30
  end
31
31
 
32
32
  page
33
33
  end
34
34
 
35
35
  # Allow the specialized class to do something that isn't 'new' with this page.
36
- def self.handle(page, space, buffer)
37
- self.new(space, buffer)
36
+ def self.handle(page, space, buffer, page_number=nil)
37
+ self.new(space, buffer, page_number)
38
38
  end
39
39
 
40
40
  # Initialize a page by passing in a buffer containing the raw page contents.
41
41
  # The buffer size should match the space's page size.
42
- def initialize(space, buffer)
42
+ def initialize(space, buffer, page_number=nil)
43
43
  unless space && buffer
44
44
  raise "Page can't be initialized from nil space or buffer (space: #{space}, buffer: #{buffer})"
45
45
  end
@@ -50,6 +50,7 @@ class Innodb::Page
50
50
 
51
51
  @space = space
52
52
  @buffer = buffer
53
+ @page_number = page_number
53
54
  end
54
55
 
55
56
  attr_reader :space
@@ -100,6 +101,20 @@ class Innodb::Page
100
101
  4 + 4 + 4 + 4 + 8 + 2 + 8 + 4
101
102
  end
102
103
 
104
+ # The start of the checksummed portion of the file header.
105
+ def pos_partial_page_header
106
+ pos_fil_header + 4
107
+ end
108
+
109
+ # The size of the portion of the fil header that is included in the
110
+ # checksum. Exclude the following:
111
+ # :checksum (offset 4, size 4)
112
+ # :flush_lsn (offset 26, size 8)
113
+ # :space_id (offset 34, size 4)
114
+ def size_partial_page_header
115
+ size_fil_header - 4 - 8 - 4
116
+ end
117
+
103
118
  # Return the byte offset of the start of the "fil" trailer, which is at
104
119
  # the end of the page.
105
120
  def pos_fil_trailer
@@ -117,6 +132,11 @@ class Innodb::Page
117
132
  pos_fil_header + size_fil_header
118
133
  end
119
134
 
135
+ # Return the size of the page body, excluding the header and trailer.
136
+ def size_page_body
137
+ size - size_fil_trailer - size_fil_header
138
+ end
139
+
120
140
  # InnoDB Page Type constants from include/fil0fil.h.
121
141
  PAGE_TYPE = {
122
142
  :ALLOCATED => {
@@ -196,7 +216,7 @@ class Innodb::Page
196
216
 
197
217
  # Return the "fil" header from the page, which is common for all page types.
198
218
  def fil_header
199
- @fil_header ||= cursor(pos_fil_header).name("fil") do |c|
219
+ @fil_header ||= cursor(pos_fil_header).name("fil_header") do |c|
200
220
  {
201
221
  :checksum => c.name("checksum") { c.get_uint32 },
202
222
  :offset => c.name("offset") { c.get_uint32 },
@@ -214,12 +234,28 @@ class Innodb::Page
214
234
  end
215
235
  end
216
236
 
237
+ # Return the "fil" trailer from the page, which is common for all page types.
238
+ def fil_trailer
239
+ @fil_trailer ||= cursor(pos_fil_trailer).name("fil_trailer") do |c|
240
+ {
241
+ :checksum => c.name("checksum") { c.get_uint32 },
242
+ :lsn_low32 => c.name("lsn_low32") { c.get_uint32 },
243
+ }
244
+ end
245
+ end
246
+
217
247
  # A helper function to return the checksum from the "fil" header, for easier
218
248
  # access.
219
249
  def checksum
220
250
  fil_header[:checksum]
221
251
  end
222
252
 
253
+ # A helper function to return the checksum from the "fil" trailer, for easier
254
+ # access.
255
+ def checksum_trailer
256
+ fil_trailer[:checksum]
257
+ end
258
+
223
259
  # A helper function to return the page offset from the "fil" header, for
224
260
  # easier access.
225
261
  def offset
@@ -240,53 +276,161 @@ class Innodb::Page
240
276
  fil_header[:next]
241
277
  end
242
278
 
243
- # A helper function to return the LSN, for easier access.
279
+ # A helper function to return the LSN from the page header, for easier access.
244
280
  def lsn
245
281
  fil_header[:lsn]
246
282
  end
247
283
 
284
+ # A helper function to return the low 32 bits of the LSN from the page header
285
+ # for use in comparing to the low 32 bits stored in the trailer.
286
+ def lsn_low32_header
287
+ fil_header[:lsn] & 0xffffffff
288
+ end
289
+
290
+ # A helper function to return the low 32 bits of the LSN as stored in the page
291
+ # trailer.
292
+ def lsn_low32_trailer
293
+ fil_trailer[:lsn_low32]
294
+ end
295
+
248
296
  # A helper function to return the page type from the "fil" header, for easier
249
297
  # access.
250
298
  def type
251
299
  fil_header[:type]
252
300
  end
253
301
 
254
- # Calculate the checksum of the page using InnoDB's algorithm. Two sections
255
- # of the page are checksummed separately, and then added together to produce
256
- # the final checksum.
257
- def calculate_checksum
302
+ # A helper function to return the space ID from the "fil" header, for easier
303
+ # access.
304
+ def space_id
305
+ fil_header[:space_id]
306
+ end
307
+
308
+ # Iterate each byte of the FIL header.
309
+ def each_page_header_byte_as_uint8
310
+ unless block_given?
311
+ return enum_for(:each_page_header_byte_as_uint8)
312
+ end
313
+
314
+ cursor(pos_partial_page_header).
315
+ each_byte_as_uint8(size_partial_page_header) do |byte|
316
+ yield byte
317
+ end
318
+ end
319
+
320
+ # Iterate each byte of the page body, except for the FIL header and
321
+ # the FIL trailer.
322
+ def each_page_body_byte_as_uint8
323
+ unless block_given?
324
+ return enum_for(:each_page_body_byte_as_uint8)
325
+ end
326
+
327
+ cursor(pos_page_body).
328
+ each_byte_as_uint8(size_page_body) do |byte|
329
+ yield byte
330
+ end
331
+ end
332
+
333
+ # Calculate the checksum of the page using InnoDB's algorithm.
334
+ def checksum_innodb
335
+ unless size == 16384
336
+ raise "Checksum calculation is only supported for 16 KiB pages"
337
+ end
338
+
339
+ @checksum_innodb ||= begin
340
+ # Calculate the InnoDB checksum of the page header.
341
+ c_partial_header = Innodb::Checksum.fold_enumerator(each_page_header_byte_as_uint8)
342
+
343
+ # Calculate the InnoDB checksum of the page body.
344
+ c_page_body = Innodb::Checksum.fold_enumerator(each_page_body_byte_as_uint8)
345
+
346
+ # Add the two checksums together, and mask the result back to 32 bits.
347
+ (c_partial_header + c_page_body) & Innodb::Checksum::MAX
348
+ end
349
+ end
350
+
351
+ def checksum_innodb?
352
+ checksum == checksum_innodb
353
+ end
354
+
355
+ # Calculate the checksum of the page using the CRC32c algorithm.
356
+ def checksum_crc32
258
357
  unless size == 16384
259
358
  raise "Checksum calculation is only supported for 16 KiB pages"
260
359
  end
261
360
 
262
- # Calculate the checksum of the FIL header, except for the following:
263
- # :checksum (offset 4, size 4)
264
- # :flush_lsn (offset 26, size 8)
265
- # :space_id (offset 34, size 4)
266
- c_partial_header =
267
- Innodb::Checksum.fold_enumerator(
268
- cursor(pos_fil_header + 4).each_byte_as_uint8(
269
- size_fil_header - 4 - 8 - 4
270
- )
271
- )
272
-
273
- # Calculate the checksum of the page body, except for the FIL header and
274
- # the FIL trailer.
275
- c_page_body =
276
- Innodb::Checksum.fold_enumerator(
277
- cursor(pos_page_body).each_byte_as_uint8(
278
- size - size_fil_trailer - size_fil_header
279
- )
280
- )
281
-
282
- # Add the two checksums together, and mask the result back to 32 bits.
283
- (c_partial_header + c_page_body) & Innodb::Checksum::MAX
284
- end
285
-
286
- # Is the page corrupt? Calculate the checksum of the page and compare to
287
- # the stored checksum; return true or false.
361
+ @checksum_crc32 ||= begin
362
+ # Calculate the CRC32c of the page header.
363
+ crc_partial_header = Digest::CRC32c.new
364
+ each_page_header_byte_as_uint8 do |byte|
365
+ crc_partial_header << byte.chr
366
+ end
367
+
368
+ # Calculate the CRC32c of the page body.
369
+ crc_page_body = Digest::CRC32c.new
370
+ each_page_body_byte_as_uint8 do |byte|
371
+ crc_page_body << byte.chr
372
+ end
373
+
374
+ # Bitwise XOR the two checksums together.
375
+ crc_partial_header.checksum ^ crc_page_body.checksum
376
+ end
377
+ end
378
+
379
+ def checksum_crc32?
380
+ checksum == checksum_crc32
381
+ end
382
+
383
+ # Is the page checksum correct?
384
+ def checksum_valid?
385
+ checksum_crc32? || checksum_innodb?
386
+ end
387
+
388
+ # Is the page checksum incorrect?
389
+ def checksum_invalid?
390
+ !checksum_valid?
391
+ end
392
+
393
+ def checksum_type
394
+ case
395
+ when checksum_crc32?
396
+ :crc32
397
+ when checksum_innodb?
398
+ :innodb
399
+ end
400
+ end
401
+
402
+ # Is the LSN stored in the header different from the one stored in the
403
+ # trailer?
404
+ def torn?
405
+ lsn_low32_header != lsn_low32_trailer
406
+ end
407
+
408
+ # Is the page in the doublewrite buffer?
409
+ def in_doublewrite_buffer?
410
+ space && space.system_space? && space.doublewrite_page?(offset)
411
+ end
412
+
413
+ # Is the space ID stored in the header different from that of the space
414
+ # provided when initializing this page?
415
+ def misplaced_space?
416
+ space && (space_id != space.space_id)
417
+ end
418
+
419
+ # Is the page number stored in the header different from the page number
420
+ # which was supposed to be read?
421
+ def misplaced_offset?
422
+ offset != @page_number
423
+ end
424
+
425
+ # Is the page misplaced in the wrong file or by offset in the file?
426
+ def misplaced?
427
+ !in_doublewrite_buffer? && (misplaced_space? || misplaced_offset?)
428
+ end
429
+
430
+ # Is the page corrupt, either due to data corruption, tearing, or in the
431
+ # wrong place?
288
432
  def corrupt?
289
- checksum != calculate_checksum
433
+ checksum_invalid? || torn? || misplaced?
290
434
  end
291
435
 
292
436
  def each_region
@@ -315,7 +459,7 @@ class Innodb::Page
315
459
  # the page buffer, since it's very large and mostly not interesting.
316
460
  def inspect
317
461
  if fil_header
318
- "#<%s: size=%i, space_id=%i, offset=%i, type=%s, prev=%s, next=%s>" % [
462
+ "#<%s: size=%i, space_id=%i, offset=%i, type=%s, prev=%s, next=%s, checksum_valid?=%s (%s), torn?=%s, misplaced?=%s>" % [
319
463
  self.class,
320
464
  size,
321
465
  fil_header[:space_id],
@@ -323,6 +467,10 @@ class Innodb::Page
323
467
  fil_header[:type],
324
468
  fil_header[:prev] || "nil",
325
469
  fil_header[:next] || "nil",
470
+ checksum_valid?,
471
+ checksum_type ? checksum_type : "unknown",
472
+ torn?,
473
+ misplaced?,
326
474
  ]
327
475
  else
328
476
  "#<#{self.class}>"
@@ -337,5 +485,9 @@ class Innodb::Page
337
485
  puts "fil header:"
338
486
  pp fil_header
339
487
  puts
488
+
489
+ puts "fil trailer:"
490
+ pp fil_trailer
491
+ puts
340
492
  end
341
493
  end
@@ -8,14 +8,14 @@ require "innodb/page/sys_ibuf_header"
8
8
  # uses within InnoDB. We'll override the self.handle method and check the
9
9
  # page offset to decide which type of SYS page this is.
10
10
  class Innodb::Page::Sys < Innodb::Page
11
- def self.handle(page, space, buffer)
11
+ def self.handle(page, space, buffer, page_number=nil)
12
12
  case
13
13
  when page.offset == 3
14
- Innodb::Page::SysIbufHeader.new(space, buffer)
14
+ Innodb::Page::SysIbufHeader.new(space, buffer, page_number)
15
15
  when page.offset == 7
16
- Innodb::Page::SysDataDictionaryHeader.new(space, buffer)
16
+ Innodb::Page::SysDataDictionaryHeader.new(space, buffer, page_number)
17
17
  when space.rseg_page?(page.offset)
18
- Innodb::Page::SysRsegHeader.new(space, buffer)
18
+ Innodb::Page::SysRsegHeader.new(space, buffer, page_number)
19
19
  else
20
20
  # We can't do anything better, so pass on the generic InnoDB::Page.
21
21
  page
@@ -228,7 +228,12 @@ class Innodb::Space
228
228
 
229
229
  # Get an Innodb::Page object for a specific page by page number.
230
230
  def page(page_number)
231
- Innodb::Page.parse(self, page_data(page_number))
231
+ data = page_data(page_number)
232
+ if data
233
+ Innodb::Page.parse(self, data, page_number)
234
+ else
235
+ nil
236
+ end
232
237
  end
233
238
 
234
239
  # Determine whether this space looks like a system space. If the initial
@@ -374,6 +379,28 @@ class Innodb::Space
374
379
  end
375
380
  end
376
381
 
382
+ # Iterate through the page numbers in the doublewrite buffer.
383
+ def each_doublewrite_page_number
384
+ return nil unless system_space?
385
+
386
+ unless block_given?
387
+ return enum_for(:each_doublewrite_page_number)
388
+ end
389
+
390
+ trx_sys.doublewrite[:page_info][0][:page_number].each do |start_page|
391
+ (start_page...(start_page+pages_per_extent)).each do |page_number|
392
+ yield page_number
393
+ end
394
+ end
395
+ end
396
+
397
+ # Return true if a page is in the doublewrite buffer.
398
+ def doublewrite_page?(page_number)
399
+ return false unless system_space?
400
+ @doublewrite_pages ||= each_doublewrite_page_number.to_a
401
+ @doublewrite_pages.include?(page_number)
402
+ end
403
+
377
404
  # Iterate through all pages in a space, returning the page number and an
378
405
  # Innodb::Page object for each one.
379
406
  def each_page(start_page=0)
@@ -1,5 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  module Innodb
4
- VERSION = "0.9.13"
4
+ VERSION = "0.9.14"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: innodb_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.13
4
+ version: 0.9.14
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-06-06 00:00:00.000000000 Z
13
+ date: 2016-09-21 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bindata
@@ -28,6 +28,22 @@ dependencies:
28
28
  - - ! '>='
29
29
  - !ruby/object:Gem::Version
30
30
  version: 1.4.5
31
+ - !ruby/object:Gem::Dependency
32
+ name: digest-crc
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: 0.4.1
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: 0.4.1
31
47
  description: Library for parsing InnoDB data files in Ruby
32
48
  email: jeremy@jcole.us
33
49
  executables: