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
@@ -1,4 +1,4 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "bindata"
4
4
 
@@ -17,7 +17,7 @@ class BufferCursor
17
17
  attr_accessor :direction
18
18
  attr_accessor :name
19
19
 
20
- def initialize(cursor, position=0, direction=:forward, name=nil)
20
+ def initialize(cursor, position = 0, direction = :forward, name = nil)
21
21
  @cursor = cursor
22
22
  @position = position
23
23
  @direction = direction
@@ -37,21 +37,25 @@ class BufferCursor
37
37
  end
38
38
  end
39
39
 
40
- @@global_tracing = false
40
+ @global_tracing = false
41
41
 
42
42
  # Enable tracing for all BufferCursor objects globally.
43
- def self.trace!(arg=true)
44
- @@global_tracing = arg
43
+ def self.trace!(arg = true) # rubocop:disable Style/OptionalBooleanParameter
44
+ @global_tracing = arg
45
+ end
46
+
47
+ def self.global_tracing?
48
+ @global_tracing
45
49
  end
46
50
 
47
51
  # Initialize a cursor within a buffer at the given position.
48
52
  def initialize(buffer, position)
49
53
  @buffer = buffer
50
- @stack = [ StackEntry.new(self, position) ]
54
+ @stack = [StackEntry.new(self, position)]
51
55
 
52
56
  trace false
53
57
  trace_with :print_trace
54
- trace_to STDOUT
58
+ trace_to $stdout
55
59
  end
56
60
 
57
61
  def inspect
@@ -62,46 +66,49 @@ class BufferCursor
62
66
  ]
63
67
  end
64
68
 
65
- def trace(arg=true)
69
+ def trace(arg = true) # rubocop:disable Style/OptionalBooleanParameter
66
70
  @instance_tracing = arg
71
+
67
72
  self
68
73
  end
69
74
 
70
75
  # Print a trace output for this cursor. The method is passed a cursor object,
71
76
  # position, raw byte buffer, and array of names.
72
- def print_trace(cursor, position, bytes, name)
77
+ def print_trace(_cursor, position, bytes, name)
73
78
  slice_size = 16
74
79
  bytes.each_slice(slice_size).each_with_index do |slice_bytes, slice_count|
75
80
  @trace_io.puts "%06i %s %-32s %s" % [
76
81
  position + (slice_count * slice_size),
77
82
  direction == :backward ? "←" : "→",
78
83
  slice_bytes.map { |n| "%02x" % n }.join,
79
- slice_count == 0 ? name.join(".") : "↵",
84
+ slice_count.zero? ? name.join(".") : "↵",
80
85
  ]
81
86
  end
82
87
  end
83
88
 
84
89
  def trace_to(file)
85
90
  @trace_io = file
91
+
86
92
  self
87
93
  end
88
94
 
89
95
  # Set a Proc or method on self to trace with.
90
- def trace_with(arg=nil)
96
+ def trace_with(arg = nil)
91
97
  if arg.nil?
92
98
  @trace_proc = nil
93
- elsif arg.class == Proc
99
+ elsif arg.instance_of?(Proc)
94
100
  @trace_proc = arg
95
- elsif arg.class == Symbol
96
- @trace_proc = lambda { |cursor, position, bytes, name| self.send(arg, cursor, position, bytes, name) }
101
+ elsif arg.instance_of?(Symbol)
102
+ @trace_proc = ->(cursor, position, bytes, name) { send(arg, cursor, position, bytes, name) }
97
103
  else
98
104
  raise "Don't know how to trace with #{arg}"
99
105
  end
106
+
100
107
  self
101
108
  end
102
109
 
103
110
  def tracing_enabled?
104
- (@@global_tracing or @instance_tracing) && @trace_proc
111
+ (self.class.global_tracing? || @instance_tracing) && @trace_proc
105
112
  end
106
113
 
107
114
  # Generate a trace record from the current cursor.
@@ -123,14 +130,10 @@ class BufferCursor
123
130
  end
124
131
 
125
132
  # Set the field name.
126
- def name(name_arg=nil)
127
- if name_arg.nil?
128
- return current.name
129
- end
133
+ def name(name_arg = nil)
134
+ return current.name if name_arg.nil?
130
135
 
