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.
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/space.rb CHANGED
@@ -1,522 +1,490 @@
1
- # -*- encoding : utf-8 -*-
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
- class Innodb::Space
6
- # InnoDB's default page size is 16KiB.
7
- DEFAULT_PAGE_SIZE = 16384
8
-
9
- # A map of InnoDB system space fixed-allocation pages. This can be used to
10
- # check whether a space is a system space or not, as non-system spaces will
11
- # not match this pattern.
12
- SYSTEM_SPACE_PAGE_MAP = {
13
- 0 => :FSP_HDR,
14
- 1 => :IBUF_BITMAP,
15
- 2 => :INODE,
16
- 3 => :SYS,
17
- 4 => :INDEX,
18
- 5 => :TRX_SYS,
19
- 6 => :SYS,
20
- 7 => :SYS,
21
- }
22
-
23
- class DataFile
24
- attr_reader :file
25
- attr_reader :size
26
- attr_reader :offset
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
- def initialize(filename, offset)
29
- @file = File.open(filename)
30
- @size = @file.stat.size
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
- def name
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
- @data_files = []
50
- @size = 0
51
- filenames.each do |filename|
52
- file = DataFile.new(filename, @size)
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
- @system_page_size = fsp_flags[:system_page_size]
58
- @page_size = fsp_flags[:page_size]
59
- @compressed = fsp_flags[:compressed]
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
- @pages = (@size / @page_size)
62
- @innodb_system = nil
63
- @record_describer = nil
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
- # The Innodb::System to which this space belongs, if any.
67
- attr_accessor :innodb_system
75
+ @pages = (@size / @page_size)
76
+ @innodb_system = nil
77
+ @record_describer = nil
78
+ end
68
79
 
69
- # An object which can be used to describe records found in pages within
70
- # this space.
71
- attr_accessor :record_describer
80
+ # The Innodb::System to which this space belongs, if any.
81
+ attr_accessor :innodb_system
72
82
 
73
- # The system default page size (in bytes), equivalent to UNIV_PAGE_SIZE.
74
- attr_reader :system_page_size
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
- # The size (in bytes) of each page in the space.
77
- attr_reader :page_size
87
+ # The system default page size (in bytes), equivalent to UNIV_PAGE_SIZE.
88
+ attr_reader :system_page_size
78
89
 
79
- # The size (in bytes) of the space
80
- attr_reader :size
90
+ # The size (in bytes) of each page in the space.
91
+ attr_reader :page_size
81
92
 
82
- # The number of pages in the space.
83
- attr_reader :pages
93
+ # The size (in bytes) of the space
94
+ attr_reader :size
84
95
 
