innodb_ruby 0.9.8 → 0.9.9

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/AUTHORS.md CHANGED
@@ -10,3 +10,4 @@ The primary authors of `innodb_ruby` have been:
10
10
  Special thanks to the following contributors who have taken an interest and contributed their fixes:
11
11
 
12
12
  * Andrew Gaul \<gaul@maginatics.com\>
13
+ * Frédéric -lefred- Descamps \<lefred@percona.com\>
data/README.md CHANGED
@@ -15,3 +15,5 @@ Various parts of this library and the tools included may have wildly differing m
15
15
 
16
16
  * Visit the [innodb_ruby mailing list on Google Groups](https://groups.google.com/d/forum/innodb_ruby) or email [innodb_ruby@googlegroups.com](mailto:innodb_ruby@googlegroups.com) &mdash; If you have questions about `innodb_ruby` or its usage.
17
17
  * See the [RubyGems page for innodb_ruby](http://rubygems.org/gems/innodb_ruby) &mdash; Gem packaged releases are published regularly to RubyGems.org, which also provides online documentation.
18
+
19
+ [![Build Status](https://travis-ci.org/jeremycole/innodb_ruby.svg?branch=master)](https://travis-ci.org/jeremycole/innodb_ruby)
data/bin/innodb_space CHANGED
@@ -178,6 +178,10 @@ def system_spaces(innodb_system)
178
178
  next unless space
179
179
  print_space_information.call(table_name, space)
180
180
  end
181
+
182
+ innodb_system.each_orphan do |table_name|
183
+ puts "%-43s (orphan/tmp)" % table_name
184
+ end
181
185
  end
182
186
 
183
187
  # Print the contents of the SYS_TABLES data dictionary table.
@@ -451,13 +455,15 @@ end
451
455
  # filled block colored based on the index the page is part of. Print a legend
452
456
  # for the colors used afterwards.
453
457
  def space_extents_illustrate(space)
458
+ width = space.pages_per_extent
454
459
  puts
455
- puts "%12s ╭%-64s╮" % [ "Start Page", "─" * 64 ]
460
+ puts "%12s ╭%-#{width}s╮" % [ "Start Page", "─" * width ]
456
461
 
457
462
  identifiers = {}
463
+ count_by_identifier = Hash.new(0)
458
464
 
459
465
  space.each_xdes do |entry|
460
- puts "%12i │%-64s│" % [
466
+ puts "%12i │%-#{width}s│" % [
461
467
  entry.xdes[:start_page],
462
468
  entry.each_page_status.inject("") { |bitmap, (page_number, page_status)|
463
469
  if page_number < space.pages
@@ -473,7 +479,9 @@ def space_extents_illustrate(space)
473
479
  if page.respond_to?(:index_id)
474
480
  identifier = page.index_id
475
481
  unless identifiers[identifier]
476
- identifiers[identifier] = "Index #{page.index_id}"
482
+ identifiers[identifier] = (page.index_id == Innodb::IbufIndex::INDEX_ID) ?
483
+ "Insert Buffer Index" :
484
+ "Index #{page.index_id}"
477
485
  if space.innodb_system
478
486
  table, index = space.innodb_system.table_and_index_name_by_id(page.index_id)
479
487
  if table && index
@@ -484,6 +492,11 @@ def space_extents_illustrate(space)
484
492
  end
485
493
  end
486
494
  bitmap += filled_block(used_fraction, identifier)
495
+ if used_fraction != 0.0
496
+ count_by_identifier[identifier] += 1
497
+ else
498
+ count_by_identifier[:free] += 1
499
+ end
487
500
  else
488
501
  bitmap += " "
489
502
  end
@@ -492,15 +505,33 @@ def space_extents_illustrate(space)
492
505
  ]
493
506
  end
494
507
 
495
- puts "%12s ╰%-64s╯" % [ "", "─" * 64 ]
508
+ puts "%12s ╰%-#{width}s╯" % [ "", "─" * width ]
496
509
 
497
510
  puts
498
- puts "Legend:"
499
- puts " %s %s" % [filled_block(1.0, nil), "System"]
511
+ puts "Legend (%s = 1 page):" % [filled_block(1.0, nil)]
512
+ puts " %-62s %8s %8s" % [
513
+ "Page Type", "Pages", "Ratio"
514
+ ]
515
+ puts " %s %-60s %8i %7.2f%%" % [
516
+ filled_block(1.0, nil),
517
+ "System",
518
+ count_by_identifier[nil],
519
+ 100.0 * (count_by_identifier[nil].to_f / space.pages.to_f),
520
+ ]
500
521
  identifiers.sort.each do |identifier, description|
501
- puts " %s %s" % [filled_block(1.0, identifier), description]
522
+ puts " %s %-60s %8i %7.2f%%" % [
523
+ filled_block(1.0, identifier),
524
+ description,
525
+ count_by_identifier[identifier],
526
+ 100.0 * (count_by_identifier[identifier].to_f / space.pages.to_f),
527
+ ]
502
528
  end
503
- puts " %s %s" % [filled_block(0.0, nil), "Free space"]
529
+ puts " %s %-60s %8i %7.2f%%" % [
530
+ filled_block(0.0, nil),
531
+ "Free space",
532
+ count_by_identifier[:free],
533
+ 100.0 * (count_by_identifier[:free].to_f / space.pages.to_f),
534
+ ]
504
535
  puts
505
536
  end
506
537
 
@@ -572,7 +603,7 @@ def space_lsn_age_illustrate(space)
572
603
  end
573
604
 
574
605
  puts
575
- puts "Legend:"
606
+ puts "Legend (%s = 1 page):" % [filled_block(1.0, nil)]
576
607
  puts " %12s %s %-12s" % [
577
608
  "Min LSN",
578
609
  lsn_legend,
@@ -759,6 +790,7 @@ def page_illustrate(page)
759
790
  blocks = Array.new(page.size, " ")
760
791
  identifiers = {}
761
792
  identifier_sort = 0
793
+ count_by_identifier = Hash.new(0)
762
794
 
763
795
  page.each_region.sort { |a,b| a[:offset] <=> b[:offset]}.each do |region|
764
796
  region[:length].times do |n|
@@ -782,6 +814,7 @@ def page_illustrate(page)
782
814
  end
783
815
  end
784
816
  blocks[region[:offset] + n] = filled_block(fraction, identifier, BLOCK_CHARS_H)
817
+ count_by_identifier[identifier] += 1
785
818
  end
786
819
  end
787
820
 
@@ -795,14 +828,33 @@ def page_illustrate(page)
795
828
  puts "%12s ╰%-#{width}s╯" % [ "", "─" * width ]
796
829
 
797
830
  puts
798
- puts "Legend:"
831
+ puts "Legend (%s = 1 byte):" % [filled_block(1.0, nil)]
832
+ puts " %-32s %8s %8s" % [
833
+ "Region Type",
834
+ "Bytes",
835
+ "Ratio",
836
+ ]
799
837
  identifiers.sort { |a,b| a[1] <=> b[1] }.each do |identifier, description|
800
- puts " %s %s" % [
838
+ puts " %s %-30s %8i %7.2f%%" % [
801
839
  filled_block(1.0, identifier),
802
- description.gsub(/^<\d+>/, "")
840
+ description.gsub(/^<\d+>/, ""),
841
+ count_by_identifier[identifier],
842
+ 100.0 * (count_by_identifier[identifier].to_f / page.size.to_f),
803
843
  ]
804
844
  end
805
- puts " %s %s" % [filled_block(0.0, nil), "Garbage"]
845
+ puts " %s %-30s %8i %7.2f%%" % [
846
+ filled_block(0.0, nil),
847
+ "Garbage",
848
+ count_by_identifier[nil],
849
+ 100.0 * (count_by_identifier[nil].to_f / page.size.to_f),
850
+ ]
851
+ free_space = page.size - count_by_identifier.inject(0) { |sum,(k,v)| sum + v }
852
+ puts " %s %-30s %8i %7.2f%%" % [
853
+ " ",
854
+ "Free",
855
+ free_space,
856
+ 100.0 * (free_space.to_f / page.size.to_f),
857
+ ]
806
858
 
807
859
  puts
808
860
  end
@@ -969,6 +1021,11 @@ The following options are supported:
969
1021
  --help, -?
970
1022
  Print this usage text.
971
1023
 
1024
+ --trace, -t
1025
+ Enable tracing of all data read. Specify twice to enable even more
1026
+ tracing (including reads during opening of the tablespace) which can
1027
+ be quite noisy.
1028
+
972
1029
  --system-space-file, -s <file>
973
1030
  Load the system tablespace file <file> (normally ibdata1).
974
1031
 
@@ -997,10 +1054,6 @@ The following options are supported:
997
1054
  --describer, -d <describer>
998
1055
  Use the named record describer to parse records in index pages.
999
1056
 
1000
- --page-size, -P <size>
1001
- Provide the page size, overriding auto-detection (in KiB): 16, 8, 4, 2, 1.
1002
- Page sizes other than 16 may not work well, or at all.
1003
-
1004
1057
  The following modes are supported:
1005
1058
 
1006
1059
  system-spaces
@@ -1129,6 +1182,7 @@ Signal.trap("INT") { exit }
1129
1182
  Signal.trap("PIPE") { exit }
1130
1183
 
1131
1184
  @options = OpenStruct.new
1185
+ @options.trace = 0
1132
1186
  @options.system_space_file = nil
1133
1187
  @options.space_file = nil
1134
1188
  @options.table_name = nil
@@ -1136,7 +1190,6 @@ Signal.trap("PIPE") { exit }
1136
1190
  @options.page = nil
1137
1191
  @options.level = nil
1138
1192
  @options.list = nil
1139
- @options.page_size = nil
1140
1193
  @options.describer = nil
1141
1194
 
1142
1195
  getopt_options = [
@@ -1150,7 +1203,6 @@ getopt_options = [
1150
1203
  [ "--level", "-l", GetoptLong::REQUIRED_ARGUMENT ],
1151
1204
  [ "--list", "-L", GetoptLong::REQUIRED_ARGUMENT ],
1152
1205
  [ "--require", "-r", GetoptLong::REQUIRED_ARGUMENT ],
1153
- [ "--page-size", "-P", GetoptLong::REQUIRED_ARGUMENT ],
1154
1206
  [ "--describer", "-d", GetoptLong::REQUIRED_ARGUMENT ],
1155
1207
  ]
1156
1208
 
@@ -1161,7 +1213,7 @@ getopt.each do |opt, arg|
1161
1213
  when "--help"
1162
1214
  usage 0
1163
1215
  when "--trace"
1164
- BufferCursor.trace!
1216
+ @options.trace += 1
1165
1217
  when "--system-space-file"
1166
1218
  @options.system_space_file = arg
1167
1219
  when "--space-file"
@@ -1178,11 +1230,6 @@ getopt.each do |opt, arg|
1178
1230
  @options.list = arg.to_sym
1179
1231
  when "--require"
1180
1232
  require File.expand_path(arg)
1181
- when "--page-size"
1182
- unless [1, 2, 4, 8, 16].include?(arg.to_i)
1183
- usage 1, "Page size #{arg} is not understood"
1184
- end
1185
- @options.page_size = arg.to_i * 1024
1186
1233
  when "--describer"
1187
1234
  @options.describer = arg
1188
1235
  end
@@ -1196,6 +1243,10 @@ if @options.system_space_file and @options.space_file
1196
1243
  usage 1, "Only one of system space or space file may be specified"
1197
1244
  end
1198
1245
 
1246
+ if @options.trace > 1
1247
+ BufferCursor.trace!
1248
+ end
1249
+
1199
1250
  innodb_system = nil
1200
1251
  if @options.system_space_file
1201
1252
  innodb_system = Innodb::System.new(@options.system_space_file)
@@ -1205,7 +1256,7 @@ space = innodb_system ? innodb_system.system_space : nil
1205
1256
  if innodb_system and @options.table_name
1206
1257
  space = innodb_system.space_by_table_name(@options.table_name)
1207
1258
  elsif @options.space_file
1208
- space = Innodb::Space.new(@options.space_file, @options.page_size)
1259
+ space = Innodb::Space.new(@options.space_file)
1209
1260
  end
1210
1261
 
1211
1262
  if @options.describer
@@ -1262,6 +1313,10 @@ if [
1262
1313
  usage 1, "Record describer must be specified using -d/--describer"
1263
1314
  end
1264
1315
 
1316
+ if @options.trace > 0
1317
+ BufferCursor.trace!
1318
+ end
1319
+
1265
1320
  case mode
1266
1321
  when "system-spaces"
1267
1322
  system_spaces(innodb_system)
@@ -145,6 +145,7 @@ class Innodb::DataDictionary
145
145
  :UNIVERSAL => 4,
146
146
  :IBUF => 8,
147
147
  :CORRUPT => 16,
148
+ :FTS => 32,
148
149
  }
149
150
 
150
151
  # A hash of INDEX_TYPE_FLAG keys by value.
@@ -228,6 +229,41 @@ class Innodb::DataDictionary
228
229
  system_space.data_dictionary_page.data_dictionary_header[:indexes]
229
230
  end
230
231
 
232
+ def data_dictionary_index_ids
233
+ if @data_dictionary_index_ids
234
+ return @data_dictionary_index_ids
235
+ end
236
+
237
+ @data_dictionary_index_ids = {}
238
+ data_dictionary_indexes.each do |table, indexes|
239
+ indexes.each do |index, root_page_number|
240
+ if root_page = system_space.page(root_page_number)
241
+ @data_dictionary_index_ids[root_page.index_id] = {
242
+ :table => table,
243
+ :index => index
244
+ }
245
+ end
246
+ end
247
+ end
248
+
249
+ @data_dictionary_index_ids
250
+ end
251
+
252
+ def data_dictionary_table?(table_name)
253
+ DATA_DICTIONARY_RECORD_DESCRIBERS.include?(table_name.to_sym)
254
+ end
255
+
256
+ def data_dictionary_index?(table_name, index_name)
257
+ return unless data_dictionary_table?(table_name)
258
+ DATA_DICTIONARY_RECORD_DESCRIBERS[table_name.to_sym].include?(index_name.to_sym)
259
+ end
260
+
261
+ def data_dictionary_index_describer(table_name, index_name)
262
+ return unless data_dictionary_index?(table_name, index_name)
263
+
264
+ DATA_DICTIONARY_RECORD_DESCRIBERS[table_name.to_sym][index_name.to_sym].new
265
+ end
266
+
231
267
  # Return an Innodb::Index object initialized to the
232
268
  # internal data dictionary index with an appropriate
233
269
  # record describer so that records can be recursed.
@@ -241,10 +277,9 @@ class Innodb::DataDictionary
241
277
  end
242
278
 
243
279
  # If we have a record describer for this index, load it.
244
- record_describer = DATA_DICTIONARY_RECORD_DESCRIBERS[table_name] &&
245
- DATA_DICTIONARY_RECORD_DESCRIBERS[table_name][index_name]
280
+ record_describer = data_dictionary_index_describer(table_name, index_name)
246
281
 
247
- system_space.index(index_root_page, record_describer.new)
282
+ system_space.index(index_root_page, record_describer)
248
283
  end
249
284
 
250
285
  # Iterate through all data dictionary indexes, yielding the
@@ -605,6 +640,10 @@ class Innodb::DataDictionary
605
640
  # Return an Innodb::RecordDescriber object describing records for a given
606
641
  # index by table name and index name.
607
642
  def record_describer_by_index_name(table_name, index_name)
643
+ if data_dictionary_index?(table_name, index_name)
644
+ return data_dictionary_index_describer(table_name, index_name)
645
+ end
646
+
608
647
  unless index = index_by_name(table_name, index_name)
609
648
  raise "Index #{index_name} for table #{table_name} not found"
610
649
  end
@@ -632,6 +671,10 @@ class Innodb::DataDictionary
632
671
  # Return an Innodb::RecordDescriber object describing the records
633
672
  # in a given index by index ID.
634
673
  def record_describer_by_index_id(index_id)
674
+ if dd_index = data_dictionary_index_ids[index_id]
675
+ return data_dictionary_index_describer(dd_index[:table], dd_index[:index])
676
+ end
677
+
635
678
  unless index = index_by_id(index_id)
636
679
  raise "Index #{index_id} not found"
637
680
  end
@@ -22,9 +22,15 @@ class Innodb::FsegEntry
22
22
  # Return an INODE entry which represents this file segment.
23
23
  def self.get_inode(space, cursor)
24
24
  address = cursor.name("address") { get_entry_address(cursor) }
25
+ if address[:offset] == 0
26
+ return nil
27
+ end
28
+
25
29
  page = space.page(address[:page_number])
26
- if page.type == :INODE
27
- page.inode_at(page.cursor(address[:offset]))
30
+ if page.type != :INODE
31
+ return nil
28
32
  end
33
+
34
+ page.inode_at(page.cursor(address[:offset]))
29
35
  end
30
36
  end
@@ -0,0 +1,50 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Innodb::IbufBitmap
4
+ BITS_PER_PAGE = 4
5
+
6
+ BITMAP_BV_FREE = 1 + 2
7
+ BITMAP_BV_BUFFERED = 4
8
+ BITMAP_BV_IBUF = 8
9
+
10
+ BITMAP_BV_ALL =
11
+ BITMAP_BV_FREE |
12
+ BITMAP_BV_BUFFERED |
13
+ BITMAP_BV_IBUF
14
+
15
+ def initialize(page, cursor)
16
+ @page = page
17
+ @bitmap = read_bitmap(page, cursor)
18
+ end
19
+
20
+ def size_bitmap
21
+ (@page.space.pages_per_bookkeeping_page * BITS_PER_PAGE) / 8
22
+ end
23
+
24
+ def read_bitmap(page, cursor)
25
+ cursor.name("ibuf_bitmap") do |c|
26
+ c.get_bytes(size_bitmap)
27
+ end
28
+ end
29
+
30
+ def each_page_status
31
+ unless block_given?
32
+ return enum_for(:each_page_status)
33
+ end
34
+
35
+ bitmap = @bitmap.enum_for(:each_byte)
36
+
37
+ bitmap.each_with_index do |byte, byte_index|
38
+ (0..1).each do |page_offset|
39
+ page_number = (byte_index * 2) + page_offset
40
+ page_bits = ((byte >> (page_offset * BITS_PER_PAGE)) & BITMAP_BV_ALL)
41
+ page_status = {
42
+ :free => (page_bits & BITMAP_BV_FREE),
43
+ :buffered => (page_bits & BITMAP_BV_BUFFERED != 0),
44
+ :ibuf => (page_bits & BITMAP_BV_IBUF != 0),
45
+ }
46
+ yield page_number, page_status
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ class Innodb::IbufIndex
2
+ INDEX_ID = 0xFFFFFFFF00000000
3
+ end
data/lib/innodb/index.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  # An InnoDB index B-tree, given an Innodb::Space and a root page number.
4
4
  class Innodb::Index
5
5
  attr_reader :root
6
+ attr_reader :space
6
7
  attr_accessor :record_describer
7
8
 
8
9
  def initialize(space, root_page_number, record_describer=nil)
@@ -2,7 +2,7 @@
2
2
 
3
3
  class Innodb::Page::Blob < Innodb::Page
4
4
  def pos_blob_header
5
- pos_fil_header + size_fil_header
5
+ pos_page_body
6
6
  end
7
7
 
8
8
  def size_blob_header
@@ -17,40 +17,50 @@ class Innodb::Page::FspHdrXdes < Innodb::Page
17
17
 
18
18
  # A value added to the adjusted exponent stored in the page size field of
19
19
  # the flags in the FSP header.
20
- FLAGS_PAGE_SIZE_ADJUST = 9
20
+ FLAGS_PAGE_SIZE_SHIFT = 9
21
+
22
+ def self.shift_page_size(page_size_shifted)
23
+ if page_size_shifted != 0
24
+ (1 << (FLAGS_PAGE_SIZE_SHIFT + page_size_shifted))
25
+ end
26
+ end
21
27
 
22
28
  # Decode the "flags" field in the FSP header, returning a hash of useful
23
- # decoded flags. Unfortunately, InnoDB has a fairly weird and broken
24
- # implementation of these flags. The flags are:
29
+ # decodings of the flags (based on MySQl 5.6 definitions). The flags are:
25
30
  #
26
31
  # Offset Size Description
27
- # 0 1 Page Format (redundant, compact). This is unfortunately
28
- # coerced to 0 if it is "compact" and no other flags are
29
- # set, making it useless to innodb_ruby.
32
+ # 0 1 Post-Antelope Flag.
30
33
  # 1 4 Compressed Page Size (zip_size). This is stored as a
31
34
  # power of 2, minus 9. Since 0 is reserved to mean "not
32
35
  # compressed", the minimum value is 1, thus making the
33
36
  # smallest page size 1024 (2 ** (9 + 1)).
34
- # 5 1 Table Format (Antelope, Barracuda). This was supposed
35
- # to reserve 6 bits, but due to a bug in InnoDB only
36
- # actually reserved 1 bit.
37
+ # 5 1 Atomic Blobs Flag.
38
+ # 6 4 System Page Size (innodb_page_size, UNIV_PAGE_SIZE).
39
+ # The setting of the system page size when the tablespace
40
+ # was created, stored in the same format as the compressed
41
+ # page size above.
42
+ # 10 1 Data Directory Flag.
37
43
  #
38
44
  def self.decode_flags(flags)
39
- # The page size for compressed pages is stored at bit offset 1 and consumes
40
- # 4 bits. Value 0 means the page is not compressed.
41
- page_size = read_bits_at_offset(flags, 4, 1)
45
+ system_page_size =
46
+ shift_page_size(read_bits_at_offset(flags, 4, 6)) ||
47
+ Innodb::Space::DEFAULT_PAGE_SIZE
48
+ compressed_page_size = shift_page_size(read_bits_at_offset(flags, 4, 1))
49
+
42
50
  {
43
- :compressed => page_size == 0 ? false : true,
44
- :page_size => page_size == 0 ?
45
- Innodb::Space::DEFAULT_PAGE_SIZE :
46
- (1 << (FLAGS_PAGE_SIZE_ADJUST + page_size)),
51
+ :system_page_size => system_page_size,
52
+ :compressed => compressed_page_size ? false : true,
53
+ :page_size => compressed_page_size || system_page_size,
54
+ :post_antelope => read_bits_at_offset(flags, 1, 0) == 1,
55
+ :atomic_blogs => read_bits_at_offset(flags, 1, 5) == 1,
56
+ :data_directory => read_bits_at_offset(flags, 1, 10) == 1,
47
57
  :value => flags,
48
58
  }
49
59
  end
50
60
 
51
61
  # The FSP header immediately follows the FIL header.
52
62
  def pos_fsp_header
53
- pos_fil_header + size_fil_header
63
+ pos_page_body
54
64
  end
55
65
 
56
66
  # The FSP header contains six 32-bit integers, one 64-bit integer, and 5
@@ -87,7 +97,7 @@ class Innodb::Page::FspHdrXdes < Innodb::Page
87
97
  :unused => c.name("unused") { c.get_uint32 },
88
98
  :size => c.name("size") { c.get_uint32 },
89
99
  :free_limit => c.name("free_limit") { c.get_uint32 },
90
- :flags => c.name("flags") {
100
+ :flags => c.name("flags") {
91
101
  self.class.decode_flags(c.get_uint32)
92
102
  },
93
103
  :frag_n_used => c.name("frag_n_used") { c.get_uint32 },
@@ -155,11 +165,12 @@ class Innodb::Page::FspHdrXdes < Innodb::Page
155
165
  })
156
166
 
157
167
  each_xdes do |xdes|
168
+ state = xdes.state || "unused"
158
169
  yield({
159
170
  :offset => xdes.offset,
160
171
  :length => size_xdes_entry,
161
- :name => :xdes,
162
- :info => "Extent Descriptor",
172
+ :name => "xdes_#{state}".to_sym,
173
+ :info => "Extent Descriptor (#{state})",
163
174
  })
164
175
  end
165
176
 
@@ -0,0 +1,47 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Innodb::Page::IbufBitmap < Innodb::Page
4
+ extend ReadBitsAtOffset
5
+
6
+ def pos_ibuf_bitmap
7
+ pos_page_body
8
+ end
9
+
10
+ def size_ibuf_bitmap
11
+ (Innodb::IbufBitmap::BITS_PER_PAGE * space.pages_per_bookkeeping_page) / 8
12
+ end
13
+
14
+ def ibuf_bitmap
15
+ Innodb::IbufBitmap.new(self, cursor(pos_ibuf_bitmap))
16
+ end
17
+
18
+ def each_region
19
+ unless block_given?
20
+ return enum_for(:each_region)
21
+ end
22
+
23
+ super do |region|
24
+ yield region
25
+ end
26
+
27
+ yield({
28
+ :offset => pos_ibuf_bitmap,
29
+ :length => size_ibuf_bitmap,
30
+ :name => :ibuf_bitmap,
31
+ :info => "Insert Buffer Bitmap",
32
+ })
33
+
34
+ nil
35
+ end
36
+
37
+ def dump
38
+ super
39
+
40
+ puts "ibuf bitmap:"
41
+ ibuf_bitmap.each_page_status do |page_number, page_status|
42
+ puts " Page %i: %s" % [page_number, page_status.inspect]
43
+ end
44
+ end
45
+ end
46
+
47
+ Innodb::Page::SPECIALIZED_CLASSES[:IBUF_BITMAP] = Innodb::Page::IbufBitmap
@@ -73,7 +73,7 @@ class Innodb::Page::Index < Innodb::Page
73
73
  # Return the byte offset of the start of the "index" page header, which
74
74
  # immediately follows the "fil" header.
75
75
  def pos_index_header
76
- pos_fil_header + size_fil_header
76
+ pos_page_body
77
77
  end
78
78
 
79
79
  # The size of the "index" header.
@@ -287,7 +287,7 @@ class Innodb::Page::Index < Innodb::Page
287
287
  # 13 bits for heap_number
288
288
  bits1 = c.name("bits1") { c.get_uint16 }
289
289
  header[:type] = RECORD_TYPES[bits1 & 0x07]
290
- header[:heap_number] = (bits1 & 0xf8) >> 3
290
+ header[:heap_number] = (bits1 & 0xfff8) >> 3
291
291
  when :redundant
292
292
  # The "next" pointer is an absolute offset within the page.
293
293
  header[:next] = c.name("next") { c.get_uint16 }
@@ -9,7 +9,7 @@ class Innodb::Page::Inode < Innodb::Page
9
9
  # Return the byte offset of the list node, which immediately follows the
10
10
  # FIL header.
11
11
  def pos_list_entry
12
- pos_fil_header + size_fil_header
12
+ pos_page_body
13
13
  end
14
14
 
15
15
  # Return the size of the list node.
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "innodb/page/sys_rseg_header"
4
4
  require "innodb/page/sys_data_dictionary_header"
5
+ require "innodb/page/sys_ibuf_header"
5
6
 
6
7
  # Another layer of indirection for pages of type SYS, as they have multiple
7
8
  # uses within InnoDB. We'll override the self.handle method and check the
@@ -9,6 +10,8 @@ require "innodb/page/sys_data_dictionary_header"
9
10
  class Innodb::Page::Sys < Innodb::Page
10
11
  def self.handle(page, space, buffer)
11
12
  case
13
+ when page.offset == 3
14
+ Innodb::Page::SysIbufHeader.new(space, buffer)
12
15
  when page.offset == 7
13
16
  Innodb::Page::SysDataDictionaryHeader.new(space, buffer)
14
17
  when space.rseg_page?(page.offset)
@@ -3,7 +3,7 @@
3
3
  class Innodb::Page::SysDataDictionaryHeader < Innodb::Page
4
4
  # The position of the data dictionary header within the page.
5
5
  def pos_data_dictionary_header
6
- pos_fil_header + size_fil_header
6
+ pos_page_body
7
7
  end
8
8
 
9
9
  # The size of the data dictionary header.
@@ -0,0 +1,45 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Innodb::Page::SysIbufHeader < Innodb::Page
4
+ def pos_ibuf_header
5
+ pos_page_body
6
+ end
7
+
8
+ def size_ibuf_header
9
+ Innodb::FsegEntry::SIZE
10
+ end
11
+
12
+ def ibuf_header
13
+ cursor(pos_ibuf_header).name("ibuf_header") do |c|
14
+ {
15
+ :fseg => c.name("fseg") {
16
+ Innodb::FsegEntry.get_inode(space, c)
17
+ }
18
+ }
19
+ end
20
+ end
21
+
22
+ def each_region
23
+ unless block_given?
24
+ return enum_for(:each_region)
25
+ end
26
+
27
+ super do |region|
28
+ yield region
29
+ end
30
+
31
+ yield({
32
+ :offset => pos_ibuf_header,
33
+ :length => size_ibuf_header,
34
+ :name => :ibuf_header,
35
+ :info => "Insert Buffer Header",
36
+ })
37
+ end
38
+
39
+ def dump
40
+ super
41
+
42
+ puts "ibuf header:"
43
+ pp ibuf_header
44
+ end
45
+ end
@@ -6,7 +6,7 @@ class Innodb::Page::SysRsegHeader < Innodb::Page
6
6
 
7
7
  # The position of the rollback segment header within the page.
8
8
  def pos_rseg_header
9
- pos_fil_header + size_fil_header
9
+ pos_page_body
10
10
  end
11
11
 
12
12
  # The size of the rollback segment header.
@@ -12,7 +12,7 @@
12
12
  class Innodb::Page::TrxSys < Innodb::Page
13
13
  # The TRX_SYS header immediately follows the FIL header.
14
14
  def pos_trx_sys_header
15
- pos_fil_header + size_fil_header
15
+ pos_page_body
16
16
  end
17
17
 
18
18
  def size_trx_sys_header
@@ -2,7 +2,7 @@
2
2
 
3
3
  class Innodb::Page::UndoLog < Innodb::Page
4
4
  def pos_undo_page_header
5
- pos_fil_header + size_fil_header
5
+ pos_page_body
6
6
  end
7
7
 
8
8
  def size_undo_page_header
data/lib/innodb/space.rb CHANGED
@@ -22,18 +22,15 @@ class Innodb::Space
22
22
 
23
23
  # Open a space file, optionally providing the page size to use. Pages
24
24
  # that aren't 16 KiB may not be supported well.
25
- def initialize(file, page_size=nil)
25
+ def initialize(file)
26
26
  @file = File.open(file)
27
27
  @size = @file.stat.size
28
28
 
29
- if page_size
30
- @page_size = page_size
31
- else
32
- @page_size = fsp_flags[:page_size]
33
- end
29
+ @system_page_size = fsp_flags[:system_page_size]
30
+ @page_size = fsp_flags[:page_size]
31
+ @compressed = fsp_flags[:compressed]
34
32
 
35
33
  @pages = (@size / @page_size)
36
- @compressed = fsp_flags[:compressed]
37
34
  @innodb_system = nil
38
35
  @record_describer = nil
39
36
  end
@@ -45,6 +42,9 @@ class Innodb::Space
45
42
  # this space.
46
43
  attr_accessor :record_describer
47
44
 
45
+ # The system default page size (in bytes), equivalent to UNIV_PAGE_SIZE.
46
+ attr_reader :system_page_size
47
+
48
48
  # The size (in bytes) of each page in the space.
49
49
  attr_reader :page_size
50
50
 
@@ -83,18 +83,24 @@ class Innodb::Space
83
83
  def raw_fsp_header_flags
84
84
  # A simple sanity check. The FIL header should be initialized in page 0,
85
85
  # to offset 0 and page type :FSP_HDR (8).
86
- page_offset = BinData::Uint32be.read(read_at_offset(4, 4))
87
- page_type = BinData::Uint16be.read(read_at_offset(24, 2))
86
+ page_offset = BinData::Uint32be.read(read_at_offset(4, 4)).to_i
87
+ page_type = BinData::Uint16be.read(read_at_offset(24, 2)).to_i
88
88
  unless page_offset == 0 && Innodb::Page::PAGE_TYPE_BY_VALUE[page_type] == :FSP_HDR
89
- raise "Something is very wrong; Page 0 does not seem to be type FSP_HDR"
89
+ raise "Something is very wrong; Page 0 does not seem to be type FSP_HDR; got page type %i but expected %i" % [
90
+ page_type,
91
+ Innodb::Page::PAGE_TYPE[:FSP_HDR][:value],
92
+ ]
90
93
  end
91
94
 
92
95
  # Another sanity check. The Space ID should be the same in both the FIL
93
96
  # and FSP headers.
94
- fil_space = BinData::Uint32be.read(read_at_offset(34, 4))
95
- fsp_space = BinData::Uint32be.read(read_at_offset(38, 4))
97
+ fil_space = BinData::Uint32be.read(read_at_offset(34, 4)).to_i
98
+ fsp_space = BinData::Uint32be.read(read_at_offset(38, 4)).to_i
96
99
  unless fil_space == fsp_space
97
- raise "Something is very wrong; FIL and FSP header Space IDs don't match"
100
+ raise "Something is very wrong; FIL and FSP header Space IDs don't match: FIL is %i but FSP is %i" % [
101
+ fil_space,
102
+ fsp_space,
103
+ ]
98
104
  end
99
105
 
100
106
  # Well, we're as sure as we can be. Read the flags field and decode it.
@@ -113,30 +119,56 @@ class Innodb::Space
113
119
  end
114
120
  end
115
121
 
116
- # The size (in bytes) of an extent.
117
- def extent_size
118
- 1048576
119
- end
120
-
121
122
  # The number of pages per extent.
122
123
  def pages_per_extent
123
- extent_size / page_size
124
+ # Note that uncompressed tables and compressed tables using the same page
125
+ # size will have a different number of pages per "extent" because InnoDB
126
+ # compression uses the FSP_EXTENT_SIZE define (which is then based on the
127
+ # UNIV_PAGE_SIZE define, which may be based on the innodb_page_size system
128
+ # variable) for compressed tables rather than something based on the actual
129
+ # compressed page size.
130
+ #
131
+ # For this reason, an "extent" differs in size as follows (the maximum page
132
+ # size supported for compressed tables is the innodb_page_size):
133
+ #
134
+ # innodb_page_size | innodb compression |
135
+ # page size | extent size | pages | page size | extent size | pages |
136
+ # 16384 | 1 MiB | 64 | 16384 | 1 MiB | 64 |
137
+ # | 8192 | 512 KiB | 64 |
138
+ # | 4096 | 256 KiB | 64 |
139
+ # | 2048 | 128 KiB | 64 |
140
+ # | 1024 | 64 KiB | 64 |
141
+ # 8192 | 1 MiB | 128 | 8192 | 1 MiB | 128 |
142
+ # | 4096 | 512 KiB | 128 |
143
+ # | 2048 | 256 KiB | 128 |
144
+ # | 1024 | 128 KiB | 128 |
145
+ # 4096 | 1 MiB | 256 | 4096 | 1 MiB | 256 |
146
+ # | 2048 | 512 KiB | 256 |
147
+ # | 1024 | 256 KiB | 256 |
148
+ #
149
+
150
+ 1048576 / system_page_size
124
151
  end
125
152
 
126
- # The number of pages per FSP_HDR/XDES page. This is crudely mapped to the
127
- # page size, and works for pages down to 1KiB.
128
- def pages_per_xdes_page
129
- page_size
153
+ # The size (in bytes) of an extent.
154
+ def extent_size
155
+ pages_per_extent * page_size
130
156
  end
131
157
 
132
- # An array of all FSP/XDES page numbers for the space.
133
- def xdes_page_numbers
134
- (0..(@pages / pages_per_xdes_page)).map { |n| n * pages_per_xdes_page }
158
+ # The number of pages per FSP_HDR/XDES/IBUF_BITMAP page. This is crudely
159
+ # mapped to the page size, and works for pages down to 1KiB.
160
+ def pages_per_bookkeeping_page
161
+ page_size
135
162
  end
136
163
 
137
164
  # The FSP_HDR/XDES page which will contain the XDES entry for a given page.
138
165
  def xdes_page_for_page(page_number)
139
- page_number - (page_number % pages_per_xdes_page)
166
+ page_number - (page_number % pages_per_bookkeeping_page)
167
+ end
168
+
169
+ # The IBUF_BITMAP page which will contain the bitmap entry for a given page.
170
+ def ibuf_bitmap_page_for_page(page_number)
171
+ page_number - (page_number % pages_per_bookkeeping_page) + 1
140
172
  end
141
173
 
142
174
  # The XDES entry offset for a given page within its FSP_HDR/XDES page's
@@ -343,6 +375,17 @@ class Innodb::Space
343
375
  end
344
376
  end
345
377
 
378
+ # An array of all FSP/XDES page numbers for the space.
379
+ def each_xdes_page_number
380
+ unless block_given?
381
+ return enum_for(:each_xdes_page_number)
382
+ end
383
+
384
+ 0.step(pages - 1, pages_per_bookkeeping_page).each do |n|
385
+ yield n
386
+ end
387
+ end
388
+
346
389
  # Iterate through all FSP_HDR/XDES pages, returning an Innodb::Page object
347
390
  # for each one.
348
391
  def each_xdes_page
@@ -350,9 +393,9 @@ class Innodb::Space
350
393
  return enum_for(:each_xdes_page)
351
394
  end
352
395
 
353
- xdes_page_numbers.each do |page_number|
396
+ each_xdes_page_number do |page_number|
354
397
  current_page = page(page_number)
355
- yield current_page if current_page
398
+ yield current_page if current_page and [:FSP_HDR, :XDES].include?(current_page.type)
356
399
  end
357
400
  end
358
401
 
data/lib/innodb/system.rb CHANGED
@@ -9,6 +9,9 @@ class Innodb::System
9
9
  # A hash of spaces by space ID.
10
10
  attr_reader :spaces
11
11
 
12
+ # Array of space names for which a space file was not found.
13
+ attr_reader :orphans
14
+
12
15
  # The Innodb::DataDictionary for this system.
13
16
  attr_reader :data_dictionary
14
17
 
@@ -17,6 +20,7 @@ class Innodb::System
17
20
 
18
21
  def initialize(system_space_file)
19
22
  @spaces = {}
23
+ @orphans = []
20
24
  @config = {
21
25
  :datadir => File.dirname(system_space_file),
22
26
  }
@@ -47,10 +51,20 @@ class Innodb::System
47
51
  add_space(space)
48
52
  end
49
53
 
54
+ # Add an orphaned space.
55
+ def add_space_orphan(space_file)
56
+ orphans << space_file
57
+ end
58
+
50
59
  # Add a space by table name, constructing an appropriate filename
51
60
  # from the provided table name.
52
61
  def add_table(table_name)
53
- add_space_file("%s/%s.ibd" % [config[:datadir], table_name])
62
+ space_file = "%s/%s.ibd" % [config[:datadir], table_name]
63
+ if File.exist?(space_file)
64
+ add_space_file(space_file)
65
+ else
66
+ add_space_orphan(table_name)
67
+ end
54
68
  end
55
69
 
56
70
  # Return an Innodb::Space object for a given space ID, looking up
@@ -93,6 +107,19 @@ class Innodb::System
93
107
  nil
94
108
  end
95
109
 
110
+ # Iterate throught all orphaned spaces.
111
+ def each_orphan
112
+ unless block_given?
113
+ return enum_for(:each_orphan)
114
+ end
115
+
116
+ orphans.each do |space_name|
117
+ yield space_name
118
+ end
119
+
120
+ nil
121
+ end
122
+
96
123
  # Iterate through all column names by table name.
97
124
  def each_column_name_by_table_name(table_name)
98
125
  unless block_given?
@@ -150,7 +177,12 @@ class Innodb::System
150
177
 
151
178
  # Return an array of the table name and index name given an index ID.
152
179
  def table_and_index_name_by_id(index_id)
153
- if index_record = data_dictionary.index_by_id(index_id)
180
+ if dd_index = data_dictionary.data_dictionary_index_ids[index_id]
181
+ # This is a data dictionary index, which won't be found in the data
182
+ # dictionary itself.
183
+ [dd_index[:table], dd_index[:index]]
184
+ elsif index_record = data_dictionary.index_by_id(index_id)
185
+ # This is a system or user index.
154
186
  [table_name_by_id(index_record["TABLE_ID"]), index_record["NAME"]]
155
187
  end
156
188
  end
@@ -250,49 +250,49 @@ class BufferCursor
250
250
  def get_uint8(position=nil)
251
251
  seek(position)
252
252
  data = read_and_advance(1)
253
- BinData::Uint8.read(data)
253
+ BinData::Uint8.read(data).to_i
254
254
  end
255
255
 
256
256
  # Read a big-endian unsigned 16-bit integer.
257
257
  def get_uint16(position=nil)
258
258
  seek(position)
259
259
  data = read_and_advance(2)
260
- BinData::Uint16be.read(data)
260
+ BinData::Uint16be.read(data).to_i
261
261
  end
262
262
 
263
263
  # Read a big-endian signed 16-bit integer.
264
264
  def get_sint16(position=nil)
265
265
  seek(position)
266
266
  data = read_and_advance(2)
267
- BinData::Int16be.read(data)
267
+ BinData::Int16be.read(data).to_i
268
268
  end
269
269
 
270
270
  # Read a big-endian unsigned 24-bit integer.
271
271
  def get_uint24(position=nil)
272
272
  seek(position)
273
273
  data = read_and_advance(3)
274
- BinData::Uint24be.read(data)
274
+ BinData::Uint24be.read(data).to_i
275
275
  end
276
276
 
277
277
  # Read a big-endian unsigned 32-bit integer.
278
278
  def get_uint32(position=nil)
279
279
  seek(position)
280
280
  data = read_and_advance(4)
281
- BinData::Uint32be.read(data)
281
+ BinData::Uint32be.read(data).to_i
282
282
  end
283
283
 
284
284
  # Read a big-endian unsigned 48-bit integer.
285
285
  def get_uint48(position=nil)
286
286
  seek(position)
287
287
  data = read_and_advance(6)
288
- BinData::Uint48be.read(data)
288
+ BinData::Uint48be.read(data).to_i
289
289
  end
290
290
 
291
291
  # Read a big-endian unsigned 64-bit integer.
292
292
  def get_uint64(position=nil)
293
293
  seek(position)
294
294
  data = read_and_advance(8)
295
- BinData::Uint64be.read(data)
295
+ BinData::Uint64be.read(data).to_i
296
296
  end
297
297
 
298
298
  # Read a big-endian unsigned integer given its size in bytes.
@@ -1,5 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  module Innodb
4
- VERSION = "0.9.8"
4
+ VERSION = "0.9.9"
5
5
  end
data/lib/innodb/xdes.rb CHANGED
@@ -117,9 +117,9 @@ class Innodb::Xdes
117
117
  bitmap = xdes[:bitmap].enum_for(:each_byte)
118
118
 
119
119
  bitmap.each_with_index do |byte, byte_index|
120
- (0..3).each_with_index do |page, page_index|
121
- page_number = xdes[:start_page] + (byte_index * 4) + page_index
122
- page_bits = ((byte >> (page * BITS_PER_PAGE)) & BITMAP_BV_ALL)
120
+ (0..3).each do |page_offset|
121
+ page_number = xdes[:start_page] + (byte_index * 4) + page_offset
122
+ page_bits = ((byte >> (page_offset * BITS_PER_PAGE)) & BITMAP_BV_ALL)
123
123
  page_status = {
124
124
  :free => (page_bits & BITMAP_BV_FREE != 0),
125
125
  :clean => (page_bits & BITMAP_BV_CLEAN != 0),
data/lib/innodb.rb CHANGED
@@ -27,6 +27,7 @@ require "innodb/data_dictionary"
27
27
  require "innodb/page"
28
28
  require "innodb/page/blob"
29
29
  require "innodb/page/fsp_hdr_xdes"
30
+ require "innodb/page/ibuf_bitmap"
30
31
  require "innodb/page/inode"
31
32
  require "innodb/page/index"
32
33
  require "innodb/page/trx_sys"
@@ -38,6 +39,8 @@ require "innodb/space"
38
39
  require "innodb/system"
39
40
  require "innodb/history"
40
41
  require "innodb/history_list"
42
+ require "innodb/ibuf_bitmap"
43
+ require "innodb/ibuf_index"
41
44
  require "innodb/inode"
42
45
  require "innodb/index"
43
46
  require "innodb/log_record"
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.8
4
+ version: 0.9.9
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: 2014-04-04 00:00:00.000000000 Z
13
+ date: 2014-09-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bindata
@@ -47,6 +47,8 @@ files:
47
47
  - lib/innodb/fseg_entry.rb
48
48
  - lib/innodb/history.rb
49
49
  - lib/innodb/history_list.rb
50
+ - lib/innodb/ibuf_bitmap.rb
51
+ - lib/innodb/ibuf_index.rb
50
52
  - lib/innodb/index.rb
51
53
  - lib/innodb/inode.rb
52
54
  - lib/innodb/list.rb
@@ -59,11 +61,13 @@ files:
59
61
  - lib/innodb/page.rb
60
62
  - lib/innodb/page/blob.rb
61
63
  - lib/innodb/page/fsp_hdr_xdes.rb
64
+ - lib/innodb/page/ibuf_bitmap.rb
62
65
  - lib/innodb/page/index.rb
63
66
  - lib/innodb/page/index_compressed.rb
64
67
  - lib/innodb/page/inode.rb
65
68
  - lib/innodb/page/sys.rb
66
69
  - lib/innodb/page/sys_data_dictionary_header.rb
70
+ - lib/innodb/page/sys_ibuf_header.rb
67
71
  - lib/innodb/page/sys_rseg_header.rb
68
72
  - lib/innodb/page/trx_sys.rb
69
73
  - lib/innodb/page/undo_log.rb