131
- unless block_given?
132
- raise "No block given"
133
- end
136
+ raise "No block given" unless block_given?
134
137
 
135
138
  current.name.push name_arg
136
139
  ret = yield(self)
@@ -139,12 +142,11 @@ class BufferCursor
139
142
  end
140
143
 
141
144
  # Return the direction of the current cursor.
142
- def direction(direction_arg=nil)
143
- if direction_arg.nil?
144
- return current.direction
145
- end
145
+ def direction(direction_arg = nil)
146
+ return current.direction if direction_arg.nil?
146
147
 
147
148
  current.direction = direction_arg
149
+
148
150
  self
149
151
  end
150
152
 
@@ -166,34 +168,40 @@ class BufferCursor
166
168
  # Move the current cursor to a new absolute position.
167
169
  def seek(position)
168
170
  current.position = position if position
171
+
169
172
  self
170
173
  end
171
174
 
172
175
  # Adjust the current cursor to a new relative position.
173
176
  def adjust(relative_position)
174
177
  current.position += relative_position
178
+
175
179
  self
176
180
  end
177
181
 
178
182
  # Save the current cursor position and start a new (nested, stacked) cursor.
179
- def push(position=nil)
183
+ def push(position = nil)
180
184
  @stack.push current.dup
181
185
  seek(position)
186
+
182
187
  self
183
188
  end
184
189
 
185
190
  # Restore the last cursor position.
186
191
  def pop
187
192
  raise "No cursors to pop" unless @stack.size > 1
193
+
188
194
  @stack.pop
195
+
189
196
  self
190
197
  end
191
198
 
192
199
  # Execute a block and restore the cursor to the previous position after
193
200
  # the block returns. Return the block's return value after restoring the
194
201
  # cursor. Optionally seek to provided position before executing block.
195
- def peek(position=nil)
202
+ def peek(position = nil)
196
203
  raise "No block given" unless block_given?
204
+
197
205
  push(position)
198
206
  result = yield(self)
199
207
  pop
@@ -219,105 +227,101 @@ class BufferCursor
219
227
  end
220
228
 
221
229
  # Return raw bytes.
222
- def get_bytes(length)
230
+ def read_bytes(length)
223
231
  read_and_advance(length)
224
232
  end
225
233
 
226
234
  # Return a null-terminated string.
227
- def get_string(length)
235
+ def read_string(length)
228
236
  BinData::Stringz.read(read_and_advance(length))
229
237
  end
230
238
 
231
239
  # Iterate through length bytes returning each as an unsigned 8-bit integer.
232
- def each_byte_as_uint8(length)
233
- unless block_given?
234
- return enum_for(:each_byte_as_uint8, length)
235
- end
240
+ def each_byte_as_uint8(length, &block)
241
+ return enum_for(:each_byte_as_uint8, length) unless block_given?
236
242
 
237
- read_and_advance(length).bytes.each do |byte|
238
- yield byte
239
- end
243
+ read_and_advance(length).bytes.each(&block)
240
244
 
241
245
  nil
242
246
  end
243
247
 
244
248
  # Return raw bytes as hex.
245
- def get_hex(length)
249
+ def read_hex(length)
246
250
  read_and_advance(length).bytes.map { |c| "%02x" % c }.join
247
251
  end
248
252
 
249
253
  # Read an unsigned 8-bit integer.
250
- def get_uint8(position=nil)
254
+ def read_uint8(position = nil)
251
255
  seek(position)
252
256
  data = read_and_advance(1)
253
257
  BinData::Uint8.read(data).to_i
254
258
  end
255
259
 
256
260
  # Read a big-endian unsigned 16-bit integer.
257
- def get_uint16(position=nil)
261
+ def read_uint16(position = nil)
258
262
  seek(position)
259
263
  data = read_and_advance(2)
260
264
  BinData::Uint16be.read(data).to_i
261
265
  end
262
266
 
263
267
  # Read a big-endian signed 16-bit integer.
264
- def get_sint16(position=nil)
268
+ def read_sint16(position = nil)
265
269
  seek(position)
266
270
  data = read_and_advance(2)
267
271
  BinData::Int16be.read(data).to_i
268
272
  end
269
273
 
270
274
  # Read a big-endian unsigned 24-bit integer.
271
- def get_uint24(position=nil)
275
+ def read_uint24(position = nil)
272
276
  seek(position)
273
277
  data = read_and_advance(3)
274
278
  BinData::Uint24be.read(data).to_i
275
279
  end
276
280
 
277
281
  # Read a big-endian unsigned 32-bit integer.
278
- def get_uint32(position=nil)
282
+ def read_uint32(position = nil)
279
283
  seek(position)
280
284
  data = read_and_advance(4)
281
285
  BinData::Uint32be.read(data).to_i
282
286
  end
283
287
 
284
288
  # Read a big-endian unsigned 48-bit integer.
285
- def get_uint48(position=nil)
289
+ def read_uint48(position = nil)
286
290
  seek(position)
287
291
  data = read_and_advance(6)
288
292
  BinData::Uint48be.read(data).to_i
289
293
  end
290
294
 
291
295
  # Read a big-endian unsigned 64-bit integer.
292
- def get_uint64(position=nil)
296
+ def read_uint64(position = nil)
293
297
  seek(position)
294
298
  data = read_and_advance(8)
295
299
  BinData::Uint64be.read(data).to_i
296
300
  end
297
301
 
298
302
  # Read a big-endian unsigned integer given its size in bytes.
299
- def get_uint_by_size(size)
303
+ def read_uint_by_size(size)
300
304
  case size
301
305
  when 1
302
- get_uint8
306
+ read_uint8
303
307
  when 2
304
- get_uint16
308
+ read_uint16
305
309
  when 3
306
- get_uint24
310
+ read_uint24
307
311
  when 4
308
- get_uint32
312
+ read_uint32
309
313
  when 6
310
- get_uint48
314
+ read_uint48
311
315
  when 8
312
- get_uint64
316
+ read_uint64
313
317
  else
314
318
  raise "Integer size #{size} not implemented"
315
319
  end
316
320
  end
317
321
 
318
322
  # Read an array of count unsigned integers given their size in bytes.
319
- def get_uint_array_by_size(size, count)
320
- (0...count).to_a.inject([]) { |a, n| a << get_uint_by_size(size); a }
323
+ def read_uint_array_by_size(size, count)
324
+ count.times.map { read_uint_by_size(size) }
321
325
  end
322
326
 
323
327
  # Read an InnoDB-compressed unsigned 32-bit integer (1-5 bytes).
@@ -327,30 +331,28 @@ class BufferCursor
327
331
  # flag for integers >= 0xf0000000.
328
332
  #
329
333
  # Optionally accept a flag (first byte) if it has already been read (as is
330
- # the case in get_imc_uint64).
331
- def get_ic_uint32(flag=nil)
332
- name("ic_uint32") {
333
- if !flag
334
- flag = peek { name("uint8_or_flag") { get_uint8 } }
335
- end
334
+ # the case in read_imc_uint64).
335
+ def read_ic_uint32(flag = nil)
336
+ name("ic_uint32") do
337
+ flag ||= peek { name("uint8_or_flag") { read_uint8 } }
336
338
 
337
339
  case
338
340
  when flag < 0x80
339
341
  adjust(+1)
340
342
  flag
341
343
  when flag < 0xc0
342
- name("uint16") { get_uint16 } & 0x7fff
344
+ name("uint16") { read_uint16 } & 0x7fff
343
345
  when flag < 0xe0
344
- name("uint24") { get_uint24 } & 0x3fffff
346
+ name("uint24") { read_uint24 } & 0x3fffff
345
347
  when flag < 0xf0
346
- name("uint32") { get_uint32 } & 0x1fffffff
348
+ name("uint32") { read_uint32 } & 0x1fffffff
347
349
  when flag == 0xf0
348
350
  adjust(+1) # Skip the flag byte.
349
- name("uint32+1") { get_uint32 }
351
+ name("uint32+1") { read_uint32 }
350
352
  else
351
- raise "Invalid flag #{flag.to_s} seen"
353
+ raise "Invalid flag #{flag} seen"
352
354
  end
353
- }
355
+ end
354
356
  end
355
357
 
356
358
  # Read an InnoDB-compressed unsigned 64-bit integer (5-9 bytes).
@@ -359,13 +361,13 @@ class BufferCursor
359
361
  # integer (1-5 bytes) while the low 32 bits are stored as a standard
360
362
  # big-endian 32-bit integer (4 bytes). This makes a combined size of
361
363
  # between 5 and 9 bytes.
362
- def get_ic_uint64
363
- name("ic_uint64") {
364
- high = name("high") { get_ic_uint32 }
365
- low = name("low") { name("uint32") { get_uint32 } }
364
+ def read_ic_uint64
365
+ name("ic_uint64") do
366
+ high = name("high") { read_ic_uint32 }
367
+ low = name("low") { name("uint32") { read_uint32 } }
366
368
 
367
369
  (high << 32) | low
368
- }
370
+ end
369
371
  end
370
372
 
371
373
  # Read an InnoDB-"much compressed" unsigned 64-bit integer (1-11 bytes).
@@ -376,31 +378,31 @@ class BufferCursor
376
378
  # is also a flag) of the low 32 bits of the value, also as an InnoDB-
377
379
  # compressed 32-bit unsigned integer. This makes for a combined size
378
380
  # of between 1 and 11 bytes.
379
- def get_imc_uint64
380
- name("imc_uint64") {
381
+ def read_imc_uint64
382
+ name("imc_uint64") do
381
383
  high = 0
382
- flag = peek { name("uint8_or_flag") { get_uint8 } }
384
+ flag = peek { name("uint8_or_flag") { read_uint8 } }
383
385
 
384
386
  if flag == 0xff
385
387
  # The high 32-bits are stored first as an ic_uint32.
386
388
  adjust(+1) # Skip the flag byte.
387
- high = name("high") { get_ic_uint32 }
389
+ high = name("high") { read_ic_uint32 }
388
390
  flag = nil
389
391
  end
390
392
 
391
393
  # The low 32-bits are stored as an ic_uint32; pass the flag we already
392
394
  # read, so we don't have to read it again.
393
- low = name("low") { get_ic_uint32(flag) }
395
+ low = name("low") { read_ic_uint32(flag) }
394
396
 
395
397
  (high << 32) | low
396
- }
398
+ end
397
399
  end
398
400
 
399
401
  # Read an array of 1-bit integers.
400
- def get_bit_array(num_bits)
402
+ def read_bit_array(num_bits)
401
403
  size = (num_bits + 7) / 8
402
404
  data = read_and_advance(size)
403
- bit_array = BinData::Array.new(:type => :bit1, :initial_length => size * 8)
405
+ bit_array = BinData::Array.new(type: :bit1, initial_length: size * 8)
404
406
  bit_array.read(data).to_ary
405
407
  end
406
408
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ReadBitsAtOffset
2
4
  # Read a given number of bits from an integer at a specific bit offset. The
3
5
  # value returned is 0-based so does not need further shifting or adjustment.
@@ -5,4 +7,3 @@ module ReadBitsAtOffset
5
7
  ((data & (((1 << bits) - 1) << offset)) >> offset)
6
8
  end
7
9
  end
8
-
@@ -1,5 +1,5 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Innodb
4
- VERSION = "0.9.14"
4
+ VERSION = "0.12.0"
5
5
  end
data/lib/innodb/xdes.rb CHANGED
@@ -1,166 +1,168 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
2
4
 
3
5
  # An InnoDB "extent descriptor entry" or "+XDES+". These structures are used
4
6
  # in the +XDES+ entry array contained in +FSP_HDR+ and +XDES+ pages.
5
7
  #
6
8
  # Note the distinction between +XDES+ _entries_ and +XDES+ _pages_.
7
- class Innodb::Xdes
8
- # Number of bits per page in the +XDES+ entry bitmap field. Currently
9
- # +XDES+ entries store two bits per page, with the following meanings:
10
- #
11
- # * 1 = free (the page is free, or not in use)
12
- # * 2 = clean (currently unused, always 1 when initialized)
13
- BITS_PER_PAGE = 2
14
-
15
- # The bit value for a free page.
16
- BITMAP_BV_FREE = 1
17
-
18
- # The bit value for a clean page (currently unused in InnoDB).
19
- BITMAP_BV_CLEAN = 2
20
-
21
- # The bitwise-OR of all bitmap bit values.
22
- BITMAP_BV_ALL = (BITMAP_BV_FREE | BITMAP_BV_CLEAN)
23
-
24
- # The values used in the +:state+ field indicating what the extent is
25
- # used for (or what list it is on).
26
- STATES = {
27
- 1 => :free, # The extent is completely empty and unused, and should
28
- # be present on the filespace's FREE list.
29
-
30
- 2 => :free_frag, # Some pages of the extent are used individually, and
31
- # the extent should be present on the filespace's
32
- # FREE_FRAG list.
33
-
34
- 3 => :full_frag, # All pages of the extent are used individually, and
35
- # the extent should be present on the filespace's
36
- # FULL_FRAG list.
37
-
38
- 4 => :fseg, # The extent is wholly allocated to a file segment.
39
- # Additional information about the state of this extent
40
- # can be derived from the its presence on particular
41
- # file segment lists (FULL, NOT_FULL, or FREE).
42
- }
43
-
44
- def initialize(page, cursor)
45
- @page = page
46
- @xdes = read_xdes_entry(page, cursor)
47
- end
9
+ module Innodb
10
+ class Xdes
11
+ # An XDES entry structure.
12
+ Entry = Struct.new(:offset, :start_page, :end_page, :fseg_id, :this, :list, :state, :bitmap, keyword_init: true)
13
+
14
+ PageStatus = Struct.new(:free, :clean, keyword_init: true)
15
+
16
+ # Number of bits per page in the +XDES+ entry bitmap field. Currently
17
+ # +XDES+ entries store two bits per page, with the following meanings:
18
+ #
19
+ # * 1 = free (the page is free, or not in use)
20
+ # * 2 = clean (currently unused, always 1 when initialized)
21
+ BITS_PER_PAGE = 2
22
+
23
+ # The bit value for a free page.
24
+ BITMAP_BV_FREE = 1
25
+
26
+ # The bit value for a clean page (currently unused in InnoDB).
27
+ BITMAP_BV_CLEAN = 2
28
+
29
+ # The bitwise-OR of all bitmap bit values.
30
+ BITMAP_BV_ALL = (BITMAP_BV_FREE | BITMAP_BV_CLEAN)
31
+
32
+ # The values used in the +:state+ field indicating what the extent is
33
+ # used for (or what list it is on).
34
+ STATES = {
35
+ # The extent is completely empty and unused, and should be present on the filespace's FREE list.
36
+ 1 => :free,
37
+
38
+ # Some pages of the extent are used individually, and the extent should be present on the filespace's
39
+ # FREE_FRAG list.
40
+ 2 => :free_frag,
41
+
42
+ # All pages of the extent are used individually, and the extent should be present on the filespace's
43
+ # FULL_FRAG list.
44
+ 3 => :full_frag,
45
+
46
+ # The extent is wholly allocated to a file segment. Additional information about the state of this extent can
47
+ # be derived from the its presence on particular file segment lists (FULL, NOT_FULL, or FREE).
48
+ 4 => :fseg,
49
+ }.freeze
50
+
51
+ attr_reader :xdes
52
+
53
+ extend Forwardable
54
+
55
+ def_delegator :xdes, :offset
56
+ def_delegator :xdes, :start_page
57
+ def_delegator :xdes, :end_page
58
+ def_delegator :xdes, :fseg_id
59
+ def_delegator :xdes, :this
60
+ def_delegator :xdes, :list
61
+ def_delegator :xdes, :state
62
+ def_delegator :xdes, :bitmap
63
+
64
+ def initialize(page, cursor)
65
+ @page = page
66
+ @xdes = read_xdes_entry(page, cursor)
67
+ end
48
68
 
49
- # Size (in bytes) of the bitmap field in the +XDES+ entry.
50
- def size_bitmap
51
- (@page.space.pages_per_extent * BITS_PER_PAGE) / 8
52
- end
69
+ # Size (in bytes) of the bitmap field in the +XDES+ entry.
70
+ def size_bitmap
71
+ (@page.space.pages_per_extent * BITS_PER_PAGE) / 8
72
+ end
53
73
 
