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/page/inode.rb
CHANGED
@@ -28,6 +28,10 @@ class Innodb::Page::Inode < Innodb::Page
|
|
28
28
|
(size - pos_inode_array - 10) / Innodb::Inode::SIZE
|
29
29
|
end
|
30
30
|
|
31
|
+
def size_inode_array
|
32
|
+
inodes_per_page * Innodb::Inode::SIZE
|
33
|
+
end
|
34
|
+
|
31
35
|
# Return the list entry.
|
32
36
|
def list_entry
|
33
37
|
cursor(pos_list_entry).name("list") do |c|
|
@@ -50,7 +54,7 @@ class Innodb::Page::Inode < Innodb::Page
|
|
50
54
|
# Read a single Inode entry from the provided byte offset by creating a
|
51
55
|
# cursor and reading the inode using the inode method.
|
52
56
|
def inode_at(cursor)
|
53
|
-
cursor.name("inode") { |c| Innodb::Inode.new_from_cursor(@space, c) }
|
57
|
+
cursor.name("inode[#{cursor.position}]") { |c| Innodb::Inode.new_from_cursor(@space, c) }
|
54
58
|
end
|
55
59
|
|
56
60
|
# Iterate through all Inodes in the inode array.
|
@@ -68,6 +72,34 @@ class Innodb::Page::Inode < Innodb::Page
|
|
68
72
|
end
|
69
73
|
end
|
70
74
|
|
75
|
+
def each_region
|
76
|
+
unless block_given?
|
77
|
+
return enum_for(:each_region)
|
78
|
+
end
|
79
|
+
|
80
|
+
super do |region|
|
81
|
+
yield region
|
82
|
+
end
|
83
|
+
|
84
|
+
yield({
|
85
|
+
:offset => pos_list_entry,
|
86
|
+
:length => size_list_entry,
|
87
|
+
:name => :list_entry,
|
88
|
+
:info => "Inode List Entry",
|
89
|
+
})
|
90
|
+
|
91
|
+
each_inode do |inode|
|
92
|
+
yield({
|
93
|
+
:offset => inode.offset,
|
94
|
+
:length => Innodb::Inode::SIZE,
|
95
|
+
:name => :inode,
|
96
|
+
:info => "Inode",
|
97
|
+
})
|
98
|
+
end
|
99
|
+
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
71
103
|
# Dump the contents of a page for debugging purposes.
|
72
104
|
def dump
|
73
105
|
super
|
@@ -41,6 +41,25 @@ class Innodb::Page::SysDataDictionaryHeader < Innodb::Page
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
def each_region
|
45
|
+
unless block_given?
|
46
|
+
return enum_for(:each_region)
|
47
|
+
end
|
48
|
+
|
49
|
+
super do |region|
|
50
|
+
yield region
|
51
|
+
end
|
52
|
+
|
53
|
+
yield({
|
54
|
+
:offset => pos_data_dictionary_header,
|
55
|
+
:length => size_data_dictionary_header,
|
56
|
+
:name => :data_dictionary_header,
|
57
|
+
:info => "Data Dictionary Header",
|
58
|
+
})
|
59
|
+
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
44
63
|
def dump
|
45
64
|
super
|
46
65
|
|
@@ -1,6 +1,9 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
3
|
class Innodb::Page::SysRsegHeader < Innodb::Page
|
4
|
+
# The number of undo log slots in the page.
|
5
|
+
UNDO_SEGMENT_SLOTS = 1024
|
6
|
+
|
4
7
|
# The position of the rollback segment header within the page.
|
5
8
|
def pos_rseg_header
|
6
9
|
pos_fil_header + size_fil_header
|
@@ -15,6 +18,10 @@ class Innodb::Page::SysRsegHeader < Innodb::Page
|
|
15
18
|
pos_rseg_header + size_rseg_header
|
16
19
|
end
|
17
20
|
|
21
|
+
def size_undo_segment_slot
|
22
|
+
4
|
23
|
+
end
|
24
|
+
|
18
25
|
# Parse the rollback segment header from the page.
|
19
26
|
def rseg_header
|
20
27
|
cursor(pos_rseg_header).name("rseg_header") do |c|
|
@@ -29,21 +36,53 @@ class Innodb::Page::SysRsegHeader < Innodb::Page
|
|
29
36
|
end
|
30
37
|
end
|
31
38
|
|
39
|
+
def history_list
|
40
|
+
Innodb::HistoryList.new(rseg_header[:history_list])
|
41
|
+
end
|
42
|
+
|
32
43
|
def each_undo_segment
|
33
44
|
unless block_given?
|
34
45
|
return enum_for(:each_undo_segment)
|
35
46
|
end
|
36
47
|
|
37
48
|
cursor(pos_undo_segment_array).name("undo_segment_array") do |c|
|
38
|
-
(0...
|
49
|
+
(0...UNDO_SEGMENT_SLOTS).each do |slot|
|
39
50
|
page_number = c.name("slot[#{slot}]") {
|
40
51
|
Innodb::Page.maybe_undefined(c.get_uint32)
|
41
52
|
}
|
42
|
-
yield slot, page_number
|
53
|
+
yield slot, page_number
|
43
54
|
end
|
44
55
|
end
|
45
56
|
end
|
46
57
|
|
58
|
+
def each_region
|
59
|
+
unless block_given?
|
60
|
+
return enum_for(:each_region)
|
61
|
+
end
|
62
|
+
|
63
|
+
super do |region|
|
64
|
+
yield region
|
65
|
+
end
|
66
|
+
|
67
|
+
yield({
|
68
|
+
:offset => pos_rseg_header,
|
69
|
+
:length => size_rseg_header,
|
70
|
+
:name => :rseg_header,
|
71
|
+
:info => "Rollback Segment Header",
|
72
|
+
})
|
73
|
+
|
74
|
+
(0...UNDO_SEGMENT_SLOTS).each do |slot|
|
75
|
+
yield({
|
76
|
+
:offset => pos_undo_segment_array + (slot * size_undo_segment_slot),
|
77
|
+
:length => size_undo_segment_slot,
|
78
|
+
:name => :undo_segment_slot,
|
79
|
+
:info => "Undo Segment Slot",
|
80
|
+
})
|
81
|
+
end
|
82
|
+
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
|
47
86
|
def dump
|
48
87
|
super
|
49
88
|
|
data/lib/innodb/page/trx_sys.rb
CHANGED
@@ -15,6 +15,18 @@ class Innodb::Page::TrxSys < Innodb::Page
|
|
15
15
|
pos_fil_header + size_fil_header
|
16
16
|
end
|
17
17
|
|
18
|
+
def size_trx_sys_header
|
19
|
+
8 + Innodb::FsegEntry::SIZE
|
20
|
+
end
|
21
|
+
|
22
|
+
def pos_rsegs_array
|
23
|
+
pos_trx_sys_header + size_trx_sys_header
|
24
|
+
end
|
25
|
+
|
26
|
+
def size_mysql_log_info
|
27
|
+
4 + 8 + 100
|
28
|
+
end
|
29
|
+
|
18
30
|
# The master's binary log information is located 2000 bytes from the end of
|
19
31
|
# the page.
|
20
32
|
def pos_mysql_master_log_info
|
@@ -33,14 +45,21 @@ class Innodb::Page::TrxSys < Innodb::Page
|
|
33
45
|
size - 200
|
34
46
|
end
|
35
47
|
|
48
|
+
def size_doublewrite_info
|
49
|
+
Innodb::FsegEntry::SIZE + (2 * (4 + 4 + 4)) + 4
|
50
|
+
end
|
51
|
+
|
36
52
|
# A magic number present in each MySQL binary log information structure,
|
37
53
|
# which helps identify whether the structure is populated or not.
|
38
54
|
MYSQL_LOG_MAGIC_N = 873422344
|
39
55
|
|
56
|
+
N_RSEGS = 128
|
57
|
+
|
40
58
|
def rsegs_array(cursor)
|
41
|
-
@rsegs_array ||= (0...
|
59
|
+
@rsegs_array ||= (0...N_RSEGS).to_a.inject([]) do |a, n|
|
42
60
|
cursor.name("slot[#{n}]") do |c|
|
43
61
|
slot = {
|
62
|
+
:offset => c.position,
|
44
63
|
:space_id => c.name("space_id") {
|
45
64
|
Innodb::Page.maybe_undefined(c.get_uint32)
|
46
65
|
},
|
@@ -134,6 +153,55 @@ class Innodb::Page::TrxSys < Innodb::Page
|
|
134
153
|
def master_log; trx_sys[:master_log]; end
|
135
154
|
def doublewrite; trx_sys[:doublewrite]; end
|
136
155
|
|
156
|
+
def each_region
|
157
|
+
unless block_given?
|
158
|
+
return enum_for(:each_region)
|
159
|
+
end
|
160
|
+
|
161
|
+
super do |region|
|
162
|
+
yield region
|
163
|
+
end
|
164
|
+
|
165
|
+
yield({
|
166
|
+
:offset => pos_trx_sys_header,
|
167
|
+
:length => size_trx_sys_header,
|
168
|
+
:name => :trx_sys_header,
|
169
|
+
:info => "Transaction System Header",
|
170
|
+
})
|
171
|
+
|
172
|
+
rsegs.each do |rseg|
|
173
|
+
yield({
|
174
|
+
:offset => rseg[:offset],
|
175
|
+
:length => 4 + 4,
|
176
|
+
:name => :rseg,
|
177
|
+
:info => "Rollback Segment",
|
178
|
+
})
|
179
|
+
end
|
180
|
+
|
181
|
+
yield({
|
182
|
+
:offset => pos_mysql_binary_log_info,
|
183
|
+
:length => size_mysql_log_info,
|
184
|
+
:name => :mysql_binary_log_info,
|
185
|
+
:info => "Binary Log Info",
|
186
|
+
})
|
187
|
+
|
188
|
+
yield({
|
189
|
+
:offset => pos_mysql_master_log_info,
|
190
|
+
:length => size_mysql_log_info,
|
191
|
+
:name => :mysql_master_log_info,
|
192
|
+
:info => "Master Log Info",
|
193
|
+
})
|
194
|
+
|
195
|
+
yield({
|
196
|
+
:offset => pos_doublewrite_info,
|
197
|
+
:length => size_doublewrite_info,
|
198
|
+
:name => :doublewrite_info,
|
199
|
+
:info => "Double Write Buffer Info",
|
200
|
+
})
|
201
|
+
|
202
|
+
nil
|
203
|
+
end
|
204
|
+
|
137
205
|
# Dump the contents of a page for debugging purposes.
|
138
206
|
def dump
|
139
207
|
super
|
data/lib/innodb/record.rb
CHANGED
@@ -17,6 +17,10 @@ class Innodb::Record
|
|
17
17
|
record[:offset]
|
18
18
|
end
|
19
19
|
|
20
|
+
def length
|
21
|
+
record[:length]
|
22
|
+
end
|
23
|
+
|
20
24
|
def next
|
21
25
|
record[:next]
|
22
26
|
end
|
@@ -37,6 +41,39 @@ class Innodb::Record
|
|
37
41
|
row && row.map { |r| "%s=%s" % [r[:name], r[:value].inspect] }.join(", ")
|
38
42
|
end
|
39
43
|
|
44
|
+
def transaction_id
|
45
|
+
record[:transaction_id]
|
46
|
+
end
|
47
|
+
|
48
|
+
def roll_pointer
|
49
|
+
record[:roll_pointer]
|
50
|
+
end
|
51
|
+
|
52
|
+
def undo
|
53
|
+
if innodb_system = @page.space.innodb_system
|
54
|
+
undo_space = innodb_system.system_space
|
55
|
+
if undo_page = undo_space.page(roll_pointer[:undo_log][:page])
|
56
|
+
new_undo_record = Innodb::UndoRecord.new(undo_page, roll_pointer[:undo_log][:offset])
|
57
|
+
new_undo_record.index_page = page
|
58
|
+
new_undo_record
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def each_undo_record
|
64
|
+
unless block_given?
|
65
|
+
return enum_for(:each_undo_record)
|
66
|
+
end
|
67
|
+
|
68
|
+
undo_record = undo
|
69
|
+
while undo_record
|
70
|
+
yield undo_record
|
71
|
+
undo_record = undo_record.prev_by_history
|
72
|
+
end
|
73
|
+
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
40
77
|
def child_page_number
|
41
78
|
record[:child_page_number]
|
42
79
|
end
|
data/lib/innodb/space.rb
CHANGED
@@ -54,6 +54,29 @@ class Innodb::Space
|
|
54
54
|
# The number of pages in the space.
|
55
55
|
attr_reader :pages
|
56
56
|
|
57
|
+
# Return a string which can uniquely identify this space. Be careful not
|
58
|
+
# to do anything which could instantiate a BufferCursor so that we can use
|
59
|
+
# this method in cursor initialization.
|
60
|
+
def name
|
61
|
+
return @name if @name
|
62
|
+
|
63
|
+
prefix = ""
|
64
|
+
if File.extname(@file.path) == ".ibd"
|
65
|
+
prefix = File.basename(File.dirname(@file.path)) + "/"
|
66
|
+
end
|
67
|
+
|
68
|
+
@name = prefix + File.basename(@file.path)
|
69
|
+
end
|
70
|
+
|
71
|
+
def inspect
|
72
|
+
"<%s file=%s, page_size=%i, pages=%i>" % [
|
73
|
+
self.class.name,
|
74
|
+
name.inspect,
|
75
|
+
page_size,
|
76
|
+
pages,
|
77
|
+
]
|
78
|
+
end
|
79
|
+
|
57
80
|
# Read the FSP header "flags" field by byte offset within the space file.
|
58
81
|
# This is useful in order to initialize the page size, as we can't properly
|
59
82
|
# read the FSP_HDR page before we know its size.
|
@@ -190,10 +213,11 @@ class Innodb::Space
|
|
190
213
|
|
191
214
|
def rseg_page?(page_number)
|
192
215
|
if trx_sys
|
193
|
-
trx_sys.rsegs.
|
194
|
-
:space_id
|
195
|
-
|
196
|
-
|
216
|
+
rseg_match = trx_sys.rsegs.select { |rseg|
|
217
|
+
rseg[:space_id] == 0 && rseg[:page_number] == page_number
|
218
|
+
}
|
219
|
+
|
220
|
+
! rseg_match.empty?
|
197
221
|
end
|
198
222
|
end
|
199
223
|
|
data/lib/innodb/system.rb
CHANGED
data/lib/innodb/undo_log.rb
CHANGED
@@ -16,24 +16,24 @@ class Innodb::UndoLog
|
|
16
16
|
8 + 8 + 2 + 2 + 1 + 1 + 8 + 2 + 2 + Innodb::List::NODE_SIZE + size_xa_header
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
cursor.name("header") do |c|
|
21
|
-
|
19
|
+
def header
|
20
|
+
@header ||= page.cursor(@position).name("header") do |c|
|
21
|
+
xid_flag = nil
|
22
22
|
{
|
23
23
|
:trx_id => c.name("trx_id") { c.get_hex(8) },
|
24
|
-
:trx_no => c.name("trx_no") { c.
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
24
|
+
:trx_no => c.name("trx_no") { c.get_hex(8) },
|
25
|
+
:delete_mark_flag => c.name("delete_mark_flag") { (c.get_uint16 != 0) },
|
26
|
+
:log_start_offset => c.name("log_start_offset") { c.get_uint16 },
|
27
|
+
:xid_flag => c.name("xid_flag") { xid_flag = (c.get_uint8 != 0) },
|
28
|
+
:ddl_flag => c.name("ddl_flag") { (c.get_uint8 != 0) },
|
29
|
+
:ddl_table_id => c.name("ddl_table_id") { c.get_uint64 },
|
30
|
+
:next_log_offset => c.name("next_log_offset") { c.get_uint16 },
|
31
|
+
:prev_log_offset => c.name("prev_log_offset") { c.get_uint16 },
|
32
32
|
:history_list_node => c.name("history_list_node") {
|
33
33
|
Innodb::List.get_node(c)
|
34
34
|
},
|
35
35
|
:xid => c.name("xid") {
|
36
|
-
if
|
36
|
+
if xid_flag
|
37
37
|
{
|
38
38
|
:format => c.name("format") { c.get_uint32 },
|
39
39
|
:trid_len => c.name("trid_len") { c.get_uint32 },
|
@@ -46,18 +46,6 @@ class Innodb::UndoLog
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
def undo_log
|
50
|
-
@undo_log ||= page.cursor(position).name("undo_log") do |c|
|
51
|
-
{
|
52
|
-
:header => read_header(c),
|
53
|
-
}
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def header
|
58
|
-
undo_log[:header]
|
59
|
-
end
|
60
|
-
|
61
49
|
def prev_address
|
62
50
|
header[:history_list_node][:prev]
|
63
51
|
end
|
@@ -66,6 +54,78 @@ class Innodb::UndoLog
|
|
66
54
|
header[:history_list_node][:next]
|
67
55
|
end
|
68
56
|
|
57
|
+
def undo_record(offset)
|
58
|
+
new_undo_record = Innodb::UndoRecord.new(page, offset)
|
59
|
+
new_undo_record.undo_log = self
|
60
|
+
new_undo_record
|
61
|
+
end
|
62
|
+
|
63
|
+
def min_undo_record
|
64
|
+
undo_record(header[:log_start_offset])
|
65
|
+
end
|
66
|
+
|
67
|
+
class UndoRecordCursor
|
68
|
+
def initialize(undo_log, offset, direction=:forward)
|
69
|
+
@initial = true
|
70
|
+
@undo_log = undo_log
|
71
|
+
@offset = offset
|
72
|
+
@direction = direction
|
73
|
+
|
74
|
+
case offset
|
75
|
+
when :min
|
76
|
+
@undo_record = @undo_log.min_undo_record
|
77
|
+
when :max
|
78
|
+
raise "Not implemented"
|
79
|
+
else
|
80
|
+
@undo_record = @undo_log.undo_record(offset)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def next_undo_record
|
85
|
+
if rec = @undo_record.next
|
86
|
+
@undo_record = rec
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def prev_undo_record
|
91
|
+
if rec = @undo_record.prev
|
92
|
+
@undo_record = rec
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def undo_record
|
97
|
+
if @initial
|
98
|
+
@initial = false
|
99
|
+
return @undo_record
|
100
|
+
end
|
101
|
+
|
102
|
+
case @direction
|
103
|
+
when :forward
|
104
|
+
next_undo_record
|
105
|
+
when :backward
|
106
|
+
prev_undo_record
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def each_undo_record
|
111
|
+
unless block_given?
|
112
|
+
return enum_for(:each_undo_record)
|
113
|
+
end
|
114
|
+
|
115
|
+
while rec = undo_record
|
116
|
+
yield rec
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def undo_record_cursor(offset, direction=:forward)
|
122
|
+
UndoRecordCursor.new(self, offset, direction)
|
123
|
+
end
|
124
|
+
|
125
|
+
def first_undo_record_cursor
|
126
|
+
undo_record_cursor(header[:log_start_offset])
|
127
|
+
end
|
128
|
+
|
69
129
|
def dump
|
70
130
|
puts "header:"
|
71
131
|
pp header
|