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/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
|