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.
- checksums.yaml +7 -0
- data/README.md +5 -6
- data/bin/innodb_log +13 -18
- data/bin/innodb_space +654 -778
- data/lib/innodb/checksum.rb +26 -24
- data/lib/innodb/data_dictionary.rb +490 -550
- data/lib/innodb/data_type.rb +362 -325
- data/lib/innodb/field.rb +102 -89
- data/lib/innodb/fseg_entry.rb +22 -26
- data/lib/innodb/history.rb +21 -21
- data/lib/innodb/history_list.rb +72 -76
- data/lib/innodb/ibuf_bitmap.rb +36 -36
- data/lib/innodb/ibuf_index.rb +6 -2
- data/lib/innodb/index.rb +245 -276
- data/lib/innodb/inode.rb +166 -124
- data/lib/innodb/list.rb +196 -183
- data/lib/innodb/log.rb +139 -110
- data/lib/innodb/log_block.rb +100 -91
- data/lib/innodb/log_group.rb +53 -64
- data/lib/innodb/log_reader.rb +97 -96
- data/lib/innodb/log_record.rb +328 -279
- data/lib/innodb/lsn.rb +86 -81
- data/lib/innodb/page/blob.rb +82 -83
- data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
- data/lib/innodb/page/ibuf_bitmap.rb +34 -34
- data/lib/innodb/page/index.rb +965 -924
- data/lib/innodb/page/index_compressed.rb +34 -34
- data/lib/innodb/page/inode.rb +103 -112
- data/lib/innodb/page/sys.rb +13 -15
- data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
- data/lib/innodb/page/sys_ibuf_header.rb +45 -42
- data/lib/innodb/page/sys_rseg_header.rb +88 -82
- data/lib/innodb/page/trx_sys.rb +204 -182
- data/lib/innodb/page/undo_log.rb +106 -92
- data/lib/innodb/page.rb +417 -414
- data/lib/innodb/record.rb +121 -164
- data/lib/innodb/record_describer.rb +66 -68
- data/lib/innodb/space.rb +381 -413
- data/lib/innodb/stats.rb +33 -35
- data/lib/innodb/system.rb +149 -171
- data/lib/innodb/undo_log.rb +129 -107
- data/lib/innodb/undo_record.rb +255 -247
- data/lib/innodb/util/buffer_cursor.rb +81 -79
- data/lib/innodb/util/read_bits_at_offset.rb +2 -1
- data/lib/innodb/version.rb +2 -2
- data/lib/innodb/xdes.rb +144 -142
- data/lib/innodb.rb +4 -5
- metadata +100 -25
data/lib/innodb/lsn.rb
CHANGED
@@ -1,103 +1,108 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# A Log Sequence Number and its byte offset into the log group.
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
18
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
+
# Returns the location coordinates of this LSN.
|
34
|
+
def location(group)
|
35
|
+
location_of(@lsn_offset, group)
|
36
|
+
end
|
33
37
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
43
|
-
|
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
|
-
|
46
|
+
# Whether LSN might point to log record data.
|
47
|
+
def record?(group)
|
48
|
+
data_offset?(@lsn_offset, group)
|
49
|
+
end
|
48
50
|
|
49
|
-
|
50
|
-
LOG_HEADER_SIZE = Innodb::Log::LOG_HEADER_SIZE
|
51
|
+
private
|
51
52
|
|
52
|
-
|
53
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
#
|
81
|
-
|
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
|
-
|
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
|
-
|
86
|
-
|
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
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
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
|
data/lib/innodb/page/blob.rb
CHANGED
@@ -1,86 +1,85 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
#
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
91
|
+
# The FSP header immediately follows the FIL header.
|
92
|
+
def pos_fsp_header
|
93
|
+
pos_page_body
|
94
|
+
end
|
86
95
|
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
131
|
-
|
132
|
-
|
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
|
-
|
136
|
-
|
137
|
-
|
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
|
-
|
145
|
-
|
146
|
-
|
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
|
-
|
152
|
-
|
153
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
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
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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
|
-
|
178
|
-
|
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
|
-
|
181
|
-
|
182
|
-
|
190
|
+
# Dump the contents of a page for debugging purposes.
|
191
|
+
def dump
|
192
|
+
super
|
183
193
|
|
184
|
-
|
185
|
-
|
186
|
-
|
194
|
+
puts "fsp header:"
|
195
|
+
pp fsp_header
|
196
|
+
puts
|
187
197
|
|
188
|
-
|
189
|
-
|
190
|
-
|
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
|