innodb_ruby 0.9.14 → 0.12.0

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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +5 -6
  3. data/bin/innodb_log +13 -18
  4. data/bin/innodb_space +654 -778
  5. data/lib/innodb/checksum.rb +26 -24
  6. data/lib/innodb/data_dictionary.rb +490 -550
  7. data/lib/innodb/data_type.rb +362 -325
  8. data/lib/innodb/field.rb +102 -89
  9. data/lib/innodb/fseg_entry.rb +22 -26
  10. data/lib/innodb/history.rb +21 -21
  11. data/lib/innodb/history_list.rb +72 -76
  12. data/lib/innodb/ibuf_bitmap.rb +36 -36
  13. data/lib/innodb/ibuf_index.rb +6 -2
  14. data/lib/innodb/index.rb +245 -276
  15. data/lib/innodb/inode.rb +166 -124
  16. data/lib/innodb/list.rb +196 -183
  17. data/lib/innodb/log.rb +139 -110
  18. data/lib/innodb/log_block.rb +100 -91
  19. data/lib/innodb/log_group.rb +53 -64
  20. data/lib/innodb/log_reader.rb +97 -96
  21. data/lib/innodb/log_record.rb +328 -279
  22. data/lib/innodb/lsn.rb +86 -81
  23. data/lib/innodb/page/blob.rb +82 -83
  24. data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
  25. data/lib/innodb/page/ibuf_bitmap.rb +34 -34
  26. data/lib/innodb/page/index.rb +965 -924
  27. data/lib/innodb/page/index_compressed.rb +34 -34
  28. data/lib/innodb/page/inode.rb +103 -112
  29. data/lib/innodb/page/sys.rb +13 -15
  30. data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
  31. data/lib/innodb/page/sys_ibuf_header.rb +45 -42
  32. data/lib/innodb/page/sys_rseg_header.rb +88 -82
  33. data/lib/innodb/page/trx_sys.rb +204 -182
  34. data/lib/innodb/page/undo_log.rb +106 -92
  35. data/lib/innodb/page.rb +417 -414
  36. data/lib/innodb/record.rb +121 -164
  37. data/lib/innodb/record_describer.rb +66 -68
  38. data/lib/innodb/space.rb +381 -413
  39. data/lib/innodb/stats.rb +33 -35
  40. data/lib/innodb/system.rb +149 -171
  41. data/lib/innodb/undo_log.rb +129 -107
  42. data/lib/innodb/undo_record.rb +255 -247
  43. data/lib/innodb/util/buffer_cursor.rb +81 -79
  44. data/lib/innodb/util/read_bits_at_offset.rb +2 -1
  45. data/lib/innodb/version.rb +2 -2
  46. data/lib/innodb/xdes.rb +144 -142
  47. data/lib/innodb.rb +4 -5
  48. metadata +100 -25
data/lib/innodb/lsn.rb CHANGED
@@ -1,103 +1,108 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  # A Log Sequence Number and its byte offset into the log group.
4
- class Innodb::LSN
5
- # The Log Sequence Number.
6
- attr_reader :lsn_no
7
-
8
- # Alias :lsn_no attribute.
9
- alias_method :no, :lsn_no
4
+ module Innodb
5
+ class LSN
6
+ # The Log Sequence Number.
7
+ attr_reader :lsn_no
8
+
9
+ # Alias :lsn_no attribute.
10
+ alias no lsn_no
11
+
12
+ # Initialize coordinates.
13
+ def initialize(lsn, offset)
14
+ @lsn_no = lsn
15
+ @lsn_offset = offset
16
+ end
10
17
 
11
- # Initialize coordinates.
12
- def initialize(lsn, offset)
13
- @lsn_no = lsn
14
- @lsn_offset = offset
15
- end
18
+ # Place LSN in a new position.
19
+ def reposition(new_lsn_no, group)
20
+ new_offset = offset_of(@lsn_no, @lsn_offset, new_lsn_no, group)
21
+ @lsn_no = new_lsn_no
22
+ @lsn_offset = new_offset
16
23
 
17
- # Place LSN in a new position.
18
- def reposition(new_lsn_no, group)
19
- new_offset = offset_of(@lsn_no, @lsn_offset, new_lsn_no, group)
20
- @lsn_no, @lsn_offset = [new_lsn_no, new_offset]
21
- end
24
+ [@lsn_no, @lsn_offset]
25
+ end
22
26
 
23
- # Advance by a given LSN amount.
24
- def advance(count_lsn_no, group)
25
- new_lsn_no = @lsn_no + count_lsn_no
26
- reposition(new_lsn_no, group)
27
- end
27
+ # Advance by a given LSN amount.
28
+ def advance(count_lsn_no, group)
29
+ new_lsn_no = @lsn_no + count_lsn_no
30
+ reposition(new_lsn_no, group)
31
+ end
28
32
 
29
- # Returns the location coordinates of this LSN.
30
- def location(group)
31
- location_of(@lsn_offset, group)
32
- end
33
+ # Returns the location coordinates of this LSN.
34
+ def location(group)
35
+ location_of(@lsn_offset, group)
36
+ end
33
37
 
34
- # Returns the LSN delta for the given amount of data.
35
- def delta(length)
36
- fragment = (@lsn_no % LOG_BLOCK_SIZE) - LOG_BLOCK_HEADER_SIZE
37
- raise "Invalid fragment #{fragment} for LSN #{@lsn_no}" unless
38
- fragment.between?(0, LOG_BLOCK_DATA_SIZE - 1)
39
- length + (fragment + length) / LOG_BLOCK_DATA_SIZE * LOG_BLOCK_FRAME_SIZE
40
- end
38
+ # Returns the LSN delta for the given amount of data.
39
+ def delta(length)
40
+ fragment = (@lsn_no % LOG_BLOCK_SIZE) - LOG_BLOCK_HEADER_SIZE
41
+ raise "Invalid fragment #{fragment} for LSN #{@lsn_no}" unless fragment.between?(0, LOG_BLOCK_DATA_SIZE - 1)
41
42
 
42
- # Whether LSN might point to log record data.
43
- def record?(group)
44
- data_offset?(@lsn_offset, group)
45
- end
43
+ length + (fragment + length) / LOG_BLOCK_DATA_SIZE * LOG_BLOCK_FRAME_SIZE
44
+ end
46
45
 
47
- private
46
+ # Whether LSN might point to log record data.
47
+ def record?(group)
48
+ data_offset?(@lsn_offset, group)
49
+ end
48
50
 
49
- # Short alias for the size of a log file header.
50
- LOG_HEADER_SIZE = Innodb::Log::LOG_HEADER_SIZE
51
+ private
51
52
 
52
- # Short aliases for the sizes of the subparts of a log block.
53
- LOG_BLOCK_SIZE = Innodb::LogBlock::BLOCK_SIZE
54
- LOG_BLOCK_HEADER_SIZE = Innodb::LogBlock::HEADER_SIZE
55
- LOG_BLOCK_TRAILER_SIZE = Innodb::LogBlock::TRAILER_SIZE
56
- LOG_BLOCK_DATA_SIZE = Innodb::LogBlock::DATA_SIZE
57
- LOG_BLOCK_FRAME_SIZE = LOG_BLOCK_HEADER_SIZE + LOG_BLOCK_TRAILER_SIZE
53
+ # Short alias for the size of a log file header.
54
+ LOG_HEADER_SIZE = Innodb::Log::LOG_HEADER_SIZE
58
55
 
