innodb_ruby 0.9.13 → 0.9.14

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.
@@ -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: