innodb_ruby 0.8.8 → 0.9.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.
- data/README.md +6 -1
- data/bin/innodb_log +8 -6
- data/bin/innodb_space +344 -212
- data/lib/innodb.rb +12 -1
- data/lib/innodb/cursor.rb +2 -2
- data/lib/innodb/data_dictionary.rb +591 -0
- data/lib/innodb/index.rb +149 -178
- data/lib/innodb/log.rb +96 -33
- data/lib/innodb/log_block.rb +34 -68
- data/lib/innodb/page.rb +1 -7
- data/lib/innodb/page/index.rb +303 -39
- data/lib/innodb/page/index_compressed.rb +4 -0
- data/lib/innodb/page/sys_data_dictionary_header.rb +1 -43
- data/lib/innodb/record.rb +34 -3
- data/lib/innodb/record_describer.rb +2 -0
- data/lib/innodb/space.rb +37 -22
- data/lib/innodb/stats.rb +46 -0
- data/lib/innodb/system.rb +122 -42
- data/lib/innodb/version.rb +1 -1
- metadata +3 -2
data/lib/innodb/index.rb
CHANGED
@@ -3,12 +3,9 @@
|
|
3
3
|
# An InnoDB index B-tree, given an Innodb::Space and a root page number.
|
4
4
|
class Innodb::Index
|
5
5
|
attr_reader :root
|
6
|
-
attr_reader :stats
|
7
|
-
attr_accessor :debug
|
8
6
|
attr_accessor :record_describer
|
9
7
|
|
10
8
|
def initialize(space, root_page_number, record_describer=nil)
|
11
|
-
@debug = false
|
12
9
|
@space = space
|
13
10
|
@record_describer = record_describer || space.record_describer
|
14
11
|
|
@@ -27,8 +24,6 @@ class Innodb::Index
|
|
27
24
|
unless @root.prev.nil? && @root.next.nil?
|
28
25
|
raise "Page #{root_page_number} is a node page, but not appear to be the root; it has previous page and next page pointers"
|
29
26
|
end
|
30
|
-
|
31
|
-
reset_stats
|
32
27
|
end
|
33
28
|
|
34
29
|
def page(page_number)
|
@@ -37,10 +32,6 @@ class Innodb::Index
|
|
37
32
|
page
|
38
33
|
end
|
39
34
|
|
40
|
-
def reset_stats
|
41
|
-
@stats = Hash.new(0)
|
42
|
-
end
|
43
|
-
|
44
35
|
# A helper function to access the index ID in the page header.
|
45
36
|
def id
|
46
37
|
@root.page_header[:index_id]
|
@@ -65,7 +56,7 @@ class Innodb::Index
|
|
65
56
|
|
66
57
|
parent_page.each_child_page do |child_page_number, child_min_key|
|
67
58
|
child_page = page(child_page_number)
|
68
|
-
child_page.record_describer =
|
59
|
+
child_page.record_describer = record_describer
|
69
60
|
if child_page.type == :INDEX
|
70
61
|
if link_proc
|
71
62
|
link_proc.call(parent_page, child_page, child_min_key, depth+1)
|
@@ -83,16 +74,42 @@ class Innodb::Index
|
|
83
74
|
|
84
75
|
# Return the first leaf page in the index by walking down the left side
|
85
76
|
# of the B-tree until a page at the given level is encountered.
|
86
|
-
def
|
77
|
+
def min_page_at_level(level)
|
78
|
+
page = @root
|
79
|
+
record = @root.min_record
|
80
|
+
while record && page.level > level
|
81
|
+
page = page(record.child_page_number)
|
82
|
+
record = page.min_record
|
83
|
+
end
|
84
|
+
page if page.level == level
|
85
|
+
end
|
86
|
+
|
87
|
+
# Return the minimum record in the index.
|
88
|
+
def min_record
|
89
|
+
if min_page = min_page_at_level(0)
|
90
|
+
min_page.min_record
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Return the last leaf page in the index by walking down the right side
|
95
|
+
# of the B-tree until a page at the given level is encountered.
|
96
|
+
def max_page_at_level(level)
|
87
97
|
page = @root
|
88
|
-
record = @root.
|
98
|
+
record = @root.max_record
|
89
99
|
while record && page.level > level
|
90
100
|
page = page(record.child_page_number)
|
91
|
-
record = page.
|
101
|
+
record = page.max_record
|
92
102
|
end
|
93
103
|
page if page.level == level
|
94
104
|
end
|
95
105
|
|
106
|
+
# Return the maximum record in the index.
|
107
|
+
def max_record
|
108
|
+
if max_page = max_page_at_level(0)
|
109
|
+
max_page.max_record
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
96
113
|
# Return the file segment with the given name from the fseg header.
|
97
114
|
def fseg(name)
|
98
115
|
@root.fseg_header[name]
|
@@ -155,7 +172,7 @@ class Innodb::Index
|
|
155
172
|
return enum_for(:each_page_at_level, level)
|
156
173
|
end
|
157
174
|
|
158
|
-
each_page_from(
|
175
|
+
each_page_from(min_page_at_level(level)) { |page| yield page }
|
159
176
|
end
|
160
177
|
|
161
178
|
# Iterate through all records on all leaf pages in ascending order.
|
@@ -171,173 +188,17 @@ class Innodb::Index
|
|
171
188
|
end
|
172
189
|
end
|
173
190
|
|
174
|
-
# Compare two arrays of fields to determine if they are equal. This follows
|
175
|
-
# the same comparison rules as strcmp and others:
|
176
|
-
# 0 = a is equal to b
|
177
|
-
# -1 = a is less than b
|
178
|
-
# +1 = a is greater than b
|
179
|
-
def compare_key(a, b)
|
180
|
-
@stats[:compare_key] += 1
|
181
|
-
|
182
|
-
return 0 if a.nil? && b.nil?
|
183
|
-
return -1 if a.nil? || (!b.nil? && a.size < b.size)
|
184
|
-
return +1 if b.nil? || (!a.nil? && a.size > b.size)
|
185
|
-
|
186
|
-
a.each_index do |i|
|
187
|
-
@stats[:compare_key_field_comparison] += 1
|
188
|
-
return -1 if a[i] < b[i][:value]
|
189
|
-
return +1 if a[i] > b[i][:value]
|
190
|
-
end
|
191
|
-
|
192
|
-
return 0
|
193
|
-
end
|
194
|
-
|
195
|
-
# Search for a record within a single page, and return either a perfect
|
196
|
-
# match for the key, or the last record closest to they key but not greater
|
197
|
-
# than the key. (If an exact match is desired, compare_key must be used to
|
198
|
-
# check if the returned record matches. This makes the function useful for
|
199
|
-
# search in both leaf and non-leaf pages.)
|
200
|
-
def linear_search_from_cursor(page, cursor, key)
|
201
|
-
@stats[:linear_search_from_cursor] += 1
|
202
|
-
|
203
|
-
this_rec = cursor.record
|
204
|
-
|
205
|
-
if @debug
|
206
|
-
puts "linear_search_from_cursor: page=%i, level=%i, start=(%s)" % [
|
207
|
-
page.offset,
|
208
|
-
page.level,
|
209
|
-
this_rec && this_rec.key_string,
|
210
|
-
]
|
211
|
-
end
|
212
|
-
|
213
|
-
# Iterate through all records until finding either a matching record or
|
214
|
-
# one whose key is greater than the desired key.
|
215
|
-
while this_rec && next_rec = cursor.record
|
216
|
-
@stats[:linear_search_from_cursor_record_scans] += 1
|
217
|
-
|
218
|
-
if @debug
|
219
|
-
puts "linear_search_from_cursor: page=%i, level=%i, current=(%s)" % [
|
220
|
-
page.offset,
|
221
|
-
page.level,
|
222
|
-
this_rec && this_rec.key_string,
|
223
|
-
]
|
224
|
-
end
|
225
|
-
|
226
|
-
# If we reach supremum, return the last non-system record we got.
|
227
|
-
return this_rec if next_rec.header[:type] == :supremum
|
228
|
-
|
229
|
-
if compare_key(key, this_rec.key) < 0
|
230
|
-
return this_rec
|
231
|
-
end
|
232
|
-
|
233
|
-
if (compare_key(key, this_rec.key) >= 0) &&
|
234
|
-
(compare_key(key, next_rec.key) < 0)
|
235
|
-
# The desired key is either an exact match for this_rec or is greater
|
236
|
-
# than it but less than next_rec. If this is a non-leaf page, that
|
237
|
-
# will mean that the record will fall on the leaf page this node
|
238
|
-
# pointer record points to, if it exists at all.
|
239
|
-
return this_rec
|
240
|
-
end
|
241
|
-
|
242
|
-
this_rec = next_rec
|
243
|
-
end
|
244
|
-
|
245
|
-
this_rec
|
246
|
-
end
|
247
|
-
|
248
|
-
# Search or a record within a single page using the page directory to limit
|
249
|
-
# the number of record comparisons required. Once the last page directory
|
250
|
-
# entry closest to but not greater than the key is found, fall back to
|
251
|
-
# linear search using linear_search_from_cursor to find the closest record
|
252
|
-
# whose key is not greater than the desired key. (If an exact match is
|
253
|
-
# desired, the returned record must be checked in the same way as the above
|
254
|
-
# linear_search_from_cursor function.)
|
255
|
-
def binary_search_by_directory(page, dir, key)
|
256
|
-
@stats[:binary_search_by_directory] += 1
|
257
|
-
|
258
|
-
return nil if dir.empty?
|
259
|
-
|
260
|
-
# Split the directory at the mid-point (using integer math, so the division
|
261
|
-
# is rounding down). Retrieve the record that sits at the mid-point.
|
262
|
-
mid = ((dir.size-1) / 2)
|
263
|
-
rec = page.record(dir[mid])
|
264
|
-
|
265
|
-
if @debug
|
266
|
-
puts "binary_search_by_directory: page=%i, level=%i, dir.size=%i, dir[%i]=(%s)" % [
|
267
|
-
page.offset,
|
268
|
-
page.level,
|
269
|
-
dir.size,
|
270
|
-
mid,
|
271
|
-
rec.key_string,
|
272
|
-
]
|
273
|
-
end
|
274
|
-
|
275
|
-
# The mid-point record was the infimum record, which is not comparable with
|
276
|
-
# compare_key, so we need to just linear scan from here. If the mid-point
|
277
|
-
# is the beginning of the page there can't be many records left to check
|
278
|
-
# anyway.
|
279
|
-
if rec.header[:type] == :infimum
|
280
|
-
return linear_search_from_cursor(page, page.record_cursor(rec.next), key)
|
281
|
-
end
|
282
|
-
|
283
|
-
# Compare the desired key to the mid-point record's key.
|
284
|
-
case compare_key(key, rec.key)
|
285
|
-
when 0
|
286
|
-
# An exact match for the key was found. Return the record.
|
287
|
-
@stats[:binary_search_by_directory_exact_match] += 1
|
288
|
-
rec
|
289
|
-
when +1
|
290
|
-
# The mid-point record's key is less than the desired key.
|
291
|
-
if dir.size > 2
|
292
|
-
# There are more entries remaining from the directory, recurse again
|
293
|
-
# using binary search on the right half of the directory, which
|
294
|
-
# represents values greater than or equal to the mid-point record's
|
295
|
-
# key.
|
296
|
-
@stats[:binary_search_by_directory_recurse_right] += 1
|
297
|
-
binary_search_by_directory(page, dir[mid...dir.size], key)
|
298
|
-
else
|
299
|
-
next_rec = page.record(dir[mid+1])
|
300
|
-
next_key = next_rec && compare_key(key, next_rec.key)
|
301
|
-
if dir.size == 1 || next_key == -1 || next_key == 0
|
302
|
-
# This is the last entry remaining from the directory, or our key is
|
303
|
-
# greater than rec and less than rec+1's key. Use linear search to
|
304
|
-
# find the record starting at rec.
|
305
|
-
@stats[:binary_search_by_directory_linear_search] += 1
|
306
|
-
linear_search_from_cursor(page, page.record_cursor(rec.offset), key)
|
307
|
-
elsif next_key == +1
|
308
|
-
@stats[:binary_search_by_directory_linear_search] += 1
|
309
|
-
linear_search_from_cursor(page, page.record_cursor(next_rec.offset), key)
|
310
|
-
else
|
311
|
-
nil
|
312
|
-
end
|
313
|
-
end
|
314
|
-
when -1
|
315
|
-
# The mid-point record's key is greater than the desired key.
|
316
|
-
if dir.size == 1
|
317
|
-
# If this is the last entry remaining from the directory, we didn't
|
318
|
-
# find anything workable.
|
319
|
-
@stats[:binary_search_by_directory_empty_result] += 1
|
320
|
-
nil
|
321
|
-
else
|
322
|
-
# Recurse on the left half of the directory, which represents values
|
323
|
-
# less than the mid-point record's key.
|
324
|
-
@stats[:binary_search_by_directory_recurse_left] += 1
|
325
|
-
binary_search_by_directory(page, dir[0...mid], key)
|
326
|
-
end
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
191
|
# Search for a record within the entire index, walking down the non-leaf
|
331
192
|
# pages until a leaf page is found, and then verifying that the record
|
332
193
|
# returned on the leaf page is an exact match for the key. If a matching
|
333
194
|
# record is not found, nil is returned (either because linear_search_in_page
|
334
195
|
# returns nil breaking the loop, or because compare_key returns non-zero).
|
335
196
|
def linear_search(key)
|
336
|
-
|
197
|
+
Innodb::Stats.increment :linear_search
|
337
198
|
|
338
199
|
page = @root
|
339
200
|
|
340
|
-
if
|
201
|
+
if Innodb.debug?
|
341
202
|
puts "linear_search: root=%i, level=%i, key=(%s)" % [
|
342
203
|
page.offset,
|
343
204
|
page.level,
|
@@ -346,7 +207,7 @@ class Innodb::Index
|
|
346
207
|
end
|
347
208
|
|
348
209
|
while rec =
|
349
|
-
linear_search_from_cursor(page
|
210
|
+
page.linear_search_from_cursor(page.record_cursor(page.infimum.next), key)
|
350
211
|
if page.level > 0
|
351
212
|
# If we haven't reached a leaf page yet, move down the tree and search
|
352
213
|
# again using linear search.
|
@@ -355,7 +216,7 @@ class Innodb::Index
|
|
355
216
|
# We're on a leaf page, so return the page and record if there is a
|
356
217
|
# match. If there is no match, break the loop and cause nil to be
|
357
218
|
# returned.
|
358
|
-
return
|
219
|
+
return rec if rec.compare_key(key) == 0
|
359
220
|
break
|
360
221
|
end
|
361
222
|
end
|
@@ -365,11 +226,11 @@ class Innodb::Index
|
|
365
226
|
# the page directory to search while making as few record comparisons as
|
366
227
|
# possible. If a matching record is not found, nil is returned.
|
367
228
|
def binary_search(key)
|
368
|
-
|
229
|
+
Innodb::Stats.increment :binary_search
|
369
230
|
|
370
231
|
page = @root
|
371
232
|
|
372
|
-
if
|
233
|
+
if Innodb.debug?
|
373
234
|
puts "binary_search: root=%i, level=%i, key=(%s)" % [
|
374
235
|
page.offset,
|
375
236
|
page.level,
|
@@ -379,7 +240,7 @@ class Innodb::Index
|
|
379
240
|
|
380
241
|
# Remove supremum from the page directory, since nothing can be scanned
|
381
242
|
# linearly from there anyway.
|
382
|
-
while rec = binary_search_by_directory(page
|
243
|
+
while rec = page.binary_search_by_directory(page.directory[0...-1], key)
|
383
244
|
if page.level > 0
|
384
245
|
# If we haven't reached a leaf page yet, move down the tree and search
|
385
246
|
# again using binary search.
|
@@ -388,10 +249,120 @@ class Innodb::Index
|
|
388
249
|
# We're on a leaf page, so return the page and record if there is a
|
389
250
|
# match. If there is no match, break the loop and cause nil to be
|
390
251
|
# returned.
|
391
|
-
return
|
252
|
+
return rec if rec.compare_key(key) == 0
|
392
253
|
break
|
393
254
|
end
|
394
255
|
end
|
395
256
|
end
|
396
257
|
|
258
|
+
# A cursor to walk the index (cursor) forwards or backward starting with
|
259
|
+
# a given record, or the minimum (:min) or maximum (:max) record in the
|
260
|
+
# index.
|
261
|
+
class IndexCursor
|
262
|
+
def initialize(index, record, direction)
|
263
|
+
Innodb::Stats.increment :index_cursor_create
|
264
|
+
@initial = true
|
265
|
+
@index = index
|
266
|
+
@direction = direction
|
267
|
+
case record
|
268
|
+
when :min
|
269
|
+
# Start at the minimum record on the minimum page in the index.
|
270
|
+
@page = index.min_page_at_level(0)
|
271
|
+
@page_cursor = @page.record_cursor(:min, direction)
|
272
|
+
when :max
|
273
|
+
# Start at the maximum record on the maximum page in the index.
|
274
|
+
@page = index.max_page_at_level(0)
|
275
|
+
@page_cursor = @page.record_cursor(:max, direction)
|
276
|
+
else
|
277
|
+
# Start at the record provided.
|
278
|
+
@page = record.page
|
279
|
+
@page_cursor = @page.record_cursor(record.offset, direction)
|
280
|
+
end
|
281
|
+
@record = @page_cursor.record
|
282
|
+
end
|
283
|
+
|
284
|
+
# Return the current record, mostly as a helper.
|
285
|
+
def current_record
|
286
|
+
@record
|
287
|
+
end
|
288
|
+
|
289
|
+
# Move to the next record in the forward direction and return it.
|
290
|
+
def next_record
|
291
|
+
Innodb::Stats.increment :index_cursor_next_record
|
292
|
+
|
293
|
+
while true
|
294
|
+
if rec = @page_cursor.record
|
295
|
+
return rec
|
296
|
+
end
|
297
|
+
|
298
|
+
unless next_page = @page.next
|
299
|
+
return nil
|
300
|
+
end
|
301
|
+
|
302
|
+
unless @page = @index.page(next_page)
|
303
|
+
raise "Failed to load next page"
|
304
|
+
end
|
305
|
+
|
306
|
+
unless @page_cursor = @page.record_cursor(:min, @direction)
|
307
|
+
raise "Failed to position cursor"
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
# Move to the previous record in the backward direction and return it.
|
313
|
+
def prev_record
|
314
|
+
Innodb::Stats.increment :index_cursor_prev_record
|
315
|
+
|
316
|
+
while true
|
317
|
+
if rec = @page_cursor.record
|
318
|
+
return rec
|
319
|
+
end
|
320
|
+
|
321
|
+
unless prev_page = @page.prev
|
322
|
+
return nil
|
323
|
+
end
|
324
|
+
|
325
|
+
unless @page = @index.page(prev_page)
|
326
|
+
raise "Failed to load prev page"
|
327
|
+
end
|
328
|
+
|
329
|
+
unless @page_cursor = @page.record_cursor(:max, @direction)
|
330
|
+
raise "Failed to position cursor"
|
331
|
+
end
|
332
|
+
end
|
333
|
+
raise "Not implemented"
|
334
|
+
end
|
335
|
+
|
336
|
+
# Return the next record in the order defined when the cursor was created.
|
337
|
+
def record
|
338
|
+
if @initial
|
339
|
+
@initial = false
|
340
|
+
return current_record
|
341
|
+
end
|
342
|
+
|
343
|
+
case @direction
|
344
|
+
when :forward
|
345
|
+
next_record
|
346
|
+
when :backward
|
347
|
+
prev_record
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
# Iterate through all records in the cursor.
|
352
|
+
def each_record
|
353
|
+
unless block_given?
|
354
|
+
return enum_for(:each_record)
|
355
|
+
end
|
356
|
+
|
357
|
+
while rec = record
|
358
|
+
yield rec
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
# Return an IndexCursor starting at the given record (an Innodb::Record,
|
364
|
+
# :min, or :max) and cursor in the direction given (:forward or :backward).
|
365
|
+
def cursor(record=:min, direction=:forward)
|
366
|
+
IndexCursor.new(self, record, direction)
|
367
|
+
end
|
397
368
|
end
|
data/lib/innodb/log.rb
CHANGED
@@ -3,50 +3,113 @@
|
|
3
3
|
# An InnoDB transaction log file.
|
4
4
|
|
5
5
|
class Innodb::Log
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
|
6
|
+
# A map of the name and position of the blocks that form the log header.
|
7
|
+
LOG_HEADER_BLOCK_MAP = {
|
8
|
+
:LOG_FILE_HEADER => 0,
|
9
|
+
:LOG_CHECKPOINT_1 => 1,
|
10
|
+
:EMPTY => 2,
|
11
|
+
:LOG_CHECKPOINT_2 => 3,
|
12
|
+
}
|
13
|
+
|
14
|
+
# Number of blocks in the log file header.
|
15
|
+
LOG_HEADER_BLOCKS = LOG_HEADER_BLOCK_MAP.size
|
16
|
+
|
17
|
+
# Maximum number of log group checkpoints.
|
18
|
+
LOG_CHECKPOINT_GROUPS = 32
|
19
19
|
|
20
20
|
# Open a log file.
|
21
21
|
def initialize(file)
|
22
|
-
@
|
23
|
-
|
24
|
-
|
25
|
-
@blocks = ((@size - DATA_START) / Innodb::LogBlock::BLOCK_SIZE)
|
26
|
-
end
|
22
|
+
@file = File.open(file)
|
23
|
+
@size = @file.stat.size
|
24
|
+
@blocks = (@size / Innodb::LogBlock::BLOCK_SIZE) - LOG_HEADER_BLOCKS
|
27
25
|
end
|
28
|
-
|
26
|
+
|
27
|
+
# The size (in bytes) of the log.
|
28
|
+
attr_reader :size
|
29
|
+
|
30
|
+
# The number of blocks in the the log.
|
29
31
|
attr_reader :blocks
|
30
32
|
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
offset
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
33
|
+
# Get the raw byte buffer for a specific block by block offset.
|
34
|
+
def block_data(offset)
|
35
|
+
raise "Invalid block offset" unless (offset % Innodb::LogBlock::BLOCK_SIZE).zero?
|
36
|
+
@file.seek(offset)
|
37
|
+
@file.read(Innodb::LogBlock::BLOCK_SIZE)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get a cursor to a block in a given offset of the log.
|
41
|
+
def block_cursor(offset)
|
42
|
+
Innodb::Cursor.new(block_data(offset), 0)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return the log header.
|
46
|
+
def header
|
47
|
+
offset = LOG_HEADER_BLOCK_MAP[:LOG_FILE_HEADER] * Innodb::LogBlock::BLOCK_SIZE
|
48
|
+
@header ||= block_cursor(offset).name("header") do |c|
|
49
|
+
{
|
50
|
+
:group_id => c.name("group_id") { c.get_uint32 },
|
51
|
+
:start_lsn => c.name("start_lsn") { c.get_uint64 },
|
52
|
+
:created_by => c.name("created_by") { c.seek(16).get_bytes(4) }
|
53
|
+
}
|
41
54
|
end
|
42
55
|
end
|
43
56
|
|
44
|
-
#
|
57
|
+
# Read a log checkpoint from the given cursor.
|
58
|
+
def read_checkpoint(c)
|
59
|
+
# Log archive related fields (e.g. group_array) are not currently in
|
60
|
+
# use or even read by InnoDB. However, for the sake of completeness,
|
61
|
+
# they are included.
|
62
|
+
{
|
63
|
+
:number => c.name("number") { c.get_uint64 },
|
64
|
+
:lsn => c.name("lsn") { c.get_uint64 },
|
65
|
+
:offset => c.name("offset") { c.get_uint32 },
|
66
|
+
:buffer_size => c.name("buffer_size") { c.get_uint32 },
|
67
|
+
:archived_lsn => c.name("archived_lsn") { c.get_uint64 },
|
68
|
+
:group_array =>
|
69
|
+
(0 .. LOG_CHECKPOINT_GROUPS - 1).map do |n|
|
70
|
+
c.name("group_array[#{n}]") do
|
71
|
+
{
|
72
|
+
:archived_file_no => c.name("archived_file_no") { c.get_uint32 },
|
73
|
+
:archived_offset => c.name("archived_offset") { c.get_uint32 },
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end,
|
77
|
+
:checksum_1 => c.name("checksum_1") { c.get_uint32 },
|
78
|
+
:checksum_2 => c.name("checksum_2") { c.get_uint32 },
|
79
|
+
:fsp_free_limit => c.name("fsp_free_limit") { c.get_uint32 },
|
80
|
+
:fsp_magic => c.name("fsp_magic") { c.get_uint32 },
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
# Return the log checkpoints.
|
85
|
+
def checkpoint
|
86
|
+
offset1 = LOG_HEADER_BLOCK_MAP[:LOG_CHECKPOINT_1] * Innodb::LogBlock::BLOCK_SIZE
|
87
|
+
offset2 = LOG_HEADER_BLOCK_MAP[:LOG_CHECKPOINT_2] * Innodb::LogBlock::BLOCK_SIZE
|
88
|
+
@checkpoint ||=
|
89
|
+
{
|
90
|
+
:checkpoint_1 => block_cursor(offset1).name("checkpoint_1") do |cursor|
|
91
|
+
read_checkpoint(cursor)
|
92
|
+
end,
|
93
|
+
:checkpoint_2 => block_cursor(offset2).name("checkpoint_2") do |cursor|
|
94
|
+
read_checkpoint(cursor)
|
95
|
+
end
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
# Return a log block with a given block index as an InnoDB::LogBlock object.
|
100
|
+
# Blocks are indexed after the log file header, starting from 0.
|
101
|
+
def block(block_index)
|
102
|
+
return nil unless block_index.between?(0, @blocks - 1)
|
103
|
+
offset = (LOG_HEADER_BLOCKS + block_index.to_i) * Innodb::LogBlock::BLOCK_SIZE
|
104
|
+
Innodb::LogBlock.new(block_data(offset))
|
105
|
+
end
|
106
|
+
|
107
|
+
# Iterate through all log blocks, returning the block index and an
|
45
108
|
# InnoDB::LogBlock object for each block.
|
46
109
|
def each_block
|
47
|
-
(0...@blocks).each do |
|
48
|
-
current_block = block(
|
49
|
-
yield
|
110
|
+
(0...@blocks).each do |block_index|
|
111
|
+
current_block = block(block_index)
|
112
|
+
yield block_index, current_block if current_block
|
50
113
|
end
|
51
114
|
end
|
52
115
|
end
|