85
- # Return a string which can uniquely identify this space. Be careful not
86
- # to do anything which could instantiate a BufferCursor so that we can use
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
- def inspect
93
- "<%s file=%s, page_size=%i, pages=%i>" % [
94
- self.class.name,
95
- name.inspect,
96
- page_size,
97
- pages,
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
- # Read the FSP header "flags" field by byte offset within the space file.
102
- # This is useful in order to initialize the page size, as we can't properly
103
- # read the FSP_HDR page before we know its size.
104
- def raw_fsp_header_flags
105
- # A simple sanity check. The FIL header should be initialized in page 0,
106
- # to offset 0 and page type :FSP_HDR (8).
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
- # Another sanity check. The Space ID should be the same in both the FIL
117
- # and FSP headers.
118
- fil_space = BinData::Uint32be.read(read_at_offset(34, 4)).to_i
119
- fsp_space = BinData::Uint32be.read(read_at_offset(38, 4)).to_i
120
- unless fil_space == fsp_space
121
- raise "Something is very wrong; FIL and FSP header Space IDs don't match: FIL is %i but FSP is %i" % [
122
- fil_space,
123
- fsp_space,
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
- # Well, we're as sure as we can be. Read the flags field and decode it.
128
- flags_value = BinData::Uint32be.read(read_at_offset(54, 4))
129
- Innodb::Page::FspHdrXdes.decode_flags(flags_value)
130
- end
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
- # The number of pages per extent.
144
- def pages_per_extent
145
- # Note that uncompressed tables and compressed tables using the same page
146
- # size will have a different number of pages per "extent" because InnoDB
147
- # compression uses the FSP_EXTENT_SIZE define (which is then based on the
148
- # UNIV_PAGE_SIZE define, which may be based on the innodb_page_size system
149
- # variable) for compressed tables rather than something based on the actual
150
- # compressed page size.
151
- #
152
- # For this reason, an "extent" differs in size as follows (the maximum page
153
- # size supported for compressed tables is the innodb_page_size):
154
- #
155
- # innodb_page_size | innodb compression |
156
- # page size | extent size | pages | page size | extent size | pages |
157
- # 16384 | 1 MiB | 64 | 16384 | 1 MiB | 64 |
158
- # | 8192 | 512 KiB | 64 |
159
- # | 4096 | 256 KiB | 64 |
160
- # | 2048 | 128 KiB | 64 |
161
- # | 1024 | 64 KiB | 64 |
162
- # 8192 | 1 MiB | 128 | 8192 | 1 MiB | 128 |
163
- # | 4096 | 512 KiB | 128 |
164
- # | 2048 | 256 KiB | 128 |
165
- # | 1024 | 128 KiB | 128 |
166
- # 4096 | 1 MiB | 256 | 4096 | 1 MiB | 256 |
167
- # | 2048 | 512 KiB | 256 |
168
- # | 1024 | 256 KiB | 256 |
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
- # The size (in bytes) of an extent.
175
- def extent_size
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
- # The FSP_HDR/XDES page which will contain the XDES entry for a given page.
186
- def xdes_page_for_page(page_number)
187
- page_number - (page_number % pages_per_bookkeeping_page)
188
- end
186
+ # The size (in bytes) of an extent.
187
+ def extent_size
188
+ pages_per_extent * page_size
189
+ end
189
190
 
190
- # The IBUF_BITMAP page which will contain the bitmap entry for a given page.
191
- def ibuf_bitmap_page_for_page(page_number)
192
- page_number - (page_number % pages_per_bookkeeping_page) + 1
193
- end
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
- # The XDES entry offset for a given page within its FSP_HDR/XDES page's
196
- # XDES array.
197
- def xdes_entry_for_page(page_number)
198
- relative_page_number = page_number - xdes_page_for_page(page_number)
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
- # Return the Innodb::Xdes entry which represents a given page.
203
- def xdes_for_page(page_number)
204
- xdes_array = page(xdes_page_for_page(page_number)).each_xdes.to_a
205
- xdes_array[xdes_entry_for_page(page_number)]
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
- def data_file_for_offset(offset)
209
- @data_files.each do |file|
210
- return file if offset < file.size
211
- offset -= file.size
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
- # Get the raw byte buffer of size bytes at offset in the file.
217
- def read_at_offset(offset, size)
218
- return nil unless offset < @size && (offset + size) <= @size
219
- data_file = data_file_for_offset(offset)
220
- data_file.file.seek(offset - data_file.offset)
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
- # Get the raw byte buffer for a specific page by page number.
225
- def page_data(page_number)
226
- read_at_offset(page_number * page_size, page_size)
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
- # Get an Innodb::Page object for a specific page by page number.
230
- def page(page_number)
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
- # Determine whether this space looks like a system space. If the initial
240
- # pages in the space match the SYSTEM_SPACE_PAGE_MAP, it is likely to be
241
- # a system space.
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
- # Return the page number for the space's TRX_SYS page.
270
- def page_trx_sys
271
- 5
272
- end
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
- def rseg_page?(page_number)
280
- if trx_sys
281
- rseg_match = trx_sys.rsegs.select { |rseg|
282
- rseg[:space_id] == 0 && rseg[:page_number] == page_number
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
- ! rseg_match.empty?
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
- # Return the page number for the space's SYS data dictionary header.
290
- def page_sys_data_dictionary
291
- 7
292
- end
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
- # Get the Innodb::Page::SysDataDictionaryHeader page for a system space.
295
- def data_dictionary_page
296
- page(page_sys_data_dictionary) if system_space?
297
- end
265
+ # Return the page number for the space's FSP_HDR page.
266
+ def page_fsp_hdr
267
+ 0
268
+ end
298
269
 
299
- # Get an Innodb::List object for a specific list by list name.
300
- def list(name)
301
- if xdes_lists.include?(name) || inode_lists.include?(name)
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
- # Get an Innodb::Index object for a specific index by root page number.
307
- def index(root_page_number, record_describer=nil)
308
- Innodb::Index.new(self, root_page_number,
309
- record_describer || @record_describer)
310
- end
275
+ def space_id
276
+ fsp[:space_id]
277
+ end
311
278
 
312
- # Iterate through all root page numbers for indexes in the space.
313
- def each_index_root_page_number
314
- unless block_given?
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
- if innodb_system
319
- # Retrieve the index root page numbers from the data dictionary.
320
- innodb_system.data_dictionary.each_index_by_space_id(space_id) do |record|
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
- nil
336
- end
289
+ def rseg_page?(page_number)
290
+ return false unless trx_sys
337
291
 
338
- # Iterate through all indexes in the space.
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
- each_index_root_page_number do |page_number|
345
- yield index(page_number)
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
- nil
349
- end
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
- inode_lists.each do |name|
363
- yield name, list(name)
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
- # Iterate through Innodb::Inode objects in the space.
368
- def each_inode
369
- unless block_given?
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
- each_inode_list.each do |name, list|
374
- list.each do |page|
375
- page.each_allocated_inode do |inode|
376
- yield inode
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
- # Iterate through the page numbers in the doublewrite buffer.
383
- def each_doublewrite_page_number
384
- return nil unless system_space?
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
- unless block_given?
387
- return enum_for(:each_doublewrite_page_number)
345
+ nil
388
346
  end
389
347
 
390
- trx_sys.doublewrite[:page_info][0][:page_number].each do |start_page|
391
- (start_page...(start_page+pages_per_extent)).each do |page_number|
392
- yield page_number
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
- # Return true if a page is in the doublewrite buffer.
398
- def doublewrite_page?(page_number)
399
- return false unless system_space?
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
- # Iterate through all pages in a space, returning the page number and an
405
- # Innodb::Page object for each one.
406
- def each_page(start_page=0)
407
- unless block_given?
408
- return enum_for(:each_page, start_page)
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
- (start_page...@pages).each do |page_number|
412
- current_page = page(page_number)
413
- yield page_number, current_page if current_page
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
- # An array of Innodb::Xdes list names.
418
- def xdes_lists
419
- [:free, :free_frag, :full_frag]
420
- end
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
- # Iterate through Innodb::Xdes lists in the space.
423
- def each_xdes_list
424
- unless block_given?
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
- xdes_lists.each do |name|
429
- yield name, list(name)
430
- end
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
- # An array of all FSP/XDES page numbers for the space.
434
- def each_xdes_page_number
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
- 0.step(pages - 1, pages_per_bookkeeping_page).each do |n|
440
- yield n
441
- end
442
- end
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
- # Iterate through all FSP_HDR/XDES pages, returning an Innodb::Page object
445
- # for each one.
446
- def each_xdes_page
447
- unless block_given?
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
- each_xdes_page_number do |page_number|
452
- current_page = page(page_number)
453
- yield current_page if current_page and [:FSP_HDR, :XDES].include?(current_page.type)
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
- # Iterate through all extent descriptors for the space, returning an
458
- # Innodb::Xdes object for each one.
459
- def each_xdes
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
- each_xdes_page do |xdes_page|
465
- xdes_page.each_xdes do |xdes|
466
- # Only return initialized XDES entries; :state will be nil for extents
467
- # that have not been allocated yet.
468
- yield xdes if xdes.xdes[:state]
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
- # Iterate through all pages, yielding the page number, page object,
474
- # and page status.
475
- def each_page_status(start_page=0)
476
- unless block_given?
477
- return enum_for(:each_page_with_status, start_page)
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
- each_xdes do |xdes|
481
- xdes.each_page_status do |page_number, page_status|
482
- next if page_number < start_page
483
- next if page_number >= @pages
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
- if this_page = page(page_number)
486
- yield page_number, this_page, page_status
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
- # A helper to produce a printable page type.
493
- def type_for_page(page, page_status)
494
- page_status[:free] ? "FREE (#{page.type})" : page.type
495
- end
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
- # Iterate through unique regions in the space by page type. This is useful
498
- # to achieve an overall view of the space.
499
- def each_page_type_region(start_page=0)
500
- unless block_given?
501
- return enum_for(:each_page_type_region, start_page)
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
- region = nil
505
- each_page_status(start_page) do |page_number, page, page_status|
506
- page_type = type_for_page(page, page_status)
507
- if region && region[:type] == page_type
508
- region[:end] = page_number
509
- region[:count] += 1
510
- else
511
- yield region if region
512
- region = {
513
- :start => page_number,
514
- :end => page_number,
515
- :type => page_type,
516
- :count => 1,
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