innodb_ruby 0.9.16 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +5 -6
- data/bin/innodb_log +13 -18
- data/bin/innodb_space +377 -757
- data/lib/innodb.rb +4 -5
- data/lib/innodb/checksum.rb +26 -24
- data/lib/innodb/data_dictionary.rb +490 -550
- data/lib/innodb/data_type.rb +362 -326
- 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 +154 -155
- data/lib/innodb/list.rb +191 -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.rb +417 -414
- 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 +964 -943
- 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/record.rb +121 -160
- data/lib/innodb/record_describer.rb +66 -68
- data/lib/innodb/space.rb +380 -418
- 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
- metadata +80 -11
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
|