59
- # Returns the coordinates of the given offset.
60
- def location_of(offset, group)
61
- log_no, log_offset = offset.divmod(group.size)
62
- block_no, block_offset = (log_offset - LOG_HEADER_SIZE).divmod(LOG_BLOCK_SIZE)
63
- [log_no, block_no, block_offset]
64
- end
56
+ # Short aliases for the sizes of the subparts of a log block.
57
+ LOG_BLOCK_SIZE = Innodb::LogBlock::BLOCK_SIZE
58
+ LOG_BLOCK_HEADER_SIZE = Innodb::LogBlock::HEADER_SIZE
59
+ LOG_BLOCK_TRAILER_SIZE = Innodb::LogBlock::TRAILER_SIZE
60
+ LOG_BLOCK_DATA_SIZE = Innodb::LogBlock::DATA_SIZE
61
+ LOG_BLOCK_FRAME_SIZE = LOG_BLOCK_HEADER_SIZE + LOG_BLOCK_TRAILER_SIZE
65
62
 
66
- # Returns the offset of the given LSN within a log group.
67
- def offset_of(lsn, offset, new_lsn, group)
68
- log_size = group.log_size
69
- group_capacity = group.capacity
70
-
71
- # Calculate the offset in LSN.
72
- if new_lsn >= lsn
73
- lsn_offset = new_lsn - lsn
74
- else
75
- lsn_offset = lsn - new_lsn
76
- lsn_offset %= group_capacity
77
- lsn_offset = group_capacity - lsn_offset
63
+ # Returns the coordinates of the given offset.
64
+ def location_of(offset, group)
65
+ log_no, log_offset = offset.divmod(group.size)
66
+ block_no, block_offset = (log_offset - LOG_HEADER_SIZE).divmod(LOG_BLOCK_SIZE)
67
+ [log_no, block_no, block_offset]
78
68
  end
79
69
 
80
- # Transpose group size offset to a group capacity offset.
81
- group_offset = offset - (LOG_HEADER_SIZE * (1 + offset / log_size))
70
+ # Returns the offset of the given LSN within a log group.
71
+ def offset_of(lsn, offset, new_lsn, group)
72
+ log_size = group.log_size
73
+ group_capacity = group.capacity
82
74
 
83
- offset = (lsn_offset + group_offset) % group_capacity
75
+ # Calculate the offset in LSN.
76
+ if new_lsn >= lsn
77
+ lsn_offset = new_lsn - lsn
78
+ else
79
+ lsn_offset = lsn - new_lsn
80
+ lsn_offset %= group_capacity
81
+ lsn_offset = group_capacity - lsn_offset
82
+ end
84
83
 
85
- # Transpose group capacity offset to a group size offset.
86
- offset + LOG_HEADER_SIZE * (1 + offset / (log_size - LOG_HEADER_SIZE))
87
- end
84
+ # Transpose group size offset to a group capacity offset.
85
+ group_offset = offset - (LOG_HEADER_SIZE * (1 + offset / log_size))
88
86
 
89
- # Whether offset points to the data area of an existing log block.
90
- def data_offset?(offset, group)
91
- log_offset = offset % group.size
92
- log_no, block_no, block_offset = location_of(offset, group)
87
+ offset = (lsn_offset + group_offset) % group_capacity
93
88
 
94
- status ||= log_no > group.logs
95
- status ||= log_offset <= LOG_HEADER_SIZE
96
- status ||= block_no < 0
97
- status ||= block_no >= group.log(log_no).blocks
98
- status ||= block_offset < Innodb::LogBlock::DATA_OFFSET
99
- status ||= block_offset >= Innodb::LogBlock::TRAILER_OFFSET
89
+ # Transpose group capacity offset to a group size offset.
90
+ offset + LOG_HEADER_SIZE * (1 + offset / (log_size - LOG_HEADER_SIZE))
91
+ end
92
+
93
+ # Whether offset points to the data area of an existing log block.
94
+ def data_offset?(offset, group)
95
+ log_offset = offset % group.size
96
+ log_no, block_no, block_offset = location_of(offset, group)
100
97
 
101
- !status
98
+ status ||= log_no > group.logs
99
+ status ||= log_offset <= LOG_HEADER_SIZE
100
+ status ||= block_no.negative?
101
+ status ||= block_no >= group.log(log_no).blocks
102
+ status ||= block_offset < Innodb::LogBlock::DATA_OFFSET
103
+ status ||= block_offset >= Innodb::LogBlock::TRAILER_OFFSET
104
+
105
+ !status
106
+ end
102
107
  end
103
108
  end
@@ -1,86 +1,85 @@
1
- # -*- encoding : utf-8 -*-
2
-
3
- class Innodb::Page::Blob < Innodb::Page
4
- def pos_blob_header
5
- pos_page_body
6
- end
7
-
8
- def size_blob_header
9
- 4 + 4
10
- end
11
-
12
- def pos_blob_data
13
- pos_blob_header + size_blob_header
14
- end
15
-
16
- def blob_header
17
- cursor(pos_blob_header).name("blob_header") do |c|
18
- {
19
- :length => c.name("length") { c.get_uint32 },
20
- :next => c.name("next") { Innodb::Page.maybe_undefined(c.get_uint32) },
21
- }
22
- end
23
- end
24
-
25
- def blob_data
26
- cursor(pos_blob_data).name("blob_data") do |c|
27
- c.get_bytes(blob_header[:length])
28
- end
29
- end
30
-
31
- def dump_hex(string)
32
- slice_size = 16
33
- bytes = string.split("").map { |s| s.ord }
34
- string.split("").each_slice(slice_size).each_with_index do |slice_bytes, slice_count|
35
- puts "%08i %-23s %-23s |%-16s|" % [
36
- (slice_count * slice_size),
37
- slice_bytes[0..8].map { |n| "%02x" % n.ord }.join(" "),
38
- slice_bytes[8..16].map { |n| "%02x" % n.ord }.join(" "),
39
- slice_bytes.join(""),
40
- ]
1
+ # frozen_string_literal: true
2
+
3
+ module Innodb
4
+ class Page
5
+ class Blob < Page
6
+ specialization_for :BLOB
7
+
8
+ def pos_blob_header
9
+ pos_page_body
10
+ end
11
+
12
+ def size_blob_header
13
+ 4 + 4
14
+ end
15
+
16
+ def pos_blob_data
17
+ pos_blob_header + size_blob_header
18
+ end
19
+
20
+ def blob_header
21
+ cursor(pos_blob_header).name("blob_header") do |c|
22
+ {
23
+ length: c.name("length") { c.read_uint32 },
24
+ next: c.name("next") { Innodb::Page.maybe_undefined(c.read_uint32) },
25
+ }
26
+ end
27
+ end
28
+
29
+ def blob_data
30
+ cursor(pos_blob_data).name("blob_data") do |c|
31
+ c.read_bytes(blob_header[:length])
32
+ end
33
+ end
34
+
35
+ def dump_hex(string)
36
+ slice_size = 16
37
+ string.chars.each_slice(slice_size).each_with_index do |slice_bytes, slice_count|
38
+ puts "%08i %-23s %-23s |%-16s|" % [
39
+ (slice_count * slice_size),
40
+ slice_bytes[0..8].map { |n| "%02x" % n.ord }.join(" "),
41
+ slice_bytes[8..16].map { |n| "%02x" % n.ord }.join(" "),
42
+ slice_bytes.join,
43
+ ]
44
+ end
45
+ end
46
+
47
+ def each_region(&block)
48
+ return enum_for(:each_region) unless block_given?
49
+
50
+ super(&block)
51
+
52
+ yield Region.new(
53
+ offset: pos_blob_header,
54
+ length: size_blob_header,
55
+ name: :blob_header,
56
+ info: "Blob Header"
57
+ )
58
+
59
+ yield Region.new(
60
+ offset: pos_blob_data,
61
+ length: blob_header[:length],
62
+ name: :blob_data,
63
+ info: "Blob Data"
64
+ )
65
+
66
+ nil
67
+ end
68
+
69
+ # Dump the contents of a page for debugging purposes.
70
+ def dump
71
+ super
72
+
73
+ puts "blob header:"
74
+ pp blob_header
75
+ puts
76
+
77
+ puts "blob data:"
78
+ dump_hex(blob_data)
79
+ puts
80
+
81
+ puts
82
+ end
41
83
  end
42
84
  end
43
-
44
- def each_region
45
- unless block_given?
46
- return enum_for(:each_region)
47
- end
48
-
49
- super do |region|
50
- yield region
51
- end
52
-
53
- yield({
54
- :offset => pos_blob_header,
55
- :length => size_blob_header,
56
- :name => :blob_header,
57
- :info => "Blob Header",
58
- })
59
-
60
- yield({
61
- :offset => pos_blob_data,
62
- :length => blob_header[:length],
63
- :name => :blob_data,
64
- :info => "Blob Data",
65
- })
66
-
67
- nil
68
- end
69
-
70
- # Dump the contents of a page for debugging purposes.
71
- def dump
72
- super
73
-
74
- puts "blob header:"
75
- pp blob_header
76
- puts
77
-
78
- puts "blob data:"
79
- dump_hex(blob_data)
80
- puts
81
-
82
- puts
83
- end
84
85
  end
85
-
86
- Innodb::Page::SPECIALIZED_CLASSES[:BLOB] = Innodb::Page::Blob
@@ -1,4 +1,4 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "innodb/list"
4
4
  require "innodb/xdes"
@@ -12,186 +12,195 @@ require "innodb/xdes"
12
12
  #
13
13
  # The basic structure of FSP_HDR and XDES pages is: FIL header, FSP header,
14
14
  # an array of 256 XDES entries, empty (unused) space, and FIL trailer.
15
- class Innodb::Page::FspHdrXdes < Innodb::Page
16
- extend ReadBitsAtOffset
17
-
18
- # A value added to the adjusted exponent stored in the page size field of
19
- # the flags in the FSP header.
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
27
-
28
- # Decode the "flags" field in the FSP header, returning a hash of useful
29
- # decodings of the flags (based on MySQl 5.6 definitions). The flags are:
30
- #
31
- # Offset Size Description
32
- # 0 1 Post-Antelope Flag.
33
- # 1 4 Compressed Page Size (zip_size). This is stored as a
34
- # power of 2, minus 9. Since 0 is reserved to mean "not
35
- # compressed", the minimum value is 1, thus making the
36
- # smallest page size 1024 (2 ** (9 + 1)).
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.
43
- #
44
- def self.decode_flags(flags)
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
-
50
- {
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_blobs => read_bits_at_offset(flags, 1, 5) == 1,
56
- :data_directory => read_bits_at_offset(flags, 1, 10) == 1,
57
- :value => flags,
58
- }
59
- end
60
-
61
- # The FSP header immediately follows the FIL header.
62
- def pos_fsp_header
63
- pos_page_body
64
- end
65
-
66
- # The FSP header contains six 32-bit integers, one 64-bit integer, and 5
67
- # list base nodes.
68
- def size_fsp_header
69
- ((4 * 6) + (1 * 8) + (5 * Innodb::List::BASE_NODE_SIZE))
70
- end
71
-
72
- # The XDES entry array immediately follows the FSP header.
73
- def pos_xdes_array
74
- pos_fsp_header + size_fsp_header
75
- end
15
+ module Innodb
16
+ class Page
17
+ class FspHdrXdes < Page
18
+ extend ReadBitsAtOffset
19
+
20
+ specialization_for :FSP_HDR
21
+ specialization_for :XDES
22
+
23
+ Flags = Struct.new(
24
+ :system_page_size,
25
+ :compressed,
26
+ :page_size,
27
+ :post_antelope,
28
+ :atomic_blobs,
29
+ :data_directory,
30
+ :value,
31
+ keyword_init: true
32
+ )
33
+
34
+ Header = Struct.new(
35
+ :space_id,
36
+ :unused,
37
+ :size, # rubocop:disable Lint/StructNewOverride
38
+ :free_limit,
39
+ :flags,
40
+ :frag_n_used,
41
+ :free,
42
+ :free_frag,
43
+ :full_frag,
44
+ :first_unused_seg,
45
+ :full_inodes,
46
+ :free_inodes,
47
+ keyword_init: true
48
+ )
49
+
50
+ # A value added to the adjusted exponent stored in the page size field of
51
+ # the flags in the FSP header.
52
+ FLAGS_PAGE_SIZE_SHIFT = 9
53
+
54
+ def self.shift_page_size(page_size_shifted)
55
+ (1 << (FLAGS_PAGE_SIZE_SHIFT + page_size_shifted)) if page_size_shifted != 0
56
+ end
76
57
 
77
- # The number of entries in the XDES array. Defined as page size divided by
78
- # extent size.
79
- def entries_in_xdes_array
80
- size / space.pages_per_extent
81
- end
58
+ # Decode the "flags" field in the FSP header, returning a hash of useful
59
+ # decodings of the flags (based on MySQl 5.6 definitions). The flags are:
60
+ #
61
+ # Offset Size Description
62
+ # 0 1 Post-Antelope Flag.
63
+ # 1 4 Compressed Page Size (zip_size). This is stored as a
64
+ # power of 2, minus 9. Since 0 is reserved to mean "not
65
+ # compressed", the minimum value is 1, thus making the
66
+ # smallest page size 1024 (2 ** (9 + 1)).
67
+ # 5 1 Atomic Blobs Flag.
68
+ # 6 4 System Page Size (innodb_page_size, UNIV_PAGE_SIZE).
69
+ # The setting of the system page size when the tablespace
70
+ # was created, stored in the same format as the compressed
71
+ # page size above.
72
+ # 10 1 Data Directory Flag.
73
+ #
74
+ def self.decode_flags(flags)
75
+ system_page_size =
76
+ shift_page_size(read_bits_at_offset(flags, 4, 6)) ||
77
+ Innodb::Space::DEFAULT_PAGE_SIZE
78
+ compressed_page_size = shift_page_size(read_bits_at_offset(flags, 4, 1))
79
+
80
+ Flags.new(
81
+ system_page_size: system_page_size,
82
+ compressed: compressed_page_size ? false : true,
83
+ page_size: compressed_page_size || system_page_size,
84
+ post_antelope: read_bits_at_offset(flags, 1, 0) == 1,
85
+ atomic_blobs: read_bits_at_offset(flags, 1, 5) == 1,
86
+ data_directory: read_bits_at_offset(flags, 1, 10) == 1,
87
+ value: flags
88
+ )
89
+ end
82
90
 
83
- def size_xdes_entry
84
- @size_xdes_entry ||= Innodb::Xdes.new(self, cursor(pos_xdes_array)).size_entry
85
- end
91
+ # The FSP header immediately follows the FIL header.
92
+ def pos_fsp_header
93
+ pos_page_body
94
+ end
86
95
 
87
- def size_xdes_array
88
- entries_in_xdes_array * size_xdes_entry
89
- end
96
+ # The FSP header contains six 32-bit integers, one 64-bit integer, and 5
97
+ # list base nodes.
98
+ def size_fsp_header
99
+ ((4 * 6) + (1 * 8) + (5 * Innodb::List::BASE_NODE_SIZE))
100
+ end
90
101
 
91
- # Read the FSP (filespace) header, which contains a few counters and flags,
92
- # as well as list base nodes for each list maintained in the filespace.
93
- def fsp_header
94
- @fsp_header ||= cursor(pos_fsp_header).name("fsp") do |c|
95
- {
96
- :space_id => c.name("space_id") { c.get_uint32 },
97
- :unused => c.name("unused") { c.get_uint32 },
98
- :size => c.name("size") { c.get_uint32 },
99
- :free_limit => c.name("free_limit") { c.get_uint32 },
100
- :flags => c.name("flags") {
101
- self.class.decode_flags(c.get_uint32)
102
- },
103
- :frag_n_used => c.name("frag_n_used") { c.get_uint32 },
104
- :free => c.name("list[free]") {
105
- Innodb::List::Xdes.new(@space, Innodb::List.get_base_node(c))
106
- },
107
- :free_frag => c.name("list[free_frag]") {
108
- Innodb::List::Xdes.new(@space, Innodb::List.get_base_node(c))
109
- },
110
- :full_frag => c.name("list[full_frag]") {
111
- Innodb::List::Xdes.new(@space, Innodb::List.get_base_node(c))
112
- },
113
- :first_unused_seg => c.name("first_unused_seg") { c.get_uint64 },
114
- :full_inodes => c.name("list[full_inodes]") {
115
- Innodb::List::Inode.new(@space, Innodb::List.get_base_node(c))
116
- },
117
- :free_inodes => c.name("list[free_inodes]") {
118
- Innodb::List::Inode.new(@space, Innodb::List.get_base_node(c))
119
- },
120
- }
121
- end
122
- end
102
+ # The XDES entry array immediately follows the FSP header.
103
+ def pos_xdes_array
104
+ pos_fsp_header + size_fsp_header
105
+ end
123
106
 
124
- # Iterate through all lists in the file space.
125
- def each_list
126
- unless block_given?
127
- return enum_for(:each_list)
128
- end
107
+ # The number of entries in the XDES array. Defined as page size divided by
108
+ # extent size.
109
+ def entries_in_xdes_array
110
+ size / space.pages_per_extent
111
+ end
129
112
 
130
- fsp_header.each do |key, value|
131
- yield key, value if value.is_a?(Innodb::List)
132
- end
133
- end
113
+ def size_xdes_entry
114
+ @size_xdes_entry ||= Innodb::Xdes.new(self, cursor(pos_xdes_array)).size_entry
115
+ end
134
116
 
135
- # Iterate through all XDES entries in order. This is useful for debugging,
136
- # but each of these entries is actually a node in some other list. The state
137
- # field in the XDES entry indicates which type of list it is present in,
138
- # although not necessarily which list (e.g. :fseg).
139
- def each_xdes
140
- unless block_given?
141
- return enum_for(:each_xdes)
142
- end
117
+ def size_xdes_array
118
+ entries_in_xdes_array * size_xdes_entry
119
+ end
143
120
 
144
- cursor(pos_xdes_array).name("xdes_array") do |c|
145
- entries_in_xdes_array.times do |n|
146
- yield Innodb::Xdes.new(self, c)
121
+ # Read the FSP (filespace) header, which contains a few counters and flags,
122
+ # as well as list base nodes for each list maintained in the filespace.
123
+ def fsp_header
124
+ @fsp_header ||= cursor(pos_fsp_header).name("fsp") do |c|
125
+ Header.new(
126
+ space_id: c.name("space_id") { c.read_uint32 },
127
+ unused: c.name("unused") { c.read_uint32 },
128
+ size: c.name("size") { c.read_uint32 },
129
+ free_limit: c.name("free_limit") { c.read_uint32 },
130
+ flags: c.name("flags") { self.class.decode_flags(c.read_uint32) },
131
+ frag_n_used: c.name("frag_n_used") { c.read_uint32 },
132
+ free: c.name("list[free]") { Innodb::List::Xdes.new(@space, Innodb::List.get_base_node(c)) },
133
+ free_frag: c.name("list[free_frag]") { Innodb::List::Xdes.new(@space, Innodb::List.get_base_node(c)) },
134
+ full_frag: c.name("list[full_frag]") { Innodb::List::Xdes.new(@space, Innodb::List.get_base_node(c)) },
135
+ first_unused_seg: c.name("first_unused_seg") { c.read_uint64 },
136
+ full_inodes: c.name("list[full_inodes]") { Innodb::List::Inode.new(@space, Innodb::List.get_base_node(c)) },
137
+ free_inodes: c.name("list[free_inodes]") { Innodb::List::Inode.new(@space, Innodb::List.get_base_node(c)) }
138
+ )
139
+ end
147
140
  end