54
- # Size (in bytes) of the an +XDES+ entry.
55
- def size_entry
56
- 8 + Innodb::List::NODE_SIZE + 4 + size_bitmap
57
- end
74
+ # Size (in bytes) of the an +XDES+ entry.
75
+ def size_entry
76
+ 8 + Innodb::List::NODE_SIZE + 4 + size_bitmap
77
+ end
58
78
 
59
- # Read an XDES entry from a cursor.
60
- def read_xdes_entry(page, cursor)
61
- extent_number = (cursor.position - page.pos_xdes_array) / size_entry
62
- start_page = page.offset + (extent_number * page.space.pages_per_extent)
63
- cursor.name("xdes[#{extent_number}]") do |c|
64
- {
65
- :offset => c.position,
66
- :start_page => start_page,
67
- :end_page => start_page + page.space.pages_per_extent - 1,
68
- :fseg_id => c.name("fseg_id") { c.get_uint64 },
69
- :this => {:page => page.offset, :offset => c.position},
70
- :list => c.name("list") { Innodb::List.get_node(c) },
71
- :state => c.name("state") { STATES[c.get_uint32] },
72
- :bitmap => c.name("bitmap") { c.get_bytes(size_bitmap) },
73
- }
79
+ # Read an XDES entry from a cursor.
80
+ def read_xdes_entry(page, cursor)
81
+ extent_number = (cursor.position - page.pos_xdes_array) / size_entry
82
+ start_page = page.offset + (extent_number * page.space.pages_per_extent)
83
+ cursor.name("xdes[#{extent_number}]") do |c|
84
+ Entry.new(
85
+ offset: c.position,
86
+ start_page: start_page,
87
+ end_page: start_page + page.space.pages_per_extent - 1,
88
+ fseg_id: c.name("fseg_id") { c.read_uint64 },
89
+ this: Innodb::Page::Address.new(page: page.offset, offset: c.position),
90
+ list: c.name("list") { Innodb::List.get_node(c) },
91
+ state: c.name("state") { STATES[c.read_uint32] },
92
+ bitmap: c.name("bitmap") { c.read_bytes(size_bitmap) }
93
+ )
94
+ end
74
95
  end
75
- end
76
96
 
77
- # Return the stored extent descriptor entry.
78
- def xdes
79
- @xdes
80
- end
97
+ # Return whether this XDES entry is allocated to an fseg (the whole extent
98
+ # then belongs to the fseg).
99
+ def allocated_to_fseg?
100
+ fseg_id != 0
101
+ end
81
102
 
82
- def offset; @xdes[:offset]; end
83
- def start_page; @xdes[:start_page]; end
84
- def end_page; @xdes[:end_page]; end
85
- def fseg_id; @xdes[:fseg_id]; end
86
- def this; @xdes[:this]; end
87
- def list; @xdes[:list]; end
88
- def state; @xdes[:state]; end
89
- def bitmap; @xdes[:bitmap]; end
90
-
91
- # Return whether this XDES entry is allocated to an fseg (the whole extent
92
- # then belongs to the fseg).
93
- def allocated_to_fseg?
94
- fseg_id != 0
95
- end
103
+ # Return the status for a given page. This is relatively inefficient as
104
+ # implemented and could be done better.
105
+ def page_status(page_number)
106
+ page_status_array = each_page_status.to_a
107
+ page_status_array[page_number - start_page][1]
108
+ end
96
109
 
