innodb_ruby 0.9.16 → 0.11.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.
- checksums.yaml +5 -5
- data/README.md +5 -6
- data/bin/innodb_log +13 -18
- data/bin/innodb_space +377 -757
- data/lib/innodb.rb +4 -5
- data/lib/innodb/checksum.rb +26 -24
- data/lib/innodb/data_dictionary.rb +490 -550
- data/lib/innodb/data_type.rb +362 -326
- data/lib/innodb/field.rb +102 -89
- data/lib/innodb/fseg_entry.rb +22 -26
- data/lib/innodb/history.rb +21 -21
- data/lib/innodb/history_list.rb +72 -76
- data/lib/innodb/ibuf_bitmap.rb +36 -36
- data/lib/innodb/ibuf_index.rb +6 -2
- data/lib/innodb/index.rb +245 -276
- data/lib/innodb/inode.rb +154 -155
- data/lib/innodb/list.rb +191 -183
- data/lib/innodb/log.rb +139 -110
- data/lib/innodb/log_block.rb +100 -91
- data/lib/innodb/log_group.rb +53 -64
- data/lib/innodb/log_reader.rb +97 -96
- data/lib/innodb/log_record.rb +328 -279
- data/lib/innodb/lsn.rb +86 -81
- data/lib/innodb/page.rb +417 -414
- data/lib/innodb/page/blob.rb +82 -83
- data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
- data/lib/innodb/page/ibuf_bitmap.rb +34 -34
- data/lib/innodb/page/index.rb +964 -943
- data/lib/innodb/page/index_compressed.rb +34 -34
- data/lib/innodb/page/inode.rb +103 -112
- data/lib/innodb/page/sys.rb +13 -15
- data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
- data/lib/innodb/page/sys_ibuf_header.rb +45 -42
- data/lib/innodb/page/sys_rseg_header.rb +88 -82
- data/lib/innodb/page/trx_sys.rb +204 -182
- data/lib/innodb/page/undo_log.rb +106 -92
- data/lib/innodb/record.rb +121 -160
- data/lib/innodb/record_describer.rb +66 -68
- data/lib/innodb/space.rb +380 -418
- data/lib/innodb/stats.rb +33 -35
- data/lib/innodb/system.rb +149 -171
- data/lib/innodb/undo_log.rb +129 -107
- data/lib/innodb/undo_record.rb +255 -247
- data/lib/innodb/util/buffer_cursor.rb +81 -79
- data/lib/innodb/util/read_bits_at_offset.rb +2 -1
- data/lib/innodb/version.rb +2 -2
- data/lib/innodb/xdes.rb +144 -142
- metadata +80 -11
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
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
|
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
|
-
|
40
|
+
@global_tracing = false
|
41
41
|
|
42
42
|
# Enable tracing for all BufferCursor objects globally.
|
43
|
-
def self.trace!(arg=true)
|
44
|
-
|
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 = [
|
54
|
+
@stack = [StackEntry.new(self, position)]
|
51
55
|
|
52
56
|
trace false
|
53
57
|
trace_with :print_trace
|
54
|
-
trace_to
|
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(
|
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
|
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.
|
99
|
+
elsif arg.instance_of?(Proc)
|
94
100
|
@trace_proc = arg
|
95
|
-
elsif arg.
|
96
|
-
@trace_proc =
|
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
|
-
(
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
303
|
+
def read_uint_by_size(size)
|
300
304
|
case size
|
301
305
|
when 1
|
302
|
-
|
306
|
+
read_uint8
|
303
307
|
when 2
|
304
|
-
|
308
|
+
read_uint16
|
305
309
|
when 3
|
306
|
-
|
310
|
+
read_uint24
|
307
311
|
when 4
|
308
|
-
|
312
|
+
read_uint32
|
309
313
|
when 6
|
310
|
-
|
314
|
+
read_uint48
|
311
315
|
when 8
|
312
|
-
|
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
|
320
|
-
|
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
|
331
|
-
def
|
332
|
-
name("ic_uint32")
|
333
|
-
|
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") {
|
344
|
+
name("uint16") { read_uint16 } & 0x7fff
|
343
345
|
when flag < 0xe0
|
344
|
-
name("uint24") {
|
346
|
+
name("uint24") { read_uint24 } & 0x3fffff
|
345
347
|
when flag < 0xf0
|
346
|
-
name("uint32") {
|
348
|
+
name("uint32") { read_uint32 } & 0x1fffffff
|
347
349
|
when flag == 0xf0
|
348
350
|
adjust(+1) # Skip the flag byte.
|
349
|
-
name("uint32+1") {
|
351
|
+
name("uint32+1") { read_uint32 }
|
350
352
|
else
|
351
|
-
raise "Invalid flag #{flag
|
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
|
363
|
-
name("ic_uint64")
|
364
|
-
high = name("high") {
|
365
|
-
low
|
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
|
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") {
|
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") {
|
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") {
|
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
|
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(:
|
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
|
-
|
data/lib/innodb/version.rb
CHANGED
data/lib/innodb/xdes.rb
CHANGED
@@ -1,166 +1,168 @@
|
|
1
|
-
#
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
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
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
-
|
132
|
-
|
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
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
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
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|