148
- end
149
- end
150
141
 
151
- def each_region
152
- unless block_given?
153
- return enum_for(:each_region)
154
- end
142
+ # Iterate through all lists in the file space.
143
+ def each_list
144
+ return enum_for(:each_list) unless block_given?
155
145
 
156
- super do |region|
157
- yield region
158
- end
146
+ fsp_header.to_h.each do |key, value|
147
+ yield key, value if value.is_a?(Innodb::List)
148
+ end
149
+ end
159
150
 
160
- yield({
161
- :offset => pos_fsp_header,
162
- :length => size_fsp_header,
163
- :name => :fsp_header,
164
- :info => "FSP Header",
165
- })
166
-
167
- each_xdes do |xdes|
168
- state = xdes.state || "unused"
169
- yield({
170
- :offset => xdes.offset,
171
- :length => size_xdes_entry,
172
- :name => "xdes_#{state}".to_sym,
173
- :info => "Extent Descriptor (#{state})",
174
- })
175
- end
151
+ # Iterate through all XDES entries in order. This is useful for debugging,
152
+ # but each of these entries is actually a node in some other list. The state
153
+ # field in the XDES entry indicates which type of list it is present in,
154
+ # although not necessarily which list (e.g. :fseg).
155
+ def each_xdes
156
+ return enum_for(:each_xdes) unless block_given?
157
+
158
+ cursor(pos_xdes_array).name("xdes_array") do |c|
159
+ entries_in_xdes_array.times do
160
+ yield Innodb::Xdes.new(self, c)
161
+ end
162
+ end
163
+ end
176
164
 
177
- nil
178
- end
165
+ def each_region(&block)
166
+ return enum_for(:each_region) unless block_given?
167
+
168
+ super(&block)
169
+
170
+ yield Region.new(
171
+ offset: pos_fsp_header,
172
+ length: size_fsp_header,
173
+ name: :fsp_header,
174
+ info: "FSP Header"
175
+ )
176
+
177
+ each_xdes do |xdes|
178
+ state = xdes.state || "unused"
179
+ yield Region.new(
180
+ offset: xdes.offset,
181
+ length: size_xdes_entry,
182
+ name: "xdes_#{state}".to_sym,
183
+ info: "Extent Descriptor (#{state})"
184
+ )
185
+ end
186
+
187
+ nil
188
+ end
179
189
 
180
- # Dump the contents of a page for debugging purposes.
181
- def dump
182
- super
190
+ # Dump the contents of a page for debugging purposes.
191
+ def dump
192
+ super
183
193
 
184
- puts "fsp header:"
185
- pp fsp_header
186
- puts
194
+ puts "fsp header:"
195
+ pp fsp_header
196
+ puts
187
197
 
188
- puts "xdes entries:"
189
- each_xdes do |xdes|
190
- pp xdes
198
+ puts "xdes entries:"
199
+ each_xdes do |xdes|
200
+ pp xdes
201
+ end
202
+ puts
203
+ end
191
204
  end
192
- puts
193
205
  end
194
206
  end
195
-
196
- Innodb::Page::SPECIALIZED_CLASSES[:FSP_HDR] = Innodb::Page::FspHdrXdes
197
- Innodb::Page::SPECIALIZED_CLASSES[:XDES] = Innodb::Page::FspHdrXdes