innodb_ruby 0.9.0 → 0.9.5
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/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
|