innodb_ruby 0.9.16 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|