innodb_ruby 0.9.0 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/innodb_log +100 -34
- data/bin/innodb_space +288 -9
- data/lib/innodb.rb +10 -0
- data/lib/innodb/data_dictionary.rb +8 -8
- data/lib/innodb/data_type.rb +49 -0
- data/lib/innodb/field.rb +21 -11
- data/lib/innodb/history.rb +30 -0
- data/lib/innodb/history_list.rb +106 -0
- data/lib/innodb/index.rb +49 -57
- data/lib/innodb/inode.rb +11 -1
- data/lib/innodb/list.rb +45 -23
- data/lib/innodb/log.rb +22 -11
- data/lib/innodb/log_block.rb +52 -82
- data/lib/innodb/log_group.rb +59 -54
- data/lib/innodb/log_reader.rb +116 -0
- data/lib/innodb/log_record.rb +317 -0
- data/lib/innodb/lsn.rb +103 -0
- data/lib/innodb/page.rb +39 -5
- data/lib/innodb/page/blob.rb +26 -0
- data/lib/innodb/page/fsp_hdr_xdes.rb +38 -6
- data/lib/innodb/page/index.rb +176 -96
- data/lib/innodb/page/inode.rb +33 -1
- data/lib/innodb/page/sys_data_dictionary_header.rb +19 -0
- data/lib/innodb/page/sys_rseg_header.rb +41 -2
- data/lib/innodb/page/trx_sys.rb +69 -1
- data/lib/innodb/record.rb +37 -0
- data/lib/innodb/space.rb +28 -4
- data/lib/innodb/system.rb +4 -0
- data/lib/innodb/undo_log.rb +84 -24
- data/lib/innodb/undo_record.rb +259 -0
- data/lib/innodb/{cursor.rb → util/buffer_cursor.rb} +135 -29
- data/lib/innodb/util/read_bits_at_offset.rb +8 -0
- data/lib/innodb/version.rb +1 -1
- data/lib/innodb/xdes.rb +2 -0
- metadata +10 -3
data/lib/innodb/inode.rb
CHANGED
@@ -29,6 +29,7 @@ class Innodb::Inode
|
|
29
29
|
# Construct a new Inode by reading an FSEG header from a cursor.
|
30
30
|
def self.new_from_cursor(space, cursor)
|
31
31
|
data = {
|
32
|
+
:offset => cursor.position,
|
32
33
|
:fseg_id => cursor.name("fseg_id") {
|
33
34
|
cursor.get_uint64
|
34
35
|
},
|
@@ -62,6 +63,7 @@ class Innodb::Inode
|
|
62
63
|
@data = data
|
63
64
|
end
|
64
65
|
|
66
|
+
def offset; @data[:offset]; end
|
65
67
|
def fseg_id; @data[:fseg_id]; end
|
66
68
|
def not_full_n_used; @data[:not_full_n_used]; end
|
67
69
|
def free; @data[:free]; end
|
@@ -70,6 +72,14 @@ class Innodb::Inode
|
|
70
72
|
def magic_n; @data[:magic_n]; end
|
71
73
|
def frag_array; @data[:frag_array]; end
|
72
74
|
|
75
|
+
def inspect
|
76
|
+
"<%s space=%s, fseg=%i>" % [
|
77
|
+
self.class.name,
|
78
|
+
space.inspect,
|
79
|
+
fseg_id,
|
80
|
+
]
|
81
|
+
end
|
82
|
+
|
73
83
|
# Helper method to determine if an Inode is in use. Inodes that are not in
|
74
84
|
# use have an fseg_id of 0.
|
75
85
|
def allocated?
|
@@ -124,7 +134,7 @@ class Innodb::Inode
|
|
124
134
|
|
125
135
|
# Compare one Innodb::Inode to another.
|
126
136
|
def ==(other)
|
127
|
-
fseg_id == other.fseg_id
|
137
|
+
fseg_id == other.fseg_id if other
|
128
138
|
end
|
129
139
|
|
130
140
|
# Dump a summary of this object for debugging purposes.
|
data/lib/innodb/list.rb
CHANGED
@@ -109,8 +109,8 @@ class Innodb::List
|
|
109
109
|
end
|
110
110
|
|
111
111
|
# Return a list cursor for the list.
|
112
|
-
def list_cursor(node
|
113
|
-
ListCursor.new(self, node)
|
112
|
+
def list_cursor(node=:min, direction=:forward)
|
113
|
+
ListCursor.new(self, node, direction)
|
114
114
|
end
|
115
115
|
|
116
116
|
# Return whether the given item is present in the list. This depends on the
|
@@ -127,36 +127,50 @@ class Innodb::List
|
|
127
127
|
return enum_for(:each)
|
128
128
|
end
|
129
129
|
|
130
|
-
|
131
|
-
|
132
|
-
yield e
|
130
|
+
list_cursor.each_node do |node|
|
131
|
+
yield node
|
133
132
|
end
|
134
133
|
end
|
135
134
|
|
136
135
|
# A list iteration cursor used primarily by the Innodb::List #cursor method
|
137
136
|
# implicitly. Keeps its own state for iterating through lists efficiently.
|
138
137
|
class ListCursor
|
139
|
-
def initialize(list, node
|
140
|
-
@
|
141
|
-
@
|
138
|
+
def initialize(list, node=:min, direction=:forward)
|
139
|
+
@initial = true
|
140
|
+
@list = list
|
141
|
+
@direction = direction
|
142
|
+
|
143
|
+
case node
|
144
|
+
when :min
|
145
|
+
@node = @list.first
|
146
|
+
when :max
|
147
|
+
@node = @list.last
|
148
|
+
else
|
149
|
+
@node = node
|
150
|
+
end
|
142
151
|
end
|
143
152
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
153
|
+
def node
|
154
|
+
if @initial
|
155
|
+
@initial = false
|
156
|
+
return @node
|
157
|
+
end
|
158
|
+
|
159
|
+
case @direction
|
160
|
+
when :forward
|
161
|
+
next_node
|
162
|
+
when :backward
|
163
|
+
prev_node
|
164
|
+
end
|
149
165
|
end
|
150
166
|
|
151
167
|
# Return the previous entry from the current position, and advance the
|
152
168
|
# cursor position to the returned entry. If the cursor is currently nil,
|
153
169
|
# return the last entry in the list and adjust the cursor position to
|
154
170
|
# that entry.
|
155
|
-
def
|
156
|
-
if @
|
157
|
-
@
|
158
|
-
else
|
159
|
-
@cursor = @list.last
|
171
|
+
def prev_node
|
172
|
+
if node = @list.prev(@node)
|
173
|
+
@node = node
|
160
174
|
end
|
161
175
|
end
|
162
176
|
|
@@ -164,11 +178,19 @@ class Innodb::List
|
|
164
178
|
# cursor position to the returned entry. If the cursor is currently nil,
|
165
179
|
# return the first entry in the list and adjust the cursor position to
|
166
180
|
# that entry.
|
167
|
-
def
|
168
|
-
if @
|
169
|
-
@
|
170
|
-
|
171
|
-
|
181
|
+
def next_node
|
182
|
+
if node = @list.next(@node)
|
183
|
+
@node = node
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def each_node
|
188
|
+
unless block_given?
|
189
|
+
return enum_for(:each_node)
|
190
|
+
end
|
191
|
+
|
192
|
+
while n = node
|
193
|
+
yield n
|
172
194
|
end
|
173
195
|
end
|
174
196
|
end
|
data/lib/innodb/log.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
3
|
# An InnoDB transaction log file.
|
4
|
-
|
5
4
|
class Innodb::Log
|
6
5
|
# A map of the name and position of the blocks that form the log header.
|
7
6
|
LOG_HEADER_BLOCK_MAP = {
|
@@ -14,32 +13,39 @@ class Innodb::Log
|
|
14
13
|
# Number of blocks in the log file header.
|
15
14
|
LOG_HEADER_BLOCKS = LOG_HEADER_BLOCK_MAP.size
|
16
15
|
|
16
|
+
# The size in bytes of the log file header.
|
17
|
+
LOG_HEADER_SIZE = LOG_HEADER_BLOCKS * Innodb::LogBlock::BLOCK_SIZE
|
18
|
+
|
17
19
|
# Maximum number of log group checkpoints.
|
18
20
|
LOG_CHECKPOINT_GROUPS = 32
|
19
21
|
|
20
22
|
# Open a log file.
|
21
|
-
def initialize(
|
22
|
-
@file = File.open(
|
23
|
+
def initialize(filename)
|
24
|
+
@file = File.open(filename)
|
23
25
|
@size = @file.stat.size
|
24
26
|
@blocks = (@size / Innodb::LogBlock::BLOCK_SIZE) - LOG_HEADER_BLOCKS
|
27
|
+
@capacity = @blocks * Innodb::LogBlock::BLOCK_SIZE
|
25
28
|
end
|
26
29
|
|
27
30
|
# The size (in bytes) of the log.
|
28
31
|
attr_reader :size
|
29
32
|
|
30
|
-
# The
|
33
|
+
# The log capacity (in bytes).
|
34
|
+
attr_reader :capacity
|
35
|
+
|
36
|
+
# The number of blocks in the log.
|
31
37
|
attr_reader :blocks
|
32
38
|
|
33
39
|
# Get the raw byte buffer for a specific block by block offset.
|
34
40
|
def block_data(offset)
|
35
41
|
raise "Invalid block offset" unless (offset % Innodb::LogBlock::BLOCK_SIZE).zero?
|
36
|
-
@file.
|
37
|
-
@file.
|
42
|
+
@file.sysseek(offset)
|
43
|
+
@file.sysread(Innodb::LogBlock::BLOCK_SIZE)
|
38
44
|
end
|
39
45
|
|
40
46
|
# Get a cursor to a block in a given offset of the log.
|
41
47
|
def block_cursor(offset)
|
42
|
-
|
48
|
+
BufferCursor.new(block_data(offset), 0)
|
43
49
|
end
|
44
50
|
|
45
51
|
# Return the log header.
|
@@ -49,7 +55,8 @@ class Innodb::Log
|
|
49
55
|
{
|
50
56
|
:group_id => c.name("group_id") { c.get_uint32 },
|
51
57
|
:start_lsn => c.name("start_lsn") { c.get_uint64 },
|
52
|
-
:
|
58
|
+
:file_no => c.name("file_no") { c.get_uint32 },
|
59
|
+
:created_by => c.name("created_by") { c.get_string(32) }
|
53
60
|
}
|
54
61
|
end
|
55
62
|
end
|
@@ -62,7 +69,7 @@ class Innodb::Log
|
|
62
69
|
{
|
63
70
|
:number => c.name("number") { c.get_uint64 },
|
64
71
|
:lsn => c.name("lsn") { c.get_uint64 },
|
65
|
-
:
|
72
|
+
:lsn_offset => c.name("lsn_offset") { c.get_uint32 },
|
66
73
|
:buffer_size => c.name("buffer_size") { c.get_uint32 },
|
67
74
|
:archived_lsn => c.name("archived_lsn") { c.get_uint64 },
|
68
75
|
:group_array =>
|
@@ -88,10 +95,14 @@ class Innodb::Log
|
|
88
95
|
@checkpoint ||=
|
89
96
|
{
|
90
97
|
:checkpoint_1 => block_cursor(offset1).name("checkpoint_1") do |cursor|
|
91
|
-
read_checkpoint(cursor)
|
98
|
+
cp = read_checkpoint(cursor)
|
99
|
+
cp.delete(:group_array)
|
100
|
+
cp
|
92
101
|
end,
|
93
102
|
:checkpoint_2 => block_cursor(offset2).name("checkpoint_2") do |cursor|
|
94
|
-
read_checkpoint(cursor)
|
103
|
+
cp = read_checkpoint(cursor)
|
104
|
+
cp.delete(:group_array)
|
105
|
+
cp
|
95
106
|
end
|
96
107
|
}
|
97
108
|
end
|
data/lib/innodb/log_block.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
|
-
require "innodb/cursor"
|
4
|
-
require "pp"
|
5
|
-
|
6
3
|
# An InnoDB transaction log block.
|
7
4
|
class Innodb::LogBlock
|
8
5
|
# Log blocks are fixed-length at 512 bytes in InnoDB.
|
@@ -11,9 +8,21 @@ class Innodb::LogBlock
|
|
11
8
|
# Offset of the header within the log block.
|
12
9
|
HEADER_OFFSET = 0
|
13
10
|
|
11
|
+
# The size of the block header.
|
12
|
+
HEADER_SIZE = 4 + 2 + 2 + 4
|
13
|
+
|
14
14
|
# Offset of the trailer within ths log block.
|
15
15
|
TRAILER_OFFSET = BLOCK_SIZE - 4
|
16
16
|
|
17
|
+
# The size of the block trailer.
|
18
|
+
TRAILER_SIZE = 4
|
19
|
+
|
20
|
+
# Offset of the start of data in the block.
|
21
|
+
DATA_OFFSET = HEADER_SIZE
|
22
|
+
|
23
|
+
# Size of the space available for log records.
|
24
|
+
DATA_SIZE = BLOCK_SIZE - HEADER_SIZE - TRAILER_SIZE
|
25
|
+
|
17
26
|
# Mask used to get the flush bit in the header.
|
18
27
|
HEADER_FLUSH_BIT_MASK = 0x80000000
|
19
28
|
|
@@ -21,15 +30,15 @@ class Innodb::LogBlock
|
|
21
30
|
# log block contents.
|
22
31
|
def initialize(buffer)
|
23
32
|
unless buffer.size == BLOCK_SIZE
|
24
|
-
raise "Log block buffer provided was not #{BLOCK_SIZE} bytes"
|
33
|
+
raise "Log block buffer provided was not #{BLOCK_SIZE} bytes"
|
25
34
|
end
|
26
35
|
|
27
36
|
@buffer = buffer
|
28
37
|
end
|
29
38
|
|
30
|
-
# Return an
|
39
|
+
# Return an BufferCursor object positioned at a specific offset.
|
31
40
|
def cursor(offset)
|
32
|
-
|
41
|
+
BufferCursor.new(@buffer, offset)
|
33
42
|
end
|
34
43
|
|
35
44
|
# Return the log block header.
|
@@ -49,6 +58,22 @@ class Innodb::LogBlock
|
|
49
58
|
end
|
50
59
|
end
|
51
60
|
|
61
|
+
# Return a slice of actual block data (that is, excluding header and
|
62
|
+
# trailer) starting at the given offset.
|
63
|
+
def data(offset = DATA_OFFSET)
|
64
|
+
length = header[:data_length]
|
65
|
+
|
66
|
+
if length == BLOCK_SIZE
|
67
|
+
length -= TRAILER_SIZE
|
68
|
+
end
|
69
|
+
|
70
|
+
if offset < DATA_OFFSET || offset > length
|
71
|
+
raise "Invalid block data offset"
|
72
|
+
end
|
73
|
+
|
74
|
+
@buffer.slice(offset, length - offset)
|
75
|
+
end
|
76
|
+
|
52
77
|
# Return the log block trailer.
|
53
78
|
def trailer
|
54
79
|
@trailer ||= cursor(TRAILER_OFFSET).name("trailer") do |c|
|
@@ -58,79 +83,28 @@ class Innodb::LogBlock
|
|
58
83
|
end
|
59
84
|
end
|
60
85
|
|
61
|
-
#
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
18 => :PAGE_REORGANIZE,
|
76
|
-
19 => :PAGE_CREATE,
|
77
|
-
20 => :UNDO_INSERT,
|
78
|
-
21 => :UNDO_ERASE_END,
|
79
|
-
22 => :UNDO_INIT,
|
80
|
-
23 => :UNDO_HDR_DISCARD,
|
81
|
-
24 => :UNDO_HDR_REUSE,
|
82
|
-
25 => :UNDO_HDR_CREATE,
|
83
|
-
26 => :REC_MIN_MARK,
|
84
|
-
27 => :IBUF_BITMAP_INIT,
|
85
|
-
28 => :LSN,
|
86
|
-
29 => :INIT_FILE_PAGE,
|
87
|
-
30 => :WRITE_STRING,
|
88
|
-
31 => :MULTI_REC_END,
|
89
|
-
32 => :DUMMY_RECORD,
|
90
|
-
33 => :FILE_CREATE,
|
91
|
-
34 => :FILE_RENAME,
|
92
|
-
35 => :FILE_DELETE,
|
93
|
-
36 => :COMP_REC_MIN_MARK,
|
94
|
-
37 => :COMP_PAGE_CREATE,
|
95
|
-
38 => :COMP_REC_INSERT,
|
96
|
-
39 => :COMP_REC_CLUST_DELETE_MARK,
|
97
|
-
40 => :COMP_REC_SEC_DELETE_MARK,
|
98
|
-
41 => :COMP_REC_UPDATE_IN_PLACE,
|
99
|
-
42 => :COMP_REC_DELETE,
|
100
|
-
43 => :COMP_LIST_END_DELETE,
|
101
|
-
44 => :COMP_LIST_START_DELETE,
|
102
|
-
45 => :COMP_LIST_END_COPY_CREATE,
|
103
|
-
46 => :COMP_PAGE_REORGANIZE,
|
104
|
-
47 => :FILE_CREATE2,
|
105
|
-
48 => :ZIP_WRITE_NODE_PTR,
|
106
|
-
49 => :ZIP_WRITE_BLOB_PTR,
|
107
|
-
50 => :ZIP_WRITE_HEADER,
|
108
|
-
51 => :ZIP_PAGE_COMPRESS,
|
109
|
-
}
|
110
|
-
|
111
|
-
SINGLE_RECORD_MASK = 0x80
|
112
|
-
RECORD_TYPE_MASK = 0x7f
|
113
|
-
|
114
|
-
# Return a preamble of the first record in this block.
|
115
|
-
def first_record_preamble
|
116
|
-
return nil unless header[:first_rec_group] > 0
|
117
|
-
cursor(header[:first_rec_group]).name("header") do |c|
|
118
|
-
type_and_flag = c.name("type") { c.get_uint8 }
|
119
|
-
type = type_and_flag & RECORD_TYPE_MASK
|
120
|
-
type = RECORD_TYPES[type] || type
|
121
|
-
single_record = (type_and_flag & SINGLE_RECORD_MASK) > 0
|
122
|
-
case type
|
123
|
-
when :MULTI_REC_END, :DUMMY_RECORD
|
124
|
-
{ :type => type }
|
125
|
-
else
|
126
|
-
{
|
127
|
-
:type => type,
|
128
|
-
:single_record => single_record,
|
129
|
-
:space => c.name("space") { c.get_ic_uint32 },
|
130
|
-
:page_number => c.name("page_number") { c.get_ic_uint32 },
|
131
|
-
}
|
132
|
-
end
|
86
|
+
# A helper function to return the checksum from the trailer, for
|
87
|
+
# easier access.
|
88
|
+
def checksum
|
89
|
+
trailer[:checksum]
|
90
|
+
end
|
91
|
+
|
92
|
+
# Calculate the checksum of the block using InnoDB's log block
|
93
|
+
# checksum algorithm.
|
94
|
+
def calculate_checksum
|
95
|
+
cksum = 1
|
96
|
+
shift = (0..24).cycle
|
97
|
+
cursor(0).each_byte_as_uint8(TRAILER_OFFSET) do |b|
|
98
|
+
cksum &= 0x7fffffff
|
99
|
+
cksum += b + (b << shift.next)
|
133
100
|
end
|
101
|
+
cksum
|
102
|
+
end
|
103
|
+
|
104
|
+
# Is the block corrupt? Calculate the checksum of the block and compare to
|
105
|
+
# the stored checksum; return true or false.
|
106
|
+
def corrupt?
|
107
|
+
checksum != calculate_checksum
|
134
108
|
end
|
135
109
|
|
136
110
|
# Dump the contents of a log block for debugging purposes.
|
@@ -142,9 +116,5 @@ class Innodb::LogBlock
|
|
142
116
|
puts
|
143
117
|
puts "trailer:"
|
144
118
|
pp trailer
|
145
|
-
|
146
|
-
puts
|
147
|
-
puts "record:"
|
148
|
-
pp first_record_preamble
|
149
119
|
end
|
150
120
|
end
|
data/lib/innodb/log_group.rb
CHANGED
@@ -1,79 +1,84 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
|
+
# Group of InnoDB logs files that make up the redo log.
|
3
4
|
class Innodb::LogGroup
|
4
|
-
|
5
|
-
|
5
|
+
|
6
|
+
# Initialize group given a set of sorted log files.
|
7
|
+
def initialize(log_files)
|
8
|
+
@logs = log_files.map { |fn| Innodb::Log.new(fn) }
|
9
|
+
sizes = @logs.map { |log| log.size }
|
10
|
+
raise "Log file sizes do not match" unless sizes.uniq.size == 1
|
6
11
|
end
|
7
12
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
+
# Iterate through all logs.
|
14
|
+
def each_log
|
15
|
+
unless block_given?
|
16
|
+
return enum_for(:each_log)
|
17
|
+
end
|
18
|
+
|
19
|
+
@logs.each do |log|
|
20
|
+
yield log
|
13
21
|
end
|
14
22
|
end
|
15
23
|
|
24
|
+
# Iterate through all blocks.
|
16
25
|
def each_block
|
17
|
-
|
18
|
-
|
19
|
-
|
26
|
+
unless block_given?
|
27
|
+
return enum_for(:each_block)
|
28
|
+
end
|
29
|
+
|
30
|
+
each_log do |log|
|
31
|
+
log.each_block do |block_index, block|
|
32
|
+
yield block_index, block
|
20
33
|
end
|
21
34
|
end
|
22
35
|
end
|
23
36
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
37
|
+
# The number of log files in the group.
|
38
|
+
def logs
|
39
|
+
@logs.count
|
40
|
+
end
|
28
41
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
max_file = file_number
|
34
|
-
max_block = block_number
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
42
|
+
# Returns the log at the given position in the log group.
|
43
|
+
def log(log_no)
|
44
|
+
@logs.at(log_no)
|
45
|
+
end
|
38
46
|
|
39
|
-
|
47
|
+
# The size in byes of each and every log in the group.
|
48
|
+
def log_size
|
49
|
+
@logs.first.size
|
40
50
|
end
|
41
51
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
{ :file => 0, :block => 0 }
|
46
|
-
else
|
47
|
-
{ :file => position[:file] + 1, :block => 0 }
|
48
|
-
end
|
49
|
-
else
|
50
|
-
{ :file => position[:file], :block => position[:block] + 1 }
|
51
|
-
end
|
52
|
+
# The size of the log group (in bytes)
|
53
|
+
def size
|
54
|
+
@logs.first.size * @logs.count
|
52
55
|
end
|
53
56
|
|
54
|
-
|
55
|
-
|
57
|
+
# The log group capacity (in bytes).
|
58
|
+
def capacity
|
59
|
+
@logs.first.capacity * @logs.count
|
56
60
|
end
|
57
61
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
if new_block.header[:block] >= old_block.header[:block]
|
62
|
-
new_block
|
63
|
-
end
|
62
|
+
# Returns the LSN coordinates of the data at the start of the log group.
|
63
|
+
def start_lsn
|
64
|
+
[@logs.first.header[:start_lsn], Innodb::Log::LOG_HEADER_SIZE]
|
64
65
|
end
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
67
|
+
# Returns the LSN coordinates of the most recent (highest) checkpoint.
|
68
|
+
def max_checkpoint_lsn
|
69
|
+
checkpoint = @logs.first.checkpoint.max_by{|f,v| v[:number]}.last
|
70
|
+
checkpoint.values_at(:lsn, :lsn_offset)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns a LogReader using the given LSN reference coordinates.
|
74
|
+
def reader(lsn_coord = start_lsn)
|
75
|
+
lsn_no, lsn_offset = lsn_coord
|
76
|
+
lsn = Innodb::LSN.new(lsn_no, lsn_offset)
|
77
|
+
Innodb::LogReader.new(lsn, self)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Parse and return a record at a given LSN.
|
81
|
+
def record(lsn_no)
|
82
|
+
reader.seek(lsn_no).record
|
78
83
|
end
|
79
84
|
end
|