innodb_ruby 0.9.14 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/space.rb
CHANGED
@@ -1,522 +1,490 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# An InnoDB space file, which can be either a multi-table ibdataN file
|
4
4
|
# or a single-table "innodb_file_per_table" .ibd file.
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
5
|
+
module Innodb
|
6
|
+
class Space
|
7
|
+
# InnoDB's default page size is 16KiB.
|
8
|
+
DEFAULT_PAGE_SIZE = 16 * 1024
|
9
|
+
|
10
|
+
# The default extent size is 1 MiB defined originally as 64 pages.
|
11
|
+
DEFAULT_EXTENT_SIZE = 64 * DEFAULT_PAGE_SIZE
|
12
|
+
|
13
|
+
# A map of InnoDB system space fixed-allocation pages. This can be used to
|
14
|
+
# check whether a space is a system space or not, as non-system spaces will
|
15
|
+
# not match this pattern.
|
16
|
+
SYSTEM_SPACE_PAGE_MAP = {
|
17
|
+
0 => :FSP_HDR,
|
18
|
+
1 => :IBUF_BITMAP,
|
19
|
+
2 => :INODE,
|
20
|
+
3 => :SYS,
|
21
|
+
4 => :INDEX,
|
22
|
+
5 => :TRX_SYS,
|
23
|
+
6 => :SYS,
|
24
|
+
7 => :SYS,
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
XDES_LISTS = %i[
|
28
|
+
free
|
29
|
+
free_frag
|
30
|
+
full_frag
|
31
|
+
].freeze
|
32
|
+
|
33
|
+
# An array of Innodb::Inode list names.
|
34
|
+
INODE_LISTS = %i[
|
35
|
+
full_inodes
|
36
|
+
free_inodes
|
37
|
+
].freeze
|
38
|
+
|
39
|
+
class DataFile
|
40
|
+
attr_reader :file
|
41
|
+
attr_reader :size
|
42
|
+
attr_reader :offset
|
43
|
+
|
44
|
+
def initialize(filename, offset)
|
45
|
+
@file = File.open(filename)
|
46
|
+
@size = @file.stat.size
|
47
|
+
@offset = offset
|
48
|
+
end
|
27
49
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@offset = offset
|
32
|
-
end
|
50
|
+
def name
|
51
|
+
prefix = ""
|
52
|
+
prefix = "#{File.basename(File.dirname(file.path))}/" if File.extname(file.path) == ".ibd"
|
33
53
|
|
34
|
-
|
35
|
-
prefix = ""
|
36
|
-
if File.extname(file.path) == ".ibd"
|
37
|
-
prefix = File.basename(File.dirname(file.path)) + "/"
|
54
|
+
prefix + File.basename(file.path)
|
38
55
|
end
|
39
|
-
|
40
|
-
prefix + File.basename(file.path)
|
41
56
|
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Open a space file, optionally providing the page size to use. Pages
|
45
|
-
# that aren't 16 KiB may not be supported well.
|
46
|
-
def initialize(filenames)
|
47
|
-
filenames = [filenames] unless filenames.is_a?(Array)
|
48
57
|
|
49
|
-
|
50
|
-
|
51
|
-
filenames
|
52
|
-
|
53
|
-
@size += file.size
|
54
|
-
@data_files << file
|
55
|
-
end
|
58
|
+
# Open a space file, optionally providing the page size to use. Pages
|
59
|
+
# that aren't 16 KiB may not be supported well.
|
60
|
+
def initialize(filenames)
|
61
|
+
filenames = [filenames] unless filenames.is_a?(Array)
|
56
62
|
|
57
|
-
|
58
|
-
|
59
|
-
|
63
|
+
@data_files = []
|
64
|
+
@size = 0
|
65
|
+
filenames.each do |filename|
|
66
|
+
file = DataFile.new(filename, @size)
|
67
|
+
@size += file.size
|
68
|
+
@data_files << file
|
69
|
+
end
|
60
70
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
71
|
+
@system_page_size = fsp_flags.system_page_size
|
72
|
+
@page_size = fsp_flags.page_size
|
73
|
+
@compressed = fsp_flags.compressed
|
65
74
|
|
66
|
-
|
67
|
-
|
75
|
+
@pages = (@size / @page_size)
|
76
|
+
@innodb_system = nil
|
77
|
+
@record_describer = nil
|
78
|
+
end
|
68
79
|
|
69
|
-
|
70
|
-
|
71
|
-
attr_accessor :record_describer
|
80
|
+
# The Innodb::System to which this space belongs, if any.
|
81
|
+
attr_accessor :innodb_system
|
72
82
|
|
73
|
-
|
74
|
-
|
83
|
+
# An object which can be used to describe records found in pages within
|
84
|
+
# this space.
|
85
|
+
attr_accessor :record_describer
|
75
86
|
|
76
|
-
|
77
|
-
|
87
|
+
# The system default page size (in bytes), equivalent to UNIV_PAGE_SIZE.
|
88
|
+
attr_reader :system_page_size
|
78
89
|
|
79
|
-
|
80
|
-
|
90
|
+
# The size (in bytes) of each page in the space.
|
91
|
+
attr_reader :page_size
|
81
92
|
|
82
|
-
|
83
|
-
|
93
|
+
# The size (in bytes) of the space
|
94
|
+
attr_reader :size
|
84
95
|
|
85
|
-
|
86
|
-
|
87
|
-
# this method in cursor initialization.
|
88
|
-
def name
|
89
|
-
@name ||= @data_files.map { |f| f.name }.join(",")
|
90
|
-
end
|
96
|
+
# The number of pages in the space.
|
97
|
+
attr_reader :pages
|
91
98
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
]
|
99
|
-
end
|
99
|
+
# Return a string which can uniquely identify this space. Be careful not
|
100
|
+
# to do anything which could instantiate a BufferCursor so that we can use
|
101
|
+
# this method in cursor initialization.
|
102
|
+
def name
|
103
|
+
@name ||= @data_files.map(&:name).join(",")
|
104
|
+
end
|
100
105
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
page_offset = BinData::Uint32be.read(read_at_offset(4, 4)).to_i
|
108
|
-
page_type = BinData::Uint16be.read(read_at_offset(24, 2)).to_i
|
109
|
-
unless page_offset == 0 && Innodb::Page::PAGE_TYPE_BY_VALUE[page_type] == :FSP_HDR
|
110
|
-
raise "Something is very wrong; Page 0 does not seem to be type FSP_HDR; got page type %i but expected %i" % [
|
111
|
-
page_type,
|
112
|
-
Innodb::Page::PAGE_TYPE[:FSP_HDR][:value],
|
106
|
+
def inspect
|
107
|
+
"<%s file=%s, page_size=%i, pages=%i>" % [
|
108
|
+
self.class.name,
|
109
|
+
name.inspect,
|
110
|
+
page_size,
|
111
|
+
pages,
|
113
112
|
]
|
114
113
|
end
|
115
114
|
|
116
|
-
#
|
117
|
-
#
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
]
|
115
|
+
# Read the FSP header "flags" field by byte offset within the space file.
|
116
|
+
# This is useful in order to initialize the page size, as we can't properly
|
117
|
+
# read the FSP_HDR page before we know its size.
|
118
|
+
def raw_fsp_header_flags
|
119
|
+
# A simple sanity check. The FIL header should be initialized in page 0,
|
120
|
+
# to offset 0 and page type :FSP_HDR (8).
|
121
|
+
page_offset = BinData::Uint32be.read(read_at_offset(4, 4)).to_i
|
122
|
+
page_type = BinData::Uint16be.read(read_at_offset(24, 2)).to_i
|
123
|
+
unless page_offset.zero? && Innodb::Page::PAGE_TYPE_BY_VALUE[page_type] == :FSP_HDR
|
124
|
+
raise "Something is very wrong; Page 0 does not seem to be type FSP_HDR; got page type %i but expected %i" % [
|
125
|
+
page_type,
|
126
|
+
Innodb::Page::PAGE_TYPE[:FSP_HDR][:value],
|
127
|
+
]
|
128
|
+
end
|
129
|
+
|
130
|
+
# Another sanity check. The Space ID should be the same in both the FIL
|
131
|
+
# and FSP headers.
|
132
|
+
fil_space = BinData::Uint32be.read(read_at_offset(34, 4)).to_i
|
133
|
+
fsp_space = BinData::Uint32be.read(read_at_offset(38, 4)).to_i
|
134
|
+
unless fil_space == fsp_space
|
135
|
+
raise "Something is very wrong; FIL and FSP header Space IDs do not match: FIL is %i but FSP is %i" % [
|
136
|
+
fil_space,
|
137
|
+
fsp_space,
|
138
|
+
]
|
139
|
+
end
|
140
|
+
|
141
|
+
# Well, we're as sure as we can be. Read the flags field and decode it.
|
142
|
+
flags_value = BinData::Uint32be.read(read_at_offset(54, 4))
|
143
|
+
Innodb::Page::FspHdrXdes.decode_flags(flags_value)
|
125
144
|
end
|
126
145
|
|
127
|
-
#
|
128
|
-
|
129
|
-
|
130
|
-
|
146
|
+
# The FSP header flags, decoded. If the page size has not been initialized,
|
147
|
+
# reach into the raw bytes of the FSP_HDR page and attempt to decode the
|
148
|
+
# flags field that way.
|
149
|
+
def fsp_flags
|
150
|
+
return fsp.flags if @page_size
|
131
151
|
|
132
|
-
# The FSP header flags, decoded. If the page size has not been initialized,
|
133
|
-
# reach into the raw bytes of the FSP_HDR page and attempt to decode the
|
134
|
-
# flags field that way.
|
135
|
-
def fsp_flags
|
136
|
-
if @page_size
|
137
|
-
return fsp[:flags]
|
138
|
-
else
|
139
152
|
raw_fsp_header_flags
|
140
153
|
end
|
141
|
-
end
|
142
154
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
1048576 / system_page_size
|
172
|
-
end
|
155
|
+
# The number of pages per extent.
|
156
|
+
def pages_per_extent
|
157
|
+
# Note that uncompressed tables and compressed tables using the same page
|
158
|
+
# size will have a different number of pages per "extent" because InnoDB
|
159
|
+
# compression uses the FSP_EXTENT_SIZE define (which is then based on the
|
160
|
+
# UNIV_PAGE_SIZE define, which may be based on the innodb_page_size system
|
161
|
+
# variable) for compressed tables rather than something based on the actual
|
162
|
+
# compressed page size.
|
163
|
+
#
|
164
|
+
# For this reason, an "extent" differs in size as follows (the maximum page
|
165
|
+
# size supported for compressed tables is the innodb_page_size):
|
166
|
+
#
|
167
|
+
# innodb_page_size | innodb compression |
|
168
|
+
# page size | extent size | pages | page size | extent size | pages |
|
169
|
+
# 16384 | 1 MiB | 64 | 16384 | 1 MiB | 64 |
|
170
|
+
# | 8192 | 512 KiB | 64 |
|
171
|
+
# | 4096 | 256 KiB | 64 |
|
172
|
+
# | 2048 | 128 KiB | 64 |
|
173
|
+
# | 1024 | 64 KiB | 64 |
|
174
|
+
# 8192 | 1 MiB | 128 | 8192 | 1 MiB | 128 |
|
175
|
+
# | 4096 | 512 KiB | 128 |
|
176
|
+
# | 2048 | 256 KiB | 128 |
|
177
|
+
# | 1024 | 128 KiB | 128 |
|
178
|
+
# 4096 | 1 MiB | 256 | 4096 | 1 MiB | 256 |
|
179
|
+
# | 2048 | 512 KiB | 256 |
|
180
|
+
# | 1024 | 256 KiB | 256 |
|
181
|
+
#
|
173
182
|
|
174
|
-
|
175
|
-
|
176
|
-
pages_per_extent * page_size
|
177
|
-
end
|
178
|
-
|
179
|
-
# The number of pages per FSP_HDR/XDES/IBUF_BITMAP page. This is crudely
|
180
|
-
# mapped to the page size, and works for pages down to 1KiB.
|
181
|
-
def pages_per_bookkeeping_page
|
182
|
-
page_size
|
183
|
-
end
|
183
|
+
DEFAULT_EXTENT_SIZE / system_page_size
|
184
|
+
end
|
184
185
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
186
|
+
# The size (in bytes) of an extent.
|
187
|
+
def extent_size
|
188
|
+
pages_per_extent * page_size
|
189
|
+
end
|
189
190
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
191
|
+
# The number of pages per FSP_HDR/XDES/IBUF_BITMAP page. This is crudely
|
192
|
+
# mapped to the page size, and works for pages down to 1KiB.
|
193
|
+
def pages_per_bookkeeping_page
|
194
|
+
page_size
|
195
|
+
end
|
194
196
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
relative_page_number / pages_per_extent
|
200
|
-
end
|
197
|
+
# The FSP_HDR/XDES page which will contain the XDES entry for a given page.
|
198
|
+
def xdes_page_for_page(page_number)
|
199
|
+
page_number - (page_number % pages_per_bookkeeping_page)
|
200
|
+
end
|
201
201
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
end
|
202
|
+
# The IBUF_BITMAP page which will contain the bitmap entry for a given page.
|
203
|
+
def ibuf_bitmap_page_for_page(page_number)
|
204
|
+
page_number - (page_number % pages_per_bookkeeping_page) + 1
|
205
|
+
end
|
207
206
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
207
|
+
# The XDES entry offset for a given page within its FSP_HDR/XDES page's
|
208
|
+
# XDES array.
|
209
|
+
def xdes_entry_for_page(page_number)
|
210
|
+
relative_page_number = page_number - xdes_page_for_page(page_number)
|
211
|
+
relative_page_number / pages_per_extent
|
212
212
|
end
|
213
|
-
nil
|
214
|
-
end
|
215
213
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
data_file.file.read(size)
|
222
|
-
end
|
214
|
+
# Return the Innodb::Xdes entry which represents a given page.
|
215
|
+
def xdes_for_page(page_number)
|
216
|
+
xdes_array = page(xdes_page_for_page(page_number)).each_xdes.to_a
|
217
|
+
xdes_array[xdes_entry_for_page(page_number)]
|
218
|
+
end
|
223
219
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
end
|
220
|
+
def data_file_for_offset(offset)
|
221
|
+
@data_files.each do |file|
|
222
|
+
return file if offset < file.size
|
228
223
|
|
229
|
-
|
230
|
-
|
231
|
-
data = page_data(page_number)
|
232
|
-
if data
|
233
|
-
Innodb::Page.parse(self, data, page_number)
|
234
|
-
else
|
224
|
+
offset -= file.size
|
225
|
+
end
|
235
226
|
nil
|
236
227
|
end
|
237
|
-
end
|
238
228
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
def system_space?
|
243
|
-
SYSTEM_SPACE_PAGE_MAP.each do |page_number, type|
|
244
|
-
# We can't use page() here, because system_space? need to be used
|
245
|
-
# in the Innodb::Page::Sys.parse to determine what type of page
|
246
|
-
# is being looked at. Using page() would cause us to keep recurse
|
247
|
-
# infinitely. Use Innodb::Page.new instead to load the page as
|
248
|
-
# simply as possible.
|
249
|
-
test_page = Innodb::Page.new(self, page_data(page_number))
|
250
|
-
return false unless test_page.type == type
|
251
|
-
end
|
252
|
-
true
|
253
|
-
end
|
254
|
-
|
255
|
-
# Return the page number for the space's FSP_HDR page.
|
256
|
-
def page_fsp_hdr
|
257
|
-
0
|
258
|
-
end
|
259
|
-
|
260
|
-
# Get (and cache) the FSP header from the FSP_HDR page.
|
261
|
-
def fsp
|
262
|
-
@fsp ||= page(page_fsp_hdr).fsp_header
|
263
|
-
end
|
264
|
-
|
265
|
-
def space_id
|
266
|
-
fsp[:space_id]
|
267
|
-
end
|
229
|
+
# Get the raw byte buffer of size bytes at offset in the file.
|
230
|
+
def read_at_offset(offset, size)
|
231
|
+
return nil unless offset < @size && (offset + size) <= @size
|
268
232
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
# Get the Innodb::Page::TrxSys page for a system space.
|
275
|
-
def trx_sys
|
276
|
-
page(page_trx_sys) if system_space?
|
277
|
-
end
|
233
|
+
data_file = data_file_for_offset(offset)
|
234
|
+
data_file.file.seek(offset - data_file.offset)
|
235
|
+
data_file.file.read(size)
|
236
|
+
end
|
278
237
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
}
|
238
|
+
# Get the raw byte buffer for a specific page by page number.
|
239
|
+
def page_data(page_number)
|
240
|
+
read_at_offset(page_number * page_size, page_size)
|
241
|
+
end
|
284
242
|
|
285
|
-
|
243
|
+
# Get an Innodb::Page object for a specific page by page number.
|
244
|
+
def page(page_number)
|
245
|
+
data = page_data(page_number)
|
246
|
+
Innodb::Page.parse(self, data, page_number) if data
|
286
247
|
end
|
287
|
-
end
|
288
248
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
249
|
+
# Determine whether this space looks like a system space. If the initial
|
250
|
+
# pages in the space match the SYSTEM_SPACE_PAGE_MAP, it is likely to be
|
251
|
+
# a system space.
|
252
|
+
def system_space?
|
253
|
+
SYSTEM_SPACE_PAGE_MAP.each do |page_number, type|
|
254
|
+
# We can't use page() here, because system_space? need to be used
|
255
|
+
# in the Innodb::Page::Sys.parse to determine what type of page
|
256
|
+
# is being looked at. Using page() would cause us to keep recurse
|
257
|
+
# infinitely. Use Innodb::Page.new instead to load the page as
|
258
|
+
# simply as possible.
|
259
|
+
test_page = Innodb::Page.new(self, page_data(page_number))
|
260
|
+
return false unless test_page.type == type
|
261
|
+
end
|
262
|
+
true
|
263
|
+
end
|
293
264
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
265
|
+
# Return the page number for the space's FSP_HDR page.
|
266
|
+
def page_fsp_hdr
|
267
|
+
0
|
268
|
+
end
|
298
269
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
fsp[name]
|
270
|
+
# Get (and cache) the FSP header from the FSP_HDR page.
|
271
|
+
def fsp
|
272
|
+
@fsp ||= page(page_fsp_hdr).fsp_header
|
303
273
|
end
|
304
|
-
end
|
305
274
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
record_describer || @record_describer)
|
310
|
-
end
|
275
|
+
def space_id
|
276
|
+
fsp[:space_id]
|
277
|
+
end
|
311
278
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
return enum_for(:each_index_root_page_number)
|
279
|
+
# Return the page number for the space's TRX_SYS page.
|
280
|
+
def page_trx_sys
|
281
|
+
5
|
316
282
|
end
|
317
283
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
yield record["PAGE_NO"]
|
322
|
-
end
|
323
|
-
else
|
324
|
-
# Guess that the index root pages will be present starting at page 3,
|
325
|
-
# and walk forward until we find a non-root page. This should work fine
|
326
|
-
# for IBD files, if they haven't added indexes online.
|
327
|
-
(3...@pages).each do |page_number|
|
328
|
-
page = page(page_number)
|
329
|
-
if page.type == :INDEX && page.root?
|
330
|
-
yield page_number
|
331
|
-
end
|
332
|
-
end
|
284
|
+
# Get the Innodb::Page::TrxSys page for a system space.
|
285
|
+
def trx_sys
|
286
|
+
page(page_trx_sys) if system_space?
|
333
287
|
end
|
334
288
|
|
335
|
-
|
336
|
-
|
289
|
+
def rseg_page?(page_number)
|
290
|
+
return false unless trx_sys
|
337
291
|
|
338
|
-
|
339
|
-
def each_index
|
340
|
-
unless block_given?
|
341
|
-
return enum_for(:each_index)
|
292
|
+
!trx_sys.rsegs.select { |rseg| rseg.space_id.zero? && rseg.page_number == page_number }.empty?
|
342
293
|
end
|
343
294
|
|
344
|
-
|
345
|
-
|
295
|
+
# Return the page number for the space's SYS data dictionary header.
|
296
|
+
def page_sys_data_dictionary
|
297
|
+
7
|
346
298
|
end
|
347
299
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
# An array of Innodb::Inode list names.
|
352
|
-
def inode_lists
|
353
|
-
[:full_inodes, :free_inodes]
|
354
|
-
end
|
355
|
-
|
356
|
-
# Iterate through Innodb::Inode lists in the space.
|
357
|
-
def each_inode_list
|
358
|
-
unless block_given?
|
359
|
-
return enum_for(:each_inode_list)
|
300
|
+
# Get the Innodb::Page::SysDataDictionaryHeader page for a system space.
|
301
|
+
def data_dictionary_page
|
302
|
+
page(page_sys_data_dictionary) if system_space?
|
360
303
|
end
|
361
304
|
|
362
|
-
|
363
|
-
|
305
|
+
# Get an Innodb::List object for a specific list by list name.
|
306
|
+
def list(name)
|
307
|
+
fsp[name] if XDES_LISTS.include?(name) || INODE_LISTS.include?(name)
|
364
308
|
end
|
365
|
-
end
|
366
309
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
return enum_for(:each_inode)
|
310
|
+
# Get an Innodb::Index object for a specific index by root page number.
|
311
|
+
def index(root_page_number, record_describer = nil)
|
312
|
+
Innodb::Index.new(self, root_page_number, record_describer || @record_describer)
|
371
313
|
end
|
372
314
|
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
315
|
+
# Iterate through all root page numbers for indexes in the space.
|
316
|
+
def each_index_root_page_number
|
317
|
+
return enum_for(:each_index_root_page_number) unless block_given?
|
318
|
+
|
319
|
+
if innodb_system
|
320
|
+
# Retrieve the index root page numbers from the data dictionary.
|
321
|
+
innodb_system.data_dictionary.each_index_by_space_id(space_id) do |record|
|
322
|
+
yield record["PAGE_NO"]
|
323
|
+
end
|
324
|
+
else
|
325
|
+
# Guess that the index root pages will be present starting at page 3,
|
326
|
+
# and walk forward until we find a non-root page. This should work fine
|
327
|
+
# for IBD files, if they haven't added indexes online.
|
328
|
+
(3...@pages).each do |page_number|
|
329
|
+
page = page(page_number)
|
330
|
+
yield page_number if page.type == :INDEX && page.root?
|
377
331
|
end
|
378
332
|
end
|
333
|
+
|
334
|
+
nil
|
379
335
|
end
|
380
|
-
end
|
381
336
|
|
382
|
-
|
383
|
-
|
384
|
-
|
337
|
+
# Iterate through all indexes in the space.
|
338
|
+
def each_index
|
339
|
+
return enum_for(:each_index) unless block_given?
|
340
|
+
|
341
|
+
each_index_root_page_number do |page_number|
|
342
|
+
yield index(page_number)
|
343
|
+
end
|
385
344
|
|
386
|
-
|
387
|
-
return enum_for(:each_doublewrite_page_number)
|
345
|
+
nil
|
388
346
|
end
|
389
347
|
|
390
|
-
|
391
|
-
|
392
|
-
|
348
|
+
# Iterate through Innodb::Inode lists in the space.
|
349
|
+
def each_inode_list
|
350
|
+
return enum_for(:each_inode_list) unless block_given?
|
351
|
+
|
352
|
+
INODE_LISTS.each do |name|
|
353
|
+
yield name, list(name)
|
393
354
|
end
|
394
355
|
end
|
395
|
-
end
|
396
356
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
@doublewrite_pages ||= each_doublewrite_page_number.to_a
|
401
|
-
@doublewrite_pages.include?(page_number)
|
402
|
-
end
|
357
|
+
# Iterate through Innodb::Inode objects in the space.
|
358
|
+
def each_inode(&block)
|
359
|
+
return enum_for(:each_inode) unless block_given?
|
403
360
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
361
|
+
each_inode_list.each do |_name, list|
|
362
|
+
list.each do |page|
|
363
|
+
page.each_allocated_inode(&block)
|
364
|
+
end
|
365
|
+
end
|
409
366
|
end
|
410
367
|
|
411
|
-
|
412
|
-
|
413
|
-
|
368
|
+
# Return an Inode by fseg_id. Iterates through the inode list, but it
|
369
|
+
# normally is fairly small, so should be relatively efficient.
|
370
|
+
def inode(fseg_id)
|
371
|
+
each_inode.select { |inode| inode.fseg_id == fseg_id }.first
|
414
372
|
end
|
415
|
-
end
|
416
373
|
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
374
|
+
# Iterate through the page numbers in the doublewrite buffer.
|
375
|
+
def each_doublewrite_page_number(&block)
|
376
|
+
return nil unless system_space?
|
377
|
+
return enum_for(:each_doublewrite_page_number) unless block_given?
|
421
378
|
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
return enum_for(:each_xdes_list)
|
379
|
+
trx_sys.doublewrite[:page_info][0][:page_number].each do |start_page|
|
380
|
+
(start_page...(start_page + pages_per_extent)).each(&block)
|
381
|
+
end
|
426
382
|
end
|
427
383
|
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
end
|
384
|
+
# Return true if a page is in the doublewrite buffer.
|
385
|
+
def doublewrite_page?(page_number)
|
386
|
+
return false unless system_space?
|
432
387
|
|
433
|
-
|
434
|
-
|
435
|
-
unless block_given?
|
436
|
-
return enum_for(:each_xdes_page_number)
|
388
|
+
@doublewrite_pages ||= each_doublewrite_page_number.to_a
|
389
|
+
@doublewrite_pages.include?(page_number)
|
437
390
|
end
|
438
391
|
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
392
|
+
# Iterate through all pages in a space, returning the page number and an
|
393
|
+
# Innodb::Page object for each one.
|
394
|
+
def each_page(start_page = 0)
|
395
|
+
return enum_for(:each_page, start_page) unless block_given?
|
443
396
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
return enum_for(:each_xdes_page)
|
397
|
+
(start_page...@pages).each do |page_number|
|
398
|
+
current_page = page(page_number)
|
399
|
+
yield page_number, current_page if current_page
|
400
|
+
end
|
449
401
|
end
|
450
402
|
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
end
|
455
|
-
end
|
403
|
+
# Iterate through Innodb::Xdes lists in the space.
|
404
|
+
def each_xdes_list
|
405
|
+
return enum_for(:each_xdes_list) unless block_given?
|
456
406
|
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
unless block_given?
|
461
|
-
return enum_for(:each_xdes)
|
407
|
+
XDES_LISTS.each do |name|
|
408
|
+
yield name, list(name)
|
409
|
+
end
|
462
410
|
end
|
463
411
|
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
end
|
412
|
+
# An array of all FSP/XDES page numbers for the space.
|
413
|
+
def each_xdes_page_number(&block)
|
414
|
+
return enum_for(:each_xdes_page_number) unless block_given?
|
415
|
+
|
416
|
+
0.step(pages - 1, pages_per_bookkeeping_page).each(&block)
|
470
417
|
end
|
471
|
-
end
|
472
418
|
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
419
|
+
# Iterate through all extent descriptor pages, returning an Innodb::Page object
|
420
|
+
# for each one.
|
421
|
+
def each_xdes_page
|
422
|
+
return enum_for(:each_xdes_page) unless block_given?
|
423
|
+
|
424
|
+
each_xdes_page_number do |page_number|
|
425
|
+
current_page = page(page_number)
|
426
|
+
yield current_page if current_page&.extent_descriptor?
|
427
|
+
end
|
478
428
|
end
|
479
429
|
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
430
|
+
# Iterate through all extent descriptors for the space, returning an
|
431
|
+
# Innodb::Xdes object for each one.
|
432
|
+
def each_xdes
|
433
|
+
return enum_for(:each_xdes) unless block_given?
|
484
434
|
|
485
|
-
|
486
|
-
|
435
|
+
each_xdes_page do |xdes_page|
|
436
|
+
xdes_page.each_xdes do |xdes|
|
437
|
+
# Only return initialized XDES entries; :state will be nil for extents
|
438
|
+
# that have not been allocated yet.
|
439
|
+
yield xdes if xdes.xdes[:state]
|
487
440
|
end
|
488
441
|
end
|
489
442
|
end
|
490
|
-
end
|
491
443
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
444
|
+
# Iterate through all pages, yielding the page number, page object,
|
445
|
+
# and page status.
|
446
|
+
def each_page_status(start_page = 0)
|
447
|
+
return enum_for(:each_page_status, start_page) unless block_given?
|
448
|
+
|
449
|
+
each_xdes do |xdes|
|
450
|
+
xdes.each_page_status do |page_number, page_status|
|
451
|
+
next if page_number < start_page
|
452
|
+
next if page_number >= @pages
|
496
453
|
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
454
|
+
if (this_page = page(page_number))
|
455
|
+
yield page_number, this_page, page_status
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
502
459
|
end
|
503
460
|
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
461
|
+
# A helper to produce a printable page type.
|
462
|
+
def type_for_page(page, page_status)
|
463
|
+
page_status[:free] ? "FREE (#{page.type})" : page.type
|
464
|
+
end
|
465
|
+
|
466
|
+
# Iterate through unique regions in the space by page type. This is useful
|
467
|
+
# to achieve an overall view of the space.
|
468
|
+
def each_page_type_region(start_page = 0)
|
469
|
+
return enum_for(:each_page_type_region, start_page) unless block_given?
|
470
|
+
|
471
|
+
region = nil
|
472
|
+
each_page_status(start_page) do |page_number, page, page_status|
|
473
|
+
page_type = type_for_page(page, page_status)
|
474
|
+
if region && region[:type] == page_type
|
475
|
+
region[:end] = page_number
|
476
|
+
region[:count] += 1
|
477
|
+
else
|
478
|
+
yield region if region
|
479
|
+
region = {
|
480
|
+
start: page_number,
|
481
|
+
end: page_number,
|
482
|
+
type: page_type,
|
483
|
+
count: 1,
|
484
|
+
}
|
485
|
+
end
|
518
486
|
end
|
487
|
+
yield region if region
|
519
488
|
end
|
520
|
-
yield region if region
|
521
489
|
end
|
522
490
|
end
|