innodb_ruby 0.7.11 → 0.7.12
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_space +162 -25
- data/lib/innodb.rb +8 -1
- data/lib/innodb/checksum.rb +30 -0
- data/lib/innodb/cursor.rb +165 -37
- data/lib/innodb/field.rb +31 -57
- data/lib/innodb/field_type.rb +124 -0
- data/lib/innodb/fseg_entry.rb +9 -6
- data/lib/innodb/index.rb +23 -12
- data/lib/innodb/inode.rb +133 -0
- data/lib/innodb/list.rb +44 -12
- data/lib/innodb/log.rb +1 -0
- data/lib/innodb/log_block.rb +2 -1
- data/lib/innodb/page.rb +93 -17
- data/lib/innodb/page/blob.rb +60 -0
- data/lib/innodb/page/fsp_hdr_xdes.rb +34 -24
- data/lib/innodb/page/index.rb +364 -190
- data/lib/innodb/page/inode.rb +20 -51
- data/lib/innodb/page/sys.rb +22 -0
- data/lib/innodb/page/sys_data_dictionary_header.rb +92 -0
- data/lib/innodb/page/sys_rseg_header.rb +59 -0
- data/lib/innodb/page/trx_sys.rb +72 -29
- data/lib/innodb/page/undo_log.rb +95 -0
- data/lib/innodb/record_describer.rb +2 -1
- data/lib/innodb/space.rb +162 -17
- data/lib/innodb/undo_log.rb +73 -0
- data/lib/innodb/version.rb +2 -1
- data/lib/innodb/xdes.rb +44 -11
- metadata +19 -6
data/lib/innodb/page/inode.rb
CHANGED
@@ -1,27 +1,17 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require "innodb/list"
|
2
3
|
|
3
4
|
# A specialized class for handling INODE pages, which contain index FSEG (file
|
4
5
|
# segment) information. This allows all extents and individual pages assigned
|
5
6
|
# to each index to be found.
|
6
7
|
class Innodb::Page::Inode < Innodb::Page
|
7
|
-
# The number of "slots" (each representing one page) in the fragment array
|
8
|
-
# within each Inode entry.
|
9
|
-
FRAG_ARRAY_N_SLOTS = 32 # FSP_EXTENT_SIZE / 2
|
10
|
-
|
11
|
-
# The size (in bytes) of each slot in the fragment array.
|
12
|
-
FRAG_SLOT_SIZE = 4
|
13
|
-
|
14
|
-
# A magic number which helps determine if an Inode structure is in use
|
15
|
-
# and populated with valid data.
|
16
|
-
MAGIC_N_VALUE = 97937874
|
17
|
-
|
18
8
|
# Return the byte offset of the list node, which immediately follows the
|
19
9
|
# FIL header.
|
20
10
|
def pos_list_entry
|
21
11
|
pos_fil_header + size_fil_header
|
22
12
|
end
|
23
13
|
|
24
|
-
# Return the
|
14
|
+
# Return the size of the list node.
|
25
15
|
def size_list_entry
|
26
16
|
Innodb::List::NODE_SIZE
|
27
17
|
end
|
@@ -32,21 +22,16 @@ class Innodb::Page::Inode < Innodb::Page
|
|
32
22
|
pos_list_entry + size_list_entry
|
33
23
|
end
|
34
24
|
|
35
|
-
# The size (in bytes) of an Inode entry.
|
36
|
-
def size_inode
|
37
|
-
(16 + (3 * Innodb::List::BASE_NODE_SIZE) +
|
38
|
-
(FRAG_ARRAY_N_SLOTS * FRAG_SLOT_SIZE))
|
39
|
-
end
|
40
|
-
|
41
25
|
# The number of Inode entries that fit on a page.
|
42
26
|
def inodes_per_page
|
43
|
-
(size - pos_inode_array - 10) /
|
27
|
+
(size - pos_inode_array - 10) / Innodb::Inode::SIZE
|
44
28
|
end
|
45
29
|
|
46
30
|
# Return the list entry.
|
47
31
|
def list_entry
|
48
|
-
|
49
|
-
|
32
|
+
cursor(pos_list_entry).name("list") do |c|
|
33
|
+
Innodb::List.get_node(c)
|
34
|
+
end
|
50
35
|
end
|
51
36
|
|
52
37
|
# Return the "previous" address pointer from the list entry. This is used
|
@@ -61,40 +46,24 @@ class Innodb::Page::Inode < Innodb::Page
|
|
61
46
|
list_entry[:next]
|
62
47
|
end
|
63
48
|
|
64
|
-
# Read an array of page numbers (32-bit integers, which may be nil) from
|
65
|
-
# the provided cursor.
|
66
|
-
def page_number_array(size, cursor)
|
67
|
-
size.times.map { |n| Innodb::Page.maybe_undefined(cursor.get_uint32) }
|
68
|
-
end
|
69
|
-
|
70
|
-
# Read a single Inode entry from the provided cursor.
|
71
|
-
def inode(cursor)
|
72
|
-
{
|
73
|
-
:fseg_id => cursor.get_uint64,
|
74
|
-
:not_full_n_used => cursor.get_uint32,
|
75
|
-
:free => Innodb::List::Xdes.new(@space,
|
76
|
-
Innodb::List.get_base_node(cursor)),
|
77
|
-
:not_full => Innodb::List::Xdes.new(@space,
|
78
|
-
Innodb::List.get_base_node(cursor)),
|
79
|
-
:full => Innodb::List::Xdes.new(@space,
|
80
|
-
Innodb::List.get_base_node(cursor)),
|
81
|
-
:magic_n => cursor.get_uint32,
|
82
|
-
:frag_array => page_number_array(FRAG_ARRAY_N_SLOTS, cursor),
|
83
|
-
}
|
84
|
-
end
|
85
|
-
|
86
49
|
# Read a single Inode entry from the provided byte offset by creating a
|
87
50
|
# cursor and reading the inode using the inode method.
|
88
|
-
def inode_at(
|
89
|
-
|
51
|
+
def inode_at(cursor)
|
52
|
+
cursor.name("inode") { |c| Innodb::Inode.new_from_cursor(@space, c) }
|
90
53
|
end
|
91
54
|
|
92
55
|
# Iterate through all Inodes in the inode array.
|
93
56
|
def each_inode
|
57
|
+
unless block_given?
|
58
|
+
return enum_for(:each_inode)
|
59
|
+
end
|
60
|
+
|
94
61
|
inode_cursor = cursor(pos_inode_array)
|
95
|
-
inodes_per_page.times do
|
96
|
-
|
97
|
-
|
62
|
+
inodes_per_page.times do |n|
|
63
|
+
inode_cursor.name("inode[#{n}]") do |c|
|
64
|
+
this_inode = Innodb::Inode.new_from_cursor(@space, c)
|
65
|
+
yield this_inode if this_inode.allocated?
|
66
|
+
end
|
98
67
|
end
|
99
68
|
end
|
100
69
|
|
@@ -107,11 +76,11 @@ class Innodb::Page::Inode < Innodb::Page
|
|
107
76
|
puts
|
108
77
|
|
109
78
|
puts "inodes:"
|
110
|
-
each_inode do |
|
111
|
-
|
79
|
+
each_inode do |inode|
|
80
|
+
inode.dump
|
112
81
|
end
|
113
82
|
puts
|
114
83
|
end
|
115
84
|
end
|
116
85
|
|
117
|
-
Innodb::Page::SPECIALIZED_CLASSES[:INODE] = Innodb::Page::Inode
|
86
|
+
Innodb::Page::SPECIALIZED_CLASSES[:INODE] = Innodb::Page::Inode
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require "innodb/page/sys_rseg_header"
|
3
|
+
require "innodb/page/sys_data_dictionary_header"
|
4
|
+
|
5
|
+
# Another layer of indirection for pages of type SYS, as they have multiple
|
6
|
+
# uses within InnoDB. We'll override the self.handle method and check the
|
7
|
+
# page offset to decide which type of SYS page this is.
|
8
|
+
class Innodb::Page::Sys < Innodb::Page
|
9
|
+
def self.handle(page, space, buffer)
|
10
|
+
case
|
11
|
+
when page.offset == 7
|
12
|
+
Innodb::Page::SysDataDictionaryHeader.new(space, buffer)
|
13
|
+
when space.rseg_page?(page.offset)
|
14
|
+
Innodb::Page::SysRsegHeader.new(space, buffer)
|
15
|
+
else
|
16
|
+
# We can't do anything better, so pass on the generic InnoDB::Page.
|
17
|
+
page
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Innodb::Page::SPECIALIZED_CLASSES[:SYS] = Innodb::Page::Sys
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
class Innodb::Page::SysDataDictionaryHeader < Innodb::Page
|
3
|
+
# The position of the data dictionary header within the page.
|
4
|
+
def pos_data_dictionary_header
|
5
|
+
pos_fil_header + size_fil_header
|
6
|
+
end
|
7
|
+
|
8
|
+
# The size of the data dictionary header.
|
9
|
+
def size_data_dictionary_header
|
10
|
+
((8 * 3) + (4 * 7) + 4 + Innodb::FsegEntry::SIZE)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Parse the data dictionary header from the page.
|
14
|
+
def data_dictionary_header
|
15
|
+
cursor(pos_data_dictionary_header).name("data_dictionary_header") do |c|
|
16
|
+
{
|
17
|
+
:max_row_id => c.name("max_row_id") { c.get_uint64 },
|
18
|
+
:max_table_id => c.name("max_table_id") { c.get_uint64 },
|
19
|
+
:max_index_id => c.name("max_index_id") { c.get_uint64 },
|
20
|
+
:max_space_id => c.name("max_space_id") { c.get_uint32 },
|
21
|
+
:unused_mix_id_low => c.name("unused_mix_id_low") { c.get_uint32 },
|
22
|
+
:indexes => c.name("indexes") {{
|
23
|
+
:SYS_TABLES => c.name("SYS_TABLES") {{
|
24
|
+
:PRIMARY => c.name("PRIMARY") { c.get_uint32 },
|
25
|
+
:ID => c.name("ID") { c.get_uint32 },
|
26
|
+
}},
|
27
|
+
:SYS_COLUMNS => c.name("SYS_COLUMNS") {{
|
28
|
+
:PRIMARY => c.name("PRIMARY") { c.get_uint32 },
|
29
|
+
}},
|
30
|
+
:SYS_INDEXES => c.name("SYS_INDEXES") {{
|
31
|
+
:PRIMARY => c.name("PRIMARY") { c.get_uint32 },
|
32
|
+
}},
|
33
|
+
:SYS_FIELDS => c.name("SYS_FIELDS") {{
|
34
|
+
:PRIMARY => c.name("PRIMARY") { c.get_uint32 },
|
35
|
+
}}
|
36
|
+
}},
|
37
|
+
:unused_space => c.name("unused_space") { c.get_bytes(4) },
|
38
|
+
:fseg => c.name("fseg") { Innodb::FsegEntry.get_inode(@space, c) },
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Iterate through all indexes in the data dictionary, yielding the table
|
44
|
+
# name, index name, and the index itself as an Innodb::Index.
|
45
|
+
def each_index
|
46
|
+
unless block_given?
|
47
|
+
return enum_for(:each_index)
|
48
|
+
end
|
49
|
+
|
50
|
+
data_dictionary_header[:indexes].each do |table_name, indexes|
|
51
|
+
indexes.each do |index_name, root_page_number|
|
52
|
+
yield table_name, index_name, @space.index(root_page_number)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def dump
|
58
|
+
super
|
59
|
+
|
60
|
+
puts
|
61
|
+
puts "data_dictionary header:"
|
62
|
+
pp data_dictionary_header
|
63
|
+
end
|
64
|
+
|
65
|
+
# A record describer for SYS_TABLES clustered records.
|
66
|
+
class SYS_TABLES_PRIMARY
|
67
|
+
def self.cursor_sendable_description(page)
|
68
|
+
{
|
69
|
+
:type => :clustered,
|
70
|
+
:key => [
|
71
|
+
["VARCHAR(100)", :NOT_NULL], # NAME
|
72
|
+
],
|
73
|
+
:row => [
|
74
|
+
[:BIGINT, :UNSIGNED, :NOT_NULL], # ID
|
75
|
+
[:INT, :UNSIGNED, :NOT_NULL], # N_COLS
|
76
|
+
[:INT, :UNSIGNED, :NOT_NULL], # TYPE
|
77
|
+
[:BIGINT, :UNSIGNED, :NOT_NULL], # MIX_ID
|
78
|
+
[:INT, :UNSIGNED, :NOT_NULL], # MIX_LEN
|
79
|
+
["VARCHAR(100)"], # CLUSTER_NAME
|
80
|
+
[:INT, :UNSIGNED, :NOT_NULL], # SPACE
|
81
|
+
]
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
RECORD_DESCRIBERS = {
|
87
|
+
:SYS_TABLES => { :PRIMARY => SYS_TABLES_PRIMARY, :ID => nil },
|
88
|
+
:SYS_COLUMNS => { :PRIMARY => nil },
|
89
|
+
:SYS_INDEXES => { :PRIMARY => nil },
|
90
|
+
:SYS_FIELDS => { :PRIMARY => nil },
|
91
|
+
}
|
92
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
class Innodb::Page::SysRsegHeader < Innodb::Page
|
3
|
+
# The position of the rollback segment header within the page.
|
4
|
+
def pos_rseg_header
|
5
|
+
pos_fil_header + size_fil_header
|
6
|
+
end
|
7
|
+
|
8
|
+
# The size of the rollback segment header.
|
9
|
+
def size_rseg_header
|
10
|
+
4 + 4 + Innodb::List::BASE_NODE_SIZE + Innodb::FsegEntry::SIZE
|
11
|
+
end
|
12
|
+
|
13
|
+
def pos_undo_segment_array
|
14
|
+
pos_rseg_header + size_rseg_header
|
15
|
+
end
|
16
|
+
|
17
|
+
# Parse the rollback segment header from the page.
|
18
|
+
def rseg_header
|
19
|
+
cursor(pos_rseg_header).name("rseg_header") do |c|
|
20
|
+
{
|
21
|
+
:max_size => c.name("max_size") { c.get_uint32 },
|
22
|
+
:history_size => c.name("history_size") { c.get_uint32 },
|
23
|
+
:history_list => c.name("history_list") {
|
24
|
+
Innodb::List::History.new(@space, Innodb::List.get_base_node(c))
|
25
|
+
},
|
26
|
+
:fseg => c.name("fseg") { Innodb::FsegEntry.get_inode(@space, c) },
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def each_undo_segment
|
32
|
+
unless block_given?
|
33
|
+
return enum_for(:each_undo_segment)
|
34
|
+
end
|
35
|
+
|
36
|
+
cursor(pos_undo_segment_array).name("undo_segment_array") do |c|
|
37
|
+
(0...1023).each do |slot|
|
38
|
+
page_number = c.name("slot[#{slot}]") {
|
39
|
+
Innodb::Page.maybe_undefined(c.get_uint32)
|
40
|
+
}
|
41
|
+
yield slot, page_number if page_number
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def dump
|
47
|
+
super
|
48
|
+
|
49
|
+
puts
|
50
|
+
puts "rollback segment header:"
|
51
|
+
pp rseg_header
|
52
|
+
|
53
|
+
puts
|
54
|
+
puts "undo segment array:"
|
55
|
+
each_undo_segment do |slot, page_number|
|
56
|
+
puts " #{slot}: #{page_number}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/innodb/page/trx_sys.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
# A specialized class for TRX_SYS pages, which contain various information
|
2
3
|
# about the transaction system within InnoDB. Only one TRX_SYS page exists in
|
3
4
|
# any given InnoDB installation, and it is page 5 of the system tablespace
|
@@ -35,14 +36,34 @@ class Innodb::Page::TrxSys < Innodb::Page
|
|
35
36
|
# which helps identify whether the structure is populated or not.
|
36
37
|
MYSQL_LOG_MAGIC_N = 873422344
|
37
38
|
|
39
|
+
def rsegs_array(cursor)
|
40
|
+
@rsegs_array ||= (0...256).to_a.inject([]) do |a, n|
|
41
|
+
cursor.name("slot[#{n}]") do |c|
|
42
|
+
slot = {
|
43
|
+
:space_id => c.name("space_id") {
|
44
|
+
Innodb::Page.maybe_undefined(c.get_uint32)
|
45
|
+
},
|
46
|
+
:page_number => c.name("page_number") {
|
47
|
+
Innodb::Page.maybe_undefined(c.get_uint32)
|
48
|
+
},
|
49
|
+
}
|
50
|
+
if slot[:space_id] && slot[:page_number]
|
51
|
+
a << slot
|
52
|
+
end
|
53
|
+
end
|
54
|
+
a
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
38
58
|
# Read a MySQL binary log information structure from a given position.
|
39
|
-
def mysql_log_info(offset)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
59
|
+
def mysql_log_info(cursor, offset)
|
60
|
+
cursor.peek(offset) do |c|
|
61
|
+
if c.name("magic_n") { c.get_uint32 } == MYSQL_LOG_MAGIC_N
|
62
|
+
{
|
63
|
+
:offset => c.name("offset") { c.get_uint64 },
|
64
|
+
:name => c.name("name") { c.get_bytes(100) },
|
65
|
+
}
|
66
|
+
end
|
46
67
|
end
|
47
68
|
end
|
48
69
|
|
@@ -53,10 +74,10 @@ class Innodb::Page::TrxSys < Innodb::Page
|
|
53
74
|
# Read a single doublewrite buffer information structure from a given cursor.
|
54
75
|
def doublewrite_page_info(cursor)
|
55
76
|
{
|
56
|
-
:magic_n => cursor.get_uint32,
|
77
|
+
:magic_n => cursor.name("magic_n") { cursor.get_uint32 },
|
57
78
|
:page_number => [
|
58
|
-
cursor.get_uint32,
|
59
|
-
cursor.get_uint32,
|
79
|
+
cursor.name("page[0]") { cursor.get_uint32 },
|
80
|
+
cursor.name("page[1]") { cursor.get_uint32 },
|
60
81
|
],
|
61
82
|
}
|
62
83
|
end
|
@@ -66,30 +87,52 @@ class Innodb::Page::TrxSys < Innodb::Page
|
|
66
87
|
DOUBLEWRITE_SPACE_ID_STORED_MAGIC_N = 1783657386
|
67
88
|
|
68
89
|
# Read the overall doublewrite buffer structures
|
69
|
-
def doublewrite_info
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
90
|
+
def doublewrite_info(cursor)
|
91
|
+
cursor.peek(pos_doublewrite_info) do |c_doublewrite|
|
92
|
+
c_doublewrite.name("doublewrite") do |c|
|
93
|
+
{
|
94
|
+
:fseg => c.name("fseg") { Innodb::FsegEntry.get_inode(@space, c) },
|
95
|
+
:page_info => [
|
96
|
+
c.name("group[0]") { doublewrite_page_info(c) },
|
97
|
+
c.name("group[1]") { doublewrite_page_info(c) },
|
98
|
+
],
|
99
|
+
:space_id_stored =>
|
100
|
+
(c.name("space_id_stored") { c.get_uint32 } ==
|
101
|
+
DOUBLEWRITE_SPACE_ID_STORED_MAGIC_N),
|
102
|
+
}
|
103
|
+
end
|
104
|
+
end
|
79
105
|
end
|
80
106
|
|
81
107
|
# Read the TRX_SYS headers and other information.
|
82
108
|
def trx_sys
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
109
|
+
@trx_sys ||= cursor(pos_trx_sys_header).name("trx_sys") do |c|
|
110
|
+
{
|
111
|
+
:trx_id => c.name("trx_id") { c.get_uint64 },
|
112
|
+
:fseg => c.name("fseg") {
|
113
|
+
Innodb::FsegEntry.get_inode(@space, c)
|
114
|
+
},
|
115
|
+
:rsegs => c.name("rsegs") {
|
116
|
+
rsegs_array(c)
|
117
|
+
},
|
118
|
+
:binary_log => c.name("binary_log") {
|
119
|
+
mysql_log_info(c, pos_mysql_binary_log_info)
|
120
|
+
},
|
121
|
+
:master_log => c.name("master_log") {
|
122
|
+
mysql_log_info(c, pos_mysql_master_log_info)
|
123
|
+
},
|
124
|
+
:doublewrite => doublewrite_info(c),
|
125
|
+
}
|
126
|
+
end
|
91
127
|
end
|
92
128
|
|
129
|
+
def trx_id; trx_sys[:trx_id]; end
|
130
|
+
def fseg; trx_sys[:fseg]; end
|
131
|
+
def rsegs; trx_sys[:rsegs]; end
|
132
|
+
def binary_log; trx_sys[:binary_log]; end
|
133
|
+
def master_log; trx_sys[:master_log]; end
|
134
|
+
def doublewrite; trx_sys[:doublewrite]; end
|
135
|
+
|
93
136
|
# Dump the contents of a page for debugging purposes.
|
94
137
|
def dump
|
95
138
|
super
|
@@ -100,4 +143,4 @@ class Innodb::Page::TrxSys < Innodb::Page
|
|
100
143
|
end
|
101
144
|
end
|
102
145
|
|
103
|
-
Innodb::Page::SPECIALIZED_CLASSES[:TRX_SYS] = Innodb::Page::TrxSys
|
146
|
+
Innodb::Page::SPECIALIZED_CLASSES[:TRX_SYS] = Innodb::Page::TrxSys
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
class Innodb::Page::UndoLog < Innodb::Page
|
4
|
+
def pos_undo_page_header
|
5
|
+
pos_fil_header + size_fil_header
|
6
|
+
end
|
7
|
+
|
8
|
+
def size_undo_page_header
|
9
|
+
2 + 2 + 2 + Innodb::List::NODE_SIZE
|
10
|
+
end
|
11
|
+
|
12
|
+
def pos_undo_segment_header
|
13
|
+
pos_undo_page_header + size_undo_page_header
|
14
|
+
end
|
15
|
+
|
16
|
+
def size_undo_segment_header
|
17
|
+
2 + 2 + Innodb::FsegEntry::SIZE + Innodb::List::BASE_NODE_SIZE
|
18
|
+
end
|
19
|
+
|
20
|
+
def pos_undo_logs
|
21
|
+
pos_undo_segment_header + size_undo_segment_header
|
22
|
+
end
|
23
|
+
|
24
|
+
UNDO_PAGE_TYPES = {
|
25
|
+
1 => :insert,
|
26
|
+
2 => :update,
|
27
|
+
}
|
28
|
+
|
29
|
+
UNDO_SEGMENT_STATES = {
|
30
|
+
1 => :active,
|
31
|
+
2 => :cached,
|
32
|
+
3 => :to_free,
|
33
|
+
4 => :to_purge,
|
34
|
+
5 => :prepared,
|
35
|
+
}
|
36
|
+
|
37
|
+
def undo_page_header
|
38
|
+
@undo_page_header ||=
|
39
|
+
cursor(pos_undo_page_header).name("undo_page_header") do |c|
|
40
|
+
{
|
41
|
+
:type => c.name("type") { UNDO_PAGE_TYPES[c.get_uint16] },
|
42
|
+
:latest_log_record_offset => c.name("latest_log_record_offset") { c.get_uint16 },
|
43
|
+
:free_offset => c.name("free_offset") { c.get_uint16 },
|
44
|
+
:page_list_node => c.name("page_list") { Innodb::List.get_node(c) },
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def prev_address
|
50
|
+
undo_page_header[:page_list_node][:prev]
|
51
|
+
end
|
52
|
+
|
53
|
+
def next_address
|
54
|
+
undo_page_header[:page_list_node][:next]
|
55
|
+
end
|
56
|
+
|
57
|
+
def undo_segment_header
|
58
|
+
@undo_segment_header ||=
|
59
|
+
cursor(pos_undo_segment_header).name("undo_segment_header") do |c|
|
60
|
+
{
|
61
|
+
:state => c.name("state") { UNDO_SEGMENT_STATES[c.get_uint16] },
|
62
|
+
:last_log_offset => c.name("last_log_offset") { c.get_uint16 },
|
63
|
+
:fseg => c.name("fseg") { Innodb::FsegEntry.get_inode(@space, c) },
|
64
|
+
:page_list => c.name("page_list") {
|
65
|
+
Innodb::List::UndoPage.new(@space, Innodb::List.get_base_node(c))
|
66
|
+
},
|
67
|
+
}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def undo_log(pos)
|
72
|
+
Innodb::UndoLog.new(self, pos)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Dump the contents of a page for debugging purposes.
|
76
|
+
def dump
|
77
|
+
super
|
78
|
+
|
79
|
+
puts "undo page header:"
|
80
|
+
pp undo_page_header
|
81
|
+
puts
|
82
|
+
|
83
|
+
puts "undo segment header:"
|
84
|
+
pp undo_segment_header
|
85
|
+
puts
|
86
|
+
|
87
|
+
puts "last undo log:"
|
88
|
+
if undo_segment_header[:last_log_offset] != 0
|
89
|
+
undo_log(undo_segment_header[:last_log_offset]).dump
|
90
|
+
end
|
91
|
+
puts
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
Innodb::Page::SPECIALIZED_CLASSES[:UNDO_LOG] = Innodb::Page::UndoLog
|