97
- # Return the status for a given page. This is relatively inefficient as
98
- # implemented and could be done better.
99
- def page_status(page_number)
100
- page_status_array = each_page_status.to_a
101
- page_status_array[page_number - xdes[:start_page]][1]
102
- end
110
+ # Iterate through all pages represented by this extent descriptor,
111
+ # yielding a page status hash for each page, containing the following
112
+ # fields:
113
+ #
114
+ # :page The page number.
115
+ # :free Boolean indicating whether the page is free.
116
+ # :clean Boolean indicating whether the page is clean (currently
117
+ # this bit is unused by InnoDB, and always set true).
118
+ def each_page_status
119
+ return enum_for(:each_page_status) unless block_given?
120
+
121
+ bitmap.each_byte.each_with_index do |byte, byte_index|
122
+ (0..3).each do |page_offset|
123
+ page_number = start_page + (byte_index * 4) + page_offset
124
+ page_bits = ((byte >> (page_offset * BITS_PER_PAGE)) & BITMAP_BV_ALL)
125
+ page_status = PageStatus.new(
126
+ free: (page_bits & BITMAP_BV_FREE != 0),
127
+ clean: (page_bits & BITMAP_BV_CLEAN != 0)
128
+ )
129
+ yield page_number, page_status
130
+ end
131
+ end
103
132
 
104
- # Iterate through all pages represented by this extent descriptor,
105
- # yielding a page status hash for each page, containing the following
106
- # fields:
107
- #
108
- # :page The page number.
109
- # :free Boolean indicating whether the page is free.
110
- # :clean Boolean indicating whether the page is clean (currently
111
- # this bit is unused by InnoDB, and always set true).
112
- def each_page_status
113
- unless block_given?
114
- return enum_for(:each_page_status)
133
+ nil
115
134
  end
116
135
 
117
- bitmap = xdes[:bitmap].enum_for(:each_byte)
118
-
119
- bitmap.each_with_index do |byte, byte_index|
120
- (0..3).each do |page_offset|
121
- page_number = xdes[:start_page] + (byte_index * 4) + page_offset
122
- page_bits = ((byte >> (page_offset * BITS_PER_PAGE)) & BITMAP_BV_ALL)
123
- page_status = {
124
- :free => (page_bits & BITMAP_BV_FREE != 0),
125
- :clean => (page_bits & BITMAP_BV_CLEAN != 0),
126
- }
127
- yield page_number, page_status
136
+ # Return the count of free pages (free bit is true) on this extent.
137
+ def free_pages
138
+ each_page_status.inject(0) do |sum, (_page_number, page_status)|
139
+ sum += 1 if page_status.free
140
+ sum
128
141
  end
129
142
  end
130
143
 
131
- nil
132
- end
133
-
134
- # Return the count of free pages (free bit is true) on this extent.
135
- def free_pages
136
- each_page_status.inject(0) do |sum, (page_number, page_status)|
137
- sum += 1 if page_status[:free]
138
- sum
144
+ # Return the count of used pages (free bit is false) on this extent.
145
+ def used_pages
146
+ @page.space.pages_per_extent - free_pages
139
147
  end
140
- end
141
-
142
- # Return the count of used pages (free bit is false) on this extent.
143
- def used_pages
144
- @page.space.pages_per_extent - free_pages
145
- end
146
148
 
147
- # Return the address of the previous list pointer from the list node
148
- # contained within the XDES entry. This is used by +Innodb::List::Xdes+
149
- # to iterate through XDES entries in a list.
150
- def prev_address
151
- xdes[:list][:prev]
152
- end
149
+ # Return the address of the previous list pointer from the list node
150
+ # contained within the XDES entry. This is used by +Innodb::List::Xdes+
151
+ # to iterate through XDES entries in a list.
152
+ def prev_address
153
+ list.prev
154
+ end
153
155
 
154
- # Return the address of the next list pointer from the list node
155
- # contained within the XDES entry. This is used by +Innodb::List::Xdes+
156
- # to iterate through XDES entries in a list.
157
- def next_address
158
- xdes[:list][:next]
159
- end
156
+ # Return the address of the next list pointer from the list node
157
+ # contained within the XDES entry. This is used by +Innodb::List::Xdes+
158
+ # to iterate through XDES entries in a list.
159
+ def next_address
160
+ list.next
161
+ end
160
162
 
161
- # Compare one Innodb::Xdes to another.
162
- def ==(other)
163
- xdes[:this][:page] == other.xdes[:this][:page] &&
164
- xdes[:this][:offset] == other.xdes[:this][:offset]
163
+ # Compare one Innodb::Xdes to another.
164
+ def ==(other)
165
+ this.page == other.this.page && this.offset == other.this.offset
166
+ end
165
167
  end
166
168
  end