innodb_ruby 0.9.8 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
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