innodb_ruby 0.9.14 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +5 -6
  3. data/bin/innodb_log +13 -18
  4. data/bin/innodb_space +654 -778
  5. data/lib/innodb/checksum.rb +26 -24
  6. data/lib/innodb/data_dictionary.rb +490 -550
  7. data/lib/innodb/data_type.rb +362 -325
  8. data/lib/innodb/field.rb +102 -89
  9. data/lib/innodb/fseg_entry.rb +22 -26
  10. data/lib/innodb/history.rb +21 -21
  11. data/lib/innodb/history_list.rb +72 -76
  12. data/lib/innodb/ibuf_bitmap.rb +36 -36
  13. data/lib/innodb/ibuf_index.rb +6 -2
  14. data/lib/innodb/index.rb +245 -276
  15. data/lib/innodb/inode.rb +166 -124
  16. data/lib/innodb/list.rb +196 -183
  17. data/lib/innodb/log.rb +139 -110
  18. data/lib/innodb/log_block.rb +100 -91
  19. data/lib/innodb/log_group.rb +53 -64
  20. data/lib/innodb/log_reader.rb +97 -96
  21. data/lib/innodb/log_record.rb +328 -279
  22. data/lib/innodb/lsn.rb +86 -81
  23. data/lib/innodb/page/blob.rb +82 -83
  24. data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
  25. data/lib/innodb/page/ibuf_bitmap.rb +34 -34
  26. data/lib/innodb/page/index.rb +965 -924
  27. data/lib/innodb/page/index_compressed.rb +34 -34
  28. data/lib/innodb/page/inode.rb +103 -112
  29. data/lib/innodb/page/sys.rb +13 -15
  30. data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
  31. data/lib/innodb/page/sys_ibuf_header.rb +45 -42
  32. data/lib/innodb/page/sys_rseg_header.rb +88 -82
  33. data/lib/innodb/page/trx_sys.rb +204 -182
  34. data/lib/innodb/page/undo_log.rb +106 -92
  35. data/lib/innodb/page.rb +417 -414
  36. data/lib/innodb/record.rb +121 -164
  37. data/lib/innodb/record_describer.rb +66 -68
  38. data/lib/innodb/space.rb +381 -413
  39. data/lib/innodb/stats.rb +33 -35
  40. data/lib/innodb/system.rb +149 -171
  41. data/lib/innodb/undo_log.rb +129 -107
  42. data/lib/innodb/undo_record.rb +255 -247
  43. data/lib/innodb/util/buffer_cursor.rb +81 -79
  44. data/lib/innodb/util/read_bits_at_offset.rb +2 -1
  45. data/lib/innodb/version.rb +2 -2
  46. data/lib/innodb/xdes.rb +144 -142
  47. data/lib/innodb.rb +4 -5
  48. metadata +100 -25
data/lib/